咱們將版本向前切換到20051025,這期的關注點是filter。咱們在使用日誌的時候可能但願加上一些過濾器,在知足某些特定條件的時候才輸出。舉個簡單的使用方式以下:安全
<nlog> <targets><target name='debug' type='Debug' layout='${basedir} ${message}' /></targets> <rules> <logger name='*' minlevel='Debug' appendTo='debug'> <filters> <whenContains layout='${message}' substring='zzz' action='Ignore' /> </filters> </logger> </rules> </nlog>
FilterFactory負責初始化程序集中的Filterapp
public sealed class FilterFactory { private static TypeDictionary _filters = new TypeDictionary(); static FilterFactory() { foreach (Assembly a in ExtensionUtils.GetExtensionAssemblies()) { AddFiltersFromAssembly(a, ""); } }
反射初始化的方式和上篇的LayoutRender沒有差異debug
public static void AddFiltersFromAssembly(Assembly theAssembly, string prefix) { try { InternalLogger.Debug("AddFiltersFromAssembly('{0}')", theAssembly.FullName); foreach (Type t in theAssembly.GetTypes()) { FilterAttribute[]attributes = (FilterAttribute[])t.GetCustomAttributes(typeof(FilterAttribute), false); if (attributes != null) { foreach (FilterAttribute attr in attributes) { AddFilter(prefix + attr.Name, t); } } } } catch (Exception ex) { InternalLogger.Error("Failed to add filters from '" + theAssembly.FullName + "': {0}", ex); } }
只是簡單的從初始化好的_filters字典裏面取出便可,若是發現沒有,就會嘗試反射建立調試
仍是在LoggerImpl
的Write
方法中以鏈式的方式嵌入日誌
for (TargetWithFilterChain awf = targets; awf != null; awf = awf.Next) { Target app = awf.Target; try { FilterCollection filterChain = awf.FilterChain; FilterResult result = FilterResult.Neutral; for (int i = 0; i < filterChain.Count; ++i) { Filter f = filterChain[i]; result = f.Check(logMessage); if (result != FilterResult.Neutral) break; } if (result == FilterResult.Ignore) { if (InternalLogger.IsDebugEnabled) { InternalLogger.Debug("{0}.{1} Rejecting message because of a filter.", logger.Name, level); } continue; } }
這裏能夠看到,若是filter 的result 是Ignore
,該Message的Target就不須要再輸出了。code
TargetWithFilterChain
是包裝Target的關鍵類型:get
internal class TargetWithFilterChain { private Target _target; private FilterCollection _filterChain; private TargetWithFilterChain _next; public TargetWithFilterChain(Target a, FilterCollection filterChain) { _target = a; _filterChain = filterChain; }
這個類型的實現很簡單,就是將Target和FilterChain打包放在一塊兒。string
這裏值得注意的細節是InternalLogger
記錄了內部一些詳情,當咱們須要的時候直接開啓InternalLogger
調試日誌組件的工做過程。it
咱們對於InternalLogger
的要求是不要影響正常程序的運行過程,僅僅在調試日誌組件功能的時候使用,所以裏面的代碼犧牲異常也保證安全。io
static InternalLogger() { try { switch (ConfigurationSettings.AppSettings["nlog.internalLogToConsole"].ToLower()) { case "false": LogToConsole = false; break; case "true": LogToConsole = true; break; default: if (EnvironmentHelper.GetSafeEnvironmentVariable("NLOG_INTERNAL_LOG_TO_CONSOLE") != null) { LogToConsole = true; } break; } string levelString = ConfigurationSettings.AppSettings["nlog.internalLogLevel"]; if (levelString == null || levelString.Length == 0) levelString = EnvironmentHelper.GetSafeEnvironmentVariable("NLOG_INTERNAL_LOG_LEVEL"); if (levelString != null && levelString.Length > 0) LogLevel = LogLevel.FromString(EnvironmentHelper.GetSafeEnvironmentVariable("NLOG_INTERNAL_LOG_LEVEL")); LogFile = ConfigurationSettings.AppSettings["nlog.internalLogFile"]; if (LogFile == null) LogFile = EnvironmentHelper.GetSafeEnvironmentVariable("NLOG_INTERNAL_LOG_FILE"); Info("NLog internal logger initialized."); } catch {} }
能夠看到InternalLogger
的開啓不依賴於原來的配置文件結構,默認不輸出,以及取環境變量處理的也很是當心。
public static string GetSafeEnvironmentVariable(string name) { try { string s = Environment.GetEnvironmentVariable(name); if (s == "") return null; else return s; } catch (SecurityException) { return ""; } }