3

经过多方面考虑,我选择了 log4Net 作为我的日志记录应用程序,所以请不要争论

好的,听到是我的问题

  • 我有一个连接到多个客户端的进程
  • 每个客户端都有一个唯一的 ID 作为字符串存储在自己的单独线程中
  • 每个具有相同唯一 ID 的 Client 可以多次连接

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

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

这种情况让我感到困惑,因为我以前也没有任何应用程序的日志记录经验。

我希望我已经把我的场景说得够清楚了:)

4

2 回答 2

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" />

从 ThreadContext中%property{ClientID}提取您的 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 已经运行之前可能不会知道这些信息。

[更新]

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

如何在 log4net appender 名称中使用 GlobalContext 属性?

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

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

<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 中的值(相当于 log4net 的 ThreadContext.Properties 对象的 NLog)获取多个输出文件。

于 2011-01-31T15:46:08.277 回答
2

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

这个想法不是基于配置文件,因为正如大家所说,一个 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);
}

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

     log4net.LogManager.GetRepository().GetAppenders()

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

Woops 忘记添加原始来源: Log4Net 邮件存档

于 2011-02-02T05:18:24.763 回答