log4net 使用 class 名称记录器记录到多个定义的附加程序

log4net logging to multiple defined appenders using class name loggers

我想记录到多个文件(仅 2 个不同的文件),但保留记录器名称与 class 名称相同,通常是这样做的:

private static readonly log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, isReadOnly);

我想保留 class 名称,以便将其输出到记录器。

问题是,我需要根据调用的 wcf 方法(这是一个 wcf 服务)在 appender 之间动态切换。我尝试了各种 log4net 配置设置,包括这个解决方案:

Configure Log4net to write to multiple files

但是许多解决方案都有我不想要的预定义记录器名称。我也不想在 log4net 配置中硬编码所有不同的 class 名称并引用那些特定的记录器(维护噩梦)。

我要找的是这样的:

public static ILog GetLogger(Type classType, bool shouldLogToFile2)
{
    if (shouldLogToFile2)
    {
        return LogManager.GetLogger(classType, file2Appender); // log to file2.log (this statement doesn't compile)
    }
    else
    {
        return LogManager.GetLogger(classType, file1Appender); // log to file1.log (this statement doesn't compile)
    }
}

然后这样称呼它:

ILog log = GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, true);
log.Info("This should log to file2.log");

如果你想拥有多个同名但使用不同附加程序的记录器,那么你必须设置多个存储库来包含它们。

正如the (somewhat scanty) documentation on repositories中所说:

Named logging repositories can be created using the LogManager.CreateRepository method. The repository for can be retrieved using the LogManager.GetRepository method. A repository created in this way will need to be configured programmatically.

然而,这并不意味着实际配置必须在代码中,而是您应该这样做:

// or wherever
string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; 

var repository = log4net.LogManager.CreateRepository("file1repo");
log4net.XmlConfigurator.ConfigureAndWatch(repository, new FileInfo(configPath));

repository = log4net.LogManager.CreateRepository("file2repo");
log4net.XmlConfigurator.ConfigureAndWatch(repository, new FileInfo(configPath));

然后您将编写代码来修改 'file2' appender 以写入 file2:

var appender = LogManager.GetRepository("file2repo").GetAppenders()
                         .OfType<RollingFileAppender>().Single();

appender.File = "file2.log";
appender.ActivateOptions(); 

您的代码如下所示:

public static ILog GetLogger(Type classType, bool shouldLogToFile2)
{
    return LogManager.GetLogger(shouldLogToFile2 ? "file2repo" : "file1repo", classType);
}

玩了几个小时之后,这就是我的想法。

想法是在每个记录器名称前加上 "File2" 作为那些应该附加到 file2.log 的记录器的前缀。然后 return 根据传入的布尔值查找您要查找的适当记录器。

为了防止将日志消息写入两个文件,我决定将所有记录器从根记录器中拉出(请参阅配置)并在代码中显式使用每个附加程序。

配置:

<log4net>       
    <appender name="File1Appender" type="log4net.Appender.RollingFileAppender">
      <file value="c:\file1.log" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="10" />
      <maximumFileSize value="50MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5p %logger %message%newline" />
      </layout>
    </appender>

    <appender name="File2Appender" type="log4net.Appender.RollingFileAppender">
      <file value="c:\file2.log" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="10" />
      <maximumFileSize value="50MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5p %logger %message%newline" />
      </layout>
    </appender>

    <root>
      <level value="ALL" />      
    </root>
    <logger name="LoggerF1">
      <appender-ref ref="File1Appender" />
    </logger>
    <logger name="LoggerF2">
      <appender-ref ref="File2Appender" />
    </logger>
</log4net>

代码:

public static ILog GetLogger(Type classType, bool shouldLogToFile2 )
{
    ILog newLogger = log4net.LogManager.GetLogger(shouldLogToFile2 ? "File2." + classType.ToString() : classType.ToString());
    Logger loggerInstance = (Logger) newLogger.Logger;
    // if the newLogger doesn't have an appender (which it won't the first time it's created) then add the appropriate appender
    if (loggerInstance.Appenders.Count == 0)
    {           
        IAppender[] appenders = LogManager.GetRepository().GetAppenders();
        IAppender appender = appenders.SingleOrDefault(a => a.Name == (shouldLogToFile2 ? "File2Appender" : "File1Appender"));
        loggerInstance.AddAppender(appender);           
    }
    return newLogger;
}

这样就全部在配置中完成了,没有在代码中重复配置。我希望两个附加程序使用相同的日志级别,这就是为什么我将它放在根记录器中而不是每个单独的记录器中。