zoukankan      html  css  js  c++  java
  • springboot 日志【转】【补】

    市面上的日志框架

    日志门面 (日志的抽象层)日志实现
    JCL(Jakarta Commons Logging)(2014) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j     JUL(java.util.logging)    Log4j2    Logback

    补充说明:Logback, Log4j , Log4j2都出于同一作者之手, 但Logback更新。

    在使用日志时, 都是选一个左边门面(抽象层)、再选一个右边的实现;该用方法时只用右边门面方法 , 

    Spring框架选用JCL日志,需要引用commons-logging.jar ,  SpringBoot框架选用 SLF4j和logback;

    每一个日志的实现框架都有自己的配置文件。就算使用slf4j门面,日志实现框架用的还是自己本身的配置文件;

     

    如何让系统中所有的日志都统一到slf4j;

    1、将系统中其他日志框架先排除出去 (比如删除log4j.jar;

    2、用中间包来替换原有的日志框架 (比如添加log4j-over-slf4j.jar);

    3、我们导入slf4j其他的实现 

     

    为什么还要jcl-over-slf4j.jar ,log4j-over-slf4j.jar, jul-to-slf4j.jar?

    这是一个历史遗留问题  , 因为一个项目只一般不可能只有一个日志框架 , 比如Spring 使用commons-logging , hibernate使用 jboss-logging , MyBatis , XXX等其它框架又用了各自的日志框架, 那么为了把各种框架都整合到一个日志中去输出 , 所以有了这些需要替换原始功能的jar包

    SpringBoot日志关系

    SpringBoot使用它来做日志功能;

        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

     

    底层依赖关系图

    总结:

    1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录;

    2)、SpringBoot也把其他的日志都替换成了slf4j;

    3)、中间替换包(以jck-over-slf4j.jar为例),其实内部被换成了SLF4JLogFactory

    @SuppressWarnings("rawtypes")
    public abstract class LogFactory {
        static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = "http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j";
        static LogFactory logFactory = new SLF4JLogFactory();

    4)、如果我们要引入其他框架?一定要把这个框架的默认日志依赖移<exclusion>除掉,比如Spring框架用的是commons-logging;

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    日志测试

    logback测试用例

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class LoggingTest {
        Logger logger = LoggerFactory.getLogger(getClass());
        @Autowired
        ApplicationContext ac;//注入上下文
    
        @Test
        public void contextLoads() {
            //日志的级别;
            //由低到高   trace<debug<info<warn<error
            //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效
            logger.trace("这是trace日志...");
            logger.debug("这是debug日志...");
            //SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别(用了info级别)
            logger.info("这是info日志...");
            logger.warn("这是warn日志...");
            logger.error("这是error日志...");
        }
        public ApplicationContext getAc() {
            return ac;
        }
        public void setAc(ApplicationContext ac) {
            this.ac = ac;
        }
    }

    application.properties配置文件

    # springboot日志的配置文件,本处由application.properties中的spring.profiles.active=logging来启用本文件
    # 指定log文件,也可以写固定的绝对路径, 如果file和path都指定了,那么file优先级更高
    #logging.file=springboot02.log
    # 指定log路径,默认使用使用spring.log作为文件名, 如果以/开头,则在当前磁盘的根目录下创建路径,如果不以/开头,则在当前应用目录下创建日志路径
    logging.path=/springbootlog01/logs
    
    # 日志占位符说明
    # %d表示日期时间
    # %thread表示线程名
    # %-5level:级别从左显示5个字符宽度,左对齐
    # %logger{50} 表示logger包路径加类名字最长50个字符
    # %msg:日志消息
    # %n是换行符
    
    #  在控制台输出的日志的格式
    logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
    # 指定文件中日志输出的格式
    logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
    
    # 指定com下的所有日志输出级别为trace
    logging.level.com=trace

    logback默认配置位于该目录: spring-boot-1.5.14.RELEASE.jar!/org/springframework/boot/logging/logback/defaults.xml和base.xml

    日志配置

    给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了

    日志系统自定义文件说明 
    Logback logback.xml 直接被日志框架识别,不经过springboot , 不支持SpringBoot的profile功能.  
    Logback logback-spring.xml 日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能  
    Log4j2 log4j2.xml    
    Log4j2 log4j2-spring.xml    
    JDK (Java Util Logging) logging.properties    

     

    logback-spring.xml 使用SpringBoot的profile功能如下:

    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <!--
            日志输出格式:
                %d表示日期时间,
                %thread表示线程名,
                %-5level:级别从左显示5个字符宽度
                %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
                %msg:日志消息,
                %n是换行符
            -->
            <layout class="ch.qos.logback.classic.PatternLayout">
                <springProfile name="dev">
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
                </springProfile>
                <springProfile name="!dev">
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
                </springProfile>
            </layout>
        </appender>

     日志切换

      slf4j+log4j的方式 (已亲测)  processon地图 log4j2 (未亲测)  
    pom.xml内容 
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <!--排队具体的日志实现logback-->
                    <exclusion>
                        <groupId>ch.qos.logback</groupId>
                        <artifactId>logback-classic</artifactId>
                    </exclusion>
                    <!--排队log4j的替换包-->
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>log4j-over-slf4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!--使用log4j的适配层,它底层依赖log4j.jar,所以不用再导log4j.jar了,在classpath根目录放一个log4j.properties即可 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
            </dependency>
    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <exclusion>
                        <artifactId>spring-boot-starter-logging</artifactId>
                        <groupId>org.springframework.boot</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
     
     说明  此处直接去掉了slf4j的实现 , 必须换成log4j的实现  此处引用的是spring-boot-starter-log4j2场景启动器,替换了spring-boot-starter-logging场景启动器的功能  
           

    自己喜欢的简易风

    日志配置

    #  在控制台输出的日志的格式
    logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss}	↓%n%msg%n

    日志格式

    2019-12-04 09:59:37    ↓
    Root WebApplicationContext: initialization completed in 813 ms
    2019-12-04 09:59:38    ↓
    Initializing ExecutorService 'applicationTaskExecutor'
    2019-12-04 09:59:38    ↓
    Tomcat started on port(s): 8080 (http) with context path '/es'
    2019-12-04 09:59:38    ↓

    在日志上打印请求唯一log标识

    重点语句

    org.slf4j.MDC;
    MDC.put(TRACE_ID, traceId);
    MDC.remove(TRACE_ID);
    
    

    使用拦截器实现

    package com.interceptors.logtag.interceptor;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.slf4j.MDC;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.UUID;
    
    /**
     * <p>日志拦截器</p>
     *
     * @author zetting
     * @date 2018/8/27 11:18
     */
    @Component
    public class LogInterceptor extends HandlerInterceptorAdapter {
        private Logger log = LoggerFactory.getLogger(this.getClass());
        /**
         * 日志跟踪标识
         */
        private static final String TRACE_ID = "TRACE_ID";
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            String traceId = UUID.randomUUID().toString();
            if (StringUtils.isEmpty(MDC.get(TRACE_ID))) {
                MDC.put(TRACE_ID, traceId);
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
            MDC.remove(TRACE_ID);
        }
    }

    【Springboot之搜索日志妙招】在日志上打印请求唯一log标识==>https://www.jianshu.com/p/b74ade8bbb57

  • 相关阅读:
    JAVA中“==”和equals
    C++中各种容器的类型与特点
    程序员面试宝典 笔记 第七章
    程序员面试宝典 笔记(第六章 预处理 const 和sizeof())
    某学长面经
    tomcat 启动日志乱码
    Jenkins关闭和重启实现方式
    linux下svn版本控制的常用命令大全
    Java中的增强 for 循环 foreach
    JS 中 cookie 的使用
  • 原文地址:https://www.cnblogs.com/whatlonelytear/p/9023004.html
Copyright © 2011-2022 走看看