Web基础之日志
日志在企业开发中有着不可或缺的作用,它可以用以记录用户操作、系统运行状态和错误信息。日志记录的好坏直接关系到系统出现问题时定位的速度。
最开始的日志一般使用log4j,后来sun公司心有不甘,在jdk 1.4中加入了一个叫java.util.logging的日志包,简称jul。两种日志的api肯定是不同的,此时日志就出现了混乱。log4j的作者Ceki Gülcü就写了一个叫Jakarta Commons Logging的接口,简称jcl,来共同管理log4j和jul,并且jcl中提供了一个日志实现simplelog。接着又改良了下log4j,写出了logback。再后来Ceki Gülcü觉得jcl不好用(的确用问题),然后写了一个新的接口slf4j,来共同管理这些日志系统。接着呢Ceki Gülcü又觉得logj4性能不够了,又写了一个log4j2,。真是专注日志100年啊。
所以他们之间的关系是这样的:

其中slf4j并不提供日志实现,而是使用外观模式创建了一个接口,来统一这些日志的api。
这也是为什么阿里规范上强制使用slf4j的原因:
【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
slf4j + log4j / log4j2
认识了之后接下来就是使用了,首先在maven中导入依赖:
<!-- log4j依赖,好像不更新了,我看版本还是12年的 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- slf4j的依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.28</version> </dependency> <!-- 告诉slf4j使用log4j,也就是绑定 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.28</version> <scope>test</scope> </dependency>
如果使用log4j2的话这么配:
<!-- log4j2的核心 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.1</version> </dependency> <!-- log4j2的api --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.1</version> </dependency> <!-- slf4j绑定log4j2 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.12.1</version> </dependency> <!-- slf4j的依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.28</version> </dependency>
这里我将使用log4j2的xml和properties两种方式来简单配置。
在resources目录下创建log4j2.xml
:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <!-- 输出源的配置:日志输出的目的地 --> <Appenders> <!-- Console标签;指定这是一个控制台输出源。 name:名称,需要在被引用才会生效 target: 控制台字体的颜色 SYSTEM_ERR:红色字体 SYSTEM_OUT:黑色字体 PatternLayout:布局,输出到控制台日志的格式。 --> <Console name="myConsole" target="SYSTEM_ERR"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %5level %logger{36} - %msg%n"/> </Console> <!-- 文件输出源 fileName:指定保存日志的文件的名字和位置。 一般写绝对路径。 --> <File name="myFile" fileName="D://demo.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> </File> <!-- 多文件输出源: fileName:实时记录日志的文件名和位置 filePattern : 文件封存的位置和格式 --> <RollingFile name="rollingFile" fileName="D://demo2.log" filePattern="D://logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <!-- 多文件拆分的规则 --> <Policies> <!-- 基于时间的 此处interval属性值的单位是由filePattern属性中文件名时间精确的单位 决定的。 --> <TimeBasedTriggeringPolicy interval="1" /> <!-- 基于文件大小的 --> <SizeBasedTriggeringPolicy size="512 MB"/> </Policies> </RollingFile> </Appenders> <Loggers> <!-- 配置日志输出的级别的 将当前级别以及比当前级别高的消息进行输出。 --> <Root level="trace"> <AppenderRef ref="myConsole"/> <!-- 可以同时多种方式输出日志 --> <AppenderRef ref="MyFile"/> <AppenderRef ref="RollingFile"/> </Root> </Loggers> </Configuration>
log4j使用properties方式:
# 配置输出源的 log4j.appender.输出源名=输出源的实现类 # 属性的配置 log4j.appender.输出源名.属性名=属性值 log4j.appender.a=org.apache.log4j.ConsoleAppender log4j.appender.a.Target=System.out log4j.appender.a.layout=org.apache.log4j.PatternLayout log4j.appender.a.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=D:/mylog.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n ### log4j.rootLogger=输出级别,输出源1,输出源2.... log4j.rootLogger=debug,a,file
log4j2的properties(参考):
status = warn name = MyApp appender.console.type = Console appender.console.name = consoleLogDemo appender.console.filter.threshold.type = ThresholdFilter appender.console.filter.threshold.level = debug appender.console.layout.type = PatternLayout appender.console.layout.pattern = %m%n appender.console.target = System_out appender.rolling.type = File appender.rolling.name = fileLogDemo appender.rolling.filter.threshold.type = ThresholdFilter appender.rolling.filter.threshold.level = error appender.rolling.layout.type = PatternLayout appender.rolling.layout.pattern = %d-%m%n appender.rolling.append = true appender.rolling.fileName = e:\\propertiesTest.log rootLogger.level = debug rootLogger.appenderRef.consolelogdemo.ref = consoleLogDemo rootLogger.appenderRef.filelogdemo.ref = fileLogDemo
输出格式参数为:
%p:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL %d:输出日志时间点的日期或时间,默认格式为ISO8601,可以指定格式如:%d{yyyy/MM/dd HH:mm:ss,SSS} %r:输出自应用程序启动到输出该log信息耗费的毫秒数 %t:输出产生该日志事件的线程名 %l:输出日志事件的发生位置,相当于%c.%M(%F:%L)的组合,包括类全名、方法、文件名以及在代码中的行数 %c:输出日志信息所属的类目,通常就是类全名 %M:输出产生日志信息的方法名 %F:输出日志消息产生时所在的文件名 %L:输出代码中的行号 %m:输出代码中指定的具体日志信息 %n:输出一个回车换行符,Windows平台为"rn",Unix平台为"n" %x:输出和当前线程相关联的NDC(嵌套诊断环境) %%:输出一个"%"字符
这是几种常用的配置,如果觉得不够的话可以查看官方文档
顺便这里推荐个讲的很详细的博客
打日志
在log4j2中可以这么用:
public class MyApp { private static final Logger logger = LoggerFactory.getLogger(MyApp.class); public static void main(String[] args) { logger.warn("Hello, {}","slf4j"); } }
使用{}
作为占位符,性能要比log4j直接字符串拼接性能好(毕竟字符串拼接的底层原理是创建StringBuilder)