为什么要打日志?怎么打日志?打什么日志?


公司的项目不会有 e.printStackTrace(); 这种代码的存在。因为这打印出来的错误信息没有日期、等级等等,分析起来不方便。再比如分布式系统中需要使用 trackID 来追踪问题,我们难道要每次打日志的时候都将 trackID 记录一下吗。像这种公共的东西,我们可以抽出一个框架来处理这些问题

为什么要用 SLF4J

在阿里开发手册有这么一条:

【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一

slf4j ,即 Simple Logging Facade for Java,简单门面日志。它是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,它只服务于各种各样的日志系统

slf4j 提供了统一的记录日志的接口,对不同日志系统的具体实现进行了抽象化,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过绑定具体的日志系统来实现。在项目中使用了 slf4j 记录日志,并且绑定了 log4j(pom.xml 中配置了相应的 jar 包依赖),则日志会以 log4j 的风格输出;后期需要改为以 logback 的风格输出日志,只需要将 jar 包 log4j 替换成 logback 即可,根本不需要修改日志文件的代码

建议统一使用 Slf4j 作为日志门面,logback 作为日志实现

日志级别

日志常用的五个级别,根据严重程度由低到高,依次为:debug(调试 ) < info(消息) < warn(警告) < error(错误) < fatal(严重错误)。通常可以根据实际所需要的颗粒度的大小选择其中的几个,当前常用 debug,info,warn,error 4个级别

在项目的 yaml 文件中可以配置日志级别

logging:
  level:
    com.xyz.spring: trace

logging.level 用来指定具体的包中应用程序日志的输出级别。上面的配置表示 com.xyz.spring 包下的所有日志输出级别为 trace,该级别会将操作数据库的 sql 打印出来,开发时设置成 trace 方便定位问题,在生产环境上,将这个日志级别再设置成 error 级别即可

Springboot 中日志的配置

Springboot 启动器中已经集成了 slf4j,就是 spring-boot-starter-web,因此无需手动导包,如果需要,可以使用以下配置

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.30</version>
</dependency>

application.yml 的日志配置

logging:
  config: logback.xml
  level:
    com.xyz: trace

config 表示读取日志配置的地点,将 locback.xml 放在 pom 文件同级包下即可。level 表示在 XXX 包下的日志输出级别,配置文件读取步骤:

  • 尝试在 classpath 下查找文件 logback-test.xml
  • 如果 logback-test.xml 文件不存在,尝试在 classpath 下查找 logback.xml
  • 如果两个文件都不存在,LogBack 用 BasicConfiguration 自动对自己进行最小化配置,这样不需要添加任何配置就可以输出日志信息

以下的 xml 文件只需要更改部分项目地址就能直接使用

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="D:IdeaProjects/SpringBoot-Item/springboot-slf4j/log"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/SpringBoot-Slf4j_%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="INFO"/>
    <logger name="com" level="INFO"/>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

在使用日志的时候,在该类中加入一行代码:

private Logger logger = LoggerFactory.getLogger(UserController.class);

或者在类之前加上 lombak 的注解

@Slf4j

日志的最佳实践

不要随便打日志,你可能觉得奇怪,打个日志难道会出什么故障码。但是线上确确实实因为日志出现过问题,下面来列举几个:

  • 线上机器 OOM。经过排查发现是因为堆外内存溢出,再排查发现是因为打了很多大 POJO 组成的 list,转化成 json 直接打印了
  • 报警不断。然鹅大部分的报警都是业务异常,比如用户无权限、访问 db 查不到数据等等,这些简单的问题不应该被我们监控到,因此日志打的不好会造成报警噪音特别大

那么我们应当遵守什么规则来打日志呢,以下是一些最佳实践:

1,系统/应用启动和参数变更。当系统启动时,可以将相关的参数信息进行打印,以便出现问题时,更准确地查询原因;参数信息可能并不存储在本地,需要通过配置中心获取,而参数信息有变更时,也需要将变更后的内容输出在日志中

2,关键操作节点与关键方法入参出参。最典型的就是在关键位置添加日志,记录用户进行的某个操作,记录关键方法的话,我们可以使用 AOP 写个注解,还能随便监控一下方法耗时

3,异常。如果是通过 try-catch 处理,需要注意日志编写的位置。如果需要将异常在本层抛出,则不需要进行日志记录,否则会出现日志重复的问题,如果打了日志,记得将异常转化成合理返回值。同时,应当注意将业务异常日志打成 info,因为很多监控系统会收集 error 的日志次数然后给你报警,但是业务异常算异常吗,至少它不应该由我们来处理

4,尽可能不要在日志中打太多数据,尤其不要把一个 pogo list 转化成 json 打进去,一般的日志实现是会 OOM 的。虽然打印的日志日志没有使用虚拟机空间,为了减少数据复制次数使用零拷贝技术放在了堆外空间,但是堆外空间也可能出现装不下的问题。我们可以将一个 pojo 拆成多个重要信息打印

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值