7

Nlogを使用してNinjatrader戦略をログに記録しています。戦略の各アカウントに関連するエントリを個別にフィルタリングできるように、すべてのnLogメッセージのプレフィックスとして戦略IDを追加できるようにしたいと思います。

 fileTarget.Layout =  "${longdate} ${callsite} ${level} ${event-context:item=MyValue}  ${message}";`

私の現在のレイアウトは上記のとおりです。event-context:itemを使用しようとしましたが、すべてのメッセージのコンテキストアイテムを印刷する方法がわかりません。

私は次のように試しました

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

しかし、最初の行Sim101にコンテキスト情報を含む1行だけを印刷し、他の行には印刷しませんでした。

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

Sim101をすべてのログ行に印刷するにはどうすればよいですか?

4

1 に答える 1

24

は、オブジェクトのプロパティ{event-context} LayoutRendererから値を書き込みます。LogEventInfoProperties

プロパティは、NLogが各ログメッセージに追加する名前付きの値を格納できる辞書です。

メッセージがログに記録されるときに有効な「StrategyId」で各ログメッセージにタグを付ける場合は、次のようなLogEventInfoオブジェクトを作成する必要があります。

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

レイアウトは次のようになります。

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

ロギング呼び出しサイトの冗長性を低くしたい場合は、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");
}

次のようなレイアウトを使用します。

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

各ログメッセージは、グローバルディクショナリの「StrategyId」の現在の値でタグ付けされます。

楽しみのために、作成したLogEventInfoオブジェクトにプロパティを適用するような流暢なAPI拡張メソッドを作成することもできます。このようなもの(テストされていない):

LogEventInfo WithProperty(this LogEventInfo theEvent, string name, string value)
{
  theEvent.Properties[name] = value;
  return theEvent;
}

次に、次のように使用できます。

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

この:

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

より明確にしたい(そして起こりうるタイプミスを減らしたい)場合は、次のようなより具体的な拡張メソッドを作成することができます。

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");

編集:ほとんどの人は、Logger.Infoメッセージごとにログを作成して呼び出すのではなく、、、、などのメソッドを使用してログメッセージを書き込みLogger.Debugます。オブジェクトを明示的に作成すると、おそらくより柔軟性がありますが、作業がより複雑になります。Logger.TraceLogEventInfoLogEventInfo

、、などのメソッドを使用しLogger.InfoLogger.Debug各ログメッセージを追加のプロパティで装飾する場合でも、それを行うことができます。

ABCとDEFの2つの異なる戦略を適用するための2つの方法(上記のように)があるとしましょう。

次のようなレイアウトを使用します。

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();

いずれの場合も、ログに記録されるメッセージは、対応する関数内に設定されている「StrategyId」でタグ付けされます。

LogEventInfoオブジェクトを作成して使用してメッセージを作成する場合は、1つの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");

、、、などのメソッドを使用してメッセージをログに記録し、またはのいずれかを使用しLogger.Infoて、各ログメッセージに含める追加情報を指定することをお勧めします。Logger.DebugLogger.TraceGlobalDiagnosticsContextMappedDiagnosticsContext

一般的には、、、メソッド、または+のいずれかを使用することをお勧めしますが、両方Logger.Infoを使用することはお勧めしません。特にコンテキスト値(StrategyId)を追加しようとしている場合は、両方を使用すると混乱する可能性があります。Logger.DebugLogger.TraceLogEventInfoLogger.Log

ソフトウェアのインストールに例えることができます。通常、コンピューターにソフトウェアをインストールするときは、インストーラーにインストールするコンポーネントをインストールさせる「標準」インストールを選択するか、インストールするコンポーネントを選択して選択する「カスタム」インストールを選択できます。あなたのことはわかりませんが、私は通常「標準」インストールを選択します。Logger.Info、、を使用することはLogger.DebugLogger.Trace「一般的な」インストールのようなものです。これらは、ロギングに最も一般的に使用される方法です。LogEventInfo+を使用Logger.Logすることは、「カスタム」インストールを選択することに似ています。使用している場合の意味LogEventInfoは、「一般的な」ロギング方法ではニーズが満たされないということです。

NLogを使用するにつれて、NLogがどのように機能するかを理解し、これらの問題のいくつかがより明白になります。

GlobalDiagnosticsContextそれは本当にグローバルであることに注意してください。静的オブジェクトです。したがって、マルチスレッドを使用している場合、2つのスレッドが同じ名前の値をディクショナリに追加しようとすると、競合が発生する可能性があります。

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

そして、あなたはそれをこのように使うことができます:

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

      logger.Debug("Hello from DEF");

      var x = CalculateSomeValue();

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

これにより、スコープの開始時にStrategyIdとDEFの名前と値のペアがGlobalDiagnosticsContextディクショナリに配置され、スコープの終了using時に削除されます。using

于 2012-11-27T18:49:14.240 に答える