动态重新配置 Log4Net

Posted

技术标签:

【中文标题】动态重新配置 Log4Net【英文标题】:Dynamically reconfigure Log4Net 【发布时间】:2010-10-18 19:07:30 【问题描述】:

我正在寻找有关在我的 ASP.NET 应用程序中 日志记录级别的最佳方法的提示。我通常使用一个简单的配置,根记录器定义默认的日志记录级别,例如

<log4net>
    <root>
    <level value="INFO" />
    <appender-ref ref="..." />
    <appender-ref ref="..." />
    ... etc ...     
    </root>
    ... etc

并且可能有多个附加程序,每个附加程序都有过滤器来定义它们使用的日志记录级别。

    我想做的第一件事是允许管理员连接到管理页面,使他们能够 (a) 查看根记录器的当前级别和 (b) 动态更改它。我不想使用“ConfigureAndWatch”并写入磁盘上的配置文件,因为我不希望这些更改在应用程序被回收时持续存在。

    接下来我想更进一步,在管理页面上能够显示一个 TreeView,其中包含应用程序中存在的所有当前 Logger 及其当前日志记录级别。并允许管理员能够在层次结构的任何级别有选择地更改日志记录级别。

我们的想法是创建一个通用管理页面,我可以将其放入我的所有应用程序中,允许管理员有选择地动态启用 DEBUG 级别的日志记录以进行故障排除。

我发现 Log4Net API 有点混乱,任何人都可以指出示例或展示实现此目的的最佳方法。

更新:

两个答案都一样好,所以我接受了第一个 - 谢谢。重复一下,我可以按如下方式获取所有当前记录器:

foreach (log4net.ILog log in log4net.LogManager.GetCurrentLoggers())

    log4net.Repository.Hierarchy.Logger logger = 
         (log4net.Repository.Hierarchy.Logger)log.Logger;
    Debug.WriteLine(
        String.Format("0 Parent 1 Level 2 EffectiveLevel 3<br>",
        logger.Name,
        logger.Parent.Name,
        logger.Level == null ? "<null>" : logger.Level.Name,
        logger.EffectiveLevel
        )
        );

EffectiveLevel 是有效级别 - 如果后者不为 null,则与 Level 相同,否则从父级继承。

上面返回的至少一个记录器将根记录器作为父记录器,这使我能够获得对根记录器的引用。

通过以上内容,应该可以重建记录器层次结构。

更新 2

再次感谢。我已经实现了一个 ASP.NET 服务器控件,它在带有复选框的 TreeView 中显示记录器层次结构,并允许用户在层次结构中的任何节点上动态更改日志记录级别。效果很好,我将把它放在我所有 ASP.NET Web 和 Web 服务应用程序的管理页面上!

【问题讨论】:

【参考方案1】:

您是否正在寻找这样的东西(未经测试的代码):

foreach (ILog logger in log4net.LogManager.GetCurrentLoggers())

  ((log4net.Repository.Hierarchy.Logger)logger).Level = 
      log4net.Core.Level.Error;

您显然可以以相同的方式提取记录器名称等。

【讨论】:

我测试了上面的代码,发现我需要遍历所有的存储库并仔细检查类型。 foreach (var repo in LogManager.GetAllRepositories()) foreach (var logger in repo.GetCurrentLoggers().OfType()) logger.Level = Level; 仅供参考:我曾经在使用动态 log4net 配置 API 时遇到并发问题 - 它似乎不是线程安全的。 这是什么版本的 log4net?我没有看到任何可转换为 Repository.Hierarchy.Logger 的 ILog 实现 刚刚完成此操作后,我需要使用logger.Logger 才能使演员工作。【参考方案2】:

我已经成功地以编程方式更改了 log4net 记录器的记录级别,但如何通过公共 API 进行更改并不明显。鉴于此记录器:

private readonly log4net.ILog mylogger;

您必须执行以下花哨的步骤才能将其设置为 Debug:

((log4net.Repository.Hierarchy.Logger)mylogger.Logger).Level =
 log4net.Core.Level.Debug;

对于某些情况——我不知道是什么导致了这个更复杂的要求——你可能需要遵循文章log4net and changing the logger levels中显示的额外步骤。

【讨论】:

【参考方案3】:

可能还不是你想要的:

using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Config;
using NUnit.Framework;

namespace ExampleConsoleApplication

  enum DebugLevel : int
   
    Fatal_Msgs = 0 , 
    Fatal_Error_Msgs = 1 , 
    Fatal_Error_Warn_Msgs = 2 , 
    Fatal_Error_Warn_Info_Msgs = 3 ,
    Fatal_Error_Warn_Info_Debug_Msgs = 4 
  


    class TestClass
    

        private static readonly ILog logger =
                 LogManager.GetLogger ( typeof ( TestClass ) );


        static void Main ( string[] args )
        
      TestClass objTestClass = new TestClass ();

            Console.WriteLine ( " START " );

      int shouldLog = 4; //CHANGE THIS FROM 0 TO 4 integer to check the functionality of the example
      //0 -- prints only FATAL messages 
      //1 -- prints FATAL and ERROR messages 
      //2 -- prints FATAL , ERROR and WARN messages 
      //3 -- prints FATAL  , ERROR , WARN and INFO messages 
      //4 -- prints FATAL  , ERROR , WARN , INFO and DEBUG messages 

      string srtLogLevel = String.Empty ; 
      switch (shouldLog)
      
        case (int)DebugLevel.Fatal_Msgs :
          srtLogLevel = "FATAL";
          break;
        case (int)DebugLevel.Fatal_Error_Msgs:
          srtLogLevel = "ERROR";
          break;
        case (int)DebugLevel.Fatal_Error_Warn_Msgs :
          srtLogLevel = "WARN";
          break;
        case (int)DebugLevel.Fatal_Error_Warn_Info_Msgs :
          srtLogLevel = "INFO"; 
          break;
        case (int)DebugLevel.Fatal_Error_Warn_Info_Debug_Msgs :
          srtLogLevel = "DEBUG" ;
          break ;
        default:
          srtLogLevel = "FATAL";
          break;
      

        objTestClass.SetLogingLevel ( srtLogLevel );


      objTestClass.LogSomething ();


            Console.WriteLine ( " END HIT A KEY TO EXIT " );
            Console.ReadLine ();
             //eof method 

    /// <summary>
    /// Activates debug level 
    /// </summary>
    /// <sourceurl>http://geekswithblogs.net/rakker/archive/2007/08/22/114900.aspx</sourceurl>
    private void SetLogingLevel ( string strLogLevel )
    
     string strChecker = "WARN_INFO_DEBUG_ERROR_FATAL" ;

      if (String.IsNullOrEmpty ( strLogLevel ) == true || strChecker.Contains ( strLogLevel ) == false)
        throw new Exception ( " The strLogLevel should be set to WARN , INFO , DEBUG ," );



      log4net.Repository.ILoggerRepository[] repositories = log4net.LogManager.GetAllRepositories ();

      //Configure all loggers to be at the debug level.
      foreach (log4net.Repository.ILoggerRepository repository in repositories)
      
        repository.Threshold = repository.LevelMap[ strLogLevel ];
        log4net.Repository.Hierarchy.Hierarchy hier = (log4net.Repository.Hierarchy.Hierarchy)repository;
        log4net.Core.ILogger[] loggers = hier.GetCurrentLoggers ();
        foreach (log4net.Core.ILogger logger in loggers)
        
          ( (log4net.Repository.Hierarchy.Logger)logger ).Level = hier.LevelMap[ strLogLevel ];
        
      

      //Configure the root logger.
      log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository ();
      log4net.Repository.Hierarchy.Logger rootLogger = h.Root;
      rootLogger.Level = h.LevelMap[ strLogLevel ];
    

    private void LogSomething ()
    
      #region LoggerUsage
      DOMConfigurator.Configure (); //tis configures the logger 
      logger.Debug ( "Here is a debug log." );
      logger.Info ( "... and an Info log." );
      logger.Warn ( "... and a warning." );
      logger.Error ( "... and an error." );
      logger.Fatal ( "... and a fatal error." );
      #endregion LoggerUsage

    
     //eof class 

 //eof namespace 






#region TheAppConfig
/*
 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="log4net"
                 type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
    </configSections>
    <log4net>
        <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
            <param name="File" value="LogTest2.txt" />
            <param name="AppendToFile" value="true" />
            <layout type="log4net.Layout.PatternLayout">
                <param name="Header" value="[Header] \r\n" />
                <param name="Footer" value="[Footer] \r\n" />
                <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n" />
            </layout>
        </appender>

        <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
            <mapping>
                <level value="ERROR" />
                <foreColor value="White" />
                <backColor value="Red, HighIntensity" />
            </mapping>
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%thread] %-5level %logger [%propertyNDC] - %message%newline" />
            </layout>
        </appender>


        <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
            <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.2.10.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
            <connectionString value="data source=ysg;initial catalog=DBGA_DEV;integrated security=true;persist security info=True;" />
            <commandText value="INSERT INTO [DBGA_DEV].[ga].[tb_Data_Log] ([Date],[Thread],[Level],[Logger],[Message]) VALUES (@log_date, @thread, @log_level, @logger, @message)" />

            <parameter>
                <parameterName value="@log_date" />
                <dbType value="DateTime" />
                <layout type="log4net.Layout.PatternLayout" value="%dateyyyy'-'MM'-'dd HH':'mm':'ss'.'fff" />
            </parameter>
            <parameter>
                <parameterName value="@thread" />
                <dbType value="String" />
                <size value="255" />
                <layout type="log4net.Layout.PatternLayout" value="%thread" />
            </parameter>
            <parameter>
                <parameterName value="@log_level" />
                <dbType value="String" />
                <size value="50" />
                <layout type="log4net.Layout.PatternLayout" value="%level" />
            </parameter>
            <parameter>
                <parameterName value="@logger" />
                <dbType value="String" />
                <size value="255" />
                <layout type="log4net.Layout.PatternLayout" value="%logger" />
            </parameter>
            <parameter>
                <parameterName value="@message" />
                <dbType value="String" />
                <size value="4000" />
                <layout type="log4net.Layout.PatternLayout" value="%messag2e" />
            </parameter>
        </appender>
        <root>
            <level value="INFO" />
            <appender-ref ref="LogFileAppender" />
            <appender-ref ref="AdoNetAppender" />
            <appender-ref ref="ColoredConsoleAppender" />
        </root>
    </log4net>
</configuration>
 */
#endregion TheAppconfig

//this is the xml added replace here your log4net and Nunit paths
//<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
        //  <SpecificVersion>False</SpecificVersion>
        //  <HintPath>..\..\..\Log4Net\log4net-1.2.10\bin\net\2.0\release\log4net.dll</HintPath>
        //</Reference>
        //<Reference Include="nunit.framework, Version=2.4.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL" />

【讨论】:

以上是关于动态重新配置 Log4Net的主要内容,如果未能解决你的问题,请参考以下文章

kill -HUP pid 更改配置后不重新启动服务,动态更新配置文件

动态重新配置 Log4Net

Vue 不重新打包,动态加载全局配置的实现过程

Vue 不重新打包,动态加载全局配置的实现过程

Vue 不重新打包,动态加载全局配置的实现过程

如何利用Confd与Etcd对CoreOS中的服务进行动态重新配置