日志打印
- 建议用{}占位而不是字符串拼接
- 打日志前先判断日志级别是否可用:
- 先根据等级过滤规则再决定写不写;
- 先往一个管道写了内容,但再经等级过滤丢弃,徒增开销。
日志框架
Slf4J
Slf4J 不是底层日志框架,只是门面框架(抽象),需要配合jul、log4j、logback、log4j2等底层框架(真正干活的)使用。
- 避免日志对代码的耦合,更换日志框架时也不需改动任何代码。不论使用哪种底层框架时,在代码层面都一样。
- 避免引入第三方jar而其中日志框架不一致时需要同时维护不同的日志框架对应的配置文件。
补充:更老的门面框架还有jcl, 所以会看到有程序应用 jcl + log4j 这种搭配。Slf4J 后采用 Slf4J + log4j,但一些 jcl + log4j 项目也想用Slf4J 时,可以通过引入 jcl-over-slf4j log桥接工具的依赖,将原本输出到jcl的日志输出重定向到 SLF4J。
如果只用了slf4j,而没有使用任何底层框架,就会出现以下错误:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
log4j
- 依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
- 配置文件 log4j.properties
log4j.rootLogger=info, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=target/spring.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
- 代码
输出import org.apache.log4j.Logger; //更多请阅读:https://www.yiibai.com/log4j/log4j_sample_program.html public class log4jExample{ static Logger log = Logger.getLogger(log4jExample.class.getName()); public static void main(String[] args) { if (log.isDebugEnabled()) { log.debug("### Hello this is an debug message"); } log.info("### Hello this is an info message"); // 不支持占位符 } }
2022-07-27 21:10:03,358 INFO [org.example.log4jExample] - ### Hello this is an info message
logback (原生实现 SLF4J)
Logback 旨在作为流行的 log4j 项目的继承者,在 log4j 1.x 停止的地方接手。
Logback 的架构非常通用,可以在不同的情况下应用。 目前,logback 分为三个模块,logback-core、logback-classic 和 logback-access。
logback-core 模块为其他两个模块奠定了基础。
logback-classic 模块可以同化为 log4j 1.x 的显着改进版本。 此外,logback-classic 原生实现了 SLF4J API。
logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能。 请注意,您可以轻松地在 logback-core 之上构建自己的模块。
-
依赖
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
-
默认配置
Logback 默认配置的步骤 参考:https://www.cnblogs.com/warking/p/5710303.html
- 尝试在 classpath下查找文件logback-test.xml;
- 如果文件不存在,则查找文件logback.xml;
- 如果两个文件都不存在,logback用BasicConfigurator自动对自己进行配置,这会导致记录输出到控制台。
logback.xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<logger name="com.apache.ibatis" level="DEBUG"/>
</configuration>
-
代码
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogbackExample { static Logger log = LoggerFactory.getLogger(LogbackExample.class); public static void main(String[] args) { if (log.isDebugEnabled()) { log.debug("### Hello this is an debug message"); } log.info("### Hello this is an info message"); // 不支持占位符 } }
输出
129 [main] INFO org.example.LogbackExample - ### Hello this is an info message
如果需要将日志按规则生成到文件,也可以在logback.xml中配置;logback.xml中各标签的含义,查阅上文。
如图可以配置将日志信息同时实时输出到终端和离线输出到日志文件。
-
@Slf4j 注解
能够少写两行代码,不用每次都在类的最前边写上:
private static final Logger logger = LoggerFactory.getLogger(this.XXX.class);
需要 lombok依赖。如果是基于SpringBoot,因为默认加入了Slf4j-api和logback的依赖,所以只需要添加lombok的依赖即可。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
- 代码
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SLF4JExample {
public static void main(String[] args) {
if (log.isDebugEnabled()) {
log.debug("### Hello this is an debug message");
}
log.info("### Hello this is an info message"); // log 由lombok提供,如报错是idea的问题
}
}
log4j 适配 Slf4j
- 依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.28</version>
</dependency>
只需要依赖 slf4j-log4j12,它自身依赖log4j 和 slf4j-api 两个包。
-
配置
由于底层框架仍然使用的是log4j,仍然需要 配置文件: log4j.propertieslog4j.rootLogger=info, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=target/spring.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
-
代码
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogSlf4jExample { static Logger log = LoggerFactory.getLogger(LogSlf4jExample.class); public static void main(String[] args) { if (log.isDebugEnabled()) { log.debug("### Hello this is an debug message"); } log.info("### Hello this is an info message"); // 不支持占位符 } }
在代码层面已经实现和使用logback时一样了,都是从 org.slf4j.LoggerFactory 这个包下 getLogger来获取 Logger 对象。
当然也可以使用 @Slf4j 注解的方法。代码是一样的,无法看出底层框架是log4j还是logback。