运行时更改log4net文件名

问题描述:

我有一个启动较小程序或任务的程序,现在我希望每个任务都动态地记录到它们自己的文件名,最好使用任务名称。 我已经挖掘了一点,似乎有一个漂亮的%property事情,你可以使用Log4Net。所以我试了一下。但似乎Log4Net在初始化之后没有为此获取更改。运行时更改log4net文件名

我写了一个小测试程序来玩。 我宁愿只构建windsor容器一次,但是Log4Net不会“重新初始化”,并创建一个新的日志文件。所以现在我必须在开始任务之前构建容器,以确保我在日志文件中获得了正确的文件名。

有谁知道是否有更好/更聪明的方法来改变运行时的日志文件名?

这里是我的testcode:

public class Program 
{ 
    static void Main(string[] args) 
    { 
     GlobalContext.Properties["LogName"] = "default"; 
     XmlConfigurator.ConfigureAndWatch(new FileInfo("log4net.config")); 

     while (true) 
     { 
      Console.WriteLine(); 
      Console.Write("> "); 
      string cmd = (Console.ReadLine() ?? string.Empty); 
      if (string.IsNullOrWhiteSpace(cmd)) continue; 
      if (cmd.ToLower() == "exit") Environment.Exit(0); 

      ExecuteTask(Regex.Split(cmd, @"\s+")); 
     } 
    } 

    private static void ExecuteTask(string[] args) 
    { 
     //This changes the filename Log4Net logs to, but Log4Net only picks it up if I rebuild my container 
     GlobalContext.Properties["LogName"] = args[0]; 

     //This should only be run once. No real need to build the container before each run, except to reinitialize Log4Net 
     var container = BuildContainer(); 
     try 
     { 
      var task = container.Resolve<ITask>(args[0]); 
      task.DoLog(); 
     } 
     catch (Exception) 
     { 
      // This doesn't work as expected, Log4Net logs to the file specified outside of the trycatch 
      GlobalContext.Properties["LogName"] = "default"; 
      var logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 
      logger.Info("Could not find "+args[0]); 
     } 
    } 


    private static WindsorContainer BuildContainer() 
    { 
     Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); 

     var container = new WindsorContainer(); 
     container.Install(
      FromAssembly.This()); 

     return container; 
    } 
} 

public interface ITask 
{ 
    void DoLog(); 
} 

public class TwoTask : ITask 
{ 
    private readonly ILogger _logger; 

    public TwoTask(ILogger logger) 
    { 
     _logger = logger; 
    } 

    public void DoLog() 
    { 
     _logger.Info("Hello!"); 
    } 
} 

public class OneTask : ITask 
{ 
    private readonly ILogger _logger; 

    public OneTask(ILogger logger) 
    { 
     _logger = logger; 
    } 

    public void DoLog() 
    { 
     _logger.Info("Hello!"); 
    } 
} 

public class Installer : IWindsorInstaller 
{ 
    public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 
     container.Register(Classes.FromThisAssembly() 
            .BasedOn(typeof(ITask)) 
            .Configure(c => c.LifeStyle.Transient.Named(c.Implementation.Name))); 
     container.AddFacility<LoggingFacility>(f => f.UseLog4Net("log4net.config")); 
    } 
} 

Nugets使用: Castle.Core,Castle.Core-log4net的,Castle.LoggingFacility,Castle.Windsor

在你的appender在log4net.config定义,你可以指定一个输出格式为文件名。例如<file value="fixedName"/>。您还可以使用<staticLogFileName value="false"/>以及一个图案(例如,

<file type="log4net.Util.PatternString" value="somePrefix-%property{SomePropertyName}-%utcdate{yyMMdd}.trace.log" />. 

这将路线基于日期不同的文件和"SomePropertyName"财产。

你将不得不在你的任务中设置这个propery,如log4net.ThreadContext.Properties["SomePropertyName"] = "someValue";

我想一个好的开关将是生成记录事件(%t%thread)的线程的名称。如果没有名称可用,则输出线程号。但是你可以在你的ITask中明确地设置线名。

+1

所有你真的做的是使用'ThreadContext',而不是'GlobalContext'但这仍然不起作用 - 文件名在配置时设置。您可以尽可能多地更改属性的值,但它不会起作用。 – stuartd

我想每个任务登录到自己的文件名动态,最好使用任务

的名称要做到这一点的唯一方法 - 假设你的任务同时运行 - 就是有一个记录器每个任务,其中每个记录器有它自己的appender

正如您已经注意到的那样,更改属性并不会更改日志文件,因为这是在配置时设置的。一个常见的解决方案是在运行时以编程方式修补appender文件名 - 这很容易,但是由于记录器共享appender,您会看到一个任务登录到另一个任务的文件。

所以,你将不得不construct loggers and appenders at runtime - 从后摘录:

// Create a new file appender 
public static log4net.Appender.IAppender CreateFileAppender(string name, 
string fileName) 
{ 
    log4net.Appender.FileAppender appender = new 
    log4net.Appender.FileAppender(); 
    appender.Name = name; 
    appender.File = fileName; 
    appender.AppendToFile = true; 

    log4net.Layout.PatternLayout layout = new 
    log4net.Layout.PatternLayout(); 
    layout.ConversionPattern = "%d [%t] %-5p %c [%x] - %m%n"; 
    layout.ActivateOptions(); 

    appender.Layout = layout; 
    appender.ActivateOptions(); 

    return appender; 
} 

和:

// Add an appender to a logger 
public static void AddAppender(string loggerName,log4net.Appender.IAppender appender) 
{ 
    log4net.ILog log = log4net.LogManager.GetLogger(loggerName); 
    log4net.Repository.Hierarchy.Logger l = 
      (log4net.Repository.Hierarchy.Logger)log.Logger; 

     l.AddAppender(appender); 
} 
+0

这些任务不会同时运行,所以他们在彼此的文件中写入应该没有问题。 如果我只是在每次运行之前就可以让Castle.Windsor解决记录器问题,我认为它可以。但这似乎也不起作用。 – Markus

+1

在这种情况下,您可以通过编程为每个任务设置文件名,log4net将创建一个新文件 - 例如, http://*.com/a/32873679/43846 – stuartd