将变量添加为所有nLog消息的前缀

jun*_*one 5 logging nlog

我正在使用Nlog记录我的Ninjatrader策略.我希望能够将策略ID作为前缀添加到我的所有nL​​og消息中,以便我可以单独过滤与策略中每个帐户相关的条目.

 fileTarget.Layout =  "${longdate} ${callsite} ${level} ${event-context:item=MyValue}  ${message}";`
Run Code Online (Sandbox Code Playgroud)

我目前的布局如上.我尝试使用event-context:item但不知道如何为所有消息打印上下文项.

我尝试如下

Logger log = LogManager.GetCurrentClassLogger();
LogEventInfo theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name);   
logger.Log(theEvent);
Run Code Online (Sandbox Code Playgroud)

但是它在第一行Sim101上只打印了一行带有上下文信息,而不是在其他行上打印.

2012-11-26 15:09:47.9777 NinjaTrader.Strategy.LODHOD.OnStartUp Debug   Sim101
2012-11-26 15:09:48.3996 NinjaTrader.Strategy.LODHOD.OnBarUpdate Trace   BAR UPDATE
2012-11-26 15:09:49.7902 NinjaTrader.Strategy.LODHOD.EntryOrders Info   PLACED ENTRY ORDERS
Run Code Online (Sandbox Code Playgroud)

如何在所有日志行上打印Sim101?

wag*_*ghe 23

{event-context} LayoutRenderer命令写入的值LogEventInfo对象的Properties属性.

属性是一个字典,您可以在其中存储您希望NLog添加到每个日志消息的命名值.

如果你想标记与"StrategyId"这实际上是被记录的消息时,你应该创建你的LogEventInfo对象,这样的事情每个日志消息:

LogEventInfo theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name);
theEvent.Properties["StrategyId"] = "Sim101";
Logger.Log(theEvent);
Run Code Online (Sandbox Code Playgroud)

你的布局看起来像这样:

fileTarget.Layout =  "${longdate} ${callsite} ${level} ${event-context:item=StrategyId}  ${message}";
Run Code Online (Sandbox Code Playgroud)

如果你希望你的日志调用网站是那么详细,你可以使用GlobalDiagnosticContext或MappedDiagnosticContext.

private void ApplyStrategyABC()
{
  NLog.GlobalDiagnosticContext.Set("StrategyId","ABC");
  //Do some stuff
  LogEventInfo theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name);
  Logger.Log(theEvent);

  NLog.GlobalDiagnosticContext.Remove("StrategyId");
}

private void ApplyStrategyDEF()
{
  NLog.GlobalDiagnosticContext.Set("StrategyId","DEF");
  //Do some stuff
  LogEventInfo theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name);
  Logger.Log(theEvent);

  NLog.GlobalDiagnosticContext.Remove("StrategyId");
}
Run Code Online (Sandbox Code Playgroud)

使用这样的布局:

fileTarget.Layout =  "${longdate} ${callsite} ${level} ${gdc:item=StrategyId}  ${message}";
Run Code Online (Sandbox Code Playgroud)

将导致每条日志消息都使用全局字典中"StrategyId"的当前值进行标记.

为了好玩,你也可以做出那种将适用您的属性,您创建的LogEventInfo对象流利的API扩展方法.像这样(未经测试):

LogEventInfo WithProperty(this LogEventInfo theEvent, string name, string value)
{
  theEvent.Properties[name] = value;
  return theEvent;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

var theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name).WithProperty("StrategyId", "ABC");
Run Code Online (Sandbox Code Playgroud)

还有这个:

var theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name).WithProperty("StrategyId", "ABC").WithProperty("SomethingElse", someLocalVariable);
Run Code Online (Sandbox Code Playgroud)

如果您想更明确(并减少可能的拼写错误),您可以制作更具体的扩展方法,如下所示:

LogEventInfo WithStrategy(this LogEventInfo theEvent, string strategy)
{
  theEvent.Properties["StrategyId"] = strategy;
  return theEvent;
}

LogEventInfo WithCurrency(this LogEventInfo theEvent, string currency)
{
  theEvent.Properties["Currency"] = currency;
  return theEvent;
}

var theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", this.Account.Name).WithStrategy("ABC").WithCurrency("US dollars");
Run Code Online (Sandbox Code Playgroud)

编辑:大多数人使用Logger.Info,Logger.Debug,Logger.Trace等方法来写自己的日志信息,而不是创建LogEventInfo并调用日志中的每条消息.如果您LogEventInfo明确地创建对象,可能会有更多灵活性,但它也会使您的工作变得更加复杂.

如果你想使用Logger.Info,Logger.Debug等等方法和装饰具有附加属性每一个日志消息,你仍然可以做到这一点.

假设您有两种方法(如上所述)应用两种不同的策略:ABC和DEF:

使用这样的布局:

fileTarget.Layout =  "${longdate} ${callsite} ${level} ${gdc:item=StrategyId}  ${message}";

public class MyClass
{
  private static readonly Logger logger = LogManager.GetCurrentClassLogger();

  private void ApplyStrategyABC()
  {
    NLog.GlobalDiagnosticContext.Set("StrategyId","ABC");
    //Do some stuff

    logger.Debug("Hello from ABC!"); 

    var x = CalculateSomeValue();

    logger.Debug("Value = {0}", x);

    NLog.GlobalDiagnosticContext.Remove("StrategyId");      
  }

  private void ApplyStrategyDEF()
  {
    NLog.GlobalDiagnosticContext.Set("StrategyId","DEF");
    //Do some stuff

    logger.Debug("Hello from DEF");

    var x = CalculateSomeValue();

    logger.Debug("Value = {0}", x);

    NLog.GlobalDiagnosticContext.Remove("StrategyId");      
  }
}

In you program call your two strategies:

var myClass = new MyClass();

myClass.ApplyStrategyABC();
myClass.ApplyStrategyDEF();
Run Code Online (Sandbox Code Playgroud)

在每种情况下,记录的消息将使用在相应功能内设置的"StrategyId"进行标记.

如果您想创建和使用LogEventInfo对象来创建消息,那么您必须意识到一个LogEventInfo对象实例的属性仅适用于该实例.如果您创建一个LogEventInfo,设置其属性,记录它,然后使用Logger.Info,Logger.Debug等记录消息,那么您将看不到您在原始LogEventInfo上设置的属性.

例如,

var logger = LogManager.GetCurrentClassLogger();
var theEvent = new LogEventInfo(NLog.LogLevel.Debug, "", "Hello 1");
theEvent.Properties["StrategyId"] = "ABC";
//This message will be tagged with StrategyId = ABC if the layout uses the event-context LayoutRenderer
logger.Log(theEvent);

//This message will NOT be tagged with StrategyId = ABC because that value was only added to the LogEventInfo
//object that was created above.  Another way to think about this is that internally
//NLog creates a LogEventInfo for each message that is logged via the Debug, Trace, etc
//methods.
logger.Debug("Hello 2");
Run Code Online (Sandbox Code Playgroud)

我会建议使用Logger.Info,Logger.Debug,Logger.Trace等方法来记录您的邮件,并使用两种GlobalDiagnosticsContextMappedDiagnosticsContext指定要包括在每个日志消息的附加信息.

一般情况下,我认为我也建议您使用的Logger.Info,Logger.Debug,Logger.Trace方法或LogEventInfo+ Logger.Log,但不能同时使用.使用这两者,特别是如果您尝试添加其他上下文值(StrategyId)可能会变得混乱.

我可以类比安装软件.通常,当您在计算机上安装软件时,您可以选择进行"典型"安装,让安装程序安装要安装的组件,或选择"自定义",然后选择要安装的组件.我不了解你,但我通常选择"典型"安装.使用Logger.Info,Logger.Debug,Logger.Trace就像是"典型"安装.这些是最常用的日志记录方法.使用LogEventInfo+ Logger.Log更像是选择"自定义"安装.如果您使用的含义LogEventInfo是"典型"日志记录方法无法满足您的需求.

当您使用NLog时,您将更熟悉它的工作原理,其中一些问题将变得更加明显.

注意,这GlobalDiagnosticsContext是真正的全球性.它是一个静态对象.因此,如果您是多线程,如果两个线程尝试向字典添加具有相同名称的值,则可能会发生冲突.

MappedDiagnosticsContext 是线程本地的(它使用线程静态字典来存储它的值),因此在多线程情况下使用它可能更好.

如果您希望获得幻想并自动调整放在GlobalDiagnosticsContext(或MappedDiagnosticsContext)中的值,则可以创建如下所示的类:

public class ScopedGlobalContext : IDisposable
{
  private string n;
  private string v;

  public ScopedGlobalContext(string name, string value)
  {
    n = name;
    v = value;
    NLog.GlobalDiagnosticsContext.Set(n, v);
  }

  public void Dispose()
  {
    NLog.GlobalDiagnosticsContext.Remove(n);
  }
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

  private void ApplyStrategyDEF()
  {
    using (new ScopedGlobalContext("StrategyId", "DEF"))
    {
      //Do some stuff

      logger.Debug("Hello from DEF");

      var x = CalculateSomeValue();

      logger.Debug("Value = {0}", x);
    }
  }
Run Code Online (Sandbox Code Playgroud)

这将GlobalDiagnosticsContextusing范围开始时将StrategyId,DEF名称值对放入字典中,并在using范围退出时将其删除.