log4j日志改json格式自定义输出内容源码及说明

log4j日志改json格式自定义输出内容源码及说明

最近项目需要接入日志管理平台,要求需要将项目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下,如下图:
log4j日志改json格式自定义输出内容源码及说明_第1张图片

3、修改log4j.properties

找到对应INFO DEBUG WARN ERROR 级别的配置区(根据要求和项目实际打印级别情况进行修改)
3.1)将layout输出程序改为:com.web.logs.JSONEventLayoutV1
3.2)将对应原有log4j的输出格式:layout.ConversionPattern 对应行注释
如下图:

log4j日志改json格式自定义输出内容源码及说明_第2张图片

以上完成无误后即可部署到环境上测试输出效果了~

{"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

你可能感兴趣的:(技术总结)