简介
- 平时控制台输出的那些信息流就叫日志,它记录了程序调试、运行时发生的变化。
- slf4j不是具体的日志框架,而是一系列的日志接口,也即是日志框架的抽象log4j和logback才是实现了的日志框架。
- logback和log4j非常相似,但是logback在一些关键执行路径上性能提升10倍以上,而且初始化内存也更小。
- 它们可以单独使用,也可以绑定slf4j一起使用。单独使用,分别调用自己的方法来输出日志信息。绑定使用,调用slf4j的api,具体使用与底层日志框架无关,日志框架提供的只是一些配置。
推荐绑定,假设项目中已经使用了logback,而我们此时加载了一个使用log4j的类库进来,那我们这时候就需要维护两个日志框架,很麻烦。而使用绑定的话,由于调用的是抽象层的API,与底层日志框架是无关的,因此可以任意更换日志框架。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Controller @RequestMapping("/") public class LoginController { private static final Logger logger=LoggerFactory.getLogger(LoginController.class); }
要进行绑定的话就要引入依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency>
包含以下必要jar包
log4j-1.2.xx.jar slf4j-api-x.x.x.jar slf4j-log4j12-x.x.x.jar
或
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.7</version> </dependency>
包含以下必要jar包
logback-classic-0.9.21.jar(目前已经有1.0.0) logback-core-0.9.21.jar(目前已经有1.0.0) slf4j-api-1.6.x.jar
log4j2
- spring 4.2.1开始推荐使用log4j2,即log4j 2.x。
- 2.3以后的版本要求jdk1.7,2.11.1或更早就已经要求为1.8了
依赖:
<!--依赖管理--> <dependencyManagement> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-bom</artifactId> <!--引入官方依赖管理文件--> <version>2.9.1</version> <!--版本统一定义为2.9.1--> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependency> <!--log4j2核心包--> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <scope>runtime</scope> </dependency> <dependency> <!--log4j2核心包--> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <scope>runtime</scope> </dependency> <dependency> <!--web项目需要--> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-web</artifactId> <scope>runtime</scope> </dependency> <dependency> <!--异步输出配置前置环境支持; 没有的话报错:Unable to invoke factory method in class org.apache.logging.log4j.core.async.AsyncLoggerConfig--> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.4.2</version> <scope>runtime</scope> </dependency> <dependency> <!--用于与slf4j桥接--> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <scope>runtime</scope> </dependency> <!-- slf4j核心包,已包含在com.github.miemiedev:mybatis-paginator中--> <!--<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> 一般都是匹配这个版本<version>1.7.25</version> </dependency>-->
- 使用前需要在classpath(WEB-INFO和resources)下新建一个配置文件,但不支持.properties文件,只能为”.xml”,”.json”或者”.jsn”。
- 系统选择配置文件的优先级(从先到后):
- classpath下的名为log4j2-test.json 或者log4j2-test.jsn的文件
- classpath下的名为log4j2-test.xml的文件
- classpath下名为log4j2.json 或者log4j2.jsn的文件
- classpath下名为log4j2.xml的文件
如果写在别的地方,则需要在web.xml中配置路径
<!-- log4j2配置文件路径 --> <context-param> <param-name>log4jConfiguration</param-name> <param-value>classpath:config/log4j2.xml</param-value> </context-param>
我们一般默认使用log4j2.xml。默认配置文件:只向控制台打印error级别以上的日志
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggersration>
配置文件实例
<?xml version="1.0" encoding="UTF-8"?> <!-- status:指定log4j本身打印日志的级别,只对log4j本身的事件有效, off表示不记录,其余的记录有trace,debug,info,warn,error,fatal; monitorinterval:指定自动重新配置的监测间隔时间即更改日志配置文件不用重启程), 单位s最小5s,0或负数表示不检测 --> <configuration status="error"> <!-- 常量定义 --> <properties> <property name="LOG_HOME"> /home/work/log/${service_name} <!--service_name是maven打包传进来的--> </property> <property name="FORMAT"> %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n </property> </properties> <!--定义所有的appender--> <appenders> <!--控制台--> <Console name="Console" target="SYSTEM_OUT"> <!--target默认即为SYSTEM_OUT--> <!--添加过滤器ThresholdFilter,控制台只输出level及以上信息 onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否则直接拒绝--> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <!--输出日志的格式;可以直接pattern="S{FORMAT}" --> <PatternLayout pattern="[%d{HH:mm:ss.SSS}] [%-5p] %l - %m%n"/> </Console> <!--File:日志文件,打印出所有信息,名为log,下面引用这个appender时直接用log; fileName:指定输出日志的目的文件的全路径,log/为项目下log目录,./log/也一样; 由于append为false,故每次运行日志都被更新,这个也挺有用的,适合临时测试用 --> <File name="log" fileName="log/test.log" append="false"> <PatternLayout pattern="[%d{HH:mm:ss.SSS}] [%-5p] %l - %m%n"/> </File> <File name="ERROR" fileName="logs/error.log"> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{yyyy.MM.dd 'at' HH:mm:ss z}] [%-5p] %l - %m%n"/> </File> <!--RollingFile:定义超过指定大小自动删除旧的创建新的日志文件; RollingRandomAccessFile:功能相同,属性也完全一样,只是效率较高--> <RollingRandomAccessFile name="RollingFile" fileName="logs/web.log" filePattern="logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.zip"> <!--不要以.gz/.zip结尾文件就不会被压缩,建议为.zip--> <PatternLayout pattern="[%d{yyyy-MM-dd 'at' HH:mm:ss z}] [%-5p] %l - %m%n"/> <Policies> <!--设置每天打包日志一次--> <!-- TimeBasedTriggeringPolicy:基于时间的滚动策略; interval:integer型,指定多久滚动一次,默认是1hour。 指定两次封存动作之间的时间间隔, 单位:以日志的命名精度来确定单位,比如yyyy-MM-dd-HH 单位为小时, yyyy-MM-dd-HH-mm 单位为分钟。 --> <!-- modulate:boolean型,说明是否对封存时间进行调制, 若modulate=true,则封存时间将以0点为边界进行偏移计算。 比如现在是早上3am,interval是4,那么第一次滚动是在4am, 接着是8am,12am…而不是7am。 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!--SizeBasedTriggeringPolicy:基于指定文件大小的滚动策略; size:定义每个日志文件的大小每次大小超过size, 则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩, 作为存档。单位推荐MB,数字和单位间推荐写个空格--> <SizeBasedTriggeringPolicy size="2 MB"/> </Policies> <!--DefaultRolloverStrategy:指定同一个文件夹下最多有几个日志文件时 开始删除最旧的,创建新的(通过max属性,默认7个,超出7个的文件夹将被删除)。 compressionLevel:压缩等级,值为0-9,只在filePattern为zip时作用, 0表示不压缩,值打包为zip格式,9表示最高压缩比(个人测试1MB能压缩到4KB)--> <DefaultRolloverStrategy max="60" compressionLevel="9"/> </RollingFile> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效; root:根logger;logger:logger--> <loggers> <!-- 建立一个默认的root的logger,没有定义level的appender则以此为level --> <root level="trace"> <!--指定该日志输出到哪个appender--> <appender-ref ref="RollingFile"/> <appender-ref ref="Console"/> <appender-ref ref="ERROR" /> <appender-ref ref="log"/> </root> <!-- 异步根logger,跟上面root只能存在一个,不能两个都配置; 不过log4j2依赖应该要到2.11.1才能使用这个--> <!-- 如果日志代码是正常代码逻辑的一部分货程序运行在单核单线程CPU上(这种情况 使用多线程的就是傻),推荐使用普通logger,如果单纯只是为了记个日志, 推荐使用异步logger,效能可能会有几十倍的提高。 <AsyncRoot level="info"> <AppenderRef ref="consoleAppender" /> </AsyncRoot> --> <!--logger:单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。 以下是过滤掉spring和mybatis的一些无用的DEBUG信息; name:指定适用的类或者类所在包的全路径; appender-ref:指定日志输出的appender,默认继承root结点下定义的appender, 如果指定了,那么会在指定的这appender和root的appender都输出,此时我们可以设置additivity="false",只在指定的appender中输出;但在代码实例定义了false居然没打印任何日志 ,只有true才有打印,且只打印一次中--> <logger name="org.springframework" level="INFO"></logger> <logger name="org.mybatis" level="INFO"></logger> <!-- 定义一个异步logger,可以和普通logger共存 --> <AsyncLogger name="com.foo.Bar" level="info" additivity="false"> <AppenderRef ref="AppenderName" /> <AppenderRef ref="AppenderName1" /> </AsyncLogger> </loggers> </configuration>
- 参考文章
- 参考文章
- 参考文章
- 参考文章
- 涉及异步日志
- 代码实例:D:/ideaprojects/thz/thz-parent/thz-manager-web/reourrces/config/log4j2.xml
日志级别
- 以下是log4j日志级别:
- log4j定义了8个级别的log,优先级从高到低依次为:
- 开发时可以只输出到控制台,但是程序投入生产时就应该打印日志,供维护人员改bug。
SpringBoot默认日志框架
- SpringBoot默认使用Logback作为日志框架,默认配置error、warn和info级别的日志。
- 日志输出内容元素:
- 时间日期:精确到毫秒
- 日志级别:ERROR, WARN, INFO, DEBUG or TRACE
- 进程ID
- 分隔符:— 标识实际日志的开始
- 线程名:方括号括起来(可能会截断控制台输出)
- Logger名:通常使用源代码的类名
- 日志内容
启用Logback原本需要引入下面依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency>
- 不过由于该依赖包含于spring-boot-starter,而spring-boot-starter包含于spring-boot-thymeleaf,所以只要引入后面其中一个即可。
日志配置
- 一般都是写在日志配置文件中,很少在application.properties。命名优先推荐为logback-spring.xml(节点)、log4j-spring.xml、log4j2-spring.xml或ldgging.properties(键值对),这样被正确加载。默认放在resources下。
- 没有配置文件的话会使用系统默认的配置文件。
- 具体配置细节参见代码实例中的配置文件。
- 其中logger结点:写了logger节点就要同时在代码里定义logger。
- 不写logger节点是没有具体到类的日志的,写了之后才会有指定类的日志,一般要求每个controller中都会有一个logger属性。
log4j配置
#输出级别最低为debug级别,后面为输出器名称 log4j.rootLogger=DEBUG, Console, Tofile, Exception #看网上命名好像都喜欢大写开头 #Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout # 可以灵活地指定布局模式 log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n # 灵活指定布局模式 #不是很懂,应该是打印sql语句吧 log4j.logger.java.sql.ResultSet=INFO log4j.logger.org.apache=INFO log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG #File log4j.appender.Tofile=org.apache.log4j.DailyRollingFileAppender #每天产生一个日志文件 log4j.appender.Tofile.layout=org.apache.log4j.PatternLayout log4j.appender.Tofile.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n #日志格式 log4j.appender.Tofile.File=logFile.%d{yyyy-MM-dd}.txt #路径,不能在前面加/,识别不了的;这么写就表示项目 log4j.appender.Tofile.File=${webApp.root}/log_path/logFile.log //webapp根目录 log4j.appender.Tofile.Append=true #将消息追加到指定文件中,默认为true,false,覆盖 log4j.appender.Tofile.Encoding=UTF-8 #定义日志编码 #还可以单独打印个异常日志出来 log4j.appender.Exception = org.apache.log4j.DailyRollingFileAppender #当前目录的上一级目录,如果此配置文件在resources下,则当前目录为项目 log4j.appender.Exception.File = ../logs/springmvc-mybatis-demo_error.log log4j.appender.Exception.Append = true log4j.appender.Exception.Threshold = ERROR log4j.appender.Exception.layout = org.apache.log4j.PatternLayout log4j.appender.Exception.layout.ConversionPattern = [sspringmvc_mybatis_demo][%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n
- 语法:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN
其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件
一般会配合log4j.appender.输出器name.MaxFileSize=500KB定义最大容量,
超出则会再生成一个原先日志文件name+.1的文件来存放日志,还可以配合
log4j.appender.OutFile.MaxHistory=30定义最大保留天数),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
配置日志信息的格式(布局),其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
其中,Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: %m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“ ”,Unix平台为“ ”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss.SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
log4j.appender.appenderName.DatePattern=yyyy-MM-dd'.log'
# DatePattern配合DailyRollingFileAppender使用
# 每天滚动一次文件;若前面配置日志文件名为logFile.log,则当天生成的日志名为logFile.log
# 到明天若有新日志生成则昨天的将更名为logFile.log昨日期.log,并生成新的logFile.log存今天日志
# 注意,没有新日志的话过了一天也不会更名
# yyyy-MM:每月,
# yyyy-ww:每周,
# yyyy-MM-dd:每天,
# yyyy-MM-dd-a:每天两次,
# yyyy-MM-dd-HH:每小时,
# yyyy-MM-dd-HH-mm:每分钟 。
示例
log4j.rootLogger=DEBUG, Console, OutFile #Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n log4j.logger.java.sql.ResultSet=INFO log4j.logger.org.apache=INFO log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG #File # 每天产生一个日志文件 log4j.appender.OutFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.OutFile.layout=org.apache.log4j.PatternLayout log4j.appender.OutFile.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n #日志路径 log4j.appender.OutFile.File=log_path/logFile.log #一分钟更名一次 log4j.appender.OutFile.DatePattern=yyyy-MM-dd-HH-mm'.log' log4j.appender.OutFile.Append=true #日志最大容量,超出自然将原内容移到别的文件里 log4j.appender.OutFile.MaxFileSize=500KB #最大保留天数 log4j.appender.OutFile.MaxHistory=30
- 参考文章
- 参考文章
- DatePattern参考文章
slf4j的应用
Logger必须作为类的静态变量使用(最好加final关键字),如此不论这个类实例化多少个,大家用的都是同一个logger,它记录的只是当前类的日志,不是每个实例的日志。
private static final Logger logger=LoggerFactory.getLogger(类名.class); //创建Logger对象,后面添加日志信息时要用到;这条语句并不会向控制台打印任何信息
用占位符记录日志,降低代码中字符串连接次数,节省了新建String对象。这意味着只有需要的String对象才会被创建,可以在运行时延迟字符串的创建。
//需要“{}”时用\\转义 logger.debug("Set \\{} differs from {}","3"); //3被当做字符 //多个占位符 logger.error("{},{},{}",1,2,3); //结果 2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] Set {} differs from 3 2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:1,2,3
项目中一般是将捕捉到的异常作为日志记录的最后一个参数,而且要放在{}可以格式化的参数之外,而且只能放在最后一个,防止被{}转为e.toString(),放在中间也不会被打印错误信息。
public class Slf4jTest { private static final Logger log=LoggerFactory.getLogger(Slf4jTest.class); public static void main(String[] args) { openFile("e://test.txt"); } public static void openFile(String filePath){ File file=new File(filePath); try { InputStream in=new FileInputStream(file); } catch (FileNotFoundException e) { log.error("cann't found file [{}]",filePath,e); //打印一行提示信息cannt...,再打印一段异常信息 } } }
结果
21:16:28.364 [main] ERROR com.java1234.test.Slf4jTest - cann't found file [e://test.txt] java.io.FileNotFoundException: e:\test.txt (系统找不到指定的文件。) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at com.java1234.test.Slf4jTest.openFile(Slf4jTest.java:21) at com.java1234.test.Slf4jTest.main(Slf4jTest.java:15)
- 尽量不要使用e.getMessage(),有的异常不一定有message
代码实例
- D:/ideawork
参考文章
- 日志配置文件中各个属性意义详见笔记:Mybatis与Spring、SpringMvc整合