如何將Log4Net 日志保存到mongodb數據庫之實踐


log4net的大名早有耳聞,一直沒真正用過,這次開發APP項目准備在服務端使用log4net。 日志的數據量較大,頻繁的寫數據庫容易影響系統整體性能,所以獨立將日志寫到mongodb數據庫是不錯的選擇。---經過2天的摸索,總結出本文檔。
 
github有個開源項目log4mongo-net,另一位斯克迪亞作者根據開源項目又做了修改http://skyd.sinaapp.com/archives/1282
所以直接拿斯克迪亞的代碼來使用。
 
1、將log4net和mongodb驅動升級為最新版本。log2net: 1.2.15   mongodb: 2.2.3.3
2、新加了一個LogHelper類,所有的日志通過LogHelper的靜態方法來寫。
 
         
 1 public class Helper
 2     {
 3         private static readonly Helper instance = new Helper();
 4         private static ILog apiLog = null;
 5         private static ILog dbLog = null;
 6 
 7         private Helper()
 8         {
 9             // XmlConfigurator.Configure();    
10             LogLog.InternalDebugging = true;
11 
12 
13         }
14         public   static ILog  ApiLog()
15         {
16             if (apiLog == null)
17             {
18                 apiLog = LogManager.GetLogger("ApiLog");
19             }
20             return apiLog;
21 
22         }
23         public static ILog DbLog()
24         {
25             if (dbLog == null)
26             {
27                 dbLog = LogManager.GetLogger("ApiLog");
28             }
29             return dbLog;
30 
31         }
32     }
 調用
 Log4Mongo.Helper.DbLog().Info("abc");

 

 
        
 
3、自定義日志內容字段。網上能找到很多Log4net添加字段的方法,但因為我用的是修改過的log4mongo。而且簡化了配置,默認就是顯示所有字段。所以基本上通過改配置的都不適用。
開始准備使用 LogicalThreadContext.Properties["CustomColumn"] = "Custom value" ,可以將更多自定義的內容寫到日志。
 執行寫日志log.Debug(object)之前,先把各種自定義內容通過LogicalThreadContext設置好。然后再執行log.Debug(object)。
這種方式雖然可以達到目的,但感覺不是很方便。另一個問題是設置LogicalThreadContext后,這些值會一直存在,在同一個線程里再次執行 log.Debug(object)時,任然會記錄這些自定義的屬性值,不太好控制,容易寫錯內容。
 
另一種辦法是通過 log.Debug(object)里的object來實現,一般情況下都是直接 log.Debug("this is message")這種方式來寫日志,也可以將一個對象傳遞給 log.Debug。在往數據庫寫數據前,可以通過loggingEvent.RenderedMessage來讀取傳遞過來的對象,
  public  class LogInfo
    {
        public string AppKey { set; get; }
 
        public string UserID { set; get; }
        public string HostName { set; get; }
        public string IPAddress { set; get; }
        public string Message { set; get; }
 
        public override string ToString()
        {
            var bsonDoc = this.ToBsonDocument();
            return bsonDoc.ToString();
        }
    }
默認loggingEvent.RenderedMessage是獲取object 的ToString()的值,這里需要重寫一下ToString(),將類轉化為BsonDocument。
loggingEvent.RenderedMessage獲取BsonDocument后再拆分為具體的屬性。
 
        private static void BuildCustomMessage(string message,ref Log log)
        {
            try
            {
                var bson = BsonDocument.Parse(message);
                foreach (var item in bson.Elements)
                {
                    string value = item.Value.ToString();
                    if (item.Value.IsBsonNull)
                    {
                        value = string.Empty;
                    }
                    log.Properties.Add(item.Name, value);
                }
            }
            catch (Exception)
            {
                log.RenderedMessage = message;
            }
 
        }
為了方便,直接用 BsonDocument.Parse(message)來判斷是復雜對象還是String。直接用try catch來處理,對性能會有一點影響。做了測試相比直接傳遞String不需要BuildCustomMessage ,傳遞復雜對象時性能相差6%左右。這6%主要是對象ToString()轉化為BsonDocument,和BuildCustomMessage兩部份。
 
 
4、在測試時發生一個奇怪的問題,單獨一個語句log.Debug(string)可以輸出到控制台,但死活寫不到數據庫。而用一個循環來執行log.Debug(string),卻能正常寫到數據庫。  log4net有輸出Shutdown called on Hierarchy的提示,但不明白是什么意思,也不知道是否有相關。
解決辦法是將寫數據庫的操作由異步改為同步。具體原因沒找到。log4net自身問題、  mongodb驅動 、 log4net緩存等等都沒法排除。
 //collection.InsertOneAsync(BuildBsonDocument(loggingEvent));
 collection.InsertOne(BuildBsonDocument(loggingEvent));
 
 
5、為了方便日后的維護,將數據庫名和Collection(表名)設置到配置文件。
<appender name="MongoDBAppender" type="Log4Mongo.MongoDBAppender, Log4Mongo">
  <connectionString value="mongodb://root:123456@localhost:27017"/>
  <DatabaseName value="log4mongo"/>
  <CollectionName value="yyyyMM"/>
</appender>
配置的值在public class MongoDBAppender : AppenderSkeleton這個類里會直接獲取,不需要做額外處理
    public string ConnectionStringName { get; set; }
    public string DatabaseName { get; set; }
     public string CollectionName { get; set; }
  <CollectionName value="yyyyMM"/>這么寫的目的是讓日志根據日期來自動分表。通過修改配置就能按日、按月、按年來分表。
 
        private IMongoCollection<Log> GetCollection()
		{
            string tableName;
            tableName = CollectionName ?? "yyyyMM";
            tableName = "log"+ string.Format("{0:" + tableName + "}", DateTime.Now);
            return GetDatabase().GetCollection<Log>(tableName); 
		}
 
 
 
 
 
 
 
 
 
 
 
 
 


免責聲明!

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



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