日志
Java体系日志很多,一直没有整理,乱乱的。好,我们先来看看日志框架组件以及大致处理流程:
- Loggers:Logger负责捕捉事件并将其发送给合适的Appender
- Appenders:也称为Handlers,负责从Logger中取出日志消息,并使用Layout来格式化消息,然后将消息发送出去,比如发送到控制台、文件或其他日志收集系统。
- Layouts:也称为Formatters,负责对日志事件进中的数据进行转换和格式化。
- Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。
框架
- log4j(最早的Java领域日志,作者Ceki Gülcü,现归属Apache)
- jul(jdk1.4后新增,看到log4j后加的)
- logback(log4j作者Ceki Gülcü另外一个框架)
- log4j2(log4j的改良版本,Apache)
门面
- jcl(Apache 推出 Jakarta Commons Logging)
- slf4j(log4j作者Ceki Gülcü写的,和logback一起出现的)
时间轴
使用
log4j
log4j 1.x 版本,2015年5月,Apache宣布log4j1.x 停止更新。最新版为1.2.17。
- 依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- classpath下配置log4j.properties
### 日志记录器Logger的全局设置 ###
#第一个参数指定输出的最低日志级别,即只输出该级别及以上级别的日志,D和E为自定义名字,对应下面appender
log4j.rootLogger=DEBUG,stdout,D,E
#*******设置控制台输出********
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.appender.D=org.apache.log4j.DailyRollingFileAppender
#指定将日志输出到哪个文件中
log4j.appender.D.File=./debug.log
#指定文件写入方式为追加
log4j.appender.D.Append=true
#指定最低输出级别,针对这个appender
log4j.appender.D.Threshold=DEBUG
log4j.appender.D.DatePattern='.'yyyy-MM-dd-HH-mm
#指定日志格式
log4j.appender.D.layout=org.apache.log4j.SimpleLayout
#********文件输出按照大小来生产文件********
log4j.appender.E=org.apache.log4j.RollingFileAppender
log4j.appender.E.File=./error.log
log4j.appender.E.Append=true
#指定最低输出级别,针对这个appender
log4j.appender.E.Threshold=ERROR
#指定日志文件的最大尺寸
log4j.appender.E.MaxFileSize=20KB
#指定最大备份数为5
log4j.appender.E.MaxBackupIndex=5
#指定日志输出格式使用自定义格式
log4j.appender.E.layout=org.apache.log4j.PatternLayout
#指定日志格式
log4j.appender.E.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#********指定具体包打印日志级别********
log4j.logger.org.mybatis=DEBUG,D
- appender
全路径 | 备注 |
---|---|
org.apache.log4j.ConsoleAppender | 控制台 |
org.apache.log4j.FileAppender | 文件 |
org.apache.log4j.DailyRollingFileAppender | 每天产生一个日志文件 |
org.apache.log4j.RollingFileAppender | 文件大小到达指定尺寸的时候产生一个新的文件 |
org.apache.log4j.WriterAppender | 将日志信息以流格式发送到任意指定的地方 |
- 属性配置
不同的appender有的属性不同,动手点到类里瞧瞧。例如RollingFileAppender的 File、Append、Threshold等。
- 伪Java 代码
import org.apache.log4j.Logger;
// 获取logger
Logger logger = Logger.getLogger(App.class.getName());
logger.error("11111");
log4j2
2.x版本配置文件后缀,只能为".xml",".json"或者".jsn"。一般建议log4j2使用xml形式配置。默认没有配置文件情况,也会输出error以上级别的日志到控制台。
- 依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
- classpath下配置log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="WARN" monitorInterval="30">
<!--先定义所有的appender-->
<appenders>
<!--这个输出控制台的配置-->
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效,也就是说打印的日志内容会到相应文件中-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息,可以作为定义具体包日志级别-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
-
配置详解
节点不区分大小写,试验过。
1.根节点Configuration
有两个属性:status
和monitorinterval
,有两个子节点:Appenders
和Loggers
(表明可以定义多个Appender
和Logger
):
status
用来指定log4j
本身的打印日志的级别。monitorinterval
用于指定log4j
自动重新配置的监测间隔时间,单位是s,最小是5s
2.Appenders
节点,常见的有三种子节点:Console
、RollingFile
、File
:
Console
节点用来定义输出到控制台的Appender
(1)name
:指定 Appender 的名字
(2)target
:SYSTEM_OUT
或SYSTEM_ERR
,一般只设置默认:SYSTEM_OUT
(3)PatternLayout
:输出格式,不设置默认为%m%n
File
节点用来定义输出到指定位置的文件的Appender
(1)name
:指定Appender
的名字
(2)fileName
:指定输出日志的目的文件带全路径的文件名
(3)PatternLayout
:输出格式,不设置默认为%m%n
RollingFile
节点用来定义超过指定大小自动删除旧的创建新的的Appender
(1)name
:指定Appender
的名字
(2)fileName
:指定输出日志的目的文件带全路径的文件名
(3)PatternLayout
:输出格式,不设置默认为%m%n
(4)filePattern
:指定新建日志文件的名称格式
(5)Policies
:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志
(6)TimeBasedTriggeringPolicy:Policies
子节点,基于时间的滚动策略,interval
属性用来指定多久滚动一次,默认是1 hour
。modulate=true
用来调整时间:比如现在是早上3am
,interval
是4,那么第一次滚动是在4am
,接着是8am
,12am
…而不是7am
(7)SizeBasedTriggeringPolicy:Policies
子节点,基于指定文件大小的滚动策略,size 属性用来定义每个日志文件的大小.
(8)DefaultRolloverStrategy
:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max
属性)。
3.Loggers
节点,常见的有两种Root
和Logger
:
Root 节点用来指定项目的根日志,如果没有单独指定 Logger
,那么就会默认使用该 Root
日志输出
level
:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
AppenderRef:Root
的子节点,用来指定该日志输出到哪个Appender
Logger 节点用来单独指定日志的形式,比如要为指定包下的class
指定不同的日志级别等。
level
:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
name
:用来指定该 Logger
所适用的类或者类所在的包全路径,继承自 Root
节点.
AppenderRef:Logger
的子节点,用来指定该日志输出到哪个Appender
,如果没有指定,就会默认继承自 Root
。如果指定了,那么会在指定的这个Appender
和 Root
的Appender
中都会输出,此时我们可以设置 Logger
的 additivity="false"
只在自定义的 Appender
中进行输出。
- 关于日志 level
共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
:
All
:最低等级的,用于打开所有日志记录.Trace
:是追踪,就是程序推进以下,你就可以写个trace
输出,所以trace
应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出.Debug
:指出细粒度信息事件对调试应用程序是非常有帮助的.Info
:消息在粗粒度级别上突出强调应用程序的运行过程.Warn
:输出警告及warn以下级别的日志.Error
:输出错误信息日志.Fatal
:输出每个严重的错误事件将会导致应用程序的退出的日志.OFF
:最高等级的,用于关闭所有日志记录.
程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。
log4j和log4j2 日志级别有8个:按照从低到高为:All(打开所有日志级别)< Trace < Debug < Info < Warn < Error < Fatal < OFF(关闭所有日志级别)
JUL
JDK 1.4 之后自带的log,位于java.util.logging包下。它使用起来也是很简单,不需要依赖额外的jar。
- 日志级别
FINEST(最低值)–>FINER–>FINE–>CONFIG–>INFO(默认级别)–>WARNING–>SEVERE(最高值)
-
默认配置
$JAVA_HOME/jre/lib/logging.properties
############################################################
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
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
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# Example to customize the SimpleFormatter output format
# to print one-line log message like this:
# <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
- 自定义配置
#配置RootLogger的Handler,有java.util.logging.ConsoleHandler,java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
#针对所有handler与包的根日志级别
.level= ALL
#文件位置,可以是任何path,这样配置默认在项目根路径下
java.util.logging.FileHandler.pattern = java%u.log
#默认一个文件最多50000条日志记录
java.util.logging.FileHandler.limit = 50000
#设置FileHandle的日志级别为WARNING(低于这个级别就不打印到文件里)
java.util.logging.FileHandler.level= WARNING
#配置生成一个文件
java.util.logging.FileHandler.count = 1
#配置使用SimpleFormatter格式器
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
#配置追加模式
java.util.logging.FileHandler.append=true
#ConsoleHandler的日志级别默认是INFO(低于这个级别就不打印到控制台)
java.util.logging.ConsoleHandler.level = INFO
#ConsoleHandler的默认格式化器时SimpleFormatter
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
#设置日志格式
java.util.logging.SimpleFormatter.format= %1$tc %2$s%n%4$s: %5$s%6$s%n
- 加载配置文件
public class JULLogConfig {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("${file.path}/logging.properties");
LogManager manager = LogManager.getLogManager();
manager.readConfiguration(inputStream);
Logger logger = Logger.getLogger(JULLogConfig.class.getName());
// 高于设置的WARNING级别,因此会打印到log文件里
logger.severe("999999999");
}
}
logback
logback
和log4j
是一个人写的springboot
默认使用的日志框架是logback
。- 三个模块组成
- logback-core
- logback-classic
- logback-access
- 其他的关于性能,关于内存占用,关于测试,关于文档详见源码及官网说明
logback-core
是其它模块的基础设施,其它模块基于它构建,显然,logback-core
提供了一些关键的通用机制。logback-classic
的地位和作用等同于 Log4J
,它也被认为是 Log4J
的一个改进版,并且它实现了简单日志门面 SLF4J
;而 logback-access
主要作为一个与 Servlet
容器交互的模块,比如说tomcat
或者 jetty
,提供一些与 HTTP
访问相关的功能。
- 依赖
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
- 配置文件logback.xml
<configuration>
<!-- 属性文件:在properties文件中找到对应的配置项 -->
<springProperty scope="context" name="logging.path" source="logging.path"/>
<springProperty scope="context" name="logging.level" source="logging.level.com.astilt.spring.boot"/>
<!-- 默认的控制台日志输出,一般生产环境都是后台启动,这个没太大作用 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{HH:mm:ss.SSS} %-5level %logger{80} - %msg%n</Pattern>
</encoder>
</appender>
<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<!--临界值过滤器,过滤掉低于指定临界值的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>${logging.level}</level>
</filter>
<!--指定被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值-->
<file>
${logging.path}/glmapper-spring-boot/glmapper-loggerone.log
</file>
<!-- 每天生成一个日志文件,保存30天的日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.path}/glmapper-spring-boot/glmapper-loggerone.log.%d{yyyy-MM-dd}</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- com.astilt.controller这个包下的${logging.level}级别的日志将会使用logFile来打印-->
<logger name="com.astilt.controller" level="${logging.level}"
additivity="false">
<appender-ref ref="logFile" />
</logger>
<root level="${logging.level}">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
xml的节点:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="glmapper-name" value="glmapper-demo" />
<contextName>${glmapper-name}</contextName>
<appender>
//xxxx
</appender>
<!-- 用来设置某一个包或者具体的某一个类的日志打印级别以及指定appender。-->
<logger>
//xxxx
</logger>
<root>
//xxxx
</root>
</configuration>
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-
节点
root:根logger,也是logger
logger:普通的logger
appender:需要被 logger
或者
root的
appender-ref指向,它种类有- ConsoleAppender:把日志添加到控制台
- FileAppender:把日志添加到文件
- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。它是FileAppender的子类
JCL
Jakarta Commons-logging(JCL)是Apache 最早提供的日志的框架接口。它本身并没有记录log的功能,只是统一了JDK Logging与Log4j的AP。
动态查找原理:Log 是一个接口声明。LogFactory 的内部会去装载具体的日志系统,并获得实现该Log 接口的实现类。LogFactory 内部装载日志系统的流程如下:
- 首先,寻找org.apache.commons.logging.LogFactory 属性配置。
- 否则,利用JDK1.3 开始提供的service 发现机制,会扫描classpah 下的META-INF/services/org.apache.commons.logging.LogFactory文件,若找到则装载里面的配置,使用里面的配置。
- 否则,从Classpath 里寻找commons-logging.properties ,找到则根据里面的配置加载。
- 否则,使用默认的配置:如果能找到Log4j 则默认使用log4j 实现,如果没有则使用JDK14Logger 实现,再没有则使用commons-logging 内部提供的SimpleLog 实现。
- 依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- 我是用是log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 伪Java 代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Log logger = LogFactory.getLog(App.class);
logger.info("111111");
- log4j.properties 如上,不列了。
Slf4J
The Simple Logging Facade for Java(SLF4J)可作为各种日志记录框架(例如java.util.logging,logback和log4j)的简单外观或抽象。
说白了就是提供统一接口,实现由各个具体的日志框架实现。目前支持的日志框架有:
- slf4j-log4j12-1.7.28.jar (log4j 1.2.x 版本)+log4j
- slf4j-jdk14-1.7.28.jar(JDK 1.4日志记录)
- slf4j-nop-1.7.28.jar
- slf4j-simple-1.7.28.jar
- slf4j-jcl-1.7.28.jar
- logback-classic-1.2.3.jar 、logback-core-1.2.3.jar
按需添加自己使用的日志jar。并加上对应的日志配置文件。
使用的伪Java代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(App.class);
logger.error("11111");
在SprngBoot中自带了slf4j+logback,不需要导入包,开箱即用,也是我平时使用最多的方式。
在使用层面,我觉得log4j、logback最方便,配置和概念都比较清晰,也比较相像。至于JUL、log4j2 使用的比较少,JCL也不多,目前基本上slf4j+logback可以畅行江湖,推荐!