logback 将日志保存CSV文件

背景

公司业务,要将数据要做本地备份记录,开始直接保存json格式,但是太占用存储,所以使用csv格式保存。

思路

1、使用csvWriter ,数据来时,将数据写入本地

不足:

如果每次数据来临时,创建一个csvWriter性能有影响,批量写入也可以,这不是主要原因。主要原因是,需要考虑什么时候创建csv文件,什么时候关闭csv流,当项目升级时,也要关闭流。注意的点太多(放弃)

2、csv其实就是一个文本,每行数据就是用逗号分割,我们可以将数据逗号分割好,直接使用info(data)来记录数据。

难点是,如何将标头写入文件。

自定义Appender、Encoder

xml文件

xml 复制代码
<appender name="USER_LOG" class="com.sjy.logback.CsvRollingFileAppender">
        <encoder class="com.sjy.logback.CsvPatternLayoutEncoder">
            <fileHeader>time,data,user,url</fileHeader>
            <charset>UTF-8</charset>
            <pattern>%msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>/var/log/user-data.%d{yyyy-MM-dd}.csv</FileNamePattern>
            <MaxHistory>7</MaxHistory>
        </rollingPolicy>
    </appender>
<logger name="userLogger" level="INFO" additivity="false">
        <appender-ref ref="USER_LOG" />
    </logger>

CsvRollingFileAppender.java

java 复制代码
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.recovery.ResilientFileOutputStream;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.util.FileUtil;

import java.io.File;
import java.io.IOException;

/*
判断文件是否存在,如果存在,将header 去除,如果不存在添加fileHeader
 */
public class CsvRollingFileAppender<E> extends RollingFileAppender<E> {
    @Override
    public void openFile(String file_name) throws IOException {
        lock.lock();
        try {
            File file = new File(file_name);
            boolean result = FileUtil.createMissingParentDirectories(file);
            if (!result) {
                addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]");
            }

            ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, FileAppender.DEFAULT_BUFFER_SIZE);
            resilientFos.setContext(context);
            CsvPatternLayoutEncoder encoder = (CsvPatternLayoutEncoder) this.getEncoder();
            String fileHeader = null;
            // 文件是否存在
            boolean isNew = !file.exists() || file.length() <= 0;
            if (!isNew) {
                // 设置null
                fileHeader = encoder.getFileHeader();
                encoder.setPatternLayoutFileHeader(null);
            }
            // 写入文件
            setOutputStream(resilientFos);
            if (!isNew) {
                encoder.setPatternLayoutFileHeader(fileHeader);
            }
        } finally {
            lock.unlock();
        }
    }
}

CsvPatternLayoutEncoder .java

java 复制代码
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.pattern.PatternLayoutEncoderBase;

public class CsvPatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> {
    private String fileHeader;
    private PatternLayout patternLayout;
    @Override
    public void start() {
        patternLayout = new PatternLayout();
        patternLayout.setContext(context);
        patternLayout.setPattern(getPattern());
        patternLayout.setFileHeader(getFileHeader());
        patternLayout.setOutputPatternAsHeader(outputPatternAsHeader);
        patternLayout.start();
        this.layout = patternLayout;
        super.start();
    }

    public String getFileHeader() {
        return fileHeader;
    }

    public void setFileHeader(String fileHeader) {
        this.fileHeader = fileHeader;
    }

    public void setPatternLayoutFileHeader(String fileHeader) {
        patternLayout.setFileHeader(fileHeader);
    }
}
java 复制代码
private static Logger log = LoggerFactory.getLogger("userLogger");
// 写入文件
public void logToFile() {
    log.info(time+","+data+","+user+","+url);
}

注意事项

  1. 因为CSV是一个用逗号分割的文本,所以在datauser数据中不要有逗号
相关推荐
Zane199432 分钟前
CAS 与原子类:Java 如何实现无锁编程
java·后端
TunerT_TQ35 分钟前
【智能体安全治理|专栏第9期】从“一堆规则”到“数字宪法”:智能体治理的下一个阶段
java·开发语言·安全·开源治理·大模型安全·ai基础设施·智能体安全
legendary_bruce41 分钟前
RAG知识库进阶-1
java·aigc
必须会一定会42 分钟前
大模型手搓文件对比工具(6):差异不用再手选
java·人工智能·ai编程
yio_yin1 小时前
MyBatis
java·前端·mybatis
程序员-Benothing1 小时前
Java ForkJoinPool 详解:从分治思想到高性能并行计算
java·开发语言·后端·面试·职场和发展
上海安当技术2 小时前
单点登录 SSO 怎么选协议?SAML 2.0 / OAuth 2.0 / OIDC / CAS 对比与 ERP、OA、CRM 接入实战
java·开发语言
mifengxing2 小时前
Java集合与泛型
java·算法·复习笔记
不才不才不不才2 小时前
Spring 源码系列(14): JDK 动态代理 vs CGLIB,以及拦截器责任链
java·开发语言·spring
流云鹤2 小时前
05Java学习day(5)
java·学习