Java日志 解读

Java日志

日志门面和日志框架的理解

​ 日志门面就像饭店的服务员,而日志框架就像是后厨的厨师。对于顾客这个应用来说,我到饭店点菜,我只需要告诉服务员我要一盘番茄炒蛋即可,我不关心后厨的所有事情。因为虽然主厨从把这道菜称之为『番茄炒蛋』A厨师换成了把这道菜称之为『西红柿炒鸡蛋』的B厨师。但是,顾客不需要关心,他只要下达『番茄炒蛋』的命令给到服务员,由服务员再去翻译给厨师就可以了。

所以,对于一个了解了”番茄炒蛋的多种叫法”的服务员来说,无论后厨如何换厨师,他都能准确的帮用户下单。

现有的日志框架

JUL(java util logging)、logback、log4j、log4j2
JCL(Jakarta Commons Logging)又称为Commons-Logging、slf4j( Simple Logging Facade for Java)

日志门面
JCL、slf4j
日志实现
JUL、logback、log4j、log4j2

1 JUL

JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。

博客讲解源码

https://blog.csdn.net/qq8266734465/article/details/87629392

1.1 入门案例

// 快速入门
@Test
public void testQuick()throws Exception{
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");
    // 2.日志记录输出
    logger.info("hello jul");

    // 通用方法进行日志记录
    logger.log(Level.INFO,"info msg");


    // 通过占位符 方式输出变量值
    String name = "itcast";
    Integer age = 13;
    logger.log(Level.INFO,"用户信息:{0},{1}",new Object[]{name,age});

}

输出结果

5月 04, 2022 12:57:28 下午 com.itheima.JULTest testQuick
信息: hello jul
5月 04, 2022 12:57:28 下午 com.itheima.JULTest testQuick
信息: info msg
5月 04, 2022 12:57:28 下午 com.itheima.JULTest testQuick
信息: 用户信息:itcast,13

1.2 日志级别

* java.util.logging.Level中定义了日志的级别:
    SEVERE(最高值)
    WARNING
    INFO (默认级别)
    CONFIG
    FINE
    FINER
    FINEST(最低值)
* 还有两个特殊的级别:
    OFF,可用来关闭日志记录。
    ALL,启用所有消息的日志记录。

// 日志级别
@Test
public void testLogLevel()throws Exception{
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");
    // 2.日志记录输出
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info"); // 默认日志输出级别
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");

}

输出结果

5月 04, 2022 12:58:48 下午 com.itheima.JULTest testLogLevel
严重: severe
5月 04, 2022 12:58:48 下午 com.itheima.JULTest testLogLevel
警告: warning
5月 04, 2022 12:58:48 下午 com.itheima.JULTest testLogLevel
信息: info

默认输出info以上级别

1.3 自定义日志级别

// 自定义日志级别
@Test
public void testLogConfig()throws Exception{
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");


    // 关闭系统默认配置
    logger.setUseParentHandlers(false);

    // 自定义配置日志级别
    // 创建ConsolHhandler 控制台输出
    ConsoleHandler consoleHandler = new ConsoleHandler();

    // 创建简单格式转换对象
    SimpleFormatter simpleFormatter = new SimpleFormatter();

    // 进行关联
    consoleHandler.setFormatter(simpleFormatter);
    logger.addHandler(consoleHandler);


    // 配置日志具体级别
    logger.setLevel(Level.ALL);
    consoleHandler.setLevel(Level.ALL);


    // 场景FileHandler  文件输出
    FileHandler fileHandler = new FileHandler("/logs/jul.log");

    // 进行关联
    fileHandler.setFormatter(simpleFormatter);
    logger.addHandler(fileHandler);

    // 2.日志记录输出
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info"); // 默认日志输出级别
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");

}

输出结果

5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
严重: severe
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
严重: severe
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
警告: warning
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
警告: warning
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
信息: info
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
信息: info
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
配置: config
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
详细: fine
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
较详细: finer
5月 04, 2022 1:08:02 下午 com.itheima.JULTest testLogConfig
非常详细: finest

1.4 Logger之间的父子关系

​ JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。并父子关系通过路径来关联。

// Logger对象父子关系
@Test
public void testLogParent()throws Exception{

    Logger logger1 = Logger.getLogger("com.itheima");
    Logger logger2 = Logger.getLogger("com");

    // 测试
    System.out.println(logger1.getParent() == logger2);
    // 所有日志记录器的顶级父元素 LogManager$RootLogger,name ""
    System.out.println("logger2 Parent:"+logger2.getParent() + ",name:" + logger2.getParent().getName());

    // 关闭默认配置
    logger2.setUseParentHandlers(false);

    // 设置logger2日志级别
    // 自定义配置日志级别
    // 创建ConsolHhandler 控制台输出
    ConsoleHandler consoleHandler = new ConsoleHandler();

    // 创建简单格式转换对象
    SimpleFormatter simpleFormatter = new SimpleFormatter();

    // 进行关联
    consoleHandler.setFormatter(simpleFormatter);
    logger2.addHandler(consoleHandler);


    // 配置日志具体级别
    logger2.setLevel(Level.ALL);
    consoleHandler.setLevel(Level.ALL);

    logger1.severe("severe");
    logger1.warning("warning");
    logger1.info("info");
    logger1.config("config");
    logger1.fine("fine");
    logger1.finer("finer");
    logger1.finest("finest");
}

输出结果

true
logger2  Parent:java.util.logging.LogManager$RootLogger@7cdbc5d3,name:

5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
严重: severe
5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
警告: warning
5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
信息: info
5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
配置: config
5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
详细: fine
5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
较详细: finer
5月 04, 2022 1:09:02 下午 com.itheima.JULTest testLogParent
非常详细: finest

自己在创建试试

Logger logger3 = Logger.getLogger("com.itheima.JULTest");
System.out.println(logger3.getParent()==logger1);
System.out.println(logger3.getParent().getParent()==logger2);
true
true

发现JUL在初始化时会创建一个顶层RootLogger(其实就是最开始的包名)

1.5 加载自定义配置文件

// 加载自定义配置文件
@Test
public void testLogProperties()throws Exception{

    // 读取配置文件,通过类加载器
    InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
    // 创建LogManager
    LogManager logManager = LogManager.getLogManager();
    // 通过LogManager加载配置文件
    logManager.readConfiguration(ins);

    // 创建日志记录器
    Logger logger = Logger.getLogger("com.itheima");

    logger.severe("severe");
    logger.warning("warning");
    logger.info("info");
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");

}

logging.properties

# RootLogger 顶级父元素指定的默认处理器为:ConsoleHandler
handlers= java.util.logging.FileHandler

# RootLogger 顶级父元素默认的日志级别为:ALL
.level= ALL

# 自定义 Logger 使用
com.itheima.handlers = java.util.logging.ConsoleHandler
com.itheima.level = CONFIG

# 关闭默认配置
com.itheima.useParentHanlders = false


# 向日志文件输出的 handler 对象
# 指定日志文件路径 /logs/java0.log
java.util.logging.FileHandler.pattern = /java%u.log
# 指定日志文件内容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件数量
java.util.logging.FileHandler.count = 1
# 指定 handler 对象日志消息格式对象
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 指定以追加方式添加日志内容
java.util.logging.FileHandler.append = true


# 向控制台输出的 handler 对象
# 指定 handler 对象的日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定 handler 对象的日志消息格式对象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8

# 指定日志消息格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

1.6 日志原理解析

  1. 初始化LogManager
    1. LogManager加载logging.properties配置
    2. 添加Logger到LogManager
  2. 从单例LogManager获取Logger
  3. 设置级别Level,并指定日志记录LogRecord
  4. Filter提供了日志级别之外更细粒度的控制
  5. Handler是用来处理日志输出位置
  6. Formatter是用来格式化LogRecord的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-19XeDXUl-1652095248574)(C:\Users\HONOR\AppData\Roaming\Typora\typora-user-images\image-20220504131805304.png)]

2 Log4j

​ Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。
官方网站: http://logging.apache.org/log4j/1.2/

2.0 Log4j使用需要导包

<!--log4j-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

2.1 入门案例

@Test
public void testQuick1() throws Exception {
    // 初始化系统配置,不需要配置文件
    BasicConfigurator.configure();
    // 创建日志记录器对象
    Logger logger = Logger.getLogger(Log4jTest.class);
    // 日志记录输出
    logger.info("hello log4j");
    // 日志级别
    logger.fatal("fatal"); // 严重错误,一般会造成系统崩溃和终止运行
    logger.error("error"); // 错误信息,但不会影响系统运行
    logger.warn("warn"); // 警告信息,可能会发生问题
    logger.info("info"); // 程序运行信息,数据库的连接、网络、IO操作等
    logger.debug("debug"); // 调试信息,一般在开发阶段使用,记录程序的变量、参数等
    logger.trace("trace"); // 追踪信息,记录程序的所有流程信息
}

输出结果

0 [main] INFO com.itheima.Log4jTest  - hello log4j
4 [main] FATAL com.itheima.Log4jTest  - fatal
4 [main] ERROR com.itheima.Log4jTest  - error
5 [main] WARN com.itheima.Log4jTest  - warn
6 [main] INFO com.itheima.Log4jTest  - info
6 [main] DEBUG com.itheima.Log4jTest  - debug

2.3 日志的级别

* 每个Logger都被了一个日志级别(log level),用来控制日志信息的输出。日志级别从高到低分
为:
    fatal 指出每个严重的错误事件将会导致应用程序的退出。
    error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
    warn 表明会出现潜在的错误情形。
    info 一般和在粗粒度级别上,强调应用程序的运行全程。
    debug 一般用于细粒度级别上,对调试应用程序非常有帮助。
    trace 是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。
* 还有两个特殊的级别:
    OFF,可用来关闭日志记录。
    ALL,启用所有消息的日志记录。

注:一般只使用4个级别,优先级从高到低为 ERROR > WARN > INFO > DEBUG

2.4 Log4j组件

Log4J 主要由 Loggers (日志记录器)、**Appenders(输出端)**和 Layout(日志格式化器)组成。
Loggers
控制日志的输出级别与日志是否输出;

Appenders 指定日志的输出方式(输出到控制台、文件等);

Layout 控制日志信息的输出格式。

2.4.1 Loggers

​ 日志记录器,负责收集处理日志记录,实例的命名就是类“XX”的full quailied name(类的全限定名),Logger的名字大小写敏感,其命名有继承机制:例如:name为org.apache.commons的logger会继承Name为org.apache的logger。
​ Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接或者间接地继承自root。root logger可以用Logger.getRootLogger()方法获取。
​ 但是,自log4j 1.2版以来, Logger 类已经取代了 Category 类。对于熟悉早期版本的log4j的人来说,Logger 类可以被视为 Category 类的别名。

自定义Logger

# RootLogger配置
log4j.rootLogger = trace,console
# 自定义Logger
log4j.logger.com.itheima = info,file
log4j.logger.org.apache = error
2.4.2 Appenders

Appender 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j 常用的输出目的地
有以下几种:

输出端类作用
ConsoleAppender将日志输出到控制台
FileAppender将日志输出到文件中
DailyRollingFileAppender将日志输出到一个日志文件,并且每天输出到一个新的文件
RollingFileAppender将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件
DBCAppenderJ把日志信息保存到数据库中

Appender输出 ==> 控制台,文件,数据库

#指定日志的输出级别与输出端
log4j.rootLogger=INFO,Console
# 控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
# 文件输出配置
log4j.appender.A = org.apache.log4j.DailyRollingFileAppender
#指定日志的输出路径
log4j.appender.A.File = D:/log.txt
log4j.appender.A.Append = true
#使用自定义日志格式化器
log4j.appender.A.layout = org.apache.log4j.PatternLayout
#指定日志的输出格式
log4j.appender.A.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%t:%r] -
[%p] %m%n
#指定日志的文件编码
log4j.appender.A.encoding=UTF-8

#mysql
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=root
log4j.appender.logDB.Sql=INSERT INTO
log(project_name,create_date,level,category,file_name,thread_name,line,all_categ
ory,message) values('itcast','%d{yyyy-MM-dd
HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
2.4.3 Layouts

布局器 Layouts用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。Log4j常用
的Layouts:

格式化器类型作用
HTMLLayout格式化日志输出为HTML表格形式
SimpleLayout简单的日志输出格式化,打印的日志格式为(info - message)
PatternLayout最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式

在 log4j.properties 配置文件中,我们定义了日志输出级别与输出端,在输出端中分别配置日志的输出
格式。

* log4j 采用类似 C 语言的 printf 函数的打印格式格式化日志信息,具体的占位符及其含义如下:
    %m 输出代码中指定的日志信息
    %p 输出优先级,及 DEBUG、INFO 等
    %n 换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n")
    %r 输出自应用启动到输出该 log 信息耗费的毫秒数
    %c 输出打印语句所属的类的全名
    %t 输出产生该日志的线程全名
    %d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日
    HH:mm:ss}
    %l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:
    Test.main(Test.java:10)
    %F 输出日志消息产生时所在的文件名称
    %L 输出代码中的行号
    %% 输出一个 "%" 字符
    
    
* 可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式。如:
    %5c 输出category名称,最小宽度是5,category<5,默认的情况下右对齐
    %-5c 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
    %.5c 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不
    会有空格
    %20.30c category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉

2.5 实战总结

测试项目
private static Logger logger = Logger.getLogger(Test.class);

@Test
public void testQuick2() throws Exception {
    // System.out.println("This is println message.");

    // 记录debug级别的信息
    logger.debug("This is debug message.");
    // 记录info级别的信息
    logger.info("This is info message.");
    // 记录error级别的信息
    logger.error("This is error message.");
}
log4j.properties
 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=trace,使用的 apeender 为=console
log4j.rootLogger = trace,console

# 自定义 logger 对象设置
log4j.logger.com.itheima = info,console
log4j.logger.org.apache = error

# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n


# %m   输出代码中指定的日志信息
# %p   输出优先级,及 DEBUG、INFO 等
# %n   换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n")
# %r   输出自应用启动到输出该 log 信息耗费的毫秒数
# %c   输出打印语句所属的类的全名
# %t   输出产生该日志的线程全名
# %d   输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
# %l   输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10)
# %F   输出日志消息产生时所在的文件名称
# %L   输出代码中的行号
# %%   输出一个 "%" 字符

# 日志文件输出的 appender 对象
log4j.appender.file = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.file.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径
log4j.appender.file.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8

#=====================================================================
# 按照文件大小拆分的 appender 对象
# 日志文件输出的 appender 对象
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
# 指定消息格式 layout
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.rollingFile.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径
log4j.appender.rollingFile.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.rollingFile.encoding = UTF-8
# 指定日志文件内容的大小
log4j.appender.rollingFile.maxFileSize = 1MB 
# 指定日志文件的数量
log4j.appender.rollingFile.maxBackupIndex = 10


# 按照时间规则拆分的 appender 对象
log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定消息格式 layout
log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.dailyFile.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径
log4j.appender.dailyFile.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.dailyFile.encoding = UTF-8
# 指定日期拆分规则
log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss


#mysql
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=123456
log4j.appender.logDB.Sql=INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')

注意 如果想将数据输出到哪里 需要在根目录下log4j.rootLogger 添加

例如

输出到数据库中

log4j.rootLogger = trace,console ,logDB

准备数据库
CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
);

3 JCL 学习

全称为Jakarta Commons Logging (Commons-Logging),是Apache提供的一个通用日志API。
它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱
(SimpleLog)。所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk
自带的日志(JUL)
JCL 有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。

博客详细讲解

https://blog.csdn.net/lingyiwin/article/details/114992351

4 SLF4J

博客详细讲解

https://blog.csdn.net/lingyiwin/article/details/115054926

5 LOGBACK

5.1 Logback主要分为三个模块:

logback-core:其它两个模块的基础模块
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能

后续的日志代码都是通过SLF4J日志门面搭建日志系统,所以在代码是没有区别,主要是通过修改配置
文件和pom.xml依赖

5.2 pom

dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<!--junti 单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

5.3 logback配置

logback会依次读取以下类型配置文件:

  • logback.groovy
  • logback-test.xml
  • logback.xml 如果均不存在会采用默认配置

5.4 测试

package com.itheima;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogbackTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);
// 快速入门
    @Test
    public void testQuick()throws Exception{

        // 日志输出
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info");
        LOGGER.debug("debug");// 默认级别
        LOGGER.trace("trace");
    }
}

5.5 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!--
        配置集中管理属性
        我们可以直接改属性的 value 值
        格式:${name}
    -->
    
     <!--
    日志输出格式:
        %-5level
        %d{yyyy-MM-dd HH:mm:ss.SSS}日期
        %c类的完整名称
        %M为method
        %L为行号
        %thread线程名称
        %m或者%msg为信息
        %n换行
      -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>
   
    
   
    
    
    <!--定义日志文件保存路径属性-->
    <property name="log_dir" value="/logs"></property>


    
    
    
    <!--控制台日志输出的 appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象 默认 System.out 改为 System.err-->
        <target>System.err</target>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    
    
    

    <!--日志文件输出的 appender-->
    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路径-->
        <file>${log_dir}/logback.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    
    
    

    <!--html 格式日志文件输出 appender-->
    <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路径-->
        <file>${log_dir}/logback.html</file>
        <!--html 消息格式配置-->
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
            </layout>
        </encoder>
    </appender>
    
    


    <!--日志拆分和归档压缩的 appender 对象-->
    <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志文件保存路径-->
        <file>${log_dir}/roll_logback.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <!--指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--按照时间和压缩格式声明拆分的文件名-->
            <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <!--按照文件大小拆分-->
            <maxFileSize>1MB</maxFileSize>
        </rollingPolicy>
        <!--日志级别过滤器-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--日志过滤规则-->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    
    

    <!--异步日志-->
    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <!--指定某个具体的 appender-->
        <appender-ref ref="rollFile"/>
    </appender>


    <!--root logger 配置-->
    <root level="ALL">
        <appender-ref ref="console"/>
        <appender-ref ref="async"/>
    </root>

    <!--自定义 looger 对象
        additivity="false" 自定义 logger 对象是否继承 rootLogger
     -->
    <logger name="com.itheima" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
</configuration>

6 LOG4J2

博客详细讲解

https://blog.csdn.net/weixin_32265569/article/details/110723441

7 @SLF4J注解

https://blog.csdn.net/cslucifer/article/details/80953400

8 SpringBoot日志与AOP

8.0 yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
#配置mapper的映射文件的位置
mybatis:
  mapper-locations: classpath:mapper/*Dao.xml


# 打印sql
logging:
  level:
    com.atguigu.log.mapper : debug


8.1 pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>

    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--log4j2-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>

    </dependency>
    <!-- aop -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
</dependencies>

8.2 Sql

CREATE TABLE `logs`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NULL DEFAULT NULL,
  `user_action` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `create_time` timestamp(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

8.3 pojo

package com.atguigu.log.pojo;

import java.sql.Date;
import java.sql.Timestamp;

public class SysLog {

    private Long id;
    private Long user_id;
    private String user_action;
    private Timestamp create_time;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getUser_id() {
        return user_id;
    }

    public void setUser_id(Long user_id) {
        this.user_id = user_id;
    }

    public String getUser_action() {
        return user_action;
    }

    public void setUser_action(String user_action) {
        this.user_action = user_action;
    }

    public Timestamp getCreate_time() {d
        return create_time;
    }

    public void setCreate_time(Timestamp create_time) {
        this.create_time = create_time;
    }
}

8.4 Mapper

package com.atguigu.log.mapper;

import com.atguigu.log.pojo.SysLog;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface SysLogMapper {

    void insert(SysLog entity);
}

resource/mapper/SysLogDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.log.mapper.SysLogMapper">



    <insert id="insert">
        insert into logs values(#{id},#{user_id},#{user_action},#{create_time});
    </insert>
</mapper>

8.5 service

package com.atguigu.log.service;


import com.atguigu.log.pojo.SysLog;

/**
 * @author Promise
 * @createTime 2018年12月18日 下午9:29:48
 * @description 日志接口
 */
public interface SysLogService {

    /**
     * 插入日志
     * @param entity
     * @return
     */
    void insertLog(SysLog entity);
}

Impl

package com.atguigu.log.service;


import com.atguigu.log.mapper.SysLogMapper;
import com.atguigu.log.pojo.SysLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("sysLogService")
public class SysLogServiceImpl implements SysLogService {

    @Autowired
    private SysLogMapper sysLogMapper;
    @Override
    public void insertLog(SysLog entity) {
        // TODO Auto-generated method stub
        sysLogMapper.insert(entity);

    }
}

8.6 自定义注解

package com.atguigu.log.annotation;



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

/**
 * @author Promise
 * @createTime 2018年12月18日 下午9:26:25
 * @description  定义一个方法级别的@log注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

8.7 切面+aop

package com.atguigu.log.aspect;



import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;

import com.atguigu.log.annotation.Log;
import com.atguigu.log.pojo.SysLog;
import com.atguigu.log.service.SysLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;




/**
 * @author Promise
 * @createTime 2018年12月18日 下午9:33:28
 * @description 切面日志配置
 */
@Aspect
@Component
public class LogAsPect {

    private final static Logger log = org.slf4j.LoggerFactory.getLogger(LogAsPect.class);

    @Autowired
    private SysLogService sysLogService;

    //表示匹配带有自定义注解的方法
    @Pointcut("@annotation(com.atguigu.log.annotation.Log)")
    public void pointcut() {}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) {
        Object result =null;
        long beginTime = System.currentTimeMillis();

        try {
            log.info("我在目标方法之前执行!");
            result = point.proceed();
            long endTime = System.currentTimeMillis();
            insertLog(point,endTime-beginTime);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
        }
        return result;
    }

    private void insertLog(ProceedingJoinPoint point ,long time) {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        SysLog sys_log = new SysLog();

        Log userAction = method.getAnnotation(Log.class);
        if (userAction != null) {
            // 注解上的描述
            sys_log.setUser_action(userAction.value());
        }

        // 请求的类名
        String className = point.getTarget().getClass().getName();
        // 请求的方法名
        String methodName = signature.getName();
        // 请求的方法参数值
        String args = Arrays.toString(point.getArgs());

        //从session中获取当前登陆人id
//		Long useride = (Long)SecurityUtils.getSubject().getSession().getAttribute("userid");

        Long userid = 1L;//应该从session中获取当前登录人的id,这里简单模拟下

        sys_log.setUser_id(userid);
        sys_log.setCreate_time(new java.sql.Timestamp(new Date().getTime()));

        log.info("当前登陆人:{},类名:{},方法名:{},参数:{},执行时间:{}",userid, className, methodName, args, time);

        sysLogService.insertLog(sys_log);
    }

    @Pointcut("execution(public * com.atguigu..log.controller..*.*(..))")
    public void pointcutController() {}

    @Before("pointcutController()")
    public void around2(JoinPoint point) {
        //获取目标方法
        String methodNam = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();

        //获取方法参数
        String params = Arrays.toString(point.getArgs());

        log.info("get in {} params :{}",methodNam,params);
    }


}

8.8 controller进行测试

package com.atguigu.log.controller;

import com.atguigu.log.annotation.Log;
import com.atguigu.log.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class HomeController {

    private final static Logger log = org.slf4j.LoggerFactory.getLogger(HomeController.class);

    @RequestMapping("/aop")
    @Log("测试aoplog")
    public Object aop(String name, String nick) {
        log.info("我被执行了!");

        return "加油";
    }

    @RequestMapping("/hello")
    public String hello(){
        return "hello world";
    }
}

8.9 主启动类

package com.atguigu.log;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@SpringBootApplication
@MapperScan("com.atguigu.log.mapper")
public class SpringbootLogApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootLogApplication.class, args);
    }
}

8.10 测试

①访问localhost:8080/hello

2022-05-04 19:40:18.607  INFO 20780 --- [nio-8080-exec-3] c.a.l.a.LogAsPect                        : get in com.atguigu.log.controller.HomeController.hello params :[]

②访问localhost:8080/aop

2022-05-04 19:29:47.682  INFO 20780 --- [nio-8080-exec-1] c.a.l.a.LogAsPect                        : 我在目标方法之前执行!
2022-05-04 19:29:47.686  INFO 20780 --- [nio-8080-exec-1] c.a.l.a.LogAsPect                        : get in com.atguigu.log.controller.HomeController.aop params :[null, null]
2022-05-04 19:29:47.692  INFO 20780 --- [nio-8080-exec-1] c.a.l.c.HomeController                   : 我被执行了!
2022-05-04 19:29:47.692  INFO 20780 --- [nio-8080-exec-1] c.a.l.a.LogAsPect                        : 当前登陆人:1,类名:com.atguigu.log.controller.HomeController,方法名:aop,参数:[null, null],执行时间:10
2022-05-04 19:29:47.710  INFO 20780 --- [nio-8080-exec-1] c.z.h.HikariDataSource                   : HikariPool-1 - Starting...
2022-05-04 19:29:47.835  INFO 20780 --- [nio-8080-exec-1] c.z.h.HikariDataSource                   : HikariPool-1 - Start completed.
2022-05-04 19:29:47.842 DEBUG 20780 --- [nio-8080-exec-1] c.a.l.m.S.insert                         : ==>  Preparing: insert into logs values(?,?,?,?);
2022-05-04 19:29:47.874 DEBUG 20780 --- [nio-8080-exec-1] c.a.l.m.S.insert                         : ==> Parameters: null, 1(Long), 测试aoplog(String), 2022-05-04 19:29:47.692(Timestamp)
2022-05-04 19:29:47.877 DEBUG 20780 --- [nio-8080-exec-1] c.a.l.m.S.insert                         : <==    Updates: 1
2022-05-04 19:40:18.607  INFO 20780 --- [nio-8080-exec-3] c.a.l.a.LogAsPect                        : get in com.atguigu.log.controller.HomeController.hello params :[]

发现带有自定义注解的方法打印出了我们要的效果

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值