【问题标题】:can I pass a custom property to NLOG and output to file?我可以将自定义属性传递给 NLOG 并输出到文件吗?
【发布时间】:2015-09-29 23:55:53
【问题描述】:

编辑 4:“From”似乎是 NLog 中的保留字。更改它“FromID”有效。这是将变量传递给 NLog 并且仍然保持代码干净的好方法!!!!谢谢迈克!!!

编辑 3。我真的很喜欢这个主意。:

按照 Mike 的建议实现了一个辅助类:

public class NLogHelper
{
    //
    // Class Properties
    //
    private Logger m_logger;
    private Dictionary<string, object> m_properties;


    //
    // Constructor
    //
    public NLogHelper(Logger logger)
    {
        m_logger = logger;
        m_properties = new Dictionary<string, object>();
    }

    //
    // Setting Logger properties per instancce
    //
    public void Set(string key, object value)
    {
        m_properties.Add(key, value);
    }

    //
    // Loggers
    //
    public void Debug(string format, params object[] args)
    {
        m_logger.Debug()
            .Message(format, args)
            .Properties(m_properties)
            .Write();
    }

在我的主要代码中,我有:

    private NLogHelper m_logger;
    public void Start() 
    {
        m_logger = new NLogHelper(LogManager.GetCurrentClassLogger());
        m_logger.Set("From", "QRT123");  // Class setting.
        m_logger.Debug("Hello ");
    }

并且配置文件中设置的目标如下:

<target xsi:type="File"
    name ="LogFile" fileName="C:\QRT\Logs\QRTLog-${shortdate}.log"
    layout ="${date}|${level}|${event-properties:item=From}|${message} "/>

但是输出有一个空白代替'from'属性???

所以我快到了……但它似乎不起作用??

编辑 2: 我现在正在尝试创建自己的 NLog 调用版本:

private void Log_Debug (string Message) 
{
   LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "What is this?", Message);
   theEvent.Properties["EmployeeID"] = m_employeeID;
   m_logger.Log(theEvent);
}

问题是我必须为调用格式化字符串(但需要很大的性能交易)......但这似乎是一个 hack??

理想情况下,我会在自定义布局渲染器中声明属性,而不是在配置文件中设置这些属性,而是为我的类的每个实例设置属性...类似于 [ID = m_ID] 的整个类。这样,每当从该类调用 NLog 时,都会设置 ID 属性,并且 NLog 的自定义布局渲染器可以使用此属性来输出它。我说得有道理吗??

我是 NLog 新手,一直在研究自定义渲染器。 基本上,我的目标是让我的日志语句是: _logger.Debug ("My Name is {0}", "Ed", ID=87);

我希望我的渲染是这样的: layout = ${ID} ${date} ${Level} ${Message}

就是这样。 ${ID} 可以有一个默认值 0。没问题。但理想情况下,我希望每次调用都能够指定一个 ID,而无需在每次我想记录时都有 3 行。

我已经看到自定义渲染器允许我自定义输出的内容,但我不确定如何在没有

的情况下自定义传递给它的属性

https://github.com/NLog/NLog/wiki/Extending%20NLog 显示了如何添加属性,但我不知道如何调用它们。

另外,https://github.com/NLog/NLog/wiki/Event-Context-Layout-Renderer 展示了我如何设置自定义属性,但这涉及到每次我想记录某些内容时创建一个 LogEventInfo 对象。

Nlog Custom layoutrenderer 展示了如何自定义输出......再次......不是如何自定义输入。

这是针对使用 VS2013 的 .NET 4.0 的 C# 控制台应用程序

谢谢 -埃德

【问题讨论】:

    标签: c# nlog


    【解决方案1】:

    事件属性(以前称为事件上下文)将是做你想做的事情的内置方式。如果您使用的是 NLog 3.2+,则可以使用 fluent api,这可能比创建 LogEventInfo 对象更具吸引力。您可以使用命名空间NLog.Fluent 来访问此api。

    然后您的布局将被定义如下:

    ${event-properties:item=ID} ${date} ${Level} ${Message}
    

    然后使用fluent api,像这样记录:

    _logger.Debug()
        .Message("My name is {0}", "Ed")
        .Property("ID", 87)
        .Write();
    

    除了如上所述为每个事件设置属性外,唯一的其他选择是使用MDCMDLS 为每个线程设置属性。

    NLog 没有办法(我发现)设置每个记录器的属性。在内部,NLog 按记录器名称缓存 Logger 实例,但不保证对于给定记录器名称始终返回相同的 Logger 实例。因此,例如,如果您在类的构造函数中调用 LogManager.GetCurrentClassLogger()大多数情况下您将为类的所有实例返回相同的 Logger 实例。在这种情况下,您将无法在记录器上为每个类实例设置单独的值。

    也许您可以创建一个日志助手类,您可以在您的类中实例化它。可以使用每个实例的属性值来初始化帮助程序类,以便与每条消息一起记录。辅助类还将提供方便的方法来记录上述消息,但只需一行代码。像这样的:

    // Example of a class that needs to use logging
    public class MyClass
    {
        private LoggerHelper _logger;
    
        public MyClass(int id)
        {
            _logger = new LoggerHelper(LogManager.GetCurrentClassLogger());
    
            // Per-instance values
            _logger.Set("ID", id);
        }
    
        public void DoStuff()
        {
            _logger.Debug("My name is {0}", "Ed");
        }
    }
    
    
    // Example of a "stateful" logger
    public class LoggerHelper
    {
        private Logger _logger;
        private Dictionary<string, object> _properties;
    
    
        public LoggerHelper(Logger logger)
        {
            _logger = logger;
            _properties = new Dictionary<string, object>();
        }
    
        public void Set(string key, object value)
        {
            _properties.Add(key, value);
        }
    
        public void Debug(string format, params object[] args)
        {
            _logger.Debug()
                .Message(format, args)
                .Properties(_properties)
                .Write();
        }
    }
    

    这将使用与上述相同的布局。

    【讨论】:

    • 谢谢!我将尝试实现帮助程序,以便我的代码保持可读性。你认为这会影响性能吗?因为所有对助手类的调用?
    • 嗨,迈克:我实施了您的建议,但似乎不起作用?在我期待新房产的地方,我得到了一个空白。我在上面的“EDIT 3”中提供了我的代码。我确保我正确拼写了该属性...'不确定这里发生了什么。没有编译发布等。
    • 让它工作。 “从”似乎是一个保留字。我将其更改为 FromID 并且有效!
    • 以这种方式使用帮助类不会显着降低性能。但是,使用 NLog 的 fluent API 与使用标准 API 可能会出现一些不同的性能特征。
    • 上面的例子确实使用了流畅的 API(或者至少,这是我让上面的语法工作的唯一方法)。
    【解决方案2】:

    NLog 4.5 支持使用消息模板的结构化日志记录:

    logger.Info("Logon by {user} from {ip_address}", "Kenny", "127.0.0.1");
    

    另见https://github.com/NLog/NLog/wiki/How-to-use-structured-logging

    另见https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-properties-with-Microsoft-Extension-Logging

    【讨论】:

    • 在 C# 6.0 中使用字符串插值logger.Info($"Logon by {"Kenny"} from ${"127.0.0.1"}");
    • @KevinBui 这个想法不是使用字符串插值而是使用消息模板:messagetemplates.org
    【解决方案3】:

    使用 MDLC 布局渲染器

    MappedDiagnosticsLogicalContext.Set("PropertyName", "PropertyValue"); MappedDiagnosticsLogicalContext.Set("PropertyName2", "另一个属性值");

    在您的 nlog 配置中:

    ${mdlc:item=PropertyName} ${mdlc:item=PropertyName2}

    https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer

    【讨论】:

      【解决方案4】:

      我有 6 个变量要发送到多个位置的结构化日志记录(因此当我收到用户报告时,我可以在我们的关键 ID 字段上搜索日志数据库)。我创建了一个利用MDLC 的日志范围类。所以它应该是线程安全的,使用 async/await 代码并且是 3 行代码,用于 6 个变量。

      public class MyClassLoggingScope : IDisposable
      {
          private readonly List<IDisposable> context;
      
          public MyClassLoggingScope(MyClass varSource)
          {
              this.context = new List<IDisposable> 
              { 
                  MappedDiagnosticsLogicalContext.SetScoped("Var1", varSource.Var1)
                  // list all scoped logging context variables
              }
          } 
      
          public void Dispose()
          {
              foreach (IDisposable disposable in this.context)
              {
                  disposable.Dispose();
              }
          }
      }
      

      用法:

      using (new MyClassLoggingScope(this))
      {
          // do the things that may cause logging somewhere in the stack
      }
      

      然后如 Flux 之前建议的那样,在日志记录配置中您可以使用 ${mdlc:item=Var1}

      【讨论】:

        【解决方案5】:

        这可能不是最好的方法,甚至不是线程安全的,而是一个快速而肮脏的解决方案:您可以使用环境变量:

        Environment.SetEnvironmentVariable("NLogOptionID", "87");
        logger.Debug("My Name id Ed");
        Environment.SetEnvironmentVariable("NLogOptionID", null);
        

        在你的布局中,你可以使用环境变量。

        ${environment:variable=NLogOptionID}
        

        【讨论】:

        • 哇。有趣的。我喜欢这个想法,因为我可以设置一次 Env 变量。现在,我正在使用 LogEventInfo(我编辑了上面的问题以添加它)......但这非常混乱......我确实需要线程安全......
        • 我不知道为什么它的评分很低。老实说,nlog 无法记录 {aspnet-user-identity} 因为它当前记录为空值,在 asp.netcore 2.2 web api 中,这是我能够实现的解决方案。我不知道为什么 HttpContext.User.Identity 没有被该布局属性呈现,但这个技巧对我有用。
        • @Ak777 它可能看起来适合你。但是,由于这会在进程级别设置变量,因此在调用 Environment.SetEnvironmentVariable 和实际调用 logger.Debug(...) 之间可能会发生另一个线程已经更新了值并且您记录了错误的用户。对于简单的控制台或 Windows 应用程序,这可能非常有用,但在 Web 项目中请谨慎使用。
        • 线程安全在您的站点上的用户越多就越重要,这在开发环境中可能工作得很好,但在生产环境中却毫无用处。如果你把它放在一个带有 lock 语句的子函数中,你可以侥幸逃脱,但是当每个日志语句都在等待其他日志语句时,你可能会遇到性能问题。
        • @DeniseSkidmore 没错,这就是为什么你不应该在 web 应用程序中使用这种方法。我在一个没有引用 nlog 但通过反射加载真实应用程序的引导程序应用程序中使用它,并且保证此时应用程序只运行一个线程。这样我就可以使用有用的信息来扩展我的日志记录。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-26
        • 2011-07-08
        相关资源
        最近更新 更多