日志是软件开发的重要组成部分。一个精心编写的日志代码提供快速的调试,维护方便,以及应用程序的运行时信息结构化存储。日志记录确实也有它的缺点。它可以减缓的应用程序
日志实现框架(具体实现):JUL(java util logging)、logback、log4j、log4j2
日志门面框架(规则接口):JCL(Jakarta Commons Logging)、slf4j
JUL日志框架
组件介绍
JUL是Java原生的日志框架,使用时不需要引入第三方类库。
1、Loggers记录器,只负责打日志。面向用户,调用其api
2、Appenders,一个Logger关联一个或多个Appenders处理器
3、Layouts:日志需要按照特定的格式打印或者输出
4、Level,每一条日志都有级别
5、Filters:过滤日志信息,哪些需要记录
日志级别
日志的级别,总共七级
Level.SEVERE:(最高级)错误
Level.WARNING:警告
Level.INFO:(默认级别)消息
Level.CONFIG:配置级别
Level.FINE:详细信息(少)
Level.FINER:详细信息(中)
Level.FINEST:(最低级)详细信息(多)
两个特殊的级别:
Level.OFF;可用来关闭日志记录
Level.ALL:启用所有日志记录
jdk默认JUL配置
cmd进入命令行窗口,使用set命令查看环境变量
再进入jre/lib,找到logging.properties
删除注释后,就剩这些了
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
com.xyz.foo.level = SEVERE
自定义JUL配置
Log4j
日志级别
Log4j是Apache的一个开放源代码项目,通过使用Log4j,
我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务 器、NT的事件记录器、UNIX Syslog守护进程等;
我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
DEBUG < INFO < WARN < ERROR < FATAL,分别用来指定这条日志信息的重要程度
如下定义日志级别为info级别,那么error和warn的日志可以显示而比他优先级低的debug信息就不显示了
log4j.rootLogger=info,A1,B2,C3
log4j.properties
lg4j使用:第⼀步,在 pom.xml ⽂件中引⼊ Log4j 包:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
第⼆步,在 resources ⽬录下创建 log4j.properties ⽂件
这个配置文件告 诉Log4J以什么样的格式、把什么样的信息、输出到什么地方。
log4j.rootLogger = info,stdout
### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
Log4j有三个主要的 组件:Loggers(记录器),Appenders(输出源)和Layouts(布局),这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出
Appenders 输出源
Log4j日志系统允许把日志输出到不同的地方,如控制台(Console)、文件(Files)、根据天数或者文件大小产生新的文件、以流的形式发送到其它地方等等。
-
org.apache.log4j.ConsoleAppender(控制台)
-
org.apache.log4j.FileAppender(文件)
-
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
-
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
-
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
配置时语法如下:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN
第一次看,我就很懵。这option1、value1啥玩意???
Layouts 布局
有时用户希望根据自己的喜好格式化自己的日志输出。Log4j可以在Appenders的后面附加Layouts来完成这个功能。
Log4j 提供的格式有下⾯ 4 种:
-
org.apache.log4j.HTMLLayout:HTML 表格
-
org.apache.log4j.PatternLayout:⾃定义
-
org.apache.log4j.SimpleLayout:包含⽇志信息的级别和信息字符串
-
org.apache.log4j.TTCCLayout:包含⽇志产⽣的时间、线程、类别等等信息
配置时语法如下:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
⾃定义格式的参数
%m:输出代码中指定的消息
%p:输出优先级
%r:输出应⽤启动到输出该⽇志信息时花费的毫秒数
%c:输出所在类的全名
%t:输出该⽇志所在的线程名
%n:输出⼀个回⻋换⾏符
%d:输出⽇志的时间点
%l:输出⽇志的发⽣位置,包括类名、线程名、⽅法名、代码⾏数
[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
![](https://i-blog.csdnimg.cn/blog_migrate/be80f4660a6b8783d132f1f5914cd7f7.png)
Log4j程序中使用
在对Logger实例进行命名时,没有限制,可以取任意自己感兴趣的名字。一般情况下建议以类的所在位置来命名Logger实例,这是目前来讲比较有效的Logger命名方式。这样可以使得每个类建立自己的日志信息,便于管理。比如:
Logger.getLogger(Log4jExample.class.getName ())
import org.apache.log4j.Logger;
/**
* @Description:
* @Author: 小羽毛
* @Date: 2023/3/7 20:41
*/
public class Log4jExample {
static Logger logger = Logger.getLogger(Log4jExample.class.getName ()) ;
public static void main(String[] args) {
// 记录debug级别的信息
logger.debug("debug.");
// 记录info级别的信息
logger.info("info.");
// 记录error级别的信息
logger.error("error.");
}
}
因为配置了日志级别是info级别,所以debug级别的信息不会打印到控制台
![](https://i-blog.csdnimg.cn/blog_migrate/023adfc8051efea9339f55b52490a2c2.png)
Log4j使用注意点
-
在打印 DEBUG 级别的⽇志时,切记要使⽤ isDebugEnabled()
如果⽇志系统的级别不是 DEBUG,就会多执⾏了字符串拼接的操作,⽩⽩浪费了性能。
if(logger.isDebugEnabled()) {
logger.debug("⽤户名是:" + getName());
}
2、在打印⽇志的时候带上类的全名和线程名,在多线程环境下,这点尤为重要,否则定位问题的时候就太难了
SLF4J
为什么用slf4j
-
在使⽤⽇志系统的时候,⼀定要使⽤ SLF4J 作为⻔⾯担当
-
SLF4J 可以统⼀⽇志系统,作为上层的抽象接⼝,不需要关注底层的⽇志实现,可以是 Log4j,也可
以是 Logback,或者 JUL、JCL。
3.使用slf4j后不再需要 isDebugEnabled() 先进⾏判断
slf4j是如何将自己的通用日志格式转成不同的日志系统的格式的呢?
不同日志系统包都会有一个Adapter,用来在slf4j和不同日志系统之间做转换
第⼀步,把 log4j 的依赖替换为 slf4j-log4j12
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
第⼆步,在 resources ⽬录下创建 log4j.properties ⽂件,内容和上面⼀篇完全相同
参数化日志消息
SLF4J 在打印⽇志的时候使⽤了占位符 {} 。
解决锚点:字符串拼接会创建很多不必要的字符串对象,极⼤的消耗了内存空间.如下
String name = "小羽毛";
int age = 8;
logger.debug(name + ",年纪:" + age + ",阿姨有糖吃吗");
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4jExample {
static Logger logger = LoggerFactory.getLogger(SLF4jExample.class.getName ()) ;
public static void main(String[] args) {
logger.info("{},想问问阿姨有糖吃吗","小羽毛");
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/7c8bebbbd338e002e30b5f6ba5007cda.png)
还可以在消息中使用两个参数 -
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4jExample {
static Logger logger = LoggerFactory.getLogger(SLF4jExample.class.getName ()) ;
public static void main(String[] args) {
logger.info("颜值:{} 分, 天赋 {}分", 100, 99);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/f5ec2c5b3c399a8e32794edab4f99cd1.png)
Logback
logback依赖
Spring Boot 的默认⽇志框架使⽤的是 Logback。
logback-classic 模块需要在 classpath 添加 slf4j-api.jar、logback-core.jar 以及 logback-classic.jar。
在 pom.xml ⽂件中添加 Logback 的依赖:
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
Maven 会⾃动导⼊另外两个依赖:
![]()
logback-core 是 Logback 的核⼼,logback-classic 是 SLF4J 的实现
默认配置策略
如果你想在每个类中打印日志,那么你需要将当前类的全称或者当前类当作参数,调用 org.slf4j.LoggerFactory.getLogger()
方法。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest {
static Logger logger = LoggerFactory.getLogger(LogbackTest.class);
public static void main(String[] args) {
logger.debug("巅峰对决");
}
}
appender:直译为“输出目的地,输出终端”
Logback 没有找到 logback-test.xml 和 logback.xml 配置文件,所以通过默认的配置策略-添加一个基本的 ConsoleAppender
来进行配置。
Appenders 包括 console,files,Syslog,TCP Sockets,JMS 等等其它的日志输出目的地。用户可以根据自己的情况轻松的创建自己的 Appender。
logback配置
⼀般来说,我们会在本地环境中配置 logback-test.xml,在⽣产环境下配置 logback.xml
配置文件命名
1、配置文件要放在classpath路径下,文件名logback-test.xml
2、在spring-boot项目中可以命名为logback-spring.xml,这样在读取完application.yml文件后springboot会自动读取logback-spring.xml文件
在 resource ⽬录下增加 logback-test.xml ⽂件
<configuration debug="true">
<!-- 配置⽇志的输出⽬的地-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- pattern ⽤来指定⽇志的输出格式-->
<pattern>%d{HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
配置 <root>,它只⽀持⼀个属性——level,值可以为:TRACE、DEBUG、INFO、WARN、ERROR、 ALL、OFF。 appender-ref ⽤来指定具体的 appender。
配置appender
配置 appender,也就是配置⽇志的输出⽬的地,通过 name 属性指定名字,通过 class 属性指定 ⽬的地:
ch.qos.logback.core.ConsoleAppender:输出到控制台。
ch.qos.logback.core.FileAppender:输出到⽂件。 ch.qos.logback.core.rolling.RollingFileAppender:⽂件⼤⼩超过阈值时产⽣⼀个新⽂件。
除了输出到本地,还可以通过 SocketAppender 和 SSLSocketAppender 输出到远程设备,通过 SMTPAppender 输出到邮件。甚⾄可以通过 DBAppender 输出到数据库中。
<!-- 配置⽇志的输出⽬的地-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- pattern ⽤来指定⽇志的输出格式-->
<pattern>%d{HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
pattern ⽤来指定⽇志的输出格式:
%d :输出的时间格式。
%relative :输出从程序启动到创建⽇志记录的时间,单位为毫秒。
%thread :⽇志的线程
%-5level :⽇志的输出级别,填充到 5 个字符。方便⽇志信息就对⻬。
%logger{length} :logger 的名称,length ⽤来缩短名称。没有指定表示完整输出;0 表示只输 出 logger 最右边点号之后的字符串;其他数字表示输出⼩数点最后边点号之前的字符数量。
%msg :⽇志的具体信息。
%n :换⾏符。
运⾏LogbackTest类,可以在控制台看到以下信息
22:47:48.364 618 [main] DEBUG com.atguigu.log.LogbackTest - 巅峰对决
日志文件拆分
文件日志以日期和大小进行拆分生成。
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>logs/demo-logback/info.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
<!--<totalSizeCap>1GB</totalSizeCap>-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
自动删除三天前日志
要实现达到一定大小后将日志文件压缩,并删除三天前的日志数据,可以结合使用 SizeAndTimeBasedRollingPolicy 滚动策略和 DeleteOlderThan 选项来配置。
监听器
创建监听器:
public class CustomLogContextListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
/**
* 存储日志路径标识
*/
public static final String LOG_PAHT_KEY = "LOG_PATH";
@Override
public boolean isResetResistant() {
return false;
}
@Override
public void onStart(LoggerContext loggerContext) {
}
@Override
public void onReset(LoggerContext loggerContext) {
}
@Override
public void onStop(LoggerContext loggerContext) {
}
@Override
public void onLevelChange(Logger logger, Level level) {
}
@Override
public void start() {
// "user.dir"是指用户当前工作目录
String s = ScriptFileUtil.rootPath + "/rpa-client-logs/";
System.setProperty(LOG_PAHT_KEY, s);
Context context = getContext();
context.putProperty(LOG_PAHT_KEY, s);
}
@Override
public void stop() {
}
@Override
public boolean isStarted() {
return false;
}
}
在初始化 logback 初始化过程中,将我们需要的日志路径设置到上下文中
<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<contextListener class="com.midea.jr.credit.accounting.utils.LoggerStartupListener" />
</configuration>
就可以直接通过声明的变量名称使用路径(${LOG_PATH}),LOG_PATH就是在CustomLogContextListener类中指定的变量名称