アプリ版:「スタンプのみでお礼する」機能のリリースについて

log4net.dllの参照を1クラスのみに絞り、他のログ出力有りのクラス、関数では、参照する1クラスを経由してLogManager.GetLogger関数で実体化されたILogを取得し、ログ出力を行いたく考えています。
そのため、dynamic型で受けて使用するようにしてみたのですが、出力結果としてフォーマットの%Cが「?」、%Mが「CallSite.Target」と出てしまい、完全修飾子名、メソッド名の出力ができません。
同じ流れのまま、dynamic型をやめ、ILogで受けるようにしてみると、%C、%Mが正常に出ていることが確認できました。
dynamic型から別の型に変えれば解決するものなのか、何かしらの変換を掛ければ使えるのかなどがわかりませんでした。
ILog型は基本的に1クラス1個管理が望ましいような話も見かけたのですが、静的参照しているlog4netのFileNotFoundExceptionなどをキャッチしたい都合もあり、クラス分けを試みた次第です。
どうかご教授の程、お願いいたします。
<環境>
VisualStudio2015 C#
log4netバージョン:2.0.8

<サンプルコード>
(メインのcsファイル)
public class main()
{
public void start()
{
dynamic logger = logMaster.getLogger(MethodBase.GetCurrentMethod().DeclaringType);
logger.Info("開始");
・・・省略・・・
logger.Info("終了");
}
}
(同プロジェクト内のlog4net参照用csファイル)
public class logMaster()
{
public static dynamic getLogger(Type type)
{
dynamic logger = null;
try
{
logger = createLogger(type);
return logger;
}
catch(FileNotFoundException)
{
return logger;
}
}

private static dynamic createLogger(Type type)
{
return LogManage.GetLogger(type);
}
}

<ログフォーマット>
"%d [%-5level] - %t %C %M %m%n"

A 回答 (5件)

log4net.dllがない時に、実装側が意識することなくコードしたいなら、log4netを利用するクラスを更にラッピングして処理を委譲する形にならできそうですね。


ラッピングするので、必要な処理を全部記述しなければなりません。

【MyLog4Net.cs】
using log4net;
using System;

namespace ClassLibrary1
{
class MyLog4Net
{
private ILog log = null;

private MyLog4Net(ILog log)
{
this.log = log;
}

public static MyLog4Net GetLogger(Type type)
{
var log = LogManager.GetLogger(type);
return new MyLog4Net(log);
}

public void Info(string message)
{
this.log.Logger.Log(type, log4net.Core.Level.Info, message, null);
}
}
}

【MyLog.cs】
using System;
using System.Collections.Generic;
using System.IO;

namespace ClassLibrary1
{
class MyLog
{
private static Dictionary<Type, MyLog> logList = new Dictionary<Type, MyLog>();
private MyLog4Net log = null;

private MyLog(Type type)
{
try
{
this.log = MyLog4Net.GetLogger(type);

}
catch (FileNotFoundException)
{
// 意図的に何もしない
}
}

public static MyLog GetLogger(Type type)
{
if (MyLog.logList.ContainsKey(type))
{
return MyLog.logList[type];
}

MyLog log = new MyLog(type);
MyLog.logList.Add(type, log);
return log;
}

public void Info(string message)
{
if (this.log == null)
{
return;
}
this.log.Info(typeof(MyLog), message);
}
}
}

【Main.cs】
namespace ClassLibrary1
{
public class Main
{
public void Start()
{
MyLog log = MyLog.GetLogger(typeof(Main));
log.Info("hoge");

MyLog log2 = MyLog.GetLogger(typeof(Main));
log.Info("fuga");
}
}
}

【Program.cs】
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
(new ClassLibrary1.Main()).Start();
}
}
}

ちなみにdynamicで行おうとすると、内部的にはCallSiteというクラスによって処理されるため、%C, %Mがそのように出てしまいます。
調べてみましたが、素のままではこれは解決できそうにはありませんでした。

log4net.dllが得られなかった時にも処理を継続したいと考えており、上記方法で対応することが仰々しくなって煩雑なようであれば、dynamicによる対応でもいいかなとは思います。
でもその時は、%C, %Mは諦めて、メッセージに、発生したクラスおよびメソッドを記述する形を取るしかなさそうですね。
    • good
    • 0
この回答へのお礼

ラッピングは自分も考えましたが、設計・実装の手間的にどうしたものか。。。って感じでしたね。
再考してみようとは思います。
とりあえず%C、%Mを%mで収束する方向で考えてみます。
長々とありがとうございました。

お礼日時:2018/12/24 12:19

> 作成してるのは、アプリではなくDLLになります。


> そのため、私の方のサンプルのstart関数が、インターフェース関数になり、インターフェース
> 関数でもログを出すことから、File.Existsの事前チェックができません。

ログが出力できる状態にない場合があるのに、ログを出力しようとするんですか?
また、実際にlog4net.dllが得られず例外となった時、そのDLLの呼び出したメソッドは、ログを出力しないだけでそれ以外の動作は正常に動作させたいとか、そういうことなのでしょうか?
    • good
    • 0

特殊な動作をその場で必要としないなら、StartupValidatorのようなクラスはいらないかもです。

    • good
    • 0
この回答へのお礼

サンプルコードありがとうございます。
ただ、説明不足でした。すみません。
作成してるのは、アプリではなくDLLになります。
そのため、私の方のサンプルのstart関数が、インターフェース関数になり、インターフェース関数でもログを出すことから、File.Existsの事前チェックができません。
start関数がFile.Existsを敷いても、ILogを使った時点でFile.Exists前にFileNotFoundがアプリに返されてしまいますし・・・。
DLLは完全提供品となり、アプリは別口の開発となり、本主旨としては「アプリ側への例外を避ける」になります。

お礼日時:2018/12/21 07:21

> 本目的としましては、外部ライブラリが存在しない場合は、専用のエラーコードを呼び出し元


> に返すというところにあります。

これは、参照すべきlog4net.dllが配置場所に存在するか、という検証を事前に行い、NGなら例外を発行すれば済むのではないでしょうか?
とどのつまり、log4netが必要になるより以前に、ファイルがあることが担保できればいいわけなので。

1.エントリポイントの走行で、あるべきdllを検証する。
2.検証が通ったら、メインクラスをインスタンス化するなどして処理を行う。

みたく。
参照設定しているdllは、初めて使われるクラスなどが利用された時に初めて読み込みに行きます。
そのため、dllを必要としていない間は、対象dllに対するエラーは発生しません。
なので、なんでもかんでもエントリポイントだかでやろうとしているのが悪いだけでは。

【Program.cs】
using System;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
try
{
StartupValidator.Validate();
MainClass.Start();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}

【StartupValidator.cs】
using System.IO;

namespace ConsoleApp1
{
static class StartupValidator
{
public static void Validate()
{
var directory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var filePath = Path.Combine(directory, @"log4net.dll");
if (!File.Exists(filePath))
{
throw new FileNotFoundException("log4net.dllが存在しない");
}
}
}
}

【MainClass.cs】
using log4net;

namespace ConsoleApp1
{
static class MainClass
{
public static void Start()
{
ILog logger = LogManager.GetLogger(typeof(Program));
logger.Info("hoge");
}
}
}
    • good
    • 0

そもそも論になりますが・・・



> log4net.dllの参照を1クラスのみに絞り、他のログ出力有りのクラス、関数では、参照する1
> クラスを経由して

それらがすべて同一プロジェクトにあるのならば、そうする必然性がわかりません。
また、別プロジェクトであったとしても、ラッピングを要するならば、ラッピングクラスが存在するプロジェクトを参照設定する必要があり、log4net.dllを参照設定することと差がありません。

ILogクラスまたはラッピングクラスしか返ってきようがないのに、インテリセンスも効かなくなり、dynamicの用途として間違っていると思います。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございますm(__)m
仰る事は凄く分かります・・・。
本目的としましては、外部ライブラリが存在しない場合は、専用のエラーコードを呼び出し元に返すというところにあります。
元々は、mainクラス側にILogのメンバ変数を作り、コンストラクタでGetLogger関数を呼んでいたのですが、コンストラクタでは戻り値を返せません。
そして、メンバ変数を辞め、start関数でILogのローカル変数を作る形にした場合は、1ステップも進むこと無く、catchができません。
そのため、どこかでcatchを敷いて、ようやくdll参照に持っていこうとした結果がサンプルです。
GetLogger関数は、あいにく静的メンバということもあり、動的DLL読み込みでインスタンスを作って、という訳にも行かないなどもあり、クラスを別途としようとした結果となります。

お礼日時:2018/12/20 20:04

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!