基于行和时间的日志打印

有个需求,是要按照行和日期进行日志切割,后来根据官网介绍和百度查阅,找到了一种方法,稍加修改,记录一下,


首先声明用到的包如下,不同的包继承的类会不同<properties>
    <slf4j.api.version>1.7.14</slf4j.api.version>
    <log4j.slf4j.impl.version>2.5</log4j.slf4j.impl.version>
    <log4j.core.version>2.5</log4j.core.version>
</properties>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.api.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>${log4j.slf4j.impl.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.core.version}</version>
</dependency>



import java.util.concurrent.atomic.AtomicInteger;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.rolling.RollingFileManager;
import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.status.StatusLogger;

/**
 * 自定义基于行和时间的滚动日志
 *
 */
@Plugin(name = "LineBasedTriggeringPolicy", category = "Core", printObject = true)
public class LineBasedTriggeringPolicy implements TriggeringPolicy {
    /**
     * Allow subclasses access to the status logger without creating another
     * instance.
     */
    protected static final StatusLogger LOGGER = StatusLogger.getLogger();
    /**
     * Rollover threshold size in bytes.
     */
    private static final long MAX_LINE_SIZE = 10 * 1024 * 2; // let 20k line
    private static final int MAX_ELAPSE_TIME = 24 * 60 * 60 * 1000; // 1day
    private final long maxLineSize;
    private final int maxElapsedTime; // in ms
    private int counter = 0;
    private long lastUpdateTime = System.currentTimeMillis();
    /**
     * 是为了防止上次日志达不到滚动条件而停止服务后,重启后原日志不滚动而直接追加新日志,导致超过规定条件
     */
    private AtomicInteger times = new AtomicInteger(1);

    private RollingFileManager manager;

    /**
     * Constructs a new instance.
     */
    protected LineBasedTriggeringPolicy() {
        this.maxLineSize = MAX_LINE_SIZE;
        maxElapsedTime = MAX_ELAPSE_TIME;
    }

    /**
     * Constructs a new instance.
     *
     * @param maxFileSize rollover threshold size in bytes.
     */
    protected LineBasedTriggeringPolicy(final long maxFileSize,
                                        int maxElapsedTime) {
        this.maxLineSize = maxFileSize;
        this.maxElapsedTime = maxElapsedTime;
    }

    /**
     * Create a LineBasedTriggeringPolicy.
     *
     * @param size The size of the file before rollover is required.
     * @return A LineBasedTriggeringPolicy.
     */
    @PluginFactory
    public static LineBasedTriggeringPolicy createPolicy(@PluginAttribute("size")
                                                         final String size, @PluginAttribute("maxElapsedTime")
                                                         final String maxElapsedTime) {
        final long maxSize = size == null ? MAX_LINE_SIZE : Integer
                .valueOf(size);
        int time = maxElapsedTime == null ? MAX_ELAPSE_TIME / 1000 : Integer
                .valueOf(maxElapsedTime);
        return new LineBasedTriggeringPolicy(maxSize, time * 1000);
    }

    /**
     * Initialize the TriggeringPolicy.
     *
     * @param manager The RollingFileManager.
     */
    public void initialize(final RollingFileManager manager) {
        this.manager = manager;
    }

    /**
     * Returns true if a rollover should occur.
     *
     * @param event A reference to the currently event.
     * @return true if a rollover should take place, false otherwise.
     */
    public boolean isTriggeringEvent(final LogEvent event) {
        if (counter == 0 && times.intValue() == 1) {
            times.incrementAndGet();
            return true;
        }
        counter++;
        int cur = counter;
        boolean ret = cur >= maxLineSize;
        if (!ret) {
            long time = System.currentTimeMillis() - lastUpdateTime;
            if (time > maxElapsedTime) {
                ret = true;
            }
        }

        if (ret) {
            counter = 0;
            lastUpdateTime = System.currentTimeMillis();
        }
        return ret;
    }

    @Override
    public String toString() {
        return "LineBasedTriggeringPolicy(size=" + maxLineSize + ")";
    }
}


测试方法

public class TestLog {

    private static final Logger logger = LoggerFactory.getLogger("com.xxx.test.log");

    public static void main(String[] args) throws InterruptedException {

        while (true) {
            logger.info("TestLogController");
            Thread.sleep(1000);
        }
    }
}


log4j2.xml配置

<?xml version="1.0" encoding="utf-8"?>
<Configuration monitorInterval="30" packages="com.xxx.controller">
    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout
                    pattern="%m%n"/>
        </Console>


        <RollingFile name="testLog" fileName="D:/testLog.log"
                     filePattern="D:/$${date:yyyy-MM}/$${date:yyyy-MM-dd}/testLog-%d{yyyy-MM-dd-HH-mm-ss}-%i.log.bak">
            <PatternLayout pattern="%m%n"/>
            <Policies>
                <LineBasedTriggeringPolicy size="10" maxElapsedTime="10"/>
            </Policies>
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="console"/>
        </Root>
        <Logger name="com.xxx.test.log" level="info" additivity="false">
            <AppenderRef ref="testLog"/>
        </Logger>
    </Loggers>
</Configuration>



启动后我们可以看到能正常输出日志,且每次都是10条

基于行和时间的日志打印

基于行和时间的日志打印

然后暂停,将休眠时间改为2S

基于行和时间的日志打印基于行和时间的日志打印

我们可以看到重启后第一次日志只有7条就回滚了,这就说明代码加了第一次输出日志就回滚生效了


我们可以看到将休眠时间改为2S后就一直每次都是5条了

基于行和时间的日志打印