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

BaiDunckley 8年前發布 | 40K 次閱讀 MongoDB 數據庫 日志處理 Log4Net

來自: http://www.cnblogs.com/zeallag/p/5205571.html

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的靜態方法來寫。

public  class LogHelper
  {
private static readonly LogHelper instance=new LogHelper();  private static ILog log = null;

private Logger() { // XmlConfigurator.Configure();
LogLog.InternalDebugging = true;

} public static LogHelper getInstance() { if (log == null) { log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } return instance; }</pre>

public void Debug(Object message, Exception exception)
{

log.Debug(message);

} public void Debug(Object message) {

log.Debug(message);

}</pre>

}
結果發現會有問題,log4net本來可以記錄日志發生所在的類名,和具體的行號。如果使用了單獨的類來寫日志,那么記錄下來的類和行號都是LogHelper的內容,沒法定位到錯誤具體發生的位置。原因就在ILog是通過LogManger.GetLogger(Type)方法來獲的,要想獲取錯誤的類和行號,那么必須在每個類里單獨調用LogManger.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)。(自己的理解,不一定準確) </div>

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();

}

}

</div>

默認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;

}

}

</div>

為了方便,直接用 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>

</div>

配置的值在public class MongoDBAppender : AppenderSkeleton這個類里會直接獲取,不需要做額外處理

public string ConnectionStringName { get; set; }

public string DatabaseName { get; set; }

public string CollectionName { get; set; }

</div>

<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); 
        }
</div> </span></span></span></span></span></span></span></span>

 本文由用戶 BaiDunckley 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!