Java日志规范
LOGBack,一个“可靠、通用、快速而又灵活的Java日志框架”。
logback 简介
Ceki Gülcü在Java日志领域世界知名。他创造了Log4J ,这个最早的Java日志框架即便在JRE内置日志功能的竞争下仍然非常流行。随后他又着手实现SLF4J 这个“简单的日志前端接口(Façade)”来替代Jakarta Commons-Logging 。
LOGBack,一个“可靠、通用、快速而又灵活的Java日志框架”。
一、日志 API
1 【强制】各应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
Lombok Slf4j 注解方式
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Fooooo {
public void barrrr() {
log.info("hello world ...");
}
}
二、日志输出
1.【强制】通用格式为:“DESC. [key1={}, key2=‘{}’, key3={}]”,例如:
log.info("execute one query request. [request='{}', response='{}'", json, queryResponseJson);
log.warn("try to re-query for incomplete query. [sql='{}']", sql, e);
- “DESC” 为行为描述,一句有区分度的话,以“.”结尾。目的:作为 “Anchor” 及锚点,能够用来定位一类问题,它需要有辨识度,需要能通过 GREP 简单地过滤到。
- “[key1={}, key2=‘{}’, key3={}]” 为案发现场上下文,其中的 key 的命名遵循Java变量命名规范,而如果 value 为字符串,建议加上单引号。
- 必须使用占位符"{}"代替字符串拼接。
2.【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。
log.error("execute one query request. [request='{}', response='{}', elapse={}, cacheResponse='{}']", json, queryResponseJson, elapse, queryContext.getCacheResponse(), e);
3.【推荐】尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用中文描述即可,否则容易产生歧义。
三、日志配置
1.【强制】避免重复打印日志,浪费磁盘空间,务必在配置文件中设置 additivity=false。
2.【强制】注意日志使用级别,ERROR 级别只记录系统逻辑出错、异常或者重要的错误信息。
ERROR:
当程序发生不可预期的错误(按照设计不应该发生)时。
或者不可恢复的错误(启动时初始化失败,直接退出)时。
WARN:
当程序发生可预期或者不影响主要功能的时候时使用。
INFO:
用于追踪程序的正常执行。
典型的在 Web 模块中,任何一处 INFO 日志的输出在一次 Web Request 中被调用的次数应该是 “常量级” 别的,且大部分情况下这个常量应该是 1。
DEBUG:
调试日志,DEBUG 日志打印的量没有限制,但需要注意不要影响执行性能。
四、日志性能
【推荐】禁止在循环体内、被频繁调用的方法、回调方法内,打 log 要小心。如无必要,一律禁止。
【推荐】对于 trace/debug 级别的日志输出,必须进行日志级别的判断,才可输出日志。如无性能损耗的生成日志内容,此条规范可选。
import lombok.extern.slf4j.Slf4j;
// 如果判断为真,那么可以输出 trace 和 debug 级别的日志
if (log.isDebugEnabled()) {
log.debug("desc log. [id={}, name='{}']", id, getName());
}
五、栈信息打印
try {
} catch (Exception e){
log.error("dsfs", e); // 会打印栈信息到 log 文件。而不是标准输出
e.printStackTrace(); // 会打印到标准输出,不推荐这种方法
}
六、logback-spring.xml
在工程src目录下建立logback.xml
注:
1.logback首先会试着查找logback.groovy文件;
2.当没有找到时,继续试着查找logback-test.xml文件;
3.当没有找到时,继续试着查找logback.xml文件;
4.如果仍然没有找到,则使用默认配置(打印到控制台)。
自定义日志配置
根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:
Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2:log4j2-spring.xml, log4j2.xml
JDK (Java Util Logging):logging.properties
Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,spring boot可以为它添加一些spring boot特有的配置项。
默认的命名规则,并且放在 src/main/resources 下面即可
如果你即想完全掌控日志配置,但又不想用logback.xml作为Logback配置的名字,application.yml可以通过logging.config属性指定自定义的名字:
logging.config=classpath:logging-config.xml
logback配置样例
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<springProperty scop="context" name="server" source="spring.application.name" defaultValue="log"/>
<springProperty scop="context" name="logstash.destination" source="logstash.destination" />
<property name="log.path" value="log" />
<property name="log.maxHistory" value="60" />
<property name="log.infoTotalSizeCap" value="10GB" />
<property name="log.errorTotalSizeCap" value="5GB" />
<property name="log.sqlTotalSizeCap" value="10GB" />
<property name="log.maxFileSize" value="100MB" /><!--KB、MB、GB-->
<property name="log.colorPattern" value="%magenta(%d{yyyy-MM-dd HH:mm:ss.SSS}) %yellow(${server} [%thread]) %highlight(%-5level) %red([%X{tl}]) %green(%logger) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} ${server} [%thread] %-5level [%X{tl}] %logger{36} - %msg%n"/>
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.colorPattern}</pattern>
</encoder>
</appender>
<!-- info日志文件输出 -->
<appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<prudent>false</prudent>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!--
maxHistory的保存时长与fileNamePattern设置有关,如果保存格式为yyyyMMddHHmm,那maxHistory的时间单位就是分钟,如果保存格式为yyyyMMdd,那maxHistory的时间单位就是天。
通常在过渡期间执行归档删除。但是,某些应用程序的生存时间可能不足以触发翻转。由此可见,对于这样短暂的应用程序,归档删除可能永远不会有执行的机会。
通过将 cleanHistoryOnStart 设置为 true,将在附加程序启动时执行归档删除。
TimeBasedRollingPolicy支持自动文件压缩。如果 fileNamePattern 选项的值以.gz 或 .zip 结尾,则启用此功能。
各种技术原因,文件轮转不是由时钟驱动的,而是取决于日志记录事件的到达。
例如,在 2002 年 3 月 8 日,假设 fileNamePattern 设置为yyyy-MM-dd (每日翻转),则文件轮转发生在午夜后第一个事件到达时,比如在00:23:47秒来了一个记录日志的实践,
那么实际上文件将在在 2002 年 3 月 9 日 00:23:47轮转,而非2002 年 3 月 9 日 00:00:00。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--
%d:表示日志的后缀,精确到天,即一天一个日志文件。如:auto-2022-01-01.log。
%i:表示如果这一天中文件大小超过了100MB,将重新记录一个文件。如:auto-2022-01-01.0.log。
-->
<fileNamePattern>${log.path}/info/info-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
<!--日志文件保留天数-->
<maxHistory>${log.maxHistory}</maxHistory>
<!-- 所有存档文件的总大小。当超过总大小上限时,最早的 Files 将被异步删除。 -->
<totalSizeCap>${log.infoTotalSizeCap}</totalSizeCap>
<!-- 每天日志归档路径以及格式 %i是索引 超过50MB 日志文件索引会以0开始-->
<maxFileSize>${log.maxFileSize}</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 异常日志文件输出 -->
<appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<prudent>false</prudent>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/error-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
<maxHistory>${log.maxHistory}</maxHistory>
<totalSizeCap>${log.errorTotalSizeCap}</totalSizeCap>
<maxFileSize>${log.maxFileSize}</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--sql日志文件 使用mybatis时,sql语句debug下才会打印-->
<appender name="sqlFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<prudent>false</prudent>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sql/sql-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
<maxHistory>${log.maxHistory}</maxHistory>
<totalSizeCap>${log.sqlTotalSizeCap}</totalSizeCap>
<maxFileSize>${log.maxFileSize}</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到logstash的appender-->
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--ThresholdFilter 过滤掉低于指定临界值的事件 过滤掉TRACE和DEBUG级别的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!--可以访问的logstash日志收集端口-->
<destination>${logstash.destination}</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>Asia/Shanghai</timeZone>
</timestamp>
<!--自定义日志输出格式-->
<pattern>
<pattern>
{
"server": "${server}",
"level": "%level",
"traceId": "%X{tl}",
"thread": "%thread",
"class": "%logger",
"message": "%message",
"stack_trace": "%exception{15}"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!-- 文件 异步日志(async) -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"
immediateFlush="false" neverBlock="true">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>2048</queueSize>
<neverBlock>true</neverBlock>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="logstash" />
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console" />
</root>
<!-- SIT+开发环境. 多个使用逗号隔开. -->
<springProfile name="test,dev">
<logger name="com.***" level="DEBUG" additivity="false">
<appender-ref ref="console" />
<appender-ref ref="ASYNC"/>
</logger>
</springProfile>
<!-- UAT+生产环境. -->
<springProfile name="uat,prod">
<logger name="com.***" level="DEBUG" additivity="false">
<appender-ref ref="errorFile" />
<appender-ref ref="sqlFile" />
<appender-ref ref="infoFile" />
<appender-ref ref="ASYNC"/>
</logger>
</springProfile>
</configuration>
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)