User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Spring   ›   SpringBoot   ›   正文
SpringBoot

SpringBoot—logback日志配置

2020-07-06 01:16:44
1789  0 0

阅读完需:约 11 分钟

  1. spring boot可以适应所有日志框架,只需在类路径下包含相应的依赖来激活各种日志系统。
  2. spring boot底层使用slf4j + logback框架来实现日志记录,所以如果想要自定义logback配置,就无需添加相关依赖了(spring-booot-stater中已包含相关依赖)
  3. 在类路径下放置自定义日志配置文件(xml配置文件),spring boot就不会使用它本身的默认日志配置了
  4. logback中文文档——http://www.logback.cn/01%E7%AC%AC%E4%B8%80%E7%AB%A0logback%E4%BB%8B%E7%BB%8D.html

上图是spring boot官方文档的提示内容,意思是:根据您的日志记录系统,将加载相应的文件使用。即如果我们使用logback日志框架,那么可以使用logback-spring.xml、logback-spring.groovy、logback.xml、logback.groovy之一作为配置文件来加载。

自定义例子(1):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <!--配置控制台日志输出格式-->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                <!--%d表示日期, %msg表示日志的信息, %n表示换行-->
                %d - %msg%n
            </pattern>
        </layout>
    </appender>

    <!--配置info信息输出到一个文件-->
    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--根据级别过滤掉匹配的日志,不输出error级别的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <!--匹配上ERROR级别的日志信息,则不会输出到该文件中-->
            <onMatch>DENY</onMatch>
            <!--匹配不上ERROR级别的日志信息,则输出到该文件中-->
            <onMismatch>ACCEPT</onMismatch>
            <!--这样过滤以后,该文件中只会输出info级别的日志信息-->
        </filter>
        <!--日志输出格式,同上-->
        <encoder>
            <pattern>
                %d - %msg%n
            </pattern>
        </encoder>

        <!--滚动策略,按时间每天生成一个日志文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径-->
            <fileNamePattern>F:/log/springboot/info.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>

    <!--配置error信息输出到一个文件-->
    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--根据范围过滤日志,只输出error级别的日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <!--日志输出格式-->
        <encoder>
            <pattern>
                %d - %msg%n
            </pattern>
        </encoder>

        <!--滚动策略,按时间每天生成一个日志文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径-->
            <fileNamePattern>F:/log/springboot/error.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>

    <root level="info">
        <appender-ref ref="consoleLog"/>
        <appender-ref ref="fileInfoLog"/>
        <appender-ref ref="fileErrorLog"/>
    </root>
</configuration>

自定义例子(2):

<?xml version="1.0" encoding="utf-8" ?>
<!--
1) 根节点<configuration>,包含下面三个属性:
    scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
    scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。-->
<configuration scan="true" scanPeriod="60" debug="false">

    <!--控制台输出日志格式-->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss} -- %-5level -- [%thread] -- %logger{50} --- %msg %n}"/>
    <!--文件输出日志格式-->
    <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{yyyy-MM-dd hh:mm:ss} -- %-5level -- [%thread] -- %logger{50} --- %msg %n}"/>

    <!--
        Appender: 设置日志信息的去向,常用的有以下几个
           1) ch.qos.logback.core.ConsoleAppender (控制台)
           2) ch.qos.logback.core.rolling.RollingFileAppender (文件大小到达指定尺寸的时候产生一个新文件)
           3) ch.qos.logback.core.FileAppender (文件)
    -->

    <!--控制台配置-->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
         <!--当环境是dev开发环境时,这部分配置才生效-->
         <springProfile name="dev">
             <!--日志输出格式-->
             <encoder>
                 <pattern>${CONSOLE_LOG_PATTERN}</pattern>
             </encoder>
         </springProfile>
         <!--当环境不是dev开发环境时,这部分配置才生效-->
         <springProfile name="!dev">
             <!--日志输出格式-->
             <encoder>
                 <pattern>--%logger{50} --- %msg %n</pattern>
             </encoder>
         </springProfile>
    </appender>

    <!--日志记录文件配置-->
    <appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--当环境是production生产环境时,这部分配置才生效
                即在production生产环境上,我们才将日志信息记录到日志文件中-->
        <springProfile name="production">
            <!--日志文件保存路径,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建 -->-->
            <file>D:\idea\logs\example-logging.log</file>
            <!--基于大小和时间的轮转策略,当日志内容超出文件大小限制后,会自动生成一个文件来继续记录和重命名-->
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--当日志内容超出文件大小限制后,会自动生成一个文件来继续记录,文件按下面格式命名-->
                <fileNamePattern>D:\idea\logs\example-logging-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
                <!--文件最大限制,默认10MB-->
                <maxFileSize>10MB</maxFileSize>
                <!--文件最大保存周期,默认7天-->
                <maxHistory>7</maxHistory>
                <!--所有归档文件总的大小限制-->
                <totalSizeCap>20GB</totalSizeCap>
            </rollingPolicy>
            <!--日志输出格式-->
            <encoder>
                <pattern>${FILE_LOG_PATTERN}</pattern>
            </encoder>
        </springProfile>
    </appender>

    <!--
        用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
        <logger>仅有一个name属性,一个可选的level和一个可选的addtivity属性
        name:
            用来指定受此logger约束的某一个包或者具体的某一个类。
        level:
            用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
            如果未设置此属性,那么当前logger将会继承上级的级别。
        additivity:
            是否向上级logger传递打印信息。默认是true。
        <logger>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger
    -->
    <logger name="com.cd.example.one" level="trace"/>
    <logger name="com.cd.example.two" level="debug"/>
    <logger name="com.cd.example.three" level="warn"/>

    <!--
        root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性。
            <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger
            使该指定的appender生效
    -->
    <root level="info">
        <appender-ref ref="consoleAppender"/>
        <appender-ref ref="fileAppender"/>
    </root>

</configuration>

关于这个日志的应用:

一般而言日志只要开启就可以了基本不需要自定义但是有时候要应用时就需要自定义配置,比如:vue+springboot实现控制台日志实时推送前台

1.自定义一个日志配置,目的是将日志转化自己进行处理。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>logback-demo</contextName>

<!--    <!–输出到控制台 ConsoleAppender–>-->
<!--    <appender name="consoleLog1" class="ch.qos.logback.core.ConsoleAppender">-->
<!--        <!–展示格式 layout–>-->
<!--        <layout class="ch.qos.logback.classic.PatternLayout">-->
<!--            <pattern>%d -1 %msg%n</pattern>-->
<!--        </layout>-->
<!--    </appender>-->

    <!--输出到控制台 ConsoleAppender-->
    <appender name="consoleLog2" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--encoder 默认配置为PatternLayoutEncoder-->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="cn.vhrs.untils.LogFilter"> </filter>
    </appender>
    
    <!--指定最基础的日志输出级别-->
    <root level="info">
        <!--appender将会添加到这个loger-->
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="consoleLog2"/>
    </root>
</configuration>

2.进行日志的处理( 日志过滤器 )

@Service
public class LogFilter extends Filter<ILoggingEvent> {

    @Override
    public FilterReply decide(ILoggingEvent event) {
        String exception = "";
        IThrowableProxy iThrowableProxy1 = event.getThrowableProxy();
        if(iThrowableProxy1!=null){
            exception = "<span class='excehtext'>"+iThrowableProxy1.getClassName()+" "+iThrowableProxy1.getMessage()+"</span></br>";
            for(int i=0; i<iThrowableProxy1.getStackTraceElementProxyArray().length;i++){
                exception += "<span class='excetext'>"+iThrowableProxy1.getStackTraceElementProxyArray()[i].toString()+"</span></br>";
            }
        }
        LoggerMessage loggerMessage = new LoggerMessage(
                event.getMessage()
                , DateFormat.getDateTimeInstance().format(new Date(event.getTimeStamp())),
                event.getThreadName(),
                event.getLoggerName(),
                event.getLevel().levelStr,
                exception,
                ""
        );
        LoggerQueue.getInstance().push(loggerMessage);
        return FilterReply.ACCEPT;
    }
}

3. 创建一个阻塞队列,作为日志系统输出的日志的一个临时载体

public class LoggerQueue {
    //队列大小
    public static final int QUEUE_MAX_SIZE = 10000;
    private static LoggerQueue alarmMessageQueue = new LoggerQueue();
    //阻塞队列
    private BlockingQueue blockingQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);

    private LoggerQueue() {
    }

    public static LoggerQueue getInstance() {
        return alarmMessageQueue;
    }

    /**
     * @Description: 消息入队
     * @Return: boolean
     * @Author: leijun
     * @Date: 2019/11/26
     **/
    public boolean push(LoggerMessage log) {
//        System.out.println("消息入队的信息===="+log);
        return this.blockingQueue.add(log);//队列满了就抛出异常,不阻塞
    }

    /**
     * @Description: 消息出队
     * @Return: com.unismc.springbootudcap.powersecurity.entity.LoggerMessage
     * @Author: leijun
     * @Date: 2019/11/26
     **/
    public LoggerMessage poll() {
        LoggerMessage result = null;
        try {
//            System.out.println("输出:"+this.blockingQueue);
            result = (LoggerMessage) this.blockingQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

4. 创建一个日志实体

/**
 * 日志消息实体
 */
@Getter
@Setter
@ToString
@AllArgsConstructor
public class LoggerMessage {
    private String body;
    private String timestamp;
    private String threadName;
    private String className;
    private String level;
    private String exception;
    private String cause;
//省略 get , set
}

5. 接下来配置WebSocket

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Autowired
    SimpMessagingTemplate messagingTemplate;
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws/websocket")
                .setAllowedOrigins("*")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue","/topic");
    }

    @PostConstruct
    public void pushLogger(){
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        LoggerMessage log = LoggerQueue.getInstance().poll();
                        if(log!=null){
                            if(messagingTemplate!=null)
                            {
                                messagingTemplate.convertAndSend("/topic/pullLogger",log);
//                                System.out.println(new ObjectMapper().writeValueAsString(log));
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        executorService.submit(runnable);
    }
}

6.在vue中去接收消息。

           //建立日志连接
            initlog(context) {
                if (context.state.stompClient == null) {
                    context.res = "<div style='color: #18d035;font-size: 14px'>通道连接成功,静默等待....</div>"
                    // this.$refs['logContainerDiv'].append();
                    // 建立连接对象
                    let socket = new SockJS('http://localhost:8081/ws/websocket');
                    // 获取STOMP子协议的客户端对象
                    context.state.stompClient = Stomp.over(socket);
                    context.state.stompClient.connect({}, () => {
                        context.state.stompClient.subscribe('/topic/pullLogger', (event) => {
                                let content = JSON.parse(event.body);
                                // console.log(content);
                                let leverhtml = '';
                                let className = "<span style='color: #229379'>" + content.className + "</span>";
                                switch (content.level) {
                                    case 'INFO':
                                        leverhtml = "<span style='color: #90ad2b'>" + content.level + "</span>";
                                        break;
                                    case 'DEBUG':
                                        leverhtml = "<span style='color: #A8C023'>" + content.level + "</span>";
                                        break;
                                    case 'WARN':
                                        leverhtml = "<span style='color: #fffa1c'>" + content.level + "</span>";
                                        break;
                                    case 'ERROR':
                                        leverhtml = "<span style='color: #e3270e'>" + content.level + "</span>";
                                        break;
                                }
                                context.state.res += "<div style='color: #18d035;font-size: 14px'>" + content.timestamp + " " + leverhtml + " --- [" + content.threadName + "] " + className + " :" + content.body + "</div>"
                                // this.$refs['logContainerDiv'].append(content.timestamp + " " + leverhtml + " --- [" + content.threadName + "] " + className + " :" + content.body + "<br/>");
                                if (content.exception != "") {
                                    context.state.res += "<div>" + content.exception + "</div>"
                                    // this.$refs['logContainerDiv'].append();
                                }
                                if (content.cause != "") {
                                    context.state.res += "<div>" + content.cause + "</div>"
                                    // this.$refs['logContainerDiv'].append(content.cause);
                                }
                                // this.$refs['logContainer'].scrollTo(this.$refs['logContainerDiv'].height() - this.$refs['logContainer'].height());
                            },
                            // {
                            //     token: "kltoen"
                            // }
                        );
                    });
                }
            }

这只是一个连接websocket的代码,我们可以将获取到的信息保存在vue中的vuex中或者localStorage中进行持久化。

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

0 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 593873浏览

随机文章
SpringBoot—纯 Java 搭建 SSM 环境
5年前
SpringSecurity—AuthenticationManager(用户的全局和局部定义)
5年前
Spring—读取配置的方式,@Value、@PropertySource、@ConfigurationProperties使用详解
5年前
Redis—StringRedisTemplate和RedisTemplate
5年前
Kotlin-表达式—变量与常量(十一)
4年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 593873 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付