【问题标题】:Change log4net filename when running运行时更改 log4net 文件名
【发布时间】:2016-08-25 05:45:41
【问题描述】:

我有一个启动较小程序或任务的程序,现在我希望每个任务都动态记录到自己的文件名,最好使用任务名称。 我已经挖掘了一下,似乎有一个漂亮的%property 可以与 Log4Net 一起使用。所以我试过了。但似乎 Log4Net 在初始化后并没有对此进行更改。

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

有谁知道在运行时是否有更好/更智能的方法来更改日志的文件名?

这是我的测试代码:

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

【问题讨论】:

    标签: c# log4net


    【解决方案1】:

    我希望每个任务都动态记录到自己的文件名,最好使用任务名称

    做到这一点的唯一方法 - 假设您的任务同时运行 - 是每个任务有一个记录器,其中每个记录器都有自己的附加程序

    您已经注意到,更改属性不会更改日志文件,因为它是在配置时设置的。一个常见的解决方案是在运行时以编程方式修补 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);
    }
    

    【讨论】:

    • 这些任务不会并行运行,因此它们在彼此的文件中写入应该没有问题。如果我能让 Castle.Windsor 在每次运行之前只解决记录器,我认为就可以了。但这似乎也不起作用。
    • 在这种情况下,您可以为每个任务以编程方式设置文件名,log4net 将创建一个新文件 - 例如stackoverflow.com/a/32873679/43846
    【解决方案2】:

    log4net.config 中定义的附加程序中,您可以指定文件名的输出格式。例如&lt;file value="fixedName"/&gt;。您还可以通过将&lt;staticLogFileName value="false"/&gt; 与模式一起使用来获得动态文件名,例如

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

    这将根据日期和您的"SomePropertyName" 属性路由到不同的文件。

    您必须在任务中设置该属性,例如log4net.ThreadContext.Properties["SomePropertyName"] = "someValue";

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

    【讨论】:

    • 您所做的一切实际上是使用ThreadContext 而不是GlobalContext 但这仍然不起作用 - 文件名是在配置时设置的。您可以随意更改属性的值,但不会产生任何效果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多