2013-10-25 31 views
3

根據this log4net文章,您應該檢查是否在任何Log.Debug語句之前啓用調試以消除語句構建成本。在任何日誌語句之前,是否有更好的選擇來總是檢查(Log.IsDebugEnabled)?Log4Net消息在未調試時的構建成本

log4net的例子:

if (log.IsDebugEnabled) 
{ 
    log.Debug("This is entry number: " + i); 
} 

我不想支付語句建設的開銷,但也不想檢查每一個日誌聲明之前。

+0

這些陳述在哪裏?他們是否一貫使用(即您是否將他們複製並粘貼到相同類型的地方)? –

回答

3

也不想檢查每一個日誌語句

當你發現自己一遍又一遍地重複相同的代碼之前,這聽起來像一個普通的抽象可能爲了。在這種情況下,您可以爲Log4Net創建自定義包裝。一些簡單的:

public static class Logger 
{ 
    private static ILog _log; 

    static Logger() 
    { 
     log4net.Config.XmlConfigurator.Configure(); 
     _log = log4net.LogManager.GetLogger("Log4Net"); 
    } 

    public static void Debug(string message) 
    { 
     if (_log.IsDebugEnabled) 
      _log.Debug(message); 
    } 

    public static void Info(string message) 
    { 
     _log.Info(message); 
    } 

    public static void Warn(string message) 
    { 
     _log.Warn(message); 
    } 

    public static void Error(string message) 
    { 
     _log.Error(message); 
    } 

    public static void Error(string message, Exception ex) 
    { 
     _log.Error(message, ex); 
    } 

    public static void Fatal(string message) 
    { 
     _log.Fatal(message); 
    } 

    public static void Fatal(string message, Exception ex) 
    { 
     _log.Fatal(message, ex); 
    } 
} 

在這種情況下,我使記錄器實例靜態。我不是100%肯定會始終按預期工作。通常我在依賴注入框架後面使用它,並將記錄器依賴性配置爲由框架處理的單例。您可以改爲使用實例方法將其作爲實例類,並將其放在靜態工廠類的後面。根據需要進行測試和調整。

有一對夫婦的額外的好處在這裏:

  1. 你在log4net的依賴被隔離到一個單獨的類。因此,如果您想要使用其他記錄器,則只需更改一個類而不是整個項目中的所有內容。
  2. 您可以很容易地將其抽象成依賴注入器。
  3. 您希望包含在所有日誌記錄語句中的任何其他常見功能都可以輕鬆全局地包含在此處。

我的第三個好處通常使用一個例子可能是這樣的:

private static string GetLocation() 
{ 
    var frame = new StackTrace(1).GetFrame(1); 
    var method = frame.GetMethod(); 
    return string.Format("{0}:{1}.{2}({3})", Environment.MachineName, method.ReflectedType.FullName, method.Name, frame.GetFileLineNumber().ToString()); 
} 

這得到更有意義從運行系統調試信息(儘管可能有性能損失,對於高音量系統值得測試)。所以我傳遞錯誤日誌記錄功能可能是這樣的:

public void Error(string message, Exception ex) 
{ 
    _log.Error(string.Format("{0}:{1}", GetLocation(), message), ex); 
} 
+1

我不會推薦用這種方式編寫一個包裝(將Info,Debug等委託給log4net的Info,Debug等)。 Log4net可以自己計算出適當的呼叫站點信息,而無需GetLocation中的代碼。正確包裝log4net的關鍵是使用Log方法並傳遞包裝類的類型作爲第一個參數。我會在答案中粘貼一個小例子。 – wageoghe

3

您可以使用lambda表達式。如:

log.Debug(() => "This is entry number:" + i); 

這樣,lambda只在.IsDebugEnabled調用後才被評估。

我們定義了一個擴展類(來自http://www.beefycode.com/post/Extension-Methods-for-Deferred-Message-Formatting-in-Log4Net.aspx拍攝),有擴展的方法,如:

public static class Log4NetExtensionMethods 
{ 
    public static void Debug(this ILog log, Func<string> formattingCallback) 
    { 
     if(log.IsDebugEnabled) 
     { 
      log.Debug(formattingCallback()); 
     } 
    } 
    // .. other methods as required... 
} 

我不知道是否有log4net的在最近的版本中添加的λ類型的支持 - 但是這已經工作爲了我。

+0

如果您試圖在只有一個特定的類上啓用調試日誌記錄,這是如何工作的? – sgmoore

-2

我會看預處理器(預編譯?)指令。

#if DEBUG 
{your logging code here} 
#endif 

類似的東西應該爲你做,然後代碼只能在調試模式下編譯。

您還可以使用的Conditional屬性在這樣一個方法: [System.Diagnostics.Conditional(「DEBUG」)] 私人無效YourMethodNameHere(YourMethodSignatureHere)

看看這個老問題有關何時/爲什麼/如何使用它們的更多信息。

http://stackoverflow.com/questions/3788605/if-debug-vs-conditionaldebug

+2

這與檢查啓用哪個日誌記錄級別無關(啓用了調試日誌記錄!= #if DEBUG) – Grhm

+0

我強烈建議不要這樣做。使用Log4Net的重點在於,您可以在不重新編譯應用程序的情況下啓用,禁用或重新配置日誌記錄 - 如果編譯出所有調試語句,這將不起作用。請考慮David的回答,可能與Grhm的提示相輔相成。 – CompuChip

6

@Grhm和@大衛有好的想法,但我不認爲大衛的包裝一樣好,因爲它可以。用這種方式包裝log4net。簡單地在包裝器上實現Debug,Info等,並將它們委託給log4net的Debug,Info等方法可以打破log4net記錄呼叫站點信息的能力。如果以這種方式換行,並告訴log4net記錄呼叫站點信息,log4net將在包裝器中寫出呼叫站點,而不是實際代碼中的呼叫站點,這正是您想要的。

我個人不喜歡使用單例記錄器,因爲你失去了調整程序不同部分日誌級別的能力。如果您正在處理多個組件,則可能需要爲一個組件啓用信息級別日誌記錄,但只對其他組件進行警告日誌記錄(或根本沒有)。使用單例記錄器,所有應用程序中的所有日誌記錄都處於同一級別。

當您錯誤地將log4net換行並且使用單個記錄器來覆蓋整個應用程序時,您正在否認自己大量的log4net內置(和強大)功能。

我回答了類似的問題(關於維護調用點信息)在這裏:

how to log method name when using wrapper class with Log4net

爲了節省時間,我已經包括了代碼示例在這裏(未編譯的和未經考驗的,而應該是接近).. 。

public class MyLog4NetWrapper 
{ 
    ILog log; 

    public MyLog4NetWrapper(string loggerName) 
    { 
    log = LogManager.GetLogger(loggerName) 
    } 

    public MyLog4NetWrapper(type loggerType) 
    { 
    log = LogManager.GetLogger(loggerType) 
    } 

    public void Info(string message) 
    { 
    if (log.IsInfoEnabled) log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, message, null); 
    } 

    //Defer expensive calculations unless logging is enabled - thanks Grhm for the example 
    public void Info(Func<string> formattingCallback) 
    { 
    if(log.IsInfoEnabled) 
    { 
     log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, formattingCallback(), null); 
    } 
    } 

    //Debug, Warn, Trace, etc are left as an exercise. 
} 

您可以在代碼中創建這些記錄器是這樣的:

public class MyClass 
{ 
    private static readonly ILog log = new MyLoggerWrapper(typeof(MyClass)); 

    public void DoSomething() 
    { 
    log.Info("Hello world!"); 
    } 
} 

編寫保存呼叫站點信息的log4net包裝器的技巧是使用Log方法並將包裝類型作爲第一個參數傳遞。

如果您要寫一個包裝器來實現您所問的功能(推遲執行日誌調用中的任何代價昂貴的代碼,而不明確檢查是否啓用了所需的日誌記錄級別),那麼您可能會並將該代碼放入包裝中,而不是將其作爲擴展方法來實現(這也會導致上述呼叫網站問題的相同丟失)。

祝你好運!

+0

將不起作用,MyLog4NetWrapper不是繼承自ILog – stenly

2

最簡單和最乾淨的方法可能是使用DebugFormat方法,該方法實際上會檢查調試級別是否啓用(請參閱Github-Code of log4net)。

0

如果包含命名空間log4net。UTIL,你可以呼籲的log4net的ILog以下擴展方法:

public static void ErrorExt(this ILog logger, Func<object> callback) 

啓用日誌錯誤級別時,這隻會調用lambda函數。無需編寫自己的擴展方法。它還通過將創建包裝在try catch方法中來防止在創建實際日誌消息時創建錯誤。