为了后期的日志统计分析方便,需要把日志保存到数据库,logback封装了输出日志到oracle、mysql等主流关系型数据库的适配器,但由于考虑mongoDB非关系数据库的存取速度和性能比较好以及可以满足日志存储的需求,所以需要自定义实现一个mongoDB的适配器,把日志输出到mongoDB。目前logback配置根据操作日志、性能日志、系统异常日志和sql日志定义四种适配器输出,分别输出到mongoDB的四个集合:log_invoke、log_performance、log_exception、log_execsql。
MongoDBAppender:自定义Mongodb输出类,用于logback输出方式的
MongoDBAppenderBase:MongoDB日志输出扩展基础类,主要是初始化和映射logback.xml的属性来获取mongoDB的连接和collection数据集。
注意:需要在logback.xml引入自己系统的配置文件,在定义mongoDB适配器使用该配置文件的mongoDB配置信息定义数据库连接项。
大致适配器类代码示例:
package xxx.xxx.logstatistics.service;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import xxxx.logstatistics.consts.LogItemNames;
import xxxx.utils.CheckUtil;
/**
*
*
* 类说明:自定义Mongodb输出类
*
* @author 作者 wu
*
*/
public class MongoDBAppender extends MongoDBAppenderBase
{
@Override
protected Document toMongoDocument(ILoggingEvent eventObject){
final Document doc = new Document();
String marker = eventObject.getMarker().toString();
doc.append("marker", marker);
doc.append("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").
format(new Date(eventObject.getTimeStamp())));
//doc.append("logger", eventObject.getLoggerName());
//doc.append("thread", eventObject.getThreadName());
//doc.append("message", eventObject.getFormattedMessage());
//获取异常日志的异常报错信息
if(StringUtils.equals(LogItemNames.EXCEPTION_MARKER, marker)){
StackTraceElementProxy[] stackTraceElementProxys = eventObject.getThrowableProxy().
getStackTraceElementProxyArray().clone();
String throwableMessage = eventObject.getThrowableProxy().getMessage();
int proxySize = stackTraceElementProxys.length;
if(proxySize > 0){
StringBuilder sb = new StringBuilder();
sb.append(eventObject.getThrowableProxy().getClassName());
sb.append(throwableMessage!=null?":"+throwableMessage:"");
for(int i = 0;i < proxySize;i++){
StackTraceElementProxy stackTraceElementProxy = stackTraceElementProxys[i];
sb.append(',');
sb.append(stackTraceElementProxy.toString());
}
doc.append("exception", sb.toString());
}
}
Map mdc = eventObject.getMDCPropertyMap();
if (mdc != null && !mdc.isEmpty()){
doc.append("requestUrl", mdc.get(LogItemNames.REQUEST_URI));
doc.append("methodName", mdc.get(LogItemNames.METHOD_NAME));
doc.append("args", mdc.get(LogItemNames.ARGS));
doc.append("userId", mdc.get(LogItemNames.USER_ID));
doc.append("ip", mdc.get(LogItemNames.CLIENT_ID));
doc.append("description", mdc.get(LogItemNames.DESCRIPTION));
//获取性能日志的请求处理时间
if(StringUtils.equals(LogItemNames.PERFORMANCE_MARKER, marker)){
doc.append("elapseTime", Long.parseLong(!CheckUtil.isNullorEmpty(mdc.get(LogItemNames.ELAPSE_TIME))?
mdc.get(LogItemNames.ELAPSE_TIME):"0"));
}
//获取SQL日志的sql类型和表名
if(StringUtils.equals(LogItemNames.SQL_MARKER, marker)){
doc.append("sql", mdc.get(LogItemNames.SQL));
doc.append("table", mdc.get(LogItemNames.TABLE));
}
}
return doc;
}
}
package xxx.xxx.logstatistics.service;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import java.net.UnknownHostException;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import com.mongodb.client.ListIndexesIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* 类说明:logback的日志扩展基础类
*
* @author 作者 wu
*
*/
public abstract class MongoDBAppenderBase
extends UnsynchronizedAppenderBase {
private MongoClient mongoClient;
private MongoDatabase mongoDataBase;
private MongoCollection mongoCollection;
private String host = "localhost";
private int port = 27017;
private String dbName = "logname";
private String collectionName="logEvents";
private Integer timeOut=null;
//保存的数据库用户名密码,暂时不需要
@SuppressWarnings("unused")
private String username;
@SuppressWarnings("unused")
private String password;
@Override
public void start() {
try {
connectToMongoDB();
super.start();
} catch (UnknownHostException e) {
addError("Error connecting to MongoDB server: " + host + ":" + port, e);
}
}
private void connectToMongoDB() throws UnknownHostException {
MongoClientOptions.Builder options=new MongoClientOptions.Builder();
if(timeOut!=null && timeOut>0)
options.serverSelectionTimeout(timeOut);
mongoClient = new MongoClient(new ServerAddress(host, port),options.build());
mongoDataBase = mongoClient.getDatabase(dbName);
mongoCollection = mongoDataBase.getCollection(collectionName);
//获取所有索引列表
ListIndexesIterable indexList = mongoCollection.listIndexes();
//判断mongodb的日志集合是否存在date字段索引,不存在则自动创建该字段索引
boolean isCreatedDateIndex = false;
for (Document document : indexList) {
Document doc = (Document)document.get("key");
if(doc.get("date") != null){
isCreatedDateIndex = true;
}
}
if(!isCreatedDateIndex){
//建立索引
mongoCollection.createIndex(new Document("date",-1));
}
}
protected abstract Document toMongoDocument(E event);
@Override
protected void append(E eventObject) {
Document document = toMongoDocument(eventObject);
mongoCollection.insertOne(document);
}
@Override
public void stop() {
if (mongoClient != null)
mongoClient.close();
super.stop();
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}