《JavaEE进阶》----15.<Spring Boot 日志>

本篇文章将记录我学习SpringBoot日志

1.日志文件的用途

2.SpringBoot日志文件的配置

3.用lombook依赖引入@Slf4j注解,从而引入log对象。方便我们打印日志。

一、日志的作用

日志主要是为了发现问题、分析问题、定位问题。除此之外、日志还有许多其他的用途。

1.系统监控

我们可以通过日志记录这个系统的运行状态,每一个方法的响应时间、响应状态等。对数据进行分析,设置不同的规则。超过阈值时进行报警。比如统计日志中关键字的数量,并在关键字数量达到一定条件时报警。

2.数据采集

数据采集是一个比较大的范围,采集的数据可以作用在很多方面。比如数据统计、推荐排序等。

数据统计:统计页面的浏览量、访客量、点击量等待,根据这些数据进行数据分析。优化公司运营策略。

推荐排序:目前推荐排序应用在各个领域。比如购物、广告、新闻等待。数据采集是推荐排序中必须做的一环。系统通过日志记录用户的浏览历史,停留时长。算法人员通过分析这些数据。训练模型给用户做推荐。

下图的数据源其中一部分就来自日志记录的数据。

3.日志审计

如今系统安全成为项目中的一个重要环节。安全审计也是系统中非常重要的部分。国家的政策法规、行业标准等都明确对日志审计提出了要求。通过系统日志分析,可以判断一些非法攻击、非法调用,以及系统处理过程中的安全隐患。

比如运行系统中运营人员在通过页面处理一些数据的时候。如果没有清楚的日志操作记录、一条数据被删除或者修改。是无法找到是谁操作的,若做了记录、就会一目了然。

还有一些内部的违规、信息泄露、比如客户信息被卖掉。如果没记录留存日志,为事后调查结果提供依据。那么事后很难追查。(一些公司查看客户信息都会被记录日志,如果频繁查询也会报警。)

二、打印日志

打印日志的步骤

1.获取日志对象(Logger)

java 复制代码
    private static final Logger logger = LoggerFactory.getLogger("LoggerController");

注意:

I、Logger类是org.slf4j包中的。

II、LoggerFactory.getLogger需要传递一个参数,标识这个日志的名称.这样可以更清晰的知道是哪个类 输出的日志.当有问题时,可以更方便直观的定位到问题类。

2.使用日志对象打印日志

通过定义一个Logger属性来打印日志。

java 复制代码
@RestController
@RequestMapping("log")
public class LoggerController {
    private static final Logger logger = LoggerFactory.getLogger("LoggerController");

    @RequestMapping("/printfLog")
    public String printfLog(){

        logger.info("我是logger打印的日志..........");
        System.out.println("打印日志!");
        return "success";
    }
}

运行结果:

三、门面系统

3.1日志框架(了解)

PS:

slf4j是一个日志门面,不是一个真正的日志框架

log4j是一个日志框架。

3.2门面模式(外观模式)

SLF4J是门面模式的典型应用。(但不仅仅使用了门面模式)

3.2.1门面模式的定义及优点:

门面模式(Facade Pattern):

又称外观模式,提供了一个统一的接口,用来访问子系统中的一群接口,其主要特征是定义了一个高层接口。让子系统更容易使用。

门面模式主要包含2中角色:

**1.外观角色(Facade):**也称门面角色,系统对外的统一接口。

**2.子系统角色(SubSystem):**可以同时有一个或多个SubSystem。每个SubSystem都不是单独的类,而是一个类的集合。SubSystem并不知道Facade的存在。对于SubSystem,Facade只是一个客户端而已。

比如去医院挂号,有个接待员就会方便很多:

接待员就是门面角色。

优点:

1.减少了系统的相互依赖,实现了客户端与子系统的耦合关系。使得子系统的变化不会影响到它的客户端。

2.提高了灵活性,简化了客户端对子系统的使用难度。客户端无需关心子系统的实现方式。只需要和门面对象交互就可以。

3.提高了安全性,可以灵活设定访问权限,不在门面对象中开通方法,就无法访问。

3.2.2 门面模式的实现

示例:当我们回家,会开各个屋的灯。离开家时,会关闭各个屋的灯。如果家里设置一个总开关来控制整个屋的灯就会很方便。我们使用门面模式来实现。

java 复制代码
package com.qyy.springbootlog.demos.facade;
public class FacadeDemo {
    public static void main(String[] args) {
        LightFacade lightFacade = new LightFacade();
        lightFacade.on();
    }

}
打开卧室的灯!
打开客厅的灯!
打开走廊的灯!

通过调用LightFacade这个所有灯的门面。我们就可以实现将卧室、客厅、走廊的灯都打开。这就是门面模式。

LightFacade这个类的实现细节:

java 复制代码
public class LightFacade {
    BedRoomLight bedRoomLight = new BedRoomLight();
    LivingRoomLight livingRoomLight = new LivingRoomLight();
    HallLight hallLight = new HallLight();
    //打开所有灯
    void on(){
        bedRoomLight.on();
        livingRoomLight.on();
        hallLight.on();
    }
    //关闭所有灯
    void off(){
        bedRoomLight.off();
        livingRoomLight.off();
        hallLight.off();
    }
}

客厅、走廊、卧室灯的实现细节:

java 复制代码
public class LivingRoomLight implements Light{
    @Override
    public void on() {
        System.out.println("打开客厅的灯!");
    }
    @Override
    public void off() {
        System.out.println("关闭客厅的灯");
    }
}
java 复制代码
public class HallLight implements Light{
    @Override
    public void on() {
        System.out.println("打开走廊的灯!");
    }
    @Override
    public void off() {
        System.out.println("关闭走廊的灯!");
    }
}
java 复制代码
public class BedRoomLight implements Light{
    @Override
    public void on() {
        System.out.println("打开卧室的灯!");
    }
    @Override
    public void off() {
        System.out.println("关闭卧室的灯!");
    }
}

灯这个接口的实现细节。

java 复制代码
interface Light {
    void on();
    void off();
}

流程图:

客户端只需要创建门面对象。来实现对所有灯的打开/关闭。

四、SLF4J 框架(门面)介绍

SLF4J就是其他日志框架的门面. SLF4J 可以理解为是提供日志服务的统一API接口,

并不涉及到具体的日志逻辑实现。

若不引入日志门面

当我们还没有引入日志门面的时候,常见的日志框架有log4J、logback等。

如果一个项目已经使用了log4j。而你依赖的另一个库,假如是Apache Active MQ,它依赖于另外一个日志框架logback。那么你需要把logback也加载进去。

存在的问题:

1.不同日志框架的API接口和配置文件不同,如果多个日志框架共存。那么不得不维护多套配置文件(这个配置文件是指用户自定义的配置文件)

2.如果要更换日志框架,程序不得不修改代码。并且修改过程中可能存在一些代码冲突。

3.如果引入的第三方框架,使用了多套,那就不得不维护多套配置。

引入日志门面

引入门面日志框架后。应用程序和日志框架(框架的具体实现)之间有了统一的API接口(门面之日框架实现)。因此应用程序只需要维护一套日志文件的配置。且当底层实现框架改变时。也不需要更改应用程序代码。

SLF4J就是这个日志门面

SLF4J让代码独立于任意一个特定的日志API。这是对于开发者很好的思想

五、日志的具体介绍

5.1日志的格式说明

我们看这段日志,讲解一下日志内容元素的具体介绍:

1.时间日期:精确到毫秒

2.日志级别:ERROR、WARN、INFO、DEBUG、TRACE

3.进程ID

4.线程名

5.Logger名(通常使用源代码的类名)

6.日志内容

5.2日志级别

日志级别代表日志信息对应问题的严重性。有了日志级别之后就可以过滤自己想看到的信息了,比如只关注error级别的,就可以根据级别过滤出来error级别的日志信息,节约开发者的信息筛选时间。
日志的级别从高到低依次为:FATAL、ERROR、WARN、INFO、DEBUG、TRACE。

FATAL:致命信息,表示需要立即被处理的系统级错误.

ERROR:错误信息,级别较高的错误日志信息,但仍然不影响系统的继续运行,但需要我们进行处理。

WARN:警告信息,不影响使用,但需要注意的问题

INFO:普通信息,用于记录应用程序正常运行时的一些信息,例如系统启动完成、请求处理完成等.

DEBUG:调试信息,需要调试时候的关键信息打印.

TRACE:追踪信息,比DEBUG更细粒度的信息事件(除非有特殊用意,否则请使用DEBUG级别替代)

注:

日志级别通常和测试人员的Bug级别没有关系。

日志级别是开发人员设置的,用来给开发人员看的。

日志级别越高,收到的消息越少。

5.3日志级别的使用

日志级别是开发人员自己设置的。开发人员根据自己的理解来判断该信息的重要程度。

针对这些级别,Logger对象分别提供了对应的方法,来输出日志.

注:

  • SpringBoot默认的日志框架是Logback,Logback没有 FATAL 级别的日志。它被映射到ERROR 。
  • 出现fatal日志,表示服务已经出现了某种程度的不可用,需要需要系统管理员紧急介入处理.通常情 况下,一个进程生命周期中应该最多只有一次FATAL记录.
java 复制代码
    @RequestMapping("/level")
    public String printfLevel(){
        logger.error("我是error级别的日志!");
        logger.warn("我是warn级别的日志!");
        logger.info("我是info级别的日志!");
        logger.debug("我是debug级别的日志!");
        logger.trace("我是trace级别的日志!");
        return "打印不同级别的日志";
    }

我们发现,debug、trace级别的日志没有被打印出来。

这与日志级别的配置有关,日志的输出级别默认是 info 级别,所以只会打印大于等于此级别的日志,也就是info、warn、error。

六、日志的配置

这里只做简单的介绍,更多的配置我们随时查阅都能查到。

常见的 Application Properties

6.1配置日志级别

只需要在配置文件中设置"logging.level"配置项即可,如下所示:

properties和yml二者转换方式:

Properties文件的点( . )对应yml文件中的换行

我们将日志级别配置为debug级别(默认是info)。这里我们使用yml类型配置,配置代码如下:

java 复制代码
logging:
  level:
    root: debug

重新运行上面的日志级别的打印。debug级别的日志就全部出来了。这里我们只截取了我们刚刚打印的部分。此时我们发现之前没有打印出来的debug级别日志就被打印出来了。除此之前Spring中的debug级别的日志也都被打印出来了。

其中root是根目录,这个代码配置整个项目的日志级别为debug。

我们还可以更细致的配置某个包、甚至某个类是debug级别

java 复制代码
logging:
#设置日志级别
  level:
    root: info
    com:
      qyy:
        springbootlog:
          demos:
            controller: debug

6.2日志配置持久化

概念

上面我们看到的日志都是输出在控制台上的,然而在线上环境中,我们需要把日志保存下来,以便于出现问题后追溯问题,把日志保存下来存到硬盘中就叫做日志的持久化
日志持久化方式

1.配置日志文件名

2.配置日志的存储目录


两个都配置的情况下.name生效。
后面可以跟绝对路径或者相对路径

示例:

复制代码
logging:
#设置日志级别
  level:
    root: debug
#设置日志文件的路径和文件名
  file:
    name: logger/springboot.log

运行之后日志内容就被存放了这个路径之下了。

复制代码
# 设置⽇志⽂件的⽬录 
logging:
  file:
  path: D:/temp

6.3 配置日志文件分割

我们可以对日志文件进行分割。这样更方便我们查看日志。

如果我们不进行配置,日志框架默认走自动配置。默认日志文件超过10M就进行分割。

复制代码
#日志分割
logger:
  logback:
    rollingpolicy:
#分割后的格式设置:⽇志⽂件名为:⽇志名.⽇期.索引
      file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i
#日志分割条件
      max-file-size: 200MB

通常不需要开发人员设置,公司运维会统一进行处理。不仅仅设置这些,还会设置日志存放时间。

小公司可能例外。什么都要开发做。

6.4配置日志格式

通常不需要我们自己设置,因为默认的已经很好了。

配置项说明:

  1. %clr(表达式){颜色} 设置输入日志的颜色

支持的颜色有:blue、cyan、faint(黑色)、green、magenta、red、yellow。

2.%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} 日期和时间--精确到毫秒

%d{} 日期

${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX} 非空表达式,获取系统属性 LOG_DATEFORMAT_PATTERN ,若属性LOG_DATEFORMAT_PATTERN 不存在,则使 用-yyyy-MM-dd HH:mm:ss.SSSXXX 格式,系统属性可以 System.getProperty("LOG_DATEFORMAT_PATTERN") 获取

%5p 显示日志级别ERROR,MARN,INFO,DEBUG,TRACE.

%t 线程名.

%c 类的全限定名.

%M method.

%L 为行号.

%thread 线程名称.

%m 或者%msg 显示输出消息.

%n 换行符

%5 若字符长度小于5,则右边用空格填充.

%-5 若字符长度小于5,则左边用空格填充.

%.15 若 字符长度超过15,截去多余字符.

%15.15 若字符长度小于15,则右边用空格填充.若字符长度超过15,截去多余字符

设置了颜色,却没有生效?需要配置,

让idea支持控制台颜色显示

  1. 打开启动配置,添加VM options

中文版

2.添加VM options -Dspring.output.ansi.enabled=ALWAYS

3.重新启动程序就可以看到颜色了

七、更简单的志日输出(重点)

每次都使用LoggerFactory.getLogger(xxx.class)很繁琐,且每个类都添加⼀遍,lombok给我们提供了 一种更简单的方式.

7.1.添加 lombok 框架支持

lombok依赖

复制代码
<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <optional>true</optional>
</dependency>

7.2 输出日志

使用@slf4j 注解输出日志,lombok提供的 @Slf4j 会帮我们提供一个日志对象log,

我们直接使用就可以。

我们点进去@slf4j 注解

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package lombok.extern.slf4j;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface Slf4j {
    String topic() default "";
}

@Retention代表生命周期。

RetentionPolicy.SOURCE 说明生命周期只存在于源码

java 复制代码
@Slf4j
@RestController
@RequestMapping("log")
public class LoggerController {

    @RequestMapping("/printfLog")
    public String printfLog(){

        log.info("我是logger打印的日志..........");
        System.out.println("打印日志!");
        return "success";
    }
    @RequestMapping("/level")
    public String printfLevel(){
        log.error("我是error级别的日志!");
        log.warn("我是warn级别的日志!");
        log.info("我是info级别的日志!");
        log.debug("我是debug级别的日志!");
        log.trace("我是trace级别的日志!");
        return "打印不同级别的日志";
    }
}

注解的生命周期只存在于源码阶段、在编译的时候就已经没有了。

写了这个注解就自动帮我们生成那一行代码。

小总结:

1.日志是程序中的重要组成部分,使用日志可以快速的发现和定位问题,Spring Boot 内置了日志框架,默认情况下使用的是 info 日志级别将日志输出到控制台的,我们可以通过 lombok 提供的 @Slf4j 注解和 log 对象快速的打印自定义日志.

  1. 日志包含 6 个级别,日志级别越高,收到的日志信息也就越少,我们可以通过配置日志的保存名称 或保存目录来将日志持久化
相关推荐
架构师沉默15 分钟前
Java 终于有自己的 AI Agent 框架了?
java·后端·架构
程序员爱酸奶15 分钟前
ThreadLocal内存泄漏深度解析
java
givemeacar21 分钟前
Spring Boot中集成MyBatis操作数据库详细教程
数据库·spring boot·mybatis
czlczl2002092524 分钟前
JVM创建对象过程
java·开发语言
一直都在57242 分钟前
线程间的通信
java·jvm
GIOTTO情1 小时前
Infoseek危机公关全链路技术解析:基于近期热点舆情的落地实践
java
Mr.45671 小时前
Spring Boot集成Redis:单机、哨兵、集群三种模式统一配置实战
spring boot·redis·bootstrap
我是人✓1 小时前
从零入门 Servlet:JavaWeb 核心组件的实操与理解
java·servlet
lay_liu1 小时前
Spring Boot 自动配置
java·spring boot·后端
殷紫川2 小时前
线上故障零扩散:全链路监控、智能告警与应急响应 SOP 完整落地指南
java·架构·监控