日志是啥?
你之前肯定接触过日志,但是可能只是在控制台在那看来看去,最后复杂粘贴扔给ai就完事。是不是?
日志分为系统日志和自定义日志
系统日志:
比如你在启动程序的时候,控制台不是打印了一堆乱七八糟的东西嘛,最后报一个 Started Arangodb002Application in 1.965 seconds (JVM running for 2.739)
即表示启动成功。那乱七八糟的东西就是系统日志,但是系统日志不只是在启动的时候有的,运行时也非常关键
自定义日志:
就是你平时写的 log.info(“打印日志”)
这个就是自定义日志。那什么时候你的自定义日志会打印呢?当一个请求要调用含有该日志的方法。自定义日志就会打印
安装依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
org.slf4j
(全称:Simpple log for Java) 指的是一个简单的日志门面
logback
时一个具体的slf4j的具体首先框架,除此之外,其实还有log4j,log4j2这两者具体实现。
其实二者关系就像接口与具体实现类一样,你不用(slf4j+logback)也行,你也可以换(slf4j+log4j)或(slf4j+log4j2)。但是目前主流的是用slf4j+logback
使用
首先我们明确日志级别
首先我们要明确日志级别
DEBUG:调试级别,记录详细调试信息,用于开发阶段排查问题。
INFO:信息级别,记录系统正常运行的重要信息,便于审计和监控。
WARNING:警告级别,记录可能影响系统运行但不会导致崩溃的问题。
ERROR:错误级别,记录系统运行中的错误,导致功能无法正常工作。
CRITICAL:严重错误级别,记录可能导致系统崩溃或无法继续运行的严重问题。
FATAL(可选):致命错误级别,记录不可恢复的错误,系统必须停止。
TRACE(可选):追踪级别,记录更详细的系统运行过程,用于复杂系统的深度调试。
这里我们一般只使用 error,warn,info这3个
颜色会有区分
但是,有一个问题,这个日志都是我们自己定义的。可什么时候该用info,什么时候用warn,什么时候用error呢?
这个新手可能很难把握。
info
一般来说,info只是输出运行的信息,我们只需要在业务关键的地方使用info。比如在controller层参数传进来的时候打印一下。在service层可能调用别的服务对吧,这时候可以把传入的参数打印一下。在mapper层会调用数据库,这也可以以打印一下。
当然,info不仅仅只用来打印参数,当我们需要看调用一个接口来看看耗时多少,这里也可以用info。
反正记住一点,info是用来帮你理清楚一个接口在程序中是怎么一层层跑下去,最后又是怎么一层层把结果传上来的。不管你需要了解什么,都可以用info来帮助你。
warn
warn只警告信息对吧。这经常在哪使用呢?
第一个,try-catch里,这个不用多说吧。
try{
graphDBService.dealEdgeBeforeSave(message);
Result result = graphDBService.saveGraphData(message);
log.info("接收到消息:{},执行结果为{}", message.getCustName(),result);
}catch (Exception e){
log.warn("消息消费异常,请检查!!,消息为:【{}】",message);
}
还有一个就是要多传入的参数进行判断是否合规。
if (ctGuarPerson.getCtGuarPersonIdNo().equals(message.getCustIdentityNo())) {
log.warn("反担保人身份证信息填写有误:反担保人【姓名:{}】与申请人身份证【{}】重复。", ctGuarPerson.getCtGuarPersonName(), message.getCustIdentityNo());
//从反担保人列表中剔除
return false;
}
error
这主要是在catch中出现。
区别
说实话,讲很难讲出来,首先,info和另外两个还是可以区别开的吧。只要不是有可能风险的,就用info!这样可以理解吧
但是warn和error怎么区别的,你怎么知道他只是警告不会导致无法运行呢?这么说,其实这一旦熟悉业务和逻辑其实是可以区分开的。但新手一开始并没有这个印象。我的建议是拿公司的代码,全局搜索log来看看同事是怎么写的。多读多学,自然就会了。
这就像慢查询优化一样,天天背八股没啥鸟用,一问就啊吧啊吧,面试官反问你一句你是如何定位到这个位置的就一脸懵逼(你是怎么定位到这里慢的呢?为什么如此确定就是这里的呢?)。反手就挂了你。还是要看经验。
如何读日志
在你写了日志后,该怎么读日志?这是很困难的,就像你一眼看到密密麻麻的日志,人都麻了
再举个例子,这是启动时的系统日志,其实还是有意义的,对吧。但是我们的关注点不在这上面
看看接下来的日志,
目前已知:我发送了一个请求,但是日志报了一个warn
2025-07-09 15:15:57.871|4316|WARN|[app-asso-service,b021a2b2c1af5ea7,b021a2b2c1af5ea7,]|http-nio-20815-exec-2|org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver|Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.central.common.model.Result<com.ludan.asso.api.dto.CustAssoVO> com.ludan.asso.moduals.graph.controller.GraphController.getGraph(com.ludan.asso.api.dto.QueryDto)]
2025-07-09 15:15:57.871
发送的时间: 2025 年 7 月 9 日下午 3 点 15 分 57 秒 871 毫秒。4316 是进程信息
warn 是日志级别
[app-asso-service,b021a2b2c1af5ea7,b021a2b2c1af5ea7,]
这是分布式系统里的,没学的不用管http-nio-20815-exec-2
记录日志的线程org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
记录日志的类名HttpMessageNotReadableException
这里是HttpMessage无法读的异常。后面request body is missing说明了这里是以body传入的参数为null!!!public com.central.common.model.Result<com.ludan.asso.api.dto.CustAssoVO>
com.ludan.asso.moduals.graph.controller.GraphController.getGraph(com.ludan.asso.api.dto.QueryDto)
8和9看的迷迷糊糊吧,但是!
public Result<CustAssoVo> getGraph(QueryDto){
}
这看懂了嘛。哪个包下的哪个类的哪个方法,传入参数和返回值都有!!!
最后,就一个方法,多看,多学,每次记下来。下次就不会一看到就懵逼了
自定义日志
以上都是使用默认的配置。可以有一些其他问题,比如:
一个上线了的程序的日志可能还让你在控制台上查看嘛?难道不是在Linux里的文件里看日志嘛,那怎么实现呢???
日志就永远往一个日志文件里写嘛,不可能的吧,那怎么每天插件一个新的日志文件呢
日志前面的怎么写,如何靠自定义配置快速定位到位置
我们只需要在resource下写一个logback.xml
即可(不要求你会写,你得给我读懂吧)
<?xml version="1.0" encoding="UTF-8"?> <!--明确字符集版本-->
<configuration debug="false">
<!--property是定义全局变量,之后直接用#{PATTERN}就可以使用了-->
<property name="PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss.SSS}|%level|%thread|%logger|%method - %message%n"/>
<!--这里定义日志文件夹和日志文件名称-->
<property name="LOG_PATH" value="logs"/>
<property name="LOG_FILE_NAME" value="app-info"/>
<!--appendr的名称和类型,这里是输出到文件的类型-->
<!--appender是处理日志输出目标的组件-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--文件的路径-->
<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
<!--定义编码器的格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<!--日志生成策略,一天生成一个新的 或 超过100MB就生成一个新的-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件名格式-->
<fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<!--滚动策略:日志文件大于100MB时生成一个新的日志文件,每天生成一个新的日志文件-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保存60天-->
<maxHistory>60</maxHistory>
</rollingPolicy>
<prudent>false</prudent>
</appender>
<!--appendr的名称和类型,这里是输出到控制台的类型-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--定义编码器的格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--输出的形式-->
<pattern>${PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
PATTERN:
如何在Linux上查看日志
一般你要有跳版机的账号和权限,和Linux的账号和密码。这不用多说,主要是命令!命令!
我推荐3个,一个是tail,一个是less,一个是grep
tail
tail -fn 100 app-info.log# 实时显示最后100行日志
tail -n 100 app-info.log# 不实时
grep
grep "关键字" app-info.log# 这样只会筛选出含有关键字的日志
less
最常用的
Enter:向下滚一行
空格:向下滚一屏
/搜索词:向下搜索指定的字符串
?搜索词:向上搜索指定的字符串
n:查找下一个匹配项
N:查找上一个匹配项
G:跳转到文章最后一行
g:跳转到第一行
=:显示当前行号
数字+Enter:跳转到指定行
q:退出
有一句话:学会看日志才是程序员成熟的开始