此次对于项目中的日志打印做了一些了解及线上系统的应用。

项目中用到的日志打印的工具是org.apache.log4j中的工具。此次主要用到的是三个:

    org.apache.log4j.ConsoleAppender(控制台)

    org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)

    org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)

在小项目中,日志按天拆分就够了,此时我就没有自己去重写日志打印工具了,直接利用的org.apache.log4j.DailyRollingFileAppender来生成,其中配置如下:

# log4j配置 info级别,控制台输出,文件输出
log4j.rootCategory=INFO,stdout,dailyRollingFile

# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n

# 文件输出
log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.file=./logs/channelserver.log
log4j.appender.dailyRollingFile.DatePattern='.'yyyy-MM-dd
log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n
log4j.appender.dailyRollingFile.Append=true

最终日志文件的形式为:

channelserver.log
channelserver.log.2018-11-11

这种形式拆分日志时,没有对日志文件进行压缩,所以日志文件占用的总内存和不拆分的时候是一样的,只是在去服务器找日志时方便一点,避免日志文件过大,不易操作。

下面介绍自定义的日志打印拆分及压缩:

我们去查看DailyRollingFileAppender、RollingFileAppender都是继承自FileAppender,那么我们自定义一个类,继承FileAppender,然后实现我们自己的日志打印拆分压缩的逻辑。其中DailyRollingFileAppender中已经实现了日志按日期拆分的逻辑,RollingFileAppender实现了按大小拆分的逻辑,此时我主要用到的是DailyRollingFileAppender,然后由于DailyRollingFileAppender中缺少按大小拆分的逻辑及压缩,那么此时我们将RollingFileAppender中按大小拆分的逻辑引用过来,那么此时我们就还缺少一个压缩的逻辑,此时压缩的逻辑我们可以自己编写,同时也可以将日志文件名称重新规范一下,最终代码如下:

package me.config;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.FileAppender;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.log4j.Layout;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;
/**
 * 类描述:日志文件生成类
 *          1、根据日期拆分
 *          2、根据大小拆分
 *          3、拆分后的日志压缩
 *
 * @Author:wangjinhui
 * @date:2018年12月03日
 * @Version:1.1.0
 */
public class TestDailyRollingFileAppender extends FileAppender {

    static final int TOP_OF_TROUBLE = -1;
    static final int TOP_OF_MINUTE = 0;
    static final int TOP_OF_HOUR = 1;
    static final int HALF_DAY = 2;
    static final int TOP_OF_DAY = 3;
    static final int TOP_OF_WEEK = 4;
    static final int TOP_OF_MONTH = 5;
    private String datePattern = "'.'yyyy-MM-dd";
    private String scheduledFilename;//上一次生成的文件名称
    private long nextCheck = System.currentTimeMillis() - 1L;//下一次的校验时间
    Date now = new Date();
    SimpleDateFormat sdf;
    TestRollingCalendar rc = new TestRollingCalendar();
    int checkPeriod = -1;
    static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");

    protected long maxFileSize = 10485760L;//文件的最大大小
    private long nextRollover = 0L;//下一次处理文件压缩的大小
    private int fileIndex = 0;//生成的文件的下标
    private String fileNamePrefix;//文件名称前缀
    private String fileNameSuffix;//文件名称后缀

    public TestDailyRollingFileAppender() {
    }

    public TestDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException {
        super(layout, filename, true);
        this.datePattern = datePattern;
        this.activateOptions();
    }

    public void setDatePattern(String pattern) {
        this.datePattern = pattern;
    }

    public String getDatePattern() {
        return this.datePattern;
    }

    public void activateOptions() {
        super.activateOptions();
        if (this.datePattern != null && this.fileName != null) {
            this.now.setTime(System.currentTimeMillis());
            this.sdf = new SimpleDateFormat(this.datePattern);
            int type = this.computeCheckPeriod();
            this.printPeriodicity(type);
            this.rc.setType(type);
            File file = new File(this.fileName);
//            this.scheduledFilename = this.fileNamePrefix + this.sdf.format(new Date(file.lastModified())) + "_" + fileIndex + this.fileNameSuffix;
            //给参数fileNamePrefix与fileNameSuffix赋值初始值
            if (StringUtils.isEmpty(this.fileNamePrefix) || StringUtils.isEmpty(this.fileNameSuffix)) {
                this.setFileNamePrefix(this.fileName);
                this.setFileNameSuffix(this.fileName);
            }
            this.scheduledFilename = initScheduleFilename(this.sdf.format(new Date(file.lastModified())));
        } else {
            LogLog.error("Either File or DatePattern options are not set for appender [" + this.name + "].");
        }

    }

    /**
     * 利用递归确定此次在生成文件时的名称
     * @param lastModifiedDate
     * @return
     */
    public String initScheduleFilename(String lastModifiedDate){
        scheduledFilename = this.fileNamePrefix + lastModifiedDate + "_" + fileIndex + this.fileNameSuffix;
        File target  = new File(scheduledFilename + ".zip");
        if (target.exists()) {
            fileIndex++;
            return initScheduleFilename(lastModifiedDate);
        } else {
            return scheduledFilename;
        }

    }

    void printPeriodicity(int type) {
        switch(type) {
            case 0:
                LogLog.debug("Appender [" + this.name + "] to be rolled every minute.");
                break;
            case 1:
                LogLog.debug("Appender [" + this.name + "] to be rolled on top of every hour.");
                break;
            case 2:
                LogLog.debug("Appender [" + this.name + "] to be rolled at midday and midnight.");
                break;
            case 3:
                LogLog.debug("Appender [" + this.name + "] to be rolled at midnight.");
                break;
            case 4:
                LogLog.debug("Appender [" + this.name + "] to be rolled at start of week.");
                break;
            case 5:
                LogLog.debug("Appender [" + this.name + "] to be rolled at start of every month.");
                break;
            default:
                LogLog.warn("Unknown periodicity for appender [" + this.name + "].");
        }

    }

    int computeCheckPeriod() {
        TestRollingCalendar rollingCalendar = new TestRollingCalendar(gmtTimeZone, Locale.getDefault());
        Date epoch = new Date(0L);
        if (this.datePattern != null) {
            for(int i = 0; i <= 5; ++i) {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.datePattern);
                simpleDateFormat.setTimeZone(gmtTimeZone);
                String r0 = simpleDateFormat.format(epoch);
                rollingCalendar.setType(i);
                Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
                String r1 = simpleDateFormat.format(next);
                if (r0 != null && r1 != null && !r0.equals(r1)) {
                    return i;
                }
            }
        }

        return -1;
    }

    /**
     * 重新生成文件的方法
     * @throws IOException
     */
    void rollOver() throws IOException {
        if (this.datePattern == null) {
            this.errorHandler.error("Missing DatePattern option in rollOver().");
        } else {
            String datedFilename = this.fileNamePrefix + this.sdf.format(this.now) + "_" + fileIndex + this.fileNameSuffix;
            fileIndex++;
            if (!this.scheduledFilename.equals(datedFilename)) {
                long size = ((CountingQuietWriter)this.qw).getCount();
                this.nextRollover = size + this.maxFileSize;
                this.closeFile();

                File file = new File(this.fileName);

                //新建压缩文件
                FileInputStream fis = null;
                ZipOutputStream out = null;
                byte[] buf = new byte[1024];
                try {
                    fis = new FileInputStream(file);
                    out = new ZipOutputStream(new FileOutputStream(scheduledFilename + ".zip"));
                    out.putNextEntry(new ZipEntry(file.getPath()));
                    LogLog.debug(fileName + " -> " + scheduledFilename + ".zip");

                    int len;
                    while ((len = fis.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    out.closeEntry();
                    fis.close();

                    LogLog.debug(fileName + " -> " + scheduledFilename + ".zip successful!");

                } catch (Exception e) {
                    LogLog.error("Failed to zip [" + this.fileName + "] is error.");
                } finally {
                    if (out != null) {
                        out.closeEntry();
                        out.close();
                    }
                    if (fis != null)
                        fis.close();
                }

                //压缩文件生成后,源文件删除
                file.delete();

                //新建日志文件
                try {
                    this.setFile(this.fileName, true, this.bufferedIO, this.bufferSize);
                    this.nextRollover = 0L;
                } catch (IOException var6) {
                    this.errorHandler.error("setFile(" + this.fileName + ", true) call failed.");
                }

                //重新赋值前一次的文件名称
                this.scheduledFilename = datedFilename;
            }
        }
    }

    protected void setQWForFiles(Writer writer) {
        this.qw = new CountingQuietWriter(writer, this.errorHandler);
    }

    public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
        super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
        if (append) {
            File f = new File(fileName);
            ((CountingQuietWriter)this.qw).setCount(f.length());
        }
    }

    protected void subAppend(LoggingEvent event) {
        long n = System.currentTimeMillis();
        long size = ((CountingQuietWriter)this.qw).getCount();
        if (n >= this.nextCheck) {
            this.now.setTime(n);
            this.nextCheck = this.rc.getNextCheckMillis(this.now);

            try {
                this.rollOver();
                fileIndex = 0;
            } catch (IOException var5) {
                if (var5 instanceof InterruptedIOException) {
                    Thread.currentThread().interrupt();
                }

                LogLog.error("rollOver() failed.", var5);
            }
        } else if (size >= this.maxFileSize && size >= this.nextRollover) {
            try {
                this.rollOver();
            } catch (IOException var5) {
                if (var5 instanceof InterruptedIOException) {
                    Thread.currentThread().interrupt();
                }
                LogLog.error("rollOver() failed.", var5);
            }
        }

        super.subAppend(event);
    }

    public void setMaxFileSize(String value) {
        this.maxFileSize = OptionConverter.toFileSize(value, this.maxFileSize + 1L);
    }

    public void setFileNamePrefix(String fileNamePrefix) {
        this.fileNamePrefix = fileName.substring(0, fileName.lastIndexOf("."));
    }

    public void setFileNameSuffix(String fileNameSuffix) {
        this.fileNameSuffix = fileName.substring(fileName.lastIndexOf("."));
    }
}

配置文件如下:

# log4j配置 info级别,控制台输出,文件输出
log4j.rootCategory=INFO,stdout,dailyRollingFile

# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n

# 文件输出
#log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile=me.config.TestDailyRollingFileAppender
log4j.appender.dailyRollingFile.file=./logs/channelserver.log
log4j.appender.dailyRollingFile.DatePattern='.'yyyy-MM-dd
log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n
log4j.appender.dailyRollingFile.Append=true

log4j.appender.dailyRollingFile.MaxFileSize=100KB

最终的结果如图:

log4j日志按日期拆分压缩及按大小拆分压缩插图

此处还是有点儿缺陷,就是fileNamePrefix及fileNameSuffix两个参数是通过配置文件log4j.appender.dailyRollingFile.file的值来拆分的,如果命名不规范的话,最终的结果就会出错,所以此处也是可以做成配置的,如同log4j.appender.dailyRollingFile.MaxFileSize参数配置一样去配置即可。

转载于:https://my.oschina.net/u/3308528/blog/2967409



log4j日志按日期拆分压缩及按大小拆分压缩插图1

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2021/12/29/log4j-appender/