使用log4Net在单个进程中记录多个客户端的帮助

时间:2011-01-31 12:28:21

标签: .net logging log4net log4net-configuration

我经过多次考虑后选择了log4Net作为我的Logging应用程序,所以请不要论这个

好的,听到是我的问题

  • 我有一个连接到多个客户端的流程
  • 每个客户端都有一个唯一的ID,在其独立的Thread
  • 中存储为String
  • 具有相同唯一ID的每个客户端可以多次连接

  • 我想为另一个.txt文件中的每个客户端创建一个日志文件。

  • 在每个新连接中,我想创建一个日期文件,其客户端ID附加日期时间和秒

这个场景让我感到困惑,因为我根本没有任何应用程序的任何日志记录经验。

我希望我的场景足够清晰:)

2 个答案:

答案 0 :(得分:3)

这对于如何将请求写入自己的文件没有回答您的问题,但log4net帮助特别推荐了一种更简单的方法。使用ThreadContext.Properties对象和其他属性来装饰日志消息,以便可以区分每个请求的消息。

http://logging.apache.org/log4net/release/faq.html

请参阅“多个客户端请求的输出可以转到不同的日志文件吗?”

如果您有客户ID,您当然可以这样做:

log4net.ThreadContext.Properties["ClientID"] = GetTheClientId();

在您的图案布局中,您可以执行以下操作:

<conversionPattern value="%date [%thread] %-5level %logger [%property{ClientID}] - %message%newline" />

%property{ClientID}将从ThreadContext中提取您的ClientID。

这将导致每条记录的消息都标记有相关的ClientID。

有关使用log4net上下文对象的详细教程,请参阅此链接:

http://www.beefycode.com/post/Log4Net-Tutorial-pt-6-Log-Event-Context.aspx

整个log4net教程非常好,特别是如果你刚开始使用log4net。

这是第1部分:

http://www.beefycode.com/post/Log4Net-Tutorial-pt-1-Getting-Started.aspx

说完所有这些之后,人们经常使用GlobalContext对象来命名他们的输出文件,如以下链接中所述:

http://geekswithblogs.net/rgupta/archive/2009/03/03/dynamic-log-filenames-with-log4net.aspx

每当我看到通过GlobalContext命名输出文件的过程时,建议的解决方案总是说要确保在log4net实际启动之前设置GlobalContext.Properties [“whatever”]值。这让我相信,即使不是不可能,也很难根据动态存储在ThreadContext中的信息创建单独的日志文件,因为在log4net已经运行之前可能无法知道该信息。

<强> [UPDATE]

这是SO的另一个链接,它说明了如何为GlobalContext中的值命名输出文件。请再次注意,在配置log4net之前和检索记录器之前,必须将文件名所基于的值设置到GlobalContext中。

How do I use a GlobalContext property in a log4net appender name?

正如我在上面和我的评论中所说,我不确定log4net是否可以创建多个输出文件(对于相同的FileAppender配置),其输出文件名由ThreadContext中的值或由线程id属性。也许其他对log4net更熟悉的人可以权衡这一点。

话虽如此,在NLog中可以做到这一点。这是一个NLog配置,它定义了一个文件目标,其名称部分来自线程id(配置的fileName部分中的注释${threadid}):

<targets>
    <target name="file" xsi:type="File" layout="${longdate} | ${processid} | ${threadid} | ${logger} | ${level} | ${message}" fileName="${basedir}/log_${threadid}.txt" />
</targets>

使用以下代码:

  class Program
  {
    public static Logger logger = LogManager.GetCurrentClassLogger();

    static void Main(string[] args)
    {

      int totalThreads = 20;
      TaskCreationOptions tco = TaskCreationOptions.None;
      Task task = null;

      logger.Info("Enter Main");

      Task[] allTasks = new Task[totalThreads];
      for (int i = 0; i < totalThreads; i++)
      {
        int ii = i;
        task = Task.Factory.StartNew(() =>
        {
          logger.Info("Inside delegate.  i = {0}", ii);
        });

        allTasks[i] = task;
      }

      logger.Info("Wait on tasks");

      Task.WaitAll(allTasks);

      logger.Info("Tasks finished");

      logger.Info("Exit Main");
    }
  }

我有4个日志文件,每个文件的名称都包含线程ID,每个文件只包含来自单个线程的消息。

同样,使用此配置(注意${mdc:item=id}):

<targets>
    <target name="file" xsi:type="File" layout="${longdate} | ${processid} | ${threadid} | ${logger} | ${level} | ${message}" fileName="${basedir}/log_${mdc:item=id}.txt" />
</targets>

这段代码:

  class Program
  {
    public static Logger logger = LogManager.GetCurrentClassLogger();

    static void Main(string[] args)
    {

      int totalThreads = 20;
      TaskCreationOptions tco = TaskCreationOptions.None;
      Task task = null;

      logger.Info("Enter Main");

      Task[] allTasks = new Task[totalThreads];
      for (int i = 0; i < totalThreads; i++)
      {
        int ii = i;
        task = Task.Factory.StartNew(() =>
        {
          MDC.Set("id",Thread.CurrentThread.ManagedThreadId.ToString());
          logger.Info("Inside delegate.  i = {0}", ii);
        });

        allTasks[i] = task;
      }

      logger.Info("Wait on tasks");

      Task.WaitAll(allTasks);

      logger.Info("Tasks finished");

      logger.Info("Exit Main");
    }
  }

我可以根据存储在MDC中的值(NLog等效于log4net的ThreadContext.Properties对象)获取多个输出文件。

答案 1 :(得分:2)

谢谢你们所有的答案和帮助,但经过大量的搜索后我终于找到了答案....不仅我可以创建多个文件,而且还有动态文件名。希望这可以帮助你们好吧:))

这个想法不是基于配置文件,因为每个人都说,一个Appender与一个文件名相关联,因此可以给一个appender一个动态文件名,但仍然不是N个文件名

所以我的配置如下

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>

  <log4net>
  </log4net>

</configuration>

[更新]:其实你甚至不需要任何配置 是的,我的配置是空的,因为我打算创建动态配置

以下是代码:

主:

SetLevel(clientID, "ALL");
AddAppender(clientID, CreateFileAppender(clientID, fileName));

ILog log = LogManager.GetLogger(clientID);
log.Any("whatever you want");

功能:

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 = false;

      log4net.Layout.PatternLayout layout = new
      log4net.Layout.PatternLayout();
      layout.ConversionPattern = "%d [%thread] %-5p %c [%a] - %m [%line] [%M]%n";
      layout.ActivateOptions();

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

      return appender;
}

public static void SetLevel(string loggerName, string levelName)
{
      log4net.ILog log = log4net.LogManager.GetLogger(loggerName);
      log4net.Repository.Hierarchy.Logger l = (log4net.Repository.Hierarchy.Logger)log.Logger;

      l.Level = l.Hierarchy.LevelMap[levelName];
}

// 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);
}

现在,我们要做的是创建一个具有指定名称的记录器,并在任何线程或任何类中随时随地获取它,甚至可以获取创建的appender并根据需要使用命令修改它

     log4net.LogManager.GetRepository().GetAppenders()

并迭代它。所以实际上一切都是动态的:)

Woops忘了加入orignal来源: Log4Net Mail archive