SpringBoot2.x下使用log4j2打印JSON日志(支持ELK机制)

  1. pom依赖
    1.1 替换日志依赖
    1.2 项目需要Jackson包,保证项目中包含jackson的依赖
  2. 修改代码
    2.1 打印pv日志类
    -2.1.1 修改配置文件
    -2.1.2 修改输出代码
    2.2 打印业务日志
    2.3 打印error日志
  3. 附录代码
    附录1:输出项目名称
    附录2:重写Log4jLogEvent源码
    附录3:完整的log4j2-spring.xml文件
  4. 推荐阅读

默认情况下SpringBoot2.x采用的是logback打印日志,而log4j2性能优于logback。
默认情况下log4j2才有的是文本方式输出日志,但是ELK搜集日志要求日志格式为JSON,那么需要修改默认的输出格式。

1. pom依赖

1.1 替换日志依赖

移除logback日志框架,采用log4j2日志框架

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!--移除logback日志包-->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--增加log4j2日志包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-core</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
       <!--增加JsonLayout日志包-->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>
        <!--升级日志jar-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.12.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.12.1</version>
        </dependency>
    </dependencies>

1.2 项目需要Jackson包,保证项目中包含jackson的依赖

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.0</version>
        </dependency>

2. 修改代码

将某些info日志输出为json格式,并打印到特定的文件中,由filebeat+kafka+logstash体系进行收集。最终在kibana上查询日志。

2.1 打印pv日志类

2.1.1 修改配置文件

某些info日志打印到特定的文件实现方案:log4j2-spring.xml中定义

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,
    当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="60">
    <Properties>
        <property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %t %c - %m%n</property>
        <property name="log_pattern_error">%d{yyyy-MM-dd HH:mm:ss.SSS} %traceId %-5level %t %c - %m%n</property>
        <property name="log.charset">UTF-8</property>
        <property name="log.path">log/</property>
        <property name="every_file_size">500 MB</property>
        <!--需替换 日志文件名称-->
        <property name="project_name">test-service</property>
    </Properties>
    <appenders>
      <!--控制台日志输出-->
        <console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="${log_pattern}" charset="${log.charset}"/>
        </console>
        <!--pv日志文件输出-->
        <RollingRandomAccessFile name="pvLogger"
                                 fileName="${log.path}${project_name}_pv.log"
                                 filePattern="${log.path}${project_name}_pv.%d{yyyy-MM-dd}_%i.log.gz">
            <JsonLayout compact="true" eventEol="true" objectMessageAsJsonObject="true"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>
            <DefaultRolloverStrategy fileIndex="nomax">
                <Delete basePath="${log.path}" maxDepth="1">
                    <IfFileName glob="*.log.gz"/>
                    <IfLastModified age="P30D"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingRandomAccessFile>
    </appenders>
    <loggers>
        <Root level="info">
            <AppenderRef ref="stdout"/>
            <AppenderRef ref="file"/>
            <AppenderRef ref="error"/>
        </Root>
 
        <AsyncLogger name="com.tellme.config.AccessLog4jUtil" additivity="false" level="info">
            <appender-ref ref="stdout"/>
            <appender-ref ref="pvLogger"/>
        </AsyncLogger>
    </loggers>

</configuration>

关键点:<JsonLayout compact="true" eventEol="true" objectMessageAsJsonObject="true"/>

即:filter中调用AccessLog4jUtil工具类打印的日志,会打印在stdout控制台上,也会打印到pvLogger文件中。

最终打印的日志格式:

最终打印的日志格式.png

2.1.2 修改输出代码

需求:message的值不是字符串,而是JSON字段,便于kibana查看:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.message.ObjectMessage;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * pv日志
 * 增加marker输出
 */
public class AccessLog4jUtil {

    private static ObjectMapper objectMapper = new ObjectMapper();


    final static Marker MARKER_WHITESPACE = MarkerManager.getMarker("ANA_WHITESPACE");

    final static org.apache.logging.log4j.Logger log = LogManager.getLogger(AccessLog4jUtil.class);

    private static String projectName = null;

    public static void logRequest(String project, String url, String method, String params, String uid, Object custom) {
        try {
            if (Objects.isNull(projectName)) {
                projectName = ProjectNameUtil.getProjectName();
            }
            Map<String, Object> map = new HashMap();
            map.put("type", "pv_access");
            map.put("uri", url);
            map.put("method", method);
            map.put("parameters", params);
            map.put("uid", uid);
            map.put("custom", custom);
            map.put("project", project);
            map.put("projectName", projectName);
            ObjectMessage objectMessage = new ObjectMessage(map);
            log.info(MARKER_WHITESPACE, objectMessage);
        } catch (Exception e) {
            log.error("", e);
        }
    }
}

Apache Log4j2 API官方使用指南(一) —— Marker类怎么用

image.png

需求:此时返回的JSON并不是很满足需求,一些字段不需要输出,message字段名需要修改...

解决办法:在业务代码中重写源码,如下图所示:

image.png

代码见附录2。

原理:使用Jackson的标签(@JsonIgnore@JsonProperty(value = "m_content"))来约束输出的json串。

最终输出.png

2.2 打印业务日志

与2.1同理,配置文件中新增busLogger,即com.tellme.config.SealLogger类打印的日志会进入新的文件

        <AsyncLogger name="com.tellme.config.SealLogger" additivity="false" level="info">
            <appender-ref ref="stdout"/>
            <appender-ref ref="busLogger"/>
        </AsyncLogger>

需要进入到kibana的业务日志均调用该类打印,使得busLog日志的收集到同一个文件中。

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.message.ObjectMessage;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 打印业务日志
 * info格式,输出的是marker格式的。
 */
public class SealLogger {

    final static org.apache.logging.log4j.Logger logger = LogManager.getLogger(SealLogger.class);

    private final static DateTimeFormatter LOG_DTF = DateTimeFormatter
            .ofPattern("yyyy-MM-dd HH:mm:ss");

    final static Marker MARKER_WHITESPACE = MarkerManager.getMarker("ANA_WHITESPACE");

    private static String projectName = null;

    public static void log(String appendLogPattern, Object... appendLogArguments) {
        FormattingTuple ft = MessageFormatter.arrayFormat(appendLogPattern, appendLogArguments);
        String appendLog = ft.getMessage();
        StackTraceElement callInfo = (new Throwable()).getStackTrace()[1];

        logDetail(callInfo, appendLog);
    }

    public static void log(Throwable e) {
        StringWriter stringWriter = new StringWriter();
        e.printStackTrace(new PrintWriter(stringWriter));
        String appendLog = stringWriter.toString();
        StackTraceElement callInfo = (new Throwable()).getStackTrace()[1];
        logDetail(callInfo, appendLog);
    }

    private static void logDetail(StackTraceElement callInfo, String appendLog) {
        try {
            if (Objects.isNull(projectName)){
                projectName = ProjectNameUtil.getProjectName();
            }
            Map<String, Object> map = new HashMap();
            map.put("type", "tell_info");
            map.put("projectName", projectName);
            map.put("time", LocalDateTime.now().format(LOG_DTF));
            map.put("className", callInfo.getClassName());
            map.put("methodName", callInfo.getMethodName());
            map.put("appendLog", appendLog);
            ObjectMessage objectMessage = new ObjectMessage(map);
            logger.info(MARKER_WHITESPACE, objectMessage);
        }catch (Exception e){
            logger.error("",e);
        }

    }

}

2.3 打印error日志

收集项目根路径com.tellme下所有的日志,到<AppenderRef ref="error"/>

        <AsyncLogger name="com.tellme" level="info" includeLocation="false" additivity="false">
            <appender-ref ref="stdout"/>
            <AppenderRef ref="file"/>
            <AppenderRef ref="error"/>
        </AsyncLogger>

详见附录2:因为修改了json打印的格式,最终得到的JSON串如下所示:

最终输出.png

3. 附录代码

附录1:输出项目名称

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;

import java.io.File;

public class ProjectNameUtil {
    final static org.apache.logging.log4j.Logger log = LogManager.getLogger(ProjectNameUtil.class);

    public static String getProjectName() {
        String projectName = null;
        try {
            java.net.URL url = SealLogger.class.getProtectionDomain().getCodeSource().getLocation();
            String urlName = java.net.URLDecoder.decode(url.getPath(), "utf-8");
            //解析url的Name字段
            if (urlName.contains("jar")) {
                String[] jars = StringUtils.splitByWholeSeparator(urlName, ".jar");
                String jarName = jars[0];
                String[] strings = StringUtils.splitByWholeSeparator(jarName, File.separator);
                projectName = strings[strings.length - 1];
            }
        } catch (Exception e) {
            log.error("", e);
        }

        return projectName;
    }
}

附录2:重写Log4jLogEvent源码

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.logging.log4j.core.impl;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.ContextDataInjector;
import org.apache.logging.log4j.core.util.*;
import org.apache.logging.log4j.core.time.Instant;
import org.apache.logging.log4j.core.time.MutableInstant;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.async.RingBufferLogEvent;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.message.LoggerNameAwareMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.message.TimestampMessage;
import org.apache.logging.log4j.util.StackLocatorUtil;
import org.apache.logging.log4j.util.StringMap;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;

/**
 * Implementation of a LogEvent.
 */
public class Log4jLogEvent implements LogEvent {

    private static final long serialVersionUID = -8393305700508709443L;
    private static final Clock CLOCK = ClockFactory.getClock();
    private static volatile NanoClock nanoClock = new DummyNanoClock();
    private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector();

    private final String loggerFqcn;
    @JsonIgnore
    private final Marker marker;
    private final Level level;
    private final String loggerName;
    private Message message;
    private final MutableInstant instant = new MutableInstant();
    private final transient Throwable thrown;
    private ThrowableProxy thrownProxy;
    private final StringMap contextData;
    private final ThreadContext.ContextStack contextStack;
    private long threadId;
    private String threadName;
    private int threadPriority;
    private StackTraceElement source;
    private boolean includeLocation;
    private boolean endOfBatch = false;
    /** @since Log4J 2.4 */
    private final transient long nanoTime;

    private String skyWalkingDynamicField = TraceContext.traceId();

    private String customParam="自定义参数配置";

    public String getCustomParam() {
        return customParam;
    }

    public String getSkyWalkingDynamicField(){
        return skyWalkingDynamicField;
    }

    /** LogEvent Builder helper class. */
    public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {

        private String loggerFqcn;
        private Marker marker;
        private Level level;
        private String loggerName;
        private Message message;
        private Throwable thrown;
        private final MutableInstant instant = new MutableInstant();
        private ThrowableProxy thrownProxy;
        private StringMap contextData = createContextData((List<Property>) null);
        private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
        private long threadId;
        private String threadName;
        private int threadPriority;
        private StackTraceElement source;
        private boolean includeLocation;
        private boolean endOfBatch = false;
        private long nanoTime;

        public Builder() {
        }

        public Builder(final LogEvent other) {
            Objects.requireNonNull(other);
            if (other instanceof RingBufferLogEvent) {
                ((RingBufferLogEvent) other).initializeBuilder(this);
                return;
            }
            if (other instanceof MutableLogEvent) {
                ((MutableLogEvent) other).initializeBuilder(this);
                return;
            }
            this.loggerFqcn = other.getLoggerFqcn();
            this.marker = other.getMarker();
            this.level = other.getLevel();
            this.loggerName = other.getLoggerName();
            this.message = other.getMessage();
            this.instant.initFrom(other.getInstant());
            this.thrown = other.getThrown();
            this.contextStack = other.getContextStack();
            this.includeLocation = other.isIncludeLocation();
            this.endOfBatch = other.isEndOfBatch();
            this.nanoTime = other.getNanoTime();

            // Avoid unnecessarily initializing thrownProxy, threadName and source if possible
            if (other instanceof Log4jLogEvent) {
                final Log4jLogEvent evt = (Log4jLogEvent) other;
                this.contextData = evt.contextData;
                this.thrownProxy = evt.thrownProxy;
                this.source = evt.source;
                this.threadId = evt.threadId;
                this.threadName = evt.threadName;
                this.threadPriority = evt.threadPriority;
            } else {
                if (other.getContextData() instanceof StringMap) {
                    this.contextData = (StringMap) other.getContextData();
                } else {
                    if (this.contextData.isFrozen()) {
                        this.contextData = ContextDataFactory.createContextData();
                    } else {
                        this.contextData.clear();
                    }
                    this.contextData.putAll(other.getContextData());

                }
                this.thrownProxy = other.getThrownProxy();
                this.source = other.getSource();
                this.threadId = other.getThreadId();
                this.threadName = other.getThreadName();
                this.threadPriority = other.getThreadPriority();
            }
        }

        public Builder setLevel(final Level level) {
            this.level = level;
            return this;
        }

        public Builder setLoggerFqcn(final String loggerFqcn) {
            this.loggerFqcn = loggerFqcn;
            return this;
        }

        public Builder setLoggerName(final String loggerName) {
            this.loggerName = loggerName;
            return this;
        }
        @JsonIgnore
        public Builder setMarker(final Marker marker) {
            this.marker = marker;
            return this;
        }

        public Builder setMessage(final Message message) {
            this.message = message;
            return this;
        }

        public Builder setThrown(final Throwable thrown) {
            this.thrown = thrown;
            return this;
        }

        public Builder setTimeMillis(final long timeMillis) {
            this.instant.initFromEpochMilli(timeMillis, 0);
            return this;
        }

        @JsonIgnore
        public Builder setInstant(final Instant instant) {
            this.instant.initFrom(instant);
            return this;
        }

        public Builder setThrownProxy(final ThrowableProxy thrownProxy) {
            this.thrownProxy = thrownProxy;
            return this;
        }

        @Deprecated
        public Builder setContextMap(final Map<String, String> contextMap) {
            contextData = ContextDataFactory.createContextData(); // replace with new instance
            if (contextMap != null) {
                for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
                    contextData.putValue(entry.getKey(), entry.getValue());
                }
            }
            return this;
        }

        public Builder setContextData(final StringMap contextData) {
            this.contextData = contextData;
            return this;
        }

        public Builder setContextStack(final ThreadContext.ContextStack contextStack) {
            this.contextStack = contextStack;
            return this;
        }
        @JsonIgnore
        public Builder setThreadId(final long threadId) {
            this.threadId = threadId;
            return this;
        }

        public Builder setThreadName(final String threadName) {
            this.threadName = threadName;
            return this;
        }

        @JsonIgnore
        public Builder setThreadPriority(final int threadPriority) {
            this.threadPriority = threadPriority;
            return this;
        }

        public Builder setSource(final StackTraceElement source) {
            this.source = source;
            return this;
        }

        public Builder setIncludeLocation(final boolean includeLocation) {
            this.includeLocation = includeLocation;
            return this;
        }

        public Builder setEndOfBatch(final boolean endOfBatch) {
            this.endOfBatch = endOfBatch;
            return this;
        }

        /**
         * Sets the nano time for the event.
         * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event
         *          was created.
         * @return this builder
         */
        public Builder setNanoTime(final long nanoTime) {
            this.nanoTime = nanoTime;
            return this;
        }

        @Override
        public Log4jLogEvent build() {
            initTimeFields();
            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown,
                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source,
                    instant.getEpochMillisecond(), instant.getNanoOfMillisecond(), nanoTime);
            result.setIncludeLocation(includeLocation);
            result.setEndOfBatch(endOfBatch);
            return result;
        }

        private void initTimeFields() {
            if (instant.getEpochMillisecond() == 0) {
                instant.initFrom(CLOCK);
            }
        }
    }

    /**
     * Returns a new empty {@code Log4jLogEvent.Builder} with all fields empty.
     * @return a new empty builder.
     */
    public static Builder newBuilder() {
        return new Builder();
    }

    public Log4jLogEvent() {
        this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
                0, null, CLOCK, nanoClock.nanoTime());
    }

    /**
    *
    * @deprecated use {@link Builder} instead. This constructor will be removed in an upcoming release.
    */
   @Deprecated
   public Log4jLogEvent(final long timestamp) {
       this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
               0, null, timestamp, 0, nanoClock.nanoTime());
   }

   /**
    * Constructor.
    * @param loggerName The name of the Logger.
    * @param marker The Marker or null.
    * @param loggerFQCN The fully qualified class name of the caller.
    * @param level The logging Level.
    * @param message The Message.
    * @param t A Throwable or null.
    * @deprecated use {@link Builder} instead. This constructor will be removed in an upcoming release.
    */
   @Deprecated
   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
                        final Message message, final Throwable t) {
       this(loggerName, marker, loggerFQCN, level, message, null, t);
   }

   /**
    * Constructor.
    * @param loggerName The name of the Logger.
    * @param marker The Marker or null.
    * @param loggerFQCN The fully qualified class name of the caller.
    * @param level The logging Level.
    * @param message The Message.
    * @param properties the properties to be merged with ThreadContext key-value pairs into the event's ReadOnlyStringMap.
    * @param t A Throwable or null.
    */
   // This constructor is called from LogEventFactories.
   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
                        final Message message, final List<Property> properties, final Throwable t) {
       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties),
           ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy
           0, // thread id
           null, // thread name
           0, // thread priority
           null, // StackTraceElement source
           CLOCK, //
           nanoClock.nanoTime());
   }

    /**
     * Constructor.
     * @param loggerName The name of the Logger.
     * @param marker The Marker or null.
     * @param loggerFQCN The fully qualified class name of the caller.
     * @param level The logging Level.
     * @param message The Message.
     * @param properties the properties to be merged with ThreadContext key-value pairs into the event's ReadOnlyStringMap.
     * @param t A Throwable or null.
     */
    // This constructor is called from LogEventFactories.
    public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN,
        final StackTraceElement source, final Level level, final Message message, final List<Property> properties,
        final Throwable t) {
        this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties),
            ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy
            0, // thread id
            null, // thread name
            0, // thread priority
            source, // StackTraceElement source
            CLOCK, //
            nanoClock.nanoTime());
    }

   /**
    * Constructor.
    * @param loggerName The name of the Logger.
    * @param marker The Marker or null.
    * @param loggerFQCN The fully qualified class name of the caller.
    * @param level The logging Level.
    * @param message The Message.
    * @param t A Throwable or null.
    * @param mdc The mapped diagnostic context.
    * @param ndc the nested diagnostic context.
    * @param threadName The name of the thread.
    * @param location The locations of the caller.
    * @param timestampMillis The timestamp of the event.
    * @deprecated use {@link Builder} instead. This constructor will be removed in an upcoming release.
    */
   @Deprecated
   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
                        final Message message, final Throwable t, final Map<String, String> mdc,
                        final ThreadContext.ContextStack ndc, final String threadName,
                        final StackTraceElement location, final long timestampMillis) {
       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0,
               threadName, 0, location, timestampMillis, 0, nanoClock.nanoTime());
   }

   /**
    * Create a new LogEvent.
    * @param loggerName The name of the Logger.
    * @param marker The Marker or null.
    * @param loggerFQCN The fully qualified class name of the caller.
    * @param level The logging Level.
    * @param message The Message.
    * @param thrown A Throwable or null.
    * @param thrownProxy A ThrowableProxy or null.
    * @param mdc The mapped diagnostic context.
    * @param ndc the nested diagnostic context.
    * @param threadName The name of the thread.
    * @param location The locations of the caller.
    * @param timestamp The timestamp of the event.
    * @return a new LogEvent
    * @deprecated use {@link Builder} instead. This method will be removed in an upcoming release.
    */
    @Deprecated
    public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
                                            final Level level, final Message message, final Throwable thrown,
                                            final ThrowableProxy thrownProxy,
                                            final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
                                            final String threadName, final StackTraceElement location,
                                            final long timestamp) {
        final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
                thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, 0, nanoClock.nanoTime());
        return result;
    }

    /**
     * Constructor.
     * @param loggerName The name of the Logger.
     * @param marker The Marker or null.
     * @param loggerFQCN The fully qualified class name of the caller.
     * @param level The logging Level.
     * @param message The Message.
     * @param thrown A Throwable or null.
     * @param thrownProxy A ThrowableProxy or null.
     * @param contextData The key-value pairs from the context.
     * @param contextStack the nested diagnostic context.
     * @param threadId the thread ID
     * @param threadName The name of the thread.
     * @param threadPriority the thread priority
     * @param source The locations of the caller.
     * @param timestampMillis The timestamp of the event.
     * @param nanoOfMillisecond the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999}
     * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was
     *          created.
     */
    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
            final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
            final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
            final String threadName, final int threadPriority, final StackTraceElement source,
            final long timestampMillis, final int nanoOfMillisecond, final long nanoTime) {
        this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
        final long millis = message instanceof TimestampMessage
                ? ((TimestampMessage) message).getTimestamp()
                : timestampMillis;
        instant.initFromEpochMilli(millis, nanoOfMillisecond);
    }
    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
                          final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
                          final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
                          final String threadName, final int threadPriority, final StackTraceElement source,
                          final Clock clock, final long nanoTime) {
        this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
        if (message instanceof TimestampMessage) {
            instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
        } else {
            instant.initFrom(clock);
        }
    }
    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
                          final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
                          final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
                          final String threadName, final int threadPriority, final StackTraceElement source,
                          final long nanoTime) {
        this.loggerName = loggerName;
        this.marker = marker;
        this.loggerFqcn = loggerFQCN;
        this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
        this.message = message;
        this.thrown = thrown;
        this.thrownProxy = thrownProxy;
        this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData;
        this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
        this.threadId = threadId;
        this.threadName = threadName;
        this.threadPriority = threadPriority;
        this.source = source;
        if (message instanceof LoggerNameAwareMessage) {
            ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
        }
        this.nanoTime = nanoTime;
    }

    private static StringMap createContextData(final Map<String, String> contextMap) {
        final StringMap result = ContextDataFactory.createContextData();
        if (contextMap != null) {
            for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
                result.putValue(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    private static StringMap createContextData(final List<Property> properties) {
        final StringMap reusable = ContextDataFactory.createContextData();
        return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable);
    }

    /**
     * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
     * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
     */
    public static NanoClock getNanoClock() {
        return nanoClock;
    }

    /**
     * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
     * <p>
     * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
     * configuration changes.
     *
     * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
     */
    public static void setNanoClock(final NanoClock nanoClock) {
        Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
        StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName());
    }

    /**
     * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event.
     * @return a new fully initialized builder.
     */
    public Builder asBuilder() {
        return new Builder(this);
    }

    @Override
    public Log4jLogEvent toImmutable() {
        if (getMessage() instanceof ReusableMessage) {
            makeMessageImmutable();
        }
        return this;
    }

    /**
     * Returns the logging Level.
     * @return the Level associated with this event.
     */
    @Override
    public Level getLevel() {
        return level;
    }

    /**
     * Returns the name of the Logger used to generate the event.
     * @return The Logger name.
     */
    @Override
    public String getLoggerName() {
        return loggerName;
    }

    /**
     * Returns the Message associated with the event.
     * @return The Message.
     */
    @JsonProperty(value = "m_content")
    @Override
    public Message getMessage() {
        return message;
    }

    public void makeMessageImmutable() {
        message = new MementoMessage(message.getFormattedMessage(), message.getFormat(), message.getParameters());
    }

    @JsonIgnore
    @Override
    public long getThreadId() {
        if (threadId == 0) {
            threadId = Thread.currentThread().getId();
        }
        return threadId;
    }

    /**
     * Returns the name of the Thread on which the event was generated.
     * @return The name of the Thread.
     */
    @Override
    public String getThreadName() {
        if (threadName == null) {
            threadName = Thread.currentThread().getName();
        }
        return threadName;
    }

    @Override
    @JsonIgnore
    public int getThreadPriority() {
        if (threadPriority == 0) {
            threadPriority = Thread.currentThread().getPriority();
        }
        return threadPriority;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getTimeMillis() {
        return instant.getEpochMillisecond();
    }

    /**
     * {@inheritDoc}
     * @since 2.11
     */
    @Override
    @JsonIgnore
    public Instant getInstant() {
        return instant;
    }

    /**
     * Returns the Throwable associated with the event, or null.
     * @return The Throwable associated with the event.
     */
    @Override
    public Throwable getThrown() {
        return thrown;
    }

    /**
     * Returns the ThrowableProxy associated with the event, or null.
     * @return The ThrowableProxy associated with the event.
     */
    @Override
    public ThrowableProxy getThrownProxy() {
        if (thrownProxy == null && thrown != null) {
            thrownProxy = new ThrowableProxy(thrown);
        }
        return thrownProxy;
    }


    /**
     * Returns the Marker associated with the event, or null.
     * @return the Marker associated with the event.
     */
    @JsonIgnore
    @Override
    public Marker getMarker() {
        return marker;
    }

    /**
     * The fully qualified class name of the class that was called by the caller.
     * @return the fully qualified class name of the class that is performing logging.
     */
    @Override
    public String getLoggerFqcn() {
        return loggerFqcn;
    }

    /**
     * Returns the {@code ReadOnlyStringMap} containing context data key-value pairs.
     * @return the {@code ReadOnlyStringMap} containing context data key-value pairs
     * @since 2.7
     */
    @Override
    public ReadOnlyStringMap getContextData() {
        return contextData;
    }
    /**
     * Returns the immutable copy of the ThreadContext Map.
     * @return The context Map.
     */
    @Override
    public Map<String, String> getContextMap() {
        return contextData.toMap();
    }

    /**
     * Returns an immutable copy of the ThreadContext stack.
     * @return The context Stack.
     */
    @Override
    public ThreadContext.ContextStack getContextStack() {
        return contextStack;
    }

    /**
     * Returns the StackTraceElement for the caller. This will be the entry that occurs right
     * before the first occurrence of FQCN as a class name.
     * @return the StackTraceElement for the caller.
     */
    @Override
    public StackTraceElement getSource() {
        if (source != null) {
            return source;
        }
        if (loggerFqcn == null || !includeLocation) {
            return null;
        }
        source = StackLocatorUtil.calcLocation(loggerFqcn);
        return source;
    }

    @Override
    public boolean isIncludeLocation() {
        return includeLocation;
    }

    @Override
    public void setIncludeLocation(final boolean includeLocation) {
        this.includeLocation = includeLocation;
    }

    @Override
    @JsonIgnore
    public boolean isEndOfBatch() {
        return endOfBatch;
    }

    @JsonIgnore
    @Override
    public void setEndOfBatch(final boolean endOfBatch) {
        this.endOfBatch = endOfBatch;
    }

    @Override
    public long getNanoTime() {
        return nanoTime;
    }

    /**
     * Creates a LogEventProxy that can be serialized.
     * @return a LogEventProxy.
     */
    protected Object writeReplace() {
        getThrownProxy(); // ensure ThrowableProxy is initialized
        return new LogEventProxy(this, this.includeLocation);
    }

    /**
     * Take a snapshot of the specified {@code LogEvent}.
     *
     * @param event the event to take a snapshot of
     * @param includeLocation if true, this method will obtain caller location information
     * @return snapshot of the event as a {@code Serializable} object
     * @see #deserialize(Serializable)
     * @see #serialize(Log4jLogEvent, boolean)
     */
    public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
        if (event instanceof Log4jLogEvent) {
            event.getThrownProxy(); // ensure ThrowableProxy is initialized
            return new LogEventProxy((Log4jLogEvent) event, includeLocation);
        }
        return new LogEventProxy(event, includeLocation);
    }

    /**
     * Take a snapshot of the specified {@code Log4jLogEvent}.
     *
     * @param event the event to take a snapshot of
     * @param includeLocation if true, this method will obtain caller location information
     * @return snapshot of the event as a {@code Serializable} object
     * @see #deserialize(Serializable)
     * @see #serialize(LogEvent, boolean)
     */
    public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) {
        event.getThrownProxy(); // ensure ThrowableProxy is initialized
        return new LogEventProxy(event, includeLocation);
    }

    public static boolean canDeserialize(final Serializable event) {
        return event instanceof LogEventProxy;
    }

    public static Log4jLogEvent deserialize(final Serializable event) {
        Objects.requireNonNull(event, "Event cannot be null");
        if (event instanceof LogEventProxy) {
            final LogEventProxy proxy = (LogEventProxy) event;
            final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
                    proxy.loggerFQCN, proxy.level, proxy.message,
                    proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId,
                    proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoOfMillisecond,
                    proxy.nanoTime);
            result.setEndOfBatch(proxy.isEndOfBatch);
            result.setIncludeLocation(proxy.isLocationRequired);
            return result;
        }
        throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
    }

    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
        throw new InvalidObjectException("Proxy required");
    }

    public static LogEvent createMemento(final LogEvent logEvent) {
        return new Builder(logEvent).build();
    }

    /**
     * Creates and returns a new immutable copy of this {@code Log4jLogEvent}.
     *
     * @return a new immutable copy of the data in this {@code Log4jLogEvent}
     */
    public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) {
        return deserialize(serialize(event, includeLocation));
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName;
        sb.append("Logger=").append(n);
        sb.append(" Level=").append(level.name());
        sb.append(" Message=").append(message == null ? null : message.getFormattedMessage());
        return sb.toString();
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        final Log4jLogEvent that = (Log4jLogEvent) o;

        if (endOfBatch != that.endOfBatch) {
            return false;
        }
        if (includeLocation != that.includeLocation) {
            return false;
        }
        if (!instant.equals(that.instant)) {
            return false;
        }
        if (nanoTime != that.nanoTime) {
            return false;
        }
        if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
            return false;
        }
        if (level != null ? !level.equals(that.level) : that.level != null) {
            return false;
        }
        if (source != null ? !source.equals(that.source) : that.source != null) {
            return false;
        }
        if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
            return false;
        }
        if (contextData != null ? !contextData.equals(that.contextData) : that.contextData != null) {
            return false;
        }
        if (!message.equals(that.message)) {
            return false;
        }
        if (!loggerName.equals(that.loggerName)) {
            return false;
        }
        if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
            return false;
        }
        if (threadId != that.threadId) {
            return false;
        }
        if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
            return false;
        }
        if (threadPriority != that.threadPriority) {
            return false;
        }
        if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
            return false;
        }
        if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        // Check:OFF: MagicNumber
        int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
        result = 31 * result + (marker != null ? marker.hashCode() : 0);
        result = 31 * result + (level != null ? level.hashCode() : 0);
        result = 31 * result + loggerName.hashCode();
        result = 31 * result + message.hashCode();
        result = 31 * result + instant.hashCode();
        result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
        result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
        result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
        result = 31 * result + (contextData != null ? contextData.hashCode() : 0);
        result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
        result = 31 * result + (int) (threadId ^ (threadId >>> 32));
        result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
        result = 31 * result + (threadPriority ^ (threadPriority >>> 32));
        result = 31 * result + (source != null ? source.hashCode() : 0);
        result = 31 * result + (includeLocation ? 1 : 0);
        result = 31 * result + (endOfBatch ? 1 : 0);
        // Check:ON: MagicNumber
        return result;
    }

    /**
     * Proxy pattern used to serialize the LogEvent.
     */
    static class LogEventProxy implements Serializable {

        private static final long serialVersionUID = -8634075037355293699L;
        private final String loggerFQCN;
        private final Marker marker;
        private final Level level;
        private final String loggerName;
        // transient since 2.8
        private final transient Message message;
        /** since 2.8 */
        private MarshalledObject<Message> marshalledMessage;
        /** since 2.8 */
        private String messageString;
        private final long timeMillis;
        /** since 2.11 */
        private final int nanoOfMillisecond;
        private final transient Throwable thrown;
        private final ThrowableProxy thrownProxy;
        /** @since 2.7 */
        private final StringMap contextData;
        private final ThreadContext.ContextStack contextStack;
        /** @since 2.6 */
        private final long threadId;
        private final String threadName;
        /** @since 2.6 */
        private final int threadPriority;
        private final StackTraceElement source;
        private final boolean isLocationRequired;
        private final boolean isEndOfBatch;
        /** @since 2.4 */
        private final transient long nanoTime;

        public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
            this.loggerFQCN = event.loggerFqcn;
            this.marker = event.marker;
            this.level = event.level;
            this.loggerName = event.loggerName;
            this.message = event.message instanceof ReusableMessage
                    ? memento((ReusableMessage) event.message)
                    : event.message;
            this.timeMillis = event.instant.getEpochMillisecond();
            this.nanoOfMillisecond = event.instant.getNanoOfMillisecond();
            this.thrown = event.thrown;
            this.thrownProxy = event.thrownProxy;
            this.contextData = event.contextData;
            this.contextStack = event.contextStack;
            this.source = includeLocation ? event.getSource() : null;
            this.threadId = event.getThreadId();
            this.threadName = event.getThreadName();
            this.threadPriority = event.getThreadPriority();
            this.isLocationRequired = includeLocation;
            this.isEndOfBatch = event.endOfBatch;
            this.nanoTime = event.nanoTime;
        }

        public LogEventProxy(final LogEvent event, final boolean includeLocation) {
            this.loggerFQCN = event.getLoggerFqcn();
            this.marker = event.getMarker();
            this.level = event.getLevel();
            this.loggerName = event.getLoggerName();

            final Message temp = event.getMessage();
            message = temp instanceof ReusableMessage
                    ? memento((ReusableMessage) temp)
                    : temp;
            this.timeMillis = event.getInstant().getEpochMillisecond();
            this.nanoOfMillisecond = event.getInstant().getNanoOfMillisecond();
            this.thrown = event.getThrown();
            this.thrownProxy = event.getThrownProxy();
            this.contextData = memento(event.getContextData());
            this.contextStack = event.getContextStack();
            this.source = includeLocation ? event.getSource() : null;
            this.threadId = event.getThreadId();
            this.threadName = event.getThreadName();
            this.threadPriority = event.getThreadPriority();
            this.isLocationRequired = includeLocation;
            this.isEndOfBatch = event.isEndOfBatch();
            this.nanoTime = event.getNanoTime();
        }

        private static Message memento(final ReusableMessage message) {
            return message.memento();
        }

        private static StringMap memento(final ReadOnlyStringMap data) {
            final StringMap result = ContextDataFactory.createContextData();
            result.putAll(data);
            return result;
        }

        private static MarshalledObject<Message> marshall(final Message msg) {
            try {
                return new MarshalledObject<>(msg);
            } catch (final Exception ex) {
                return null;
            }
        }

        private void writeObject(final java.io.ObjectOutputStream s) throws IOException {
            this.messageString = message.getFormattedMessage();
            this.marshalledMessage = marshall(message);
            s.defaultWriteObject();
        }

        /**
         * Returns a Log4jLogEvent using the data in the proxy.
         * @return Log4jLogEvent.
         */
        protected Object readResolve() {
            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown,
                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis,
                    nanoOfMillisecond, nanoTime);
            result.setEndOfBatch(isEndOfBatch);
            result.setIncludeLocation(isLocationRequired);
            return result;
        }

        private Message message() {
            if (marshalledMessage != null) {
                try {
                    return marshalledMessage.get();
                } catch (final Exception ex) {
                    // ignore me
                }
            }
            return new SimpleMessage(messageString);
        }
    }

    @JsonProperty(value = "m_content")
    public void setMessage(Message message) {
        this.message = message;
    }
}

附录3:完整的log4j2-spring.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration monitorInterval="60">
    <Properties>
        <property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %t %c - %m%n</property>
        <property name="log_pattern_error">%d{yyyy-MM-dd HH:mm:ss.SSS} %traceId %-5level %t %c - %m%n</property>
        <property name="log.charset">UTF-8</property>
        <property name="log.path">log/</property>
        <property name="every_file_size">500 MB</property>
        <!--需替换 日志文件名称-->
        <property name="project_name">test-service</property>

    </Properties>
    <appenders>
        <console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="${log_pattern}" charset="${log.charset}"/>
        </console>

        <RollingRandomAccessFile name="file"
                                 fileName="${log.path}${project_name}_common.log"
                                 filePattern="${log.path}${project_name}_common.%d{yyyy-MM-dd}_%i.log.gz">
            <PatternLayout pattern="${log_pattern}" charset="${log.charset}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>
            <DefaultRolloverStrategy fileIndex="nomax">
                <Delete basePath="${log.path}" maxDepth="1">
                    <IfFileName glob="*.log.gz"/>
                    <IfLastModified age="P30D"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingRandomAccessFile>

        <RollingRandomAccessFile name="error"
                                 fileName="${log.path}${project_name}_error.log"
                                 filePattern="${log.path}${project_name}.%d{yyyy-MM-dd}_%i.log.gz">
            <Filters>
                <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <!-- compact如果为真,追加器不使用行尾和缩进。默认值为false。-->
            <JsonLayout compact="true" eventEol="true" stacktraceAsString="true"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>
            <DefaultRolloverStrategy fileIndex="nomax">
                <Delete basePath="${log.path}" maxDepth="1">
                    <IfFileName glob="*.log.gz"/>
                    <IfLastModified age="P30D"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingRandomAccessFile>
        <!--SealLogger-->
        <RollingRandomAccessFile name="busLogger"
                                 fileName="${log.path}${project_name}_seal_info.log"
                                 filePattern="${log.path}${project_name}_seal_info.%d{yyyy-MM-dd}_%i.log.gz">
            <JsonLayout compact="true" eventEol="true" objectMessageAsJsonObject="true"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>
            <DefaultRolloverStrategy fileIndex="nomax">
                <Delete basePath="${log.path}" maxDepth="1">
                    <IfFileName glob="*.log.gz"/>
                    <IfLastModified age="P30D"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingRandomAccessFile>

        <!--pv-->
        <RollingRandomAccessFile name="pvLogger"
                                 fileName="${log.path}${project_name}_pv.log"
                                 filePattern="${log.path}${project_name}_pv.%d{yyyy-MM-dd}_%i.log.gz">
            <JsonLayout compact="true" eventEol="true" objectMessageAsJsonObject="true"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>
            <DefaultRolloverStrategy fileIndex="nomax">
                <Delete basePath="${log.path}" maxDepth="1">
                    <IfFileName glob="*.log.gz"/>
                    <IfLastModified age="P30D"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingRandomAccessFile>
    </appenders>
    <loggers>
        <Root level="info">
            <AppenderRef ref="stdout"/>
            <AppenderRef ref="file"/>
            <AppenderRef ref="error"/>
        </Root>
        <AsyncLogger name="com.tellme" level="info" includeLocation="false" additivity="false">
            <appender-ref ref="stdout"/>
            <AppenderRef ref="file"/>
            <AppenderRef ref="error"/>
        </AsyncLogger>

        <AsyncLogger name="com.tellme.config.SealLogger" additivity="false" level="info">
            <appender-ref ref="stdout"/>
            <appender-ref ref="busLogger"/>
        </AsyncLogger>
        <AsyncLogger name="com.tellme.config.AccessLog4jUtil" additivity="false" level="info">
            <appender-ref ref="stdout"/>
            <appender-ref ref="pvLogger"/>
        </AsyncLogger>
    </loggers>

</configuration>

4. 推荐阅读

Log4j2 进阶使用(转载)

Apache Log4j2 API官方使用指南(一) —— Marker类怎么用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容