Java日志学习
不要再滥用System.out.println()
了
日志概念
日志是什么
生活中的日志是记录你生活的点点滴滴,让它把你内心的世界表露出来,更好的诠释自己的内心世界,而电脑里的日志可以是有价值的信息宝库,也可以是毫无价值的数据泥潭。
网络设备、系统及服务程序等,在运作时都会产生一个叫log
的事件记录;每一行日志都记载着日期、时间、使用者及动作等相关操作的描述。
日志有什么优点
输出日志,而不是用System.out.println()
,有以下几个好处:
- 可以设置输出样式,避免自己每次都写
"ERROR: " + var
; - 可以设置输出级别,禁止某些级别输出。例如,只输出错误日志;
- 可以被重定向到文件,这样可以在程序运行结束后查看日志;
- 可以按包名控制日志级别,只输出某些包打的日志;
- ……
总之就是十分友好。
Java中的日志
常见的日志工具
- [日志实现]
Java
标准库内置的日志包java.util.logging
- [日志接口][不活跃]
Apache
软件基金会的Commons Logging
- [日志接口][活跃]
QOS.ch
的日志抽象SLF4J
- [日志实现][不活跃]
Apache
软件基金会的log4j
- [日志实现][活跃]
QOS.ch
的Logback
我的选择
截止当前(2021年7月1日)以上只有SLF4J
和Logback
近期(一个月内)还有commit
所以笔者做以下选择
SLF4J
+Logback
准备工作
新建示例工程
笔者选择使用更为简单的Maven
工程
目录结构如下(已删去无关文件)
│ pom.xml
│
├───src
└───main
└───java
└───com
└───example
App.java
唯一源代码文件App.java
package com.example;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
配置工程依赖
<dependencies>
<!-- slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.31</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- logback-classic(已含有对slf4j的集成包) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
上手
最基本的用法
public static void main(String[] args) {
System.out.println("Hello World!");
helloSLF4J();
}
public static void helloSLF4J() {
Logger logger = LoggerFactory.getLogger(App.class);
logger.info("Hello SLF4J!");
}
控制台输出
Hello World!
16:38:54.863 [main] INFO com.example.App - Hello SLF4J!
看到没,时间 [线程] 日志等级 所在类 - 信息
的友善格式完胜直接System.out.println()
SLF4J日志等级
优先级由低到高:
- trace - 一般用来追踪详细的程序运行流
- debug - 这类日志往往用在调试场景
- info - 用来记录程序运行的一些关键信息
- warn - 用来记录一些警告信息
- error - 用来记录运行时的错误信息
代码实例
Logger logger = LoggerFactory.getLogger(App.class);
logger.trace("Hello trace!");
logger.debug("Hello debug!");
logger.info("Hello info!");
logger.warn("Hello warn!");
logger.error("Hello error!");
控制台输出
16:56:14.662 [main] DEBUG com.example.App - Hello debug!
16:56:14.669 [main] INFO com.example.App - Hello info!
16:56:14.669 [main] WARN com.example.App - Hello warn!
16:56:14.669 [main] ERROR com.example.App - Hello error!
可以看到除了trace其余等级的日志都打印了出来,为什么trace那么可怜?答案下节揭晓。
配置文件(Logback)
SLF4J不过是日志接口而已,要想对日志进行配置,还需要在resources目录下新建一个配置文件。
│ pom.xml
│
├───src
└───main
├───java
│ └───com
│ └───example
│ App.java
│
└───resources
logback.xml
配置文件格式可以是groovy
或xml
,笔者不熟悉groovy
,所以选择xml
格式。
Groovy
Groovy
是一种基于JVM
的敏捷开发语言,它结合了Python
、Ruby
和 Smalltalk
的许多强大的特性。既可以作为脚本语言直接解释执行,也可以编译成Java
字节码执行,十分强大。
这里不做过多介绍。
XML
XML 指可扩展标记语言(eXtensible Markup Language)。
XML 被设计用来传输和存储数据。
logback.xml内容如下
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>xml %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT" />
</root>
</configuration>
名词解释
名词 | 意义 |
---|---|
configuration | 配置 |
appender | 输出源 |
encoder | 编码器 |
pattern | 模式 |
layout | 布局 |
root | 根 |
笔者从logback
官方手册中复制了一个最简单的配置,但又有所修改。
文件定义了一个名为STDOUT
的appender
,又它的class
属性可以看出,它是一个向控制台输出的appender
。
这个appender
的encoder
的pattern
是xml %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
。
Conversion Word
像%d{HH:mm:ss.SSS}中的{HH:mm:ss.SSS}是代表时间的格式,{}中的内容是转义词的一些属性。
更详细的解释可以看这里
转义词(Conversion Word)表[部分]
名词 | 意义 |
---|---|
%d | 当前时间 |
%thread | 当前线程 |
%level | 当前消息等级 |
%logger | 当前logger |
%msg | 消息内容 |
%n | 换行 |
搞懂了%
和{}
的意义,但这个%-5level
中间夹的-5
又是怎么回事?
Format modifiers
用格式修饰符可以美观地对齐内容。
点击这里看更多用法与详解。
格式修饰符(Format modifiers)表[部分]
格式修饰符 | 左对齐 | 最小宽度[字符] | 最大宽度[字符] |
---|---|---|---|
%20logger | 否 | 20 | 无 |
%-20loggerr | 是 | 20 | 无 |
%.30logger | - | 无 | 30 |
所以,%-5level
就是将等级以最小5个字符左对齐。
root
是配置的根,它的输出等级是trace
,root
中引用了名为STDOUT
的appender
。
让我们再运行一次工程。
xml 17:30:34.232 [main] TRACE com.example.App - Hello trace!
xml 17:30:34.234 [main] DEBUG com.example.App - Hello debug!
xml 17:30:34.235 [main] INFO com.example.App - Hello info!
xml 17:30:34.235 [main] WARN com.example.App - Hello warn!
xml 17:30:34.235 [main] ERROR com.example.App - Hello error!
look!!!
每行日志开头都由我们写进pattern
的xml开头,我们的xml
配置文件生效了。而且这一次,trace
级别的日志打印出来了!再也不是没人爱了!
输出日志到文件
新增appender
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.log</file>
<encoder>
<pattern>file %date %level [%thread] %logger{36} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
别忘了在root
中引用
<appender-ref ref="FILE" />
运行代码
发现项目目录下出现了myApp.log文件
查看内容
file 2021-07-09 12:53:33,940 TRACE [main] com.example.App [App.java:22] Hello trace!
file 2021-07-09 12:53:33,942 DEBUG [main] com.example.App [App.java:23] Hello debug!
file 2021-07-09 12:53:33,943 INFO [main] com.example.App [App.java:24] Hello info!
file 2021-07-09 12:53:33,943 WARN [main] com.example.App [App.java:25] Hello warn!
file 2021-07-09 12:53:33,948 ERROR [main] com.example.App [App.java:26] Hello error!
正如我们定义appender
一样。
参考资料
[1] 百度百科 日志
[2] 廖雪峰的官方网站 Java教程
[3] SLF4J官网
[4] Logback官网
[5] Java日志框架:SLF4J详解
[6] SLF4J日志级别以及使用场景
[7] XML 教程