日志是啥?

你之前肯定接触过日志,但是可能只是在控制台在那看来看去,最后复杂粘贴扔给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

使用

首先我们明确日志级别

首先我们要明确日志级别

  1. DEBUG:调试级别,记录详细调试信息,用于开发阶段排查问题。

  2. INFO:信息级别,记录系统正常运行的重要信息,便于审计和监控。

  3. WARNING:警告级别,记录可能影响系统运行但不会导致崩溃的问题。

  4. ERROR:错误级别,记录系统运行中的错误,导致功能无法正常工作。

  5. CRITICAL:严重错误级别,记录可能导致系统崩溃或无法继续运行的严重问题。

  6. FATAL(可选):致命错误级别,记录不可恢复的错误,系统必须停止。

  7. 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)]
  1. 2025-07-09 15:15:57.871 发送的时间: 2025 年 7 月 9 日下午 3 点 15 分 57 秒 871 毫秒。

  2. 4316 是进程信息

  3. warn 是日志级别

  4. [app-asso-service,b021a2b2c1af5ea7,b021a2b2c1af5ea7,] 这是分布式系统里的,没学的不用管

  5. http-nio-20815-exec-2 记录日志的线程

  6. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver 记录日志的类名

  7. HttpMessageNotReadableException 这里是HttpMessage无法读的异常。后面request body is missing说明了这里是以body传入的参数为null!!!

  8. public com.central.common.model.Result<com.ludan.asso.api.dto.CustAssoVO>

  9. 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:退出

有一句话:学会看日志才是程序员成熟的开始


比较是偷走幸福的小偷