2013-06-12 137 views
3

我想使用歸檔來限制日誌文件的數量,並且我希望每個歸檔日誌的文件名都是日誌所來自的日期。夠簡單。NLog存檔時日期不正確

這是我的目標之一:

<target xsi:type="File" name="info" fileName="${basedir}/logs/info.log" 
     layout="${date:format=HH\:mm\:ss}&#009;|&#009;${uppercase:${level}}&#009;|&#009;${message}" 
     archiveEvery="Day" 
     archiveFileName="${basedir}/logs/archive/info/${shortdate}.{#}.log" 
     archiveNumbering="Rolling" 
     maxArchiveFiles="30"/> 

我已閱讀,你必須有{#}在archiveFileName或歸檔不會在所有的工作時,你有文件名的日期,這有點令人討厭,但我想我可以忍受這一點。

然而,由於歸檔後進行日期改變時,$ {} shortdate總會成爲天的日期,即一天(或更多)所需的日期之後。如果今天記錄了一條消息,2013-06-12,那麼當它明天存檔時,它將被放置在名爲2013-06-13.log的文件中。

是否有任何方式獲取正確的日期?我看到有人提出一個變量,但我不明白這是如何工作的......這看起來像是一個顯而易見的特性。它應該絕對是是歸檔編號模式之一!與日期相比,現在可用的編號模式看起來不切實際。

此問題與Delete log files after x days有關。如果可以在不使用存檔功能的情況下設置最大數量的日誌文件(因爲這是我真正需要的存檔功能),那實際上會更好,因爲我不必使用{#},但是我懷疑這是不可能的。

回答

0

這可能被認爲是Rube Goldberg方法的一種,但它可能有效......您可以編寫計算「上一個」日期的自定義LayoutRenderer。此LayoutRenderer的參數將是目標配置中的「archiveEvery」設置。

使用ShortDateLayoutRenderer爲基礎...

(從NLOG的git倉庫兩者...)

[LayoutRenderer("shortdate")] 
[ThreadAgnostic] 
public class ShortDateLayoutRenderer : LayoutRenderer 
{ 
    /// <summary> 
    /// Gets or sets a value indicating whether to output UTC time instead of local time. 
    /// </summary> 
    /// <docgen category='Rendering Options' order='10' /> 
    [DefaultValue(false)] 
    public bool UniversalTime { get; set; } 

    /// <summary> 
    /// Renders the current short date string (yyyy-MM-dd) and appends it to the specified <see cref="StringBuilder" />. 
    /// </summary> 
    /// <param name="builder">The <see cref="StringBuilder"/> to append the rendered data to.</param> 
    /// <param name="logEvent">Logging event.</param> 
    protected override void Append(StringBuilder builder, LogEventInfo logEvent) 
    { 
     var ts = logEvent.TimeStamp; 
     if (this.UniversalTime) 
     { 
      ts = ts.ToUniversalTime(); 
     } 

     builder.Append(ts.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); 
    } 
} 

一個PreviousDateLayoutRenderer可能是這個樣子: (請注意,我既沒有編制也沒有測試過這段代碼,但我之前編寫過LayoutRenderer)。

[LayoutRenderer("previousdate")] 
[ThreadAgnostic] 
public class PreviousDateLayoutRenderer : LayoutRenderer 
{ 
    /// <summary> 
    /// Gets or sets a value indicating whether to output UTC time instead of local time. 
    /// </summary> 
    /// <docgen category='Rendering Options' order='10' /> 
    [DefaultValue(false)] 
    public bool UniversalTime { get; set; } 

    /// <summary> 
    /// Gets or sets the value indicating the unit of time to subtract to get previous date. 
    /// </summary> 
    [DefaultValue("Day")] 
    public string TimeUnit { get; set; } 

    /// <summary> 
    /// Gets the current date, subtracts one TimeUnit, renders the resulting short date string, 
    /// then appends it to the specified <see cref="StringBuilder" />. 
    /// </summary> 
    /// <param name="builder">The <see cref="StringBuilder"/> to append the rendered data to.</param> 
    /// <param name="logEvent">Logging event.</param> 
    protected override void Append(StringBuilder builder, LogEventInfo logEvent) 
    { 
     var ts = logEvent.TimeStamp; 
     if (this.UniversalTime) 
     { 
      ts = ts.ToUniversalTime(); 
     } 

     // This could certainly be better. Probably smarter to put code in the setter of the 
     // TimeUnit property to compute a TimeSpan member variable that could then be subtracted 
     // in this method rather than check the TimeUnit and compute the TimeSpan every time. 

     TimeSpan span; 

     switch (TimeUnit) 
     { 
      case "Day": 
      span = TimeSpan.FromDays(1); 
      break; 
      case "Hour": 
      span = TimeSpan.FromHours(1); 
      break; 
     } 

     ts -= span; 

     builder.Append(ts.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); 
    } 
} 

或者,你可以寫一個LayoutRendererWrapper,將適用非常類似的邏輯,但它會被傳遞一個字符串(即包裝會嘗試解釋爲日期),希望從減去所需的時間跨度。

給出的包裝,因爲我以上簡要描述的,它可能會像下面這樣配置:

<target xsi:type="File" name="info" fileName="${basedir}/logs/info.log" 
     layout="${date:format=HH\:mm\:ss}&#009;|&#009;${uppercase:${level}}&#009;|&#009;${message}" 
     archiveEvery="Day" 
     archiveFileName="${basedir}/logs/archive/info/${previousdate{shortdate,"Day"}}.{#}.log" 
     archiveNumbering="Rolling" 
     maxArchiveFiles="30"/> 

我的建議假定歸檔文件應基於「以前的日期」爲當前日期來命名。這也取決於你上面提到的事實,即文件是在日期改變時滾動的,因此將「當前」日期分配給文件名而不是「上一個」日期。當然有些情況下這種方法可能不會給出你想要的結果。如果您的應用程序僅在平日運行,該怎麼辦?它週一整天運行,然後在週二的第一個日誌中日誌文件滾動並根據前一天(星期一)的日期命名。沒事兒。在本週的其餘時間,也就是週末前,這很好。當程序在星期五運行時,會記錄日誌。該計劃不會在週末結束。在星期一,第一次記錄消息時,日誌文件將被滾動。在這種情況下,前一個日期將是星期日,您可能更希望前一個日期是星期五。也可能出現這樣的情況,無論出於何種原因,某一天都沒有日誌。第二天有一個日誌,導致翻滾。同樣,我描述的方法將確定之前的日期是實際的「實際」前一天,當您可能更喜歡「上一個」來表示前一天「當有任何日誌寫入時」。

這已經得到了很長的一段時間,但也許你會發現它很有用。

+1

我的申請只會在辦公時間內使用,所以如你所說,只是在前一天獲得並不能完全解決問題。不過,您的帖子仍然有幫助。我可以製作一個獲取當前日誌名稱的LayoutRenderer,然後如果我用當前日誌命名,它應該全部解決。 – moggizx

+0

我的方法也可以修改,以限制「前一天」的工作日。所以,當日志文件在星期一推出時,它會發現「前一天」是星期五。這種行爲可能是硬編碼的,或者可以通過配置設置來控制。 – wageoghe

+0

對不起,我只是覺得如果你不能相信日期,那就沒有意義了。我的方法不起作用,因爲歸檔對於具有動態文件名的日誌根本不起作用。我懷疑這是因爲它會在歸檔時嘗試使用配置的日誌名稱查找日誌,並且如果自從創建當前日誌文件以來它已經發生更改,那麼它將不會找到它並因此無法將其歸檔... I已決定放棄這一點,但感謝你的幫助! – moggizx