2012-11-26 136 views
5

我正在使用Nlog記錄我的Ninjatrader策略。我希望能夠將策略ID作爲前綴添加到我的所有nL​​og消息中,以便我可以單獨篩選策略中與每個帳戶相關的條目。將變量作爲前綴添加到所有nLog消息

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,而不是在其他行的背景信息只有一條線。

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?

回答

22

{event-context}LayoutRendererLogEventInfo對象的Properties屬性中寫入一個值。

屬性是一個字典,您可以在其中存儲您希望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」在全球字典中的電流值。

爲了好玩,您還可以製作一種流暢的API擴展方法,將您的屬性應用於您創建的LogEventInfo對象。事情是這樣的(未經測試):如果你想更明確的

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

(並減少可能的:

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

這錯字錯誤),您可以製作更具體的擴展方法,如下所示:

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

在每種情況下,消息記錄將被標記爲在相應函數內部設置的「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"); 

我會建議使用Logger.InfoLogger.DebugLogger.Trace等方法來記錄你的消息,並使用兩種GlobalDiagnosticsContextMappedDiagnosticsContext指定要包括在每個附加信息日誌消息。

一般情況下,我認爲我也建議您使用的Logger.InfoLogger.DebugLogger.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); 
    } 
} 

而且你可以使用這樣的:

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範圍退出時。

+0

這裏是我的完整問題: 我必須在每次調用跟蹤函數之前添加一個事件嗎?當我添加事件屬性並做一個logger.info(theEvent)時,它立即打印我的日誌的後綴。 例如2012年11月28日14:18:52.3277 NinjaTrader.Strategy.LODHOD.OnStartUp信息日誌事件:記錄器=''級別=信息消息='d4d0d3849c2440f5b41de65d744ede61'SequenceID = 270 然而,下一個logger.info沒有捕獲我的屬性 2012-11-28 14:18:52.9996 NinjaTrader.Strategy.LODHOD.OnBarUpdate信息BAR UPDATE 我使用的代碼在這裏。 http://codeviewer.org/view/code:2c95 – junkone

+0

和我用的日誌是在這裏。 http://codeviewer.org/view/code:2c96 – junkone

+0

您不必使用LogEventInfo類創建消息,並使用Log方法記錄它們。通常,大多數人不會。他們使用.Info,.Debug,.Trace等方法編寫他們的日誌代碼。我會在我的答案中添加一些例子。 – wageoghe