利用log4net創建日志文件時過濾日志,這是坑還是?


前言

網上貌似沒有太多關於log4net過濾日志的資料,在研究過程中發現一點小問題,這里做下記錄,希望對后續有用到的童鞋起到一丟丟幫助作用。

log4net日志過濾

由於是在.NET Core中使用,所以這里為了演示,我們創建一個.NET Core控制台程序,同時呢通過安裝log4net最新穩定版本(2.0.8),好了,對於.NET Core而言,在開發時可直接配置web.config啟用日志功能,此時會將不同級別日志直接放在同一文件中,在實際開發中無論我們使用log4net還是serilog或者自己寫一個也好,大部分都會根據不同級別創建不同目錄,這樣更加易於后續跟蹤和排查問題。但是也會存在特殊的需求,比如本文中,我們只需要創建兩個日志文件,一個用於正常的信息文件記錄,一個是審計信息記錄,而且日志文件名可能不是日期格式,而是由我們自己根據配置給定,所以基於以上需求,我們來完成此項任務,首先,我們在控制台根目錄下創建如下單獨的log4net .config配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>

  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="log.txt" />
    <appendToFile value="true" />
    <countDirection value="1"/>
    <maximumFileSize value="10MB" />
    <maxSizeRollBackups value="-1" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"  />
    </layout>
  </appender>
  <root>
    <level value="DEBUG"/>
    <appender-ref ref="RollingFileAppender" />
  </root>
</log4net>

接下來則是讀取上述配置文件,在.NET Framework中我們需要在Properties文件夾下的AssemblyInfo類中添加讀取上述日志配置文件類,如下:

[assembly: log4net.Config.XmlConfigurator(ConfigFile ="log4net.config", Watch = true)]

但是到了.NET Core中壓根就沒有了上述Properties文件夾,此時我能想到的辦法只能根據日志配置文件所在的目錄去讀取(不知是否還有其他更好的辦法),並按照所提供的api,創建控制台程序集倉儲和讀取配置文件中XML的根節點並最終寫到log4net配置中,如下:

            var log4netFullPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\"));

            var log4netConfig = new XmlDocument();
            log4netConfig.Load(File.OpenRead(Path.Combine(log4netFullPath, "log4net.config")));

            var repo = LogManager.CreateRepository(Assembly.GetEntryAssembly(), typeof(Repository.Hierarchy.Hierarchy));

            Config.XmlConfigurator.Configure(repo, log4netConfig["log4net"]);

接下來我們則是創建類的日志接口,測試並打印日志,如下:

    class Program
    {
        static readonly ILog log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args) { var log4netFullPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\")); var log4netConfig = new XmlDocument(); log4netConfig.Load(File.OpenRead(Path.Combine(log4netFullPath, "log4net.config"))); var repo = LogManager.CreateRepository(Assembly.GetEntryAssembly(), typeof(Repository.Hierarchy.Hierarchy)); Config.XmlConfigurator.Configure(repo, log4netConfig["log4net"]); log.Info("Program start success......"); } }

在log4net中,通過添加節點PropertyFilter來實現屬性過濾,如下:

 <filter type="log4net.Filter.PropertyFilter">
      <key value="" />
      <stringToMatch value="" />
      <acceptOnMatch value="" />
 </filter>

如上key代表需要我們定義的鍵,而stringToMatch代表我們通過對應鍵所添加的值,最后acceptOnMatch代表是否接受匹配,為布爾值,默認為true(接收匹配就記錄)。若我們在配置文件appender節點小添加如下節點:

<filter type="log4net.Filter.PropertyFilter">
      <key value="filter" />
      <stringToMatch value="1" />
      <acceptOnMatch value="false" />
 </filter>

如上設置代表,當我們對應設置屬性的鍵為filter,而值為1時則記錄日志,接下來我們在上述控制台基礎通過log4net提供的api去設置線程級別的屬性,添加如下:

ThreadContext.Properties["filter"] = "1";
log.Info("1......");

ThreadContext.Properties["filter"] = "0";
log.Info("0......");

我們通過設置鍵filter的值為1和0時都記錄下來了,很顯然沒達到要求,我們只想記錄對應值等於1的日志,此時需要在上述節點繼續添加如下一行來只記錄此屬性鍵對應值的日志:

好了,到此有了對log4net日志過濾的基礎鋪墊,接下來實現我們的需求:創建兩個日志文件,一個記錄正常信息(排除審計),一個只記錄審計信息 。接下來我們對配置文件修改如下:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>

  <!--只接收auditing日志-->
  <appender name="AuditingRollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="auditing.txt" />
    <appendToFile value="true" />
    <countDirection value="1"/>
    <maximumFileSize value="10MB" />
    <maxSizeRollBackups value="-1" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"  />
    </layout>
    
    <filter type="log4net.Filter.PropertyFilter">
      <key value="filter" />
      <stringToMatch value="auditing" />
    </filter>
    <filter type="log4net.Filter.DenyAllFilter" />
  
  </appender>

  <!--只接收除auditing以外的日志-->
  <appender name="ExceptAuditingRollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="non-auditing.txt" />
  <appendToFile value="true" />
  <countDirection value="1"/>
  <maximumFileSize value="10MB" />
  <maxSizeRollBackups value="-1" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"  />
  </layout>
    
  <filter type="log4net.Filter.PropertyFilter">
    <key value="filter" />
    <stringToMatch value="auditing" />
    <acceptOnMatch value="false" />
  </filter>
    
  </appender>
  <root>
    <level value="DEBUG"/>
    <appender-ref ref="AuditingRollingFileAppender" />
    <appender-ref ref="ExceptAuditingRollingFileAppender" />
  </root>
</log4net>

上述配置文件就不用我多講,接下來我們將控制台中上述測試打印的日志給移除,我們添加如下代碼進行測試:

 //只接收auditing日志
ThreadContext.Properties["filter"] = "auditing";
log.Info("auditing.......");

//只接收除了auditing以外日志
ThreadContext.Properties["filter"] = "non-auditing";
log.Info("non-auditing.......");
log.Warn("測試非審計......");

//只接收auditing日志
ThreadContext.Properties["filter"] = "auditing";
log.Info("測試審計.......");

雖然創建一個審計日志文件和一個非審計日志文件,我們的配置也沒任何毛病,但是通過上述日志輸出發現,非審計文件壓根沒有日志卻全部到了審計文件里,這是為何呢? 經過排查,我開始猜測log4net難道是對值進行模糊匹配嗎?為了驗證猜想,我將上述對非審計的值(多加一個字母Z)修改成如下:

 

只要我們將上述非審計日志的值設置時並不包含審計日志里的值(auditing)就沒問題,真香,哈哈。上述排除auditing節點以外日志的節點是從反向考慮,當然我們也可以從正向考慮,設置為其他值,還是注意不要包含auditing,並且只記錄該值的記錄,如下

  <!--只接收除auditing以外的日志-->
  <appender name="ExceptAuditingRollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="non-auditing.txt" />
    <appendToFile value="true" />
    <countDirection value="1"/>
    <maximumFileSize value="10MB" />
    <maxSizeRollBackups value="-1" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"  />
    </layout>

    <filter type="log4net.Filter.PropertyFilter">
      <key value="filter" />
      <stringToMatch value="other" />
      <acceptOnMatch value="true" />
    </filter>
    <filter type="log4net.Filter.DenyAllFilter" />
  </appender>

 

如上我們在輸出非審計日志時,我們設置了INFO和Warn級別日志,要是我們想過濾掉上述Warn以及其他級別,只想保留Info級別日志,那么我們應該腫么辦呢?我們添加輸出內置INFO級別日志,除此級別外拒絕其他級別的日志,如下:

<filter type="log4net.Filter.LevelMatchFilter">
      <levelToMatch value="INFO"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter"/>

 

 

在log4net中除了設置屬性過濾外,還可以比如通過日志中的字符串進行匹配,比如我們添加如下字符串匹配節點,說明包含cache_log的值將不會被記錄:

<filter type="log4net.Filter.StringMatchFilter">
      <stringToMatch value="cache_log" />
      <acceptOnMatch value="false" />
 </filter>

log4net運行時創建日志

我們可能會遇到根據什么規則或者需要在運行時創建日志文件,這個時候就不能如上寫死日志文件名了,我們通過log4net提供給我們的api【%property】來實現,我們將上述節點file進行如下修改:

<file type="log4net.Util.PatternString" value="D:\logs\%property{LogName}" />

接着添加創建日志文件名代碼,注意要將如下第一行放在第二行前面,否則創建的文件名將為null,如下:

 ThreadContext.Properties["LogName"] = "runtime.log";

 Config.XmlConfigurator.Configure(repo, log4netConfig["log4net"]);

總結 

log4net在以前.NET Framework中用到的比較多,但是並未使用過過濾日志這一特性,我們通過業務模塊不同來過濾日志,最近要使用時研究發現的問題,看到網上對此資料甚少,所以在此作為備忘錄,能夠幫到有需要的童鞋當然再好不過啦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM