• springboot日志使用 SLF4J+Logback 实现(springboot默认的日志实现),日志打印到控制台及日志输出到指定文件


    还是直接上代码

    @Slf4j 这玩意 默认支持 不用引入

    yml 配置文件

    # 日志配置  如果配置了xml 这个就不生效了 xml优先级最高
    #logging:
    #  file:
    #    path: /home/logs  # 日志目录地址
    #    name:  /home/logs/skeleton.log
    #    max-size: 1KB  # 设置日志大小的最大大小 1KB 用于演示 单位包括 KB、MB、GB
    #    max-history: 3  # 默认存储最近7
    #    total-size-cap: 3KB  #日志文档的总大小:当日志文档总大小超过该阈值会删除备份
    #  level:
    #    root: info  #全局的日志等级 哪些日志输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    下面分享 xml 方式
    在 资源目录下创建 logback-spring.xml 粘贴走 即可 重启 看控制台变化 还有磁盘 有没有写入

    坑 :
    我在创建的时候 发现 xml 没有生效 排查了半天 发现 在创建 logback-spring.xml 这个文件的时候
    我不是手打的 是复制的 文件名 前面多了个空格 导致不生效
    所以如果有小伙伴 和我一样 复制的文件名 一定要小心
    文件名两边不要复制多了 空格

    
    <configuration  scan="true" scanPeriod="10 seconds">
        
        
        
        
    
        <contextName>logbackcontextName>
        
        
        <property name="log.path" value="/home/logs/xx-skeleton" />
    
        <timestamp key="datetime" datePattern="yyyy-MM-dd"/>
    
        
        
        
        
        
        
        
        <property name="CONSOLE_LOG_PATTERN"
                  value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
    
    
        
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            
            
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>INFOlevel>
            filter>
            <encoder>
                <Pattern>${CONSOLE_LOG_PATTERN}Pattern>
                
                <charset>UTF-8charset>
            encoder>
        appender>
    
    
        
    
        
        <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            
            <file>${log.path}/${datetime}/log_info.logfile>
            
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
                <charset>UTF-8charset>
            encoder>
            
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                
                <fileNamePattern>${log.path}/${datetime}/info/log-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MBmaxFileSize>
                timeBasedFileNamingAndTriggeringPolicy>
                
                <maxHistory>7maxHistory>
            rollingPolicy>
            
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFOlevel>
                <onMatch>ACCEPTonMatch>
                <onMismatch>DENYonMismatch>
            filter>
        appender>
    
        
        <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            
            <file>${log.path}/${datetime}/log_warn.logfile>
            
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
                <charset>UTF-8charset> 
            encoder>
            
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}/${datetime}/warn/log-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MBmaxFileSize>
                timeBasedFileNamingAndTriggeringPolicy>
                
                <maxHistory>7maxHistory>
            rollingPolicy>
            
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>warnlevel>
                <onMatch>ACCEPTonMatch>
                <onMismatch>DENYonMismatch>
            filter>
        appender>
    
    
        
        <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            
            <file>${log.path}/${datetime}/log_error.logfile>
            
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
                <charset>UTF-8charset> 
            encoder>
            
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}/${datetime}/error/log-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MBmaxFileSize>
                timeBasedFileNamingAndTriggeringPolicy>
                
                <maxHistory>7maxHistory>
            rollingPolicy>
            
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERRORlevel>
                <onMatch>ACCEPTonMatch>
                <onMismatch>DENYonMismatch>
            filter>
        appender>
    
        
        
        
        <springProfile name="dev">
            
            <logger name="com.xx" level="INFO" />
    
            
            <root level="INFO">
                <appender-ref ref="CONSOLE" />
                <appender-ref ref="INFO_FILE" />
                <appender-ref ref="WARN_FILE" />
                <appender-ref ref="ERROR_FILE" />
            root>
        springProfile>
    
    
        
        <springProfile name="prd">
    
            <root level="INFO">
                <appender-ref ref="CONSOLE" />
                <appender-ref ref="DEBUG_FILE" />
                <appender-ref ref="INFO_FILE" />
                <appender-ref ref="ERROR_FILE" />
                <appender-ref ref="WARN_FILE" />
            root>
        springProfile>
    
    configuration>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168

    测试

    @RestController
    @RequestMapping("/test-cg")
    @Slf4j
    public class TestCgController extends BaseController {
        @GetMapping("index14")
        @ApiOperation(value = "日志测试")
        @PassToken
        public R index14(){
            log.trace("我是trace");
            log.debug("我是 debug");
            log.info("我是info");
            log.warn("我是warn");
            log.error("我是error");
            return R.success();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最后再送 一个 定时任务 删除过期 日志文件 防止删除失败

    定时任务 用的 xxl-job 不太清除这个定时任务插件 可百度

    package com.xxx.init.job;
    
    import com.xxx.api.out.R;
    import com.xxx.init.aop.XxlJobTask;
    import com.xxx.init.utils.BaseDataUtil;
    
    import com.xxl.job.core.handler.annotation.XxlJob;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    
    
    import java.nio.file.*;
    
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.util.ArrayList;
    
    import java.util.List;
    
    /**
     * User:Json
     * Date: 2024/4/18
     **/
    @Slf4j
    @Component
    public class RuntimeFileClearJob {
    
        @Value("${spring.application.name}")
        private String serviceName;
    
        private static final String LOG_DIRECTORY_WINDOWS = "home\\logs"; // Windows系统的日志目录
        private static final String LOG_DIRECTORY_LINUX = "/home/logs/"; // Linux系统的日志目录
    
    
        //jobHandler 必须唯一  每天凌晨3点13分0秒触发 0 13 3 * * ?
        @XxlJob("planRuntimeFileClearJava")
        @XxlJobTask(jobDesc = "日志文件的清空-Java", cron = "0 13 3 * * ?", jobHandler = "planRuntimeFileClearJava", routeStrategy = "ROUND")
        //自定义注解
        public R planRuntimeFileClearJava() {
            Integer clearCacheDay = BaseDataUtil.getSystemConfigNacos().getClearCacheDay();
            log.info("===========清空缓存日志文件开始执行==========");
            if (clearCacheDay == null) {
                //从nacos里读,读不到就设置为7天。
                clearCacheDay = 7;
            }
            cleanupLogs(clearCacheDay);
            log.info("===========清空缓存日志文件结束执行==========");
            return R.success();
        }
    
    
        public void cleanupLogs(Integer clearCacheDay) {
            String logDirectory = getLogDirectory();
            if (logDirectory == null) {
                log.error("日志文件 目录不能为空!");
                return;
            }
    
            List<String> oldFolders = getOldFolders(logDirectory, clearCacheDay);
            if (!CollectionUtils.isEmpty(oldFolders)) {
                deleteOldFolders(oldFolders);
            }
    
        }
    
        //获取日志根目录
        private String getLogDirectory() {
            // 根据运行环境确定日志目录
            String osName = System.getProperty("os.name").toLowerCase();
            if (osName.contains("windows")) {
                String drive = Paths.get("").toAbsolutePath().getRoot().toString();
                return drive + "\\" + LOG_DIRECTORY_WINDOWS + "\\" + serviceName;
            } else {
                return LOG_DIRECTORY_LINUX + "/" + serviceName;
            }
        }
    
        //获取过期文件夹 不会递归着找 只会找当前目录下的  文件夹格式是 : 2024-04-18 会根据文件夹检索
        private List<String> getOldFolders(String logDirectory, Integer clearCacheDay) {
            List<String> oldFolders = new ArrayList<>();
            LocalDate today = LocalDate.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    
            try {
                Files.list(Paths.get(logDirectory))
                        .filter(Files::isDirectory)
                        .forEach(dir -> {
                            String dirName = dir.getFileName().toString();
                            try {
                                LocalDate folderDate = LocalDate.parse(dirName, formatter);
                                if (folderDate.isBefore(today.minusDays(clearCacheDay))) {
                                    oldFolders.add(dir.toString());
                                }
                            } catch (Exception e) {
                                // Ignore non-date directories
                                log.error("获取过期文件夹报错:" + e.getMessage());
                            }
                        });
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return oldFolders;
        }
    
    
        //删除过期文件夹  linux 注意 jar是否有 删除文件夹的权限 未测 linux
        private void deleteOldFolders(List<String> oldFolders) {
            oldFolders.forEach(folder -> {
                try {
                    Files.walk(Paths.get(folder))
                            .sorted((path1, path2) -> -path1.compareTo(path2))
                            .forEach(path -> {
                                try {
                                    Files.deleteIfExists(path);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            });
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("删除过期文件夹报错:" + e.getMessage());
                }
            });
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
  • 相关阅读:
    sublime删除特定内容所在行
    深入ReentrantLock
    【【萌新的SOC学习之 VDMA 彩条显示实验之一】】
    【Java核心知识】ThreadLocal相关知识
    流浪动物救助小程序|基于微信小程序的流浪动物救助系统设计与实现(源码+数据库+文档)
    NExT-GPT: Any-to-Any Multimodal LLM论文笔记
    微服务Day2——Nacos注册中心入门
    流量1---------1
    MyBatis拦截器 Interceptor 实现多数据源切换
    教你如何批量给视频添加上自定义封面
  • 原文地址:https://blog.csdn.net/Drug_/article/details/137913293