添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
天涯  ·  Apache ...·  2 年前    · 
天涯  ·  slf4j的no applicable ...·  3 年前    · 
crhgxdqw  ·  Python3 ...·  3 年前    · 
谦逊的盒饭  ·  Electron ...·  11 月前    · 
强健的皮带  ·  WPF自学入门(七)WPF ...·  1 年前    · 
逆袭的斑马  ·  azure - Top 100件azure ...·  1 年前    · 

作为程序员,程序开发写代码一定少不了记录日志(没日志的代码都是耍流氓!)今天记录一下自己新搭建spring-boot项目后对日志的思考。

起因:项目是一个无页面,通过直接访问数据库,然后调用第三方api实现数据上传的简单应用。因为项目很小,所以在项目完成后,为了使jar包的大小更小(强迫症,想把无用的maven引用全部去掉),所以打包完成后对lib下的jar包进行查看,发现关于log的jar包很多,由此引发联想,难道一个简单的日志都要用这么多的jar包吗?

查看自己的代码,发现只用到了如下代码

commons-logging和slf4j是java中的日志门面,即它们提供了一套通用的接口,具体的实现可以由开发者自由选择。log4j和logback则是具体的日志实现方案。这是典型的门面模式

比较常用的搭配是commons-logging+log4j,slf4j+logback

spring默认使用commons-logging+log4j

spring-boot默认使用slf4j+logback

SLF4J是编译时绑定到具体的日志框架,性能优于采用运行时搜寻的方式的commons-logging

不需要使用logger.isDebugEnabled()来解决日志因为字符拼接产生的性能问题

logger.info("my name is {}", "medusar");
logger.info("my name is " + "medusar");

在效率上,第一行比第二行更高,因为如果当前日志级别是ERROR,第一行不会进行字符串拼接,而第二行,无论日志级别是什么,都会先进行字符串拼接。

所以为了解决这个问题,commons-logging等框架提供了下面的方式:

if (log.isDebugEnabled()){
        log.debug("dddd"+"eee");

这些包的目的是什么?

答案是:统一日志记录。a(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx等等,每个框架都是自己使用的日志框架,如何统一他们的?

  • 将系统中其他日志框架先排除出去;
  • 用中间包来替换原有的日志框架;
  • 我们导入slf4j其他的实现
  • 说白了就是,其他的框架还以为他们用的还是自己的日志框架,而实际上,他们的日志框架已经不存在了,存在的是slf4j替换过后的对原有框架的实现的中间包

    形象的比喻是:春秋战国时期,各个诸侯纷纷自立为王,他们的臣子都拥戴自己的大王,忽然有一天,秦国的影密卫,将其中几个大王偷偷换成了自己的亲信,让他们代替这几个诸侯国的大王,然后将真正的大王们都杀了,做的神不知鬼不觉,他们的大臣们依然死心塌地的拥戴着自己的大王,并不知道大王已经是替身;实际上他们的大王已经都听命于秦国。表面是各国纷乱,而实际上他们已经实现了统一,都归秦国管理!!!

    每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;

    使用logback 配置文件是:

    logback.xml:直接就被日志框架识别了。

    logback-spring.xml:日志框架就不直接加载日志的配置项,由 SpringBoot 解析日志配置,可以使用 SpringBoot 的高级 Profile 功能。

    logback-spring.xml完整配置文件:

    logback的语法可以描述为:<configuration>元素包含零个或多个<appender>元素,后跟零个或多个<logger>元素,后跟至多一个<root>元素。 configuration :根标签 scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true,在logback-spring.xml使用时可能会报错,所以不能一起使用 scanPeriod:默认情况下,将每分钟扫描一次配置文件的更改。可以以毫秒、秒、分钟或小时为单位指定值。未指定时间单位为毫秒 <configuration> <!-- 用来引入外部资源文件,resource:表示资源路径 --> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <!-- 每个logger都关联到logger上下文,默认上下文名称为default,可以设置成其他名字,用于区分不同应用程序的记录, 一旦设置,不能修改,可以通过%contextName来打印日志上下文名称 --> <contextName>logback</contextName> <!-- 日志级别:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF --> <!-- 自定义变量,使用${}来使用变量 name:变量名 value:变量值 scope:变量的作用范围--> <property name="log_path" value="log" scope="context" /> <property name="log_pattern" value="%clr(%d{yy-MM-dd hh-mm-ss.SSS}){yellow} [%clr(%t){magenta}] [%clr(%p)] %clr(%C{1}.%M:%L) |:%clr(%m%n){blue}"/> <property name="log_fileNamePattern" value="-%d{yyyy-MM-dd}.%i.log"/> <property name="log_maxFileSize" value="10MB"/> <property name="log_maxHistory" value="15"/> <property name="dev_log_level" value="DEBUG" /> <property name="test_log_level" value="DEBUG" /> <property name="prod_log_level" value="INFO" /> <!-- 输出到控制台 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>${log_pattern}</pattern> </encoder> </appender> appender标签:定义日志输出策略,包括日志文件名称,输出格式,日志滚动策略,日志文件大小等等,具体看下面说明 name:策略名称 class:策略类型 控制台输出:ch.qos.logback.core.ConsoleAppender 文件滚动输出:ch.qos.logback.core.rolling.RollingFileAppender <file>:日志输出的路径及文件名 <encoder>:设置日志输出的格式和编码 <rollingPolicy>:设置日志切割策略 <fileNamePattern>:定义了日志的切割方式,切割后存放的文件名。 <maxFileSize>:触发策略,指定单个日志文件的上限大小,超限就会进行切割。 <maxHistory>:日志文件保留天数 <filter>:过滤规则 具体可看:http://logback.qos.ch/manual/configuration.html http://logback.qos.ch/manual/appenders.html <!-- 时间滚动输出 系统日志 --> <appender name="SYSTEM" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log_path}/app.log</file> <encoder> <pattern>${log_pattern}</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${logpath}/info/app${log_fileNamePattern}</fileNamePattern> <maxFileSize>${log_maxFileSize}</maxFileSize> <maxHistory>${log_maxHistory}</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUG</level> </filter> </appender> <!-- sql输出到sql.log文件 --> <appender name="SQL" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log_path}/sql.log</file> <encoder charset="UTF-8"> <pattern>${log_pattern}</pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${log_path}/sql-${log_fileNamePattern}</fileNamePattern> <maxFileSize>${log_maxFileSize}</maxFileSize> <maxHistory>${log_maxHistory}</maxHistory> </rollingPolicy> </appender> <!-- logger:记录器用来指定某一个包或者具体的类的日志打印级别。 name:指定某个包或者具体的类 level:指定日志输出级别。 additivity:如果没有指定level值,是否从最近的父标签继承level值,值为true或false--> <logger name="org.springframework" level="WARN" /> <logger name="org.apache" additivity="false" level="WARN" /> <logger name="org.mybatis" additivity="false" level="DEBUG" /> <!-- 测试环境 --> <springProfile name="test"> <root level="${test_log_level}"> <appender-ref ref="CONSOLE" /> </root> </springProfile> <!-- 开发环境 --> <springProfile name="dev"> <root level="${dev_log_level}"> <appender-ref ref="CONSOLE" /> </root> </springProfile> <!-- 生产环境 --> <springProfile name="prod"> <logger name="com.boot.sql" level="DEBUG" additivity="false"> <appender-ref ref="SQL" /> </logger> <root level="${prod_log_level}"> <appender-ref ref="SYSTEM" /> </root> </springProfile> </configuration>

    使用log42 配置文件是(pom排除其他,引入中间包):

    log4j2.xml

    log4j2-spring.xml

    使用jul 配置文件是(pom排除其他,引入中间包,存在已知的类加载问题,这些问题会导致从“可执行 jar”运行时出现问题。建议在从“可执行 jar”运行时尽可能避免使用。):

    logging.properties

      补充,以前我们使用spring的时候,Logging是spring中唯一强制的外部依赖,spring中默认使用的日志是commons-logging,简称JCL,这里说的强制性,是因为在spring-core这个模块中引入了该依赖。不过,引入了该依赖,也无需做任何其他的配置,它是日志门面,它内部会有自己的算法去找日志门面的实现类,比如log4j,如果说没有引入其他日志依赖,它默认就会去找JDK自带的java.util.logging简称jul作为其日志实现类

    log4j1.x版本已经停止更新了,log4j2.x习惯性叫做`log4j2`。(大家还记得前段时间关于log4j漏洞的事情吧)
    

    更换spring的common-logging日志系统 依赖配置,排除commons-logging

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

    这个时候如果运行程序会抛异常,因为我们把 Spring 依赖的 commons-logging 排除了, 而这个依赖是必须有的,不是可选的。

    加入转换包

    这个转换包就相当于我们上面说的那个转换器,spring会调用jcl-over-slf4j,spring以为调用的还是commons-logging,但是实际上commons-logging已经被我们删除掉了,并且jcl-over-slf4j还可以将输出转换为slf4j类型的输出,又因为我们前面引入了logback的依赖,lagback是slf4j的实现,所以最终输出的就是logback的日志系统

        <!-- 其他日志框架的中间转换包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
    复制代码
  • 中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)
  • 30 道 Vue 面试题,内含详细讲解(涵盖入门到精通,自测 Vue 掌握程度)
  • 大菠萝?Pinia已经来了,再不学你就out了
  • 一个合格(优秀)的前端都应该阅读这些文章
  • 「2021」高频前端面试题汇总之JavaScript篇(上)
  • 私信