簡體   English   中英

在自定義Log4Net appender中使用IoC

[英]Using IoC in a custom Log4Net appender

不幸的是,當我使用Log4Net搜索涉及Unity或IoC的任何內容時,我已經搜索了很長一段時間,我得到的唯一結果是如何通過IoC自動填充ILog 不是我想要做的。

我有一個自定義Appender,通過WCF將數據傳輸到服務器。 我想要做的是傳遞一個工廠方法,最好是由Unity生成的方法,為我創建WCF客戶端類,這樣我就可以在單元測試期間替換存根的真實客戶端類。 問題是我找不到任何關於如何將參數傳遞給自定義log4net appender的解釋。

以下是我通常使用Unity實現此操作的方法。

public class WcfAppender : BufferingAppenderSkeleton
{
    public WcfAppender(Func<ClientLoggerClient> loggerClientFactory) //How do I get this Func passed in?
    {
        LoggerClientFactory = loggerClientFactory;
    }


    private readonly Func<ClientLoggerClient> LoggerClientFactory; 
    private static readonly ILog Logger = LogManager.GetLogger(typeof(WcfAppender));
    private static readonly string LoggerName = typeof(WcfAppender).FullName;

    public override void ActivateOptions()
    {
        base.ActivateOptions();
        this.Fix = FixFlags.All;
    }

    protected override bool FilterEvent(LoggingEvent loggingEvent)
    {
        if (loggingEvent.LoggerName.Equals(LoggerName))
            return false;
        else
            return base.FilterEvent(loggingEvent);
    }


    protected override void SendBuffer(LoggingEvent[] events)
    {
        try
        {
            var client = LoggerClientFactory();
            try
            {
                client.LogRecord(events.Select(CreateWrapper).ToArray());
            }
            finally
            {
                client.CloseConnection();
            }
        }
        catch (Exception ex)
        {
            Logger.Error("Error sending error log to server", ex);
        }
    }

    private ErrorMessageWrapper CreateWrapper(LoggingEvent arg)
    {
        var wrapper = new ErrorMessageWrapper();

        //(Snip)

        return wrapper;
    }
}

但是我不調用container.Resolve<WcfAppender>()它在log4net庫中調用new WcfAppender() 如何告訴log4net庫使用new WcfAppender(factoryGeneratedFromUnity)呢?

我認為samy是對的 ,這是我如何解決它的實現。

在我初始化Unity容器之前調用了ActivateOptions() ,所以為了安全起見我只是將工廠方法的檢索放在SendBuffer方法中。 我最終使用LogManager.GetRepository().Properties屬性來存儲工廠對象。

public class WcfAppender : BufferingAppenderSkeleton
{
    private static readonly ILog Logger = LogManager.GetLogger(typeof(WcfAppender));
    private static readonly string LoggerName = typeof(WcfAppender).FullName;

    public override void ActivateOptions()
    {
        base.ActivateOptions();
        this.Fix = FixFlags.All;
    }

    protected override bool FilterEvent(LoggingEvent loggingEvent)
    {
        if (loggingEvent.LoggerName.Equals(LoggerName))
            return false;
        else
            return base.FilterEvent(loggingEvent);
    }


    protected override void SendBuffer(LoggingEvent[] events)
    {
        try
        {
            var clientFactory = log4net.LogManager.GetRepository().Properties["ClientLoggerFactory"] as Func<ClientLoggerClient>;
            if (clientFactory != null)
            {
                var client = clientFactory();
                try
                {
                    client.LogRecord(events.Select(CreateWrapper).ToArray());
                }
                finally
                {
                    client.CloseConnection();
                }
            }
        }
        catch (Exception ex)
        {
            Logger.Error("Error sending error log to server", ex);
        }
    }

    private ErrorMessageWrapper CreateWrapper(LoggingEvent arg)
    {
        var wrapper = new ErrorMessageWrapper();

        //SNIP...

        return wrapper;
    }

}

然后我創建工廠並在程序啟動期間存儲它。

static class Program
{
    private static readonly ILog Logger = LogManager.GetLogger(typeof(Program));

    static void Main(string[] args)
    {
        log4net.Util.SystemInfo.NullText = String.Empty;

        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

        Logger.Debug("Service Starting");
        using (var container = new UnityContainer())
        {
            var debugMode = args.Contains("--debug", StringComparer.InvariantCultureIgnoreCase);

            BaseUnityConfiguration.Configure(container, debugMode);
            LogManager.GetRepository().Properties["ClientLoggerFactory"] = container.Resolve<Func<ClientLoggerClient>>();

            //...

從我在代碼中看到的,appender是由ParseAppender方法中的XmlHierarchyConfigurator類創建的:

protected IAppender ParseAppender(XmlElement appenderElement)
{
    string attribute = appenderElement.GetAttribute("name");
    string attribute2 = appenderElement.GetAttribute("type");
    // <snip>
    try
    {
        IAppender appender = (IAppender)Activator.CreateInstance(SystemInfo.GetTypeFromString(attribute2, true, true));
        appender.Name = attribute;
        // <snip>

該方法通過調用IOptionHandler接口實現的方法完成加載appender(您已經在appender中使用它,因為您的祖先樹中有AppenderSkeleton類)

IOptionHandler optionHandler = appender as IOptionHandler;
if (optionHandler != null)
{
    optionHandler.ActivateOptions();
}

log4net未知的任何xml參數都將推送到具有相同名稱的屬性。 因此,可以完全從xml配置文件配置appender,並通過讓appender實現IOptionHandler啟動所需的任何特定行為

您必須將Func<ClientLoggerClient>傳遞給appender的唯一方法(重建自定義log4net)是使用您在Apateder中調用的靜態事件, ActivateOptions()方法將由您的解析方法獲取的事件選擇; 該事件可以包含來自appender的xml配置的一些自定義,因此您可以基於此來路由解析。 讓事件給你的appender提供足夠的Func<ClientLoggerClient> ,你就可以了。

我能夠在創建后通過Unity容器運行appender實例:

ILog logger = LogManager.GetLogger("My Logger");
IAppender[] appenders = logger.Logger.Repository.GetAppenders();
foreach (IAppender appender in appenders)
{
    container.BuildUp(appender.GetType(), appender);
}

container.RegisterInstance(logger);

BuildUp()方法將填充appender類中的injection屬性:

public class LogDatabaseSaverAppender : AppenderSkeleton
{
    [Dependency]
    public IContextCreator ContextCreator { get; set; }

    ...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM