日志的作用
常见的日志组件
日志组件对性能有一定影响,尤其是需要大量使用日志的项目,应该注意日志组件的选择。
日志之所以采用门面模式,是为了在项目中使用统一的日志api,方便以后切换。比如项目统一使用commons-logging的日志接口来记录日志,使用log4j作具体实现,后续可以直接切换为log4j2等其它日志实现组件,无需修改记录日志的相关java代码。
ssm老项目常用 commons-logging+log4j。
slf4j支持占位符,性能更高,springboot默认使用 slf4j+logback,通常也是使用 slf4j+logback。
springboot默认集成了spring-boot-starter-logging,采用slf4j+logback作日志
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
dependency>
这个依赖包含了
可以看出,spring-boot-starter-logging可以将其它日志类型转换为slf4j,如果要统一第三方jar依赖中的日志,exclusion移除第三方依赖自带的日志组件即可,spring-boot-starter-logging会自动适配为slf4j。
logback的日志级别
TRACE < DEBUG < INFO(默认) < WARN < ERROR < FATAL
logback日志默认格式
2020-05-24 16:51:38.323 INFO 25168 --- [ main] com.chy.visit.VisitApplication : No active profile set, falling back to default profiles: default
日期时间 日志级别 pid 线程名(main是主线程) 哪个类输出的日志(包名.类名) 信息
pid即进程id,记录当前java进程的pid
resources下新建 logback.xml 或 logback-spring.xml ,springboot应用启动时会自动加载这个文件,官方推荐使用logback-spring.xml,因为可以自动应用一些spring的配置。
彩色日志+区分环境+逐日输出+springboot admin实时查看日志+常用输出级别
<configuration debug="false">
<property name="SERVER-NAME" value="user-server"/>
<springProfile name="dev">
<property name="LOG_HOME" value="C:/Users/chy/Desktop/日志"/>
springProfile>
<springProfile name="test">
<property name="LOG_HOME" value="/app/微服务/${SERVER-NAME}"/>
springProfile>
<springProfile name="prod">
<property name="LOG_HOME" value="/var/logs/${SERVER-NAME}"/>
springProfile>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:-}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(LN:%L ){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>utf8charset>
encoder>
appender>
<appender name="DAY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/${SERVER-NAME}-%d{yyyy-MM-dd}.logFileNamePattern>
<MaxHistory>30MaxHistory>
rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
encoder>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>500MBMaxFileSize>
triggeringPolicy>
appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${LOG_HOME}/${SERVER-NAME}-all.logfile>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}pattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DAY_FILE"/>
<appender-ref ref="FILE"/>
root>
<logger name="com.apache.ibatis" level="WARN" />
<logger name="java.sql.Connection" level="WARN" />
<logger name="java.sql.Statement" level="WARN" />
<logger name="java.sql.PreparedStatement" level="WARN" />
<logger name="java.sql.ResultSet" level="DEBUG" />
<logger name="com.alibaba.druid.pool.DruidPooledResultSet" level="ERROR" />
<logger name="org.apache.shiro" level="WARN" />
<logger name="springfox.documentation" level="WARN" />
configuration>
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Xxx {
private static Log log = LogFactory.getLog(this.getClass());
public void xxx(){
log.info("xxx");
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Xxx {
private static Logger logger = LoggerFactory.getLogger(this.getClass());
public void xxx(){
String username="xxx", tel="xxx", address="xxx";
log.info("username:{},tel:{},address:{}",username,tel,address);
}
}
可以用 topic 属性设置日志主题,缺省时默认为所在类的全限定类名
// 17:37:09.322 [main] INFO com.chy.mall.service.UserServiceImpl - 日志信息
@Slf4j
// 17:34:08.554 [main] INFO Xxx服务 - xxx日志信息
// 17:34:08.554 [main] INFO Xxx定时任务 - xxx日志信息
@Slf4j(topic = "Xxx服务")
@Slf4j(topic = "Xxx定时任务")
1、接口、rpc调用、重要的service方法,务必打印出方法入参、返回值
2、写代码时务必要在关键、重要之处务必打印日志,考虑出现bug时,打印的日志是否方便排查、定位问题
3、复杂类型尽量转换为 json 格式打印,json格式更通用,便于 copy 到 postman 测试、修复数据等
//用户信息user=User(userId=1, username=chy, tel=1888xxxx, orderList=null)
log.info("用户信息user={}", user);
//用户信息user={"tel":"1888xxxx","userId":1,"username":"chy"}
log.info("用户信息user={}", JSON.toJSONString(user));