最近项目需要接入日志管理平台,要求需要将项目log4j日志格式改为json,没系研究过log4j的我一时间还真被难住了,功夫不负有心人最后还是被我实现了。
本人借鉴三劫散仙发表的技术篇https://blog.csdn.net/u010454030/article/details/77994616文中引用的jsonevent-layout源码进行了分析和对log4j的进一步对接实现,话不多说,上代码:
1、所需maven依赖
maven 项目需要pom.xml引入依赖(非maven项目需要自行搜索对应jar包添加到lib):
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>asm</artifactId>
<version>1.0.2</version>
<scope>test</scope>
</dependency>
2、添加工具类到项目中
HostData.java
该类用于获取主机名称。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class HostData {
public String hostName;
public String getHostName() {
return this.hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
public HostData() {
try {
this.hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException var2) {
this.setHostIp("unknown-host");
}
}
}
JSONEventLayoutV1.java
该类中可使用this.logstashEvent.put(“自定义字段”,"***");进行追加输出内容:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import net.minidev.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
public class JSONEventLayoutV1 extends Layout {
private boolean locationInfo;
private String customUserFields;
private boolean ignoreThrowable;
private boolean activeIgnoreThrowable;
private String hostname;
private String hostip;
private String threadName;
private StringBuffer messagestr;
private long timestamp;
private String ndc;
private Map mdc;
private LocationInfo info;
private HashMap<String, Object> exceptionInformation;
private static Integer version = 1;
private JSONObject logstashEvent;
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS;
public static final String ADDITIONAL_DATA_PROPERTY = "com.chinatelecom.web.logs.JSONEventLayoutV1.UserFields";
public static String dateFormat(long timestamp) {
Date date = new Date();
//格式里的时如果用hh表示用12小时制,HH表示用24小时制。MM必须是大写!
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
date.setTime(timestamp);
return sdf.format(date);
}
public JSONEventLayoutV1() {
this(true);
}
public JSONEventLayoutV1(boolean locationInfo) {
this.locationInfo = false;
this.ignoreThrowable = false;
this.activeIgnoreThrowable = this.ignoreThrowable;
this.hostname = (new HostData()).getHostName();
this.locationInfo = locationInfo;
}
public String format(LoggingEvent loggingEvent) {
this.threadName = loggingEvent.getThreadName();
this.timestamp = loggingEvent.getTimeStamp();
this.exceptionInformation = new HashMap();
this.messagestr = new StringBuffer();
this.mdc = loggingEvent.getProperties();
this.ndc = loggingEvent.getNDC();
this.logstashEvent = new JSONObject();
String whoami = this.getClass().getSimpleName();
this.logstashEvent.put("app_name","testpro");
this.logstashEvent.put("log_type", "desc");
this.addEventData("level", loggingEvent.getLevel().toString());
this.addEventData("thread", this.threadName);
this.logstashEvent.put("log_time", dateFormat(this.timestamp));
this.logstashEvent.put("HOSTNAME", this.hostname);
if (this.locationInfo) {
this.info = loggingEvent.getLocationInformation();
this.addEventData("logger", this.info.getClassName()+"."+this.info.getMethodName()+"("+this.info.getFileName()+":"+this.info.getLineNumber()+")");
}
String userFieldsProperty;
if (this.getUserFields() != null) {
userFieldsProperty = this.getUserFields();
LogLog.debug("[" + whoami + "] Got user data from log4j property: " + userFieldsProperty);
this.addUserFields(userFieldsProperty);
}
if (System.getProperty("com.chinatelecom.web.logs.JSONEventLayoutV1.UserFields") != null) {
if (this.getUserFields() != null) {
LogLog.warn("[" + whoami + "] Loading UserFields from command-line. This will override any UserFields set in the log4j configuration file");
}
userFieldsProperty = System.getProperty("com.chinatelecom.web.logs.JSONEventLayoutV1.UserFields");
LogLog.debug("[" + whoami + "] Got user data from system property: " + userFieldsProperty);
this.addUserFields(userFieldsProperty);
}
if(loggingEvent.getRenderedMessage() != null){
this.messagestr.append(" message_info------> "+loggingEvent.getRenderedMessage()+"\n");
}
if (loggingEvent.getThrowableInformation() != null) {
ThrowableInformation throwableInformation = loggingEvent.getThrowableInformation();
if (throwableInformation.getThrowable().getClass().getCanonicalName() != null) {
this.messagestr.append(" exception_class------> "+throwableInformation.getThrowable().getClass().getCanonicalName()+"\n");
}
if (throwableInformation.getThrowable().getMessage() != null) {
this.messagestr.append(" exception_message------> "+throwableInformation.getThrowable().getMessage()+"\n");
}
if (throwableInformation.getThrowableStrRep() != null) {
String stackTrace = StringUtils.join(throwableInformation.getThrowableStrRep(), "\n");
this.messagestr.append(" stacktrace ------> "+stackTrace+"\n");
}
}
this.logstashEvent.put("message",this.messagestr.toString());
return this.logstashEvent.toString() + "\n";
}
public boolean ignoresThrowable() {
return this.ignoreThrowable;
}
public boolean getLocationInfo() {
return this.locationInfo;
}
public void setLocationInfo(boolean locationInfo) {
this.locationInfo = locationInfo;
}
public String getUserFields() {
return this.customUserFields;
}
public void setUserFields(String userFields) {
this.customUserFields = userFields;
}
public void activateOptions() {
this.activeIgnoreThrowable = this.ignoreThrowable;
}
private void addUserFields(String data) {
if (null != data) {
String[] pairs = data.split(",");
String[] arr$ = pairs;
int len$ = pairs.length;
for(int i$ = 0; i$ < len$; ++i$) {
String pair = arr$[i$];
String[] userField = pair.split(":", 2);
if (userField[0] != null) {
String key = userField[0];
String val = userField[1];
this.addEventData(key, val);
}
}
}
}
private void addEventData(String keyname, Object keyval) {
if (null != keyval) {
this.logstashEvent.put(keyname, keyval);
}
}
static {
ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss", UTC);
}
}
新建以上两个java到项目路径:
com\web\logs下,如下图:
3、修改log4j.properties
找到对应INFO DEBUG WARN ERROR 级别的配置区(根据要求和项目实际打印级别情况进行修改)
3.1)将layout输出程序改为:com.web.logs.JSONEventLayoutV1
3.2)将对应原有log4j的输出格式:layout.ConversionPattern 对应行注释
如下图:
以上完成无误后即可部署到环境上测试输出效果了~
{"message":" message_info------> Takes:17 ms [com.chinatelecom.dqmh.order.dao.impl.My189DaoPro.getMenuByFastCode(com.chinatelecom.dqmh.order.model.daobean.inBean.TreeMenuInputDaoBean@492a832d)] \n","HOSTNAME":"dqysh050050","level":"DEBUG","thread":"http-bio-9591-exec-30","logger":"com.chinatelecom.tymh.base.aop.TimeHandler.invoke(TimeHandler.java:37)","app_name":"testpro","log_type":"desc","log_time":"2020-02-27 14:34:58"}
以上是我的技术总结,转载请注明出处,本人愚钝有任何建议小伙伴和或有问题的小伙伴可以找我交流,qq:1252883458