刚学习 Java 的时候我们都是用 System.out.println()
来打印我们的输出结果,但是在实际工作中除了单元测试几乎不会使用这种输出方式。
这是为什么呢?
这主要是因为 System.out.println()
会将内容打印到控制台,线上会打印到 tomcat 控制台,这样:
- 严重影响程序性能
- 不方便后续排查问题的时候查看和使用
那生产环境怎么查看程序运行和异常信息呢?
答案就是:Log
线上主要使用log4j2或者logback这些第三方库的异步方法,进行日志的统一收集。
输出日志,而不是用System.out.println(),有以下几个好处
- 可以设置输出样式,避免自己每次都写”ERROR: “ + var;
- 可以设置输出级别,禁止某些级别输出。例如,只输出错误日志;
- 可以被重定向到文件,这样可以在程序运行结束后查看日志;
- 可以按包名控制日志级别,只输出某些包打的日志;
- 可以……
那如何使用日志?下面我们就来逐一进行介绍。
Commons Logging
Commons Logging是一个第三方日志库,它是由Apache创建的日志模块。
Commons Logging的特色是,它可以挂接不同的日志系统,并通过配置文件指定挂接的日志系统。默认情况下,Commons Loggin自动搜索并使用Log4j(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Logging。
使用Commons Logging只需要和两个类打交道,并且只有两步:
第一步,通过LogFactory获取Log类的实例; 第二步,使用Log实例的方法打日志。
示例代码如下:
1 | import org.apache.commons.logging.Log; |
运行上述代码,肯定会得到编译错误,类似error: package org.apache.commons.logging does not exist(找不到org.apache.commons.logging这个包)。
可以在pom里配置下,比如
1 | <dependency> |
Log4j
前面介绍了Commons Logging,可以作为“日志接口”来使用。而真正的“日志实现”可以使用Log4j。
Log4j是一种非常流行的日志框架。
Log4j是一个组件化设计的日志系统,它的架构大致如下:
当我们使用Log4j输出一条日志时,Log4j自动通过不同的Appender把同一条日志输出到不同的目的地。例如:
console:输出到屏幕;
file:输出到文件;
socket:通过网络输出到远程计算机;
jdbc:输出到数据库
在输出日志的过程中,通过Filter来过滤哪些log需要被输出,哪些log不需要被输出。例如,仅输出ERROR级别的日志。
最后,通过Layout来格式化日志信息,例如,自动添加日期、时间、方法名称等信息。
上述结构虽然复杂,但我们在实际使用的时候,并不需要关心Log4j的API,而是通过配置文件来配置它。
以XML配置为例,使用Log4j的时候,我们把一个log4j2.xml的文件放到classpath下就可以让Log4j读取配置文件并按照我们的配置来输出日志。下面是一个配置文件的例子:
1 |
|
虽然配置Log4j比较繁琐,但一旦配置完成,使用起来就非常方便。对上面的配置文件,凡是INFO级别的日志,会自动输出到屏幕,而ERROR级别的日志,不但会输出到屏幕,还会同时输出到文件。并且,一旦日志文件达到指定大小(1MB),Log4j就会自动切割新的日志文件,并最多保留10份。
同样 Log4j 也需要在pom里配置下
1 | <dependency> |
SLF4J和Logback
前面介绍了Commons Logging和Log4j这一对好基友,它们一个负责充当日志API,一个负责实现日志底层,搭配使用非常便于开发。
有的童鞋可能还听说过SLF4J和Logback。这两个东东看上去也像日志,它们又是啥?
其实SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4j,是一个日志的实现。
为什么有了Commons Logging和Log4j,又会蹦出来SLF4J和Logback?这是因为Java有着非常悠久的开源历史,不但OpenJDK本身是开源的,而且我们用到的第三方库,几乎全部都是开源的。开源生态丰富的一个特定就是,同一个功能,可以找到若干种互相竞争的开源库。
因为对Commons Logging的接口不满意,有人就搞了SLF4J。因为对Log4j的性能不满意,有人就搞了Logback。
我们先来看看SLF4J对Commons Logging的接口有何改进。在Commons Logging中,我们要打印日志,有时候得这么写:
1 | int score = 99; |
拼字符串是一个非常麻烦的事情,所以SLF4J的日志接口改进成这样了:
1 | int score = 99; |
SLF4J的日志接口传入的是一个带占位符的字符串,用后面的变量自动替换占位符,所以看起来更加自然。
如何使用SLF4J?它的接口实际上和Commons Logging几乎一模一样:
1 | import org.slf4j.Logger; |
同样需要在pom里配置一下要使用的包
1 | <dependency> |
和Log4j类似,我们仍然需要一个Logback的配置文件,把logback.xml放到classpath下,配置如下:
1 |
|
效果展示
首先贴一下我的代码
DateTimeUtils.class
1 | package com.haxianhe.utils; |
DateUtilsTest.class
1 | package test.com.haxianhe.utils; |
程序运行后在log里打印的内容如下