背景
在分布式系統中,我們有多個web app,這些web app可能分別部署在不同的物理服務器上,並且有各自的日志輸出。當生產問題來臨時,很多時候都需要去各個日志文件中查找可能的異常,相當耗費人力。日志存儲多以文本文件形式存在,當有需求需要對日志進行分析挖掘時,這個處理起來也是諸多不便,而且效率低下。
為了方便對這些日志進行統一管理和分析,我們可以將日志統一輸出到指定的數據庫系統中,再由日志分析系統去管理。由於這里是mongodb的篇章,所以主觀上以mongodb來做日志數據存儲;客觀上,一是因為它輕便、簡單,與log4j整合方便,對系統的侵入性低。二是因為它與大型的關系型數據庫相比有很多優勢,比如查詢快速、bson存儲結構利於擴展、免費等。
解決方案
整合mongodb和log4j
1、安裝mongodb數據庫,並在本地啟動,默認端口是27017,詳細請參考:玩轉mongodb(一):初識mongodb
2、新建一個maven(maven版本要求3.0以上)工程,選擇maven-archetype-quickstart,工程名:log4j2mongo
3、在pom.xml文件中,添加log4j、log4mongo-java、mongo-java-driver三個依賴。具體代碼如下:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 3 <modelVersion>4.0.0</modelVersion> 4 5 <groupId>com.manyjar</groupId> 6 <artifactId>log4j2mongo</artifactId> 7 <version>0.0.1-SNAPSHOT</version> 8 <packaging>jar</packaging> 9 10 <name>log4j2mongo</name> 11 <url>http://maven.apache.org</url> 12 13 <properties> 14 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 15 <junit.version>3.8.1</junit.version> 16 <log4j.version>1.2.17</log4j.version> 17 <log4mongo.version>0.7.4</log4mongo.version> 18 <mongo-java-driver.version>2.8.0</mongo-java-driver.version> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>junit</groupId> 24 <artifactId>junit</artifactId> 25 <version>${junit.version}</version> 26 <scope>test</scope> 27 </dependency> 28 29 <dependency> 30 <groupId>log4j</groupId> 31 <artifactId>log4j</artifactId> 32 <version>${log4j.version}</version> 33 </dependency> 34 35 <dependency> 36 <groupId>org.log4mongo</groupId> 37 <artifactId>log4mongo-java</artifactId> 38 <version>${log4mongo.version}</version> 39 </dependency> 40 41 <dependency> 42 <groupId>org.mongodb</groupId> 43 <artifactId>mongo-java-driver</artifactId> 44 <version>${mongo-java-driver.version}</version> 45 </dependency> 46 </dependencies> 47 </project>
4、在resources文件夾中,添加log4j.properties文件。文件中主要添加log4j對mongodb的適配器org.log4mongo.MongoDbAppender。這里的適配器是log4mongo-java這個jar包提供。mongodb數據庫的ip:127.0.0.1,port:27017,庫名:logs,集合名:log。具體配置如下:
1 log4j.rootLogger=DEBUG,MongoDB 2 log4j.appender.MongoDB=org.log4mongo.MongoDbAppender 3 log4j.appender.MongoDB.databaseName=logs 4 log4j.appender.MongoDB.collectionName=log 5 log4j.appender.MongoDB.hostname=127.0.0.1 6 log4j.appender.MongoDB.port=27017 7 8 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 9 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 10 log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n
5、在java文件夾中,com.manyjar.log4j2mongo這個包中,添加Main.java文件,觀察文件中的代碼可以發現,和我們平時用log4j來寫日志一模一樣,把日志流到mongodb這件事情,對業務開發的程序員完全透明。具體代碼如下:
1 package com.manyjar.log4j2mongo; 2 3 import org.apache.log4j.Logger; 4 5 import com.mongodb.BasicDBObject; 6 import com.mongodb.DBObject; 7 8 public class Main { 9 10 public static void main(String[] args) { 11 Logger logger = Logger.getLogger(Main.class); 12 13 for (int i = 0; i < 10000000; i++) { 14 DBObject bson = new BasicDBObject(); 15 bson.put("name", "ryan"+i); 16 logger.debug(bson); 17 } 18 } 19 }
6、步驟5中,我們執行了1000萬次的日志插入,數據結構如下:

默認的數據量大小有10G:

這里,我們可以看到,日志的數據存儲量相對不小。如果需要修改日志數據的存儲結構,可以用log4mongo的源代碼進行二次開發。
如果數據量過大,我們可以用TTL索引(過期自動刪除)或固定集合大小兩種方式來解決:
TTL索引:db.log_events.createIndex({"timestamp": 1},{expireAfterSeconds: 60*60*24*30}) #1個月后過期后刪除
將log集合修改成固定大小集合:db.runCommand({"convertToCapped":"log",size:10000})。
喜歡請微信掃描下面二維碼,關注我公眾號--“精修Java”,做一些實戰項目中的問題和解決方案分享。

