> 文章列表 > Java日志处理

Java日志处理

Java日志处理

日志

日志就是Logging,它的目的是为了取代System.out.println()

输出日志,而不是用System.out.println(),有以下几个好处:
(1)可以设置输出样式,避免自己每次都写“ERROR: ” + var
(2)可以设置输出级别,禁止某些级别输出。例如:只输出错误日志
(3)可以被重定向到文件,这样可以在程序运行结束后查看日志
(4)可以按包名控制日志级别,只输出某些包打的日志

日志及分类(三类)

日志根据记录内容不同,分为三类
(1)SQL日志:记录系统执行的SQL语句
(2)异常日志:记录系统运行中发生的异常事件
(3)业务日志:记录系统运行过程,如用户登录、操作记录

JDK Logging(7个级别)

Java标准库内置了日志包java.util.logging,可以直接用

JDK的Logging定义了7个日志级别,从严重到普通:
①server
②warning
③info(默认)
④config
⑤fine
⑥finer
⑦finest

默认级别是INFO。因此,INFO级别以下的日志,不会被打印出来。使用日志级别的好处在于,调整级别,就可以屏蔽掉很多调试相关的日志输出

Logging系统在JVM启动时读取配置文件并完成初始化,一旦开始运行main()方法,就无法修改配置;配置不太方便,需要在JVM启动时传递参数。因此,Java标准库内置的Logging使用并不是非常广泛

Logger logger = Logger.getGlobal();
logger.severe("");
logger.warning("");
logger.info("");
logger.config("");
logger.fine("");
logger.finer("");
logger.finest("");

输出

Mar 02, 2019 6:32:13 PM Hello main
INFO: start process...
Mar 02, 2019 6:32:13 PM Hello main
WARNING: memory is running out...
Mar 02, 2019 6:32:13 PM Hello main
SEVERE: process will be terminated...

使用日志最大的好处是,它自动打印了时间、调用类、调用方法等很多有用的信息

(1)日志是为了替代System.out.println(),可以定义格式,重定向到文件等
(2)日志可以存档,便于追踪问题
(3)日志记录可以按级别分类,便于打开或关闭某些级别
(4)可以根据配置文件调整日志,无需修改代码
(5)Java标准库提供了java.util.logging来实现日志功能

Commons Logging和log4j

(1)通过Commons Logging实现日志,不需要修改代码即可使用Log4j
(2)使用Log4j只需要把log4j2.xml和相关jar放入classpath
(3)如果要更换Log4j,只需要移除log4j2.xml和相关jar
(4)只有扩展Log4j时,才需要引用Log4j的接口(例如,将日志加密写入数据库的功能,需要自己开发)

Commons Logging(6个级别)

和Java标准库提供的日志不同,Commons Logging是一个第三方日志库,它是由Apache创建的日志模块
Commons Logging的特色:可以挂接不同的日志系统,并通过配置文件指定挂接的日志系统。默认情况下,Commons Logging自动搜索并使用Log4j(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Logging(需要导入commons logging.jar)

commons-logging有6级日志,但是apache建议使用4级,即 ERROR、WARN、INFO、DEBUG。什么情况下输出日志由程序中写日志的方法决定,输出什么级别以上的日志和输出到哪里由配置文件决定

级别 描述
①FATAL 记录具有致命日志级别的消息
②ERROR 记录具有错误日志级别的消息
③WARN 表明会出现潜在错误的情形
④INFO 使用信息日志级别记录消息(默认)
⑤DEBUG 记录调试日志级别的错误
⑥TRACE 使用跟踪日志级别记录消息

需要和两个类打交道,并且只有两步
第一步,通过LogFactory获取Log类的实例; 第二步,使用Log实例的方法打日志

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;Log log = LogFactory.getLog(Main.class);  // Main为本类
log.info("start...");
log.warn("end.");

类名 描述
LogFactory 抽象类,日志工厂,获取日志类
LogFactoryImpl LogFactory的实现类,真正获取日志对象的地方
Log4JLogger 对log4j的日志对象封装
Jdk14Logger 对JDK1.4的日志对象封装
Jdk13LumberjackLogger 对JDK1.3以及以前版本的日志对象封装
SimpleLog commons-logging自带日志对象

使用Commons Logging时,如果在静态方法中引用Log,通常直接定义一个静态类型变量
在静态方法中引用Log

public class Main {static final Log log = LogFactory.getLog(Main.class);static void foo() {log.info("foo");}
}

在实例方法中引用Log,通常定义一个实例变量
在实例方法中引用Log

public class Person {protected final Log log = LogFactory.getLog(getClass());void foo() {log.info("foo");}
}

注意到实例变量log的获取方式是LogFactory.getLog(getClass()),虽然也可以用LogFactory.getLog(Person.class),但是前一种方式有个非常大的好处,就是子类可以直接使用该log实例

父类使用getLog(getClass()),子类可以直接使用log实例
在子类中使用父类实例化的log

public class Student extends Person {void bar() {log.info("bar");}
}

由于Java类的动态特性,子类获取的log字段实际上相当于LogFactory.getLog(Student.class),但却是从父类继承而来,并且无需改动代码

Commons Logging的日志方法,例如info(),除了标准的info(String)外,还提供了一个非常有用的重载方法:info(String, Throwable),这使得记录异常更加简单:

try {
} catch (Exception e) {log.error("got exception!", e);
}

log4j

Commons Logging,可以作为“日志接口”来使用。而真正的“日志实现”可以使用Log4j。
Log4j是一种非常流行的日志框架,是一个组件化设计的日志系统,它的架构大致如下
Java日志处理

使用Log4j输出一条日志时,Log4j自动通过不同的Appender把同一条日志输出到不同的目的地
(1)console:输出到屏幕
(2)file:输出到文件
(3)socket:通过网络输出到远程计算机
(4)jdbc:输出到数据库

Log4j也是一个第三方库,需要从这里下载Log4j
Java日志处理

下载jar文件:https://logging.apache.org/log4j/2.x/download.html

以XML配置为例,使用Log4j的时候,把一个log4j2.xml的文件放到classpath下就可以让Log4j读取配置文件并按照我们的配置来输出日志

<?xml version="1.0" encoding="UTF-8"?>
<Configuration><Properties><!-- 定义日志格式 --><Property name="log.pattern">%d{MM-dd HH:mm:ss.SSS} [%t] %-5level%logger{36}%n%msg%n%n</Property><!-- 定义文件名变量 --><Property name="file.err.filename">log/err.log</Property><Property name="file.err.pattern">log/err.%i.log.gz</Property></Properties><!-- 定义Appender,即目的地 --><Appenders><!-- 定义输出到屏幕 --><Console name="console" target="SYSTEM_OUT"><!-- 日志格式引用上面定义的log.pattern --><PatternLayout pattern="${log.pattern}" /></Console><!-- 定义输出到文件,文件名引用上面定义的file.err.filename --><RollingFile name="err" bufferedIO="true"fileName="${file.err.filename}" filePattern="${file.err.pattern}"><PatternLayout pattern="${log.pattern}" /><Policies><!-- 根据文件大小自动切割日志 --><SizeBasedTriggeringPolicy size="1 MB" /></Policies><!-- 保留最近10份 --><DefaultRolloverStrategy max="10" /></RollingFile></Appenders><Loggers><Root level="info"><!-- 对info级别的日志,输出到console --><AppenderRef ref="console" level="info" /><!-- 对error级别的日志,输出到err,即上面定义的RollingFile --><AppenderRef ref="err" level="error" /></Root></Loggers>
</Configuration>

虽然配置Log4j比较繁琐,但一旦配置完成,使用起来就非常方便。对上面的配置文件,凡是INFO级别的日志,会自动输出到屏幕,而ERROR级别的日志,不但会输出到屏幕,还会同时输出到文件。并且,一旦日志文件达到指定大小(1MB),Log4j就会自动切割新的日志文件,并最多保留10份

在开发阶段,始终使用Commons Logging接口来写入日志,并且开发阶段无需引入Log4j。如果需要把日志写入文件, 只需要把正确的配置文件和Log4j相关的jar包放入classpath,就可以自动把日志切换成使用Log4j写入,无需修改任何代码

log4j.properties

在CLASSPATH 下建立log4j.properties
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=%d %5p %c{1}:%L - %m%nlog4j.appender.file=org.apache.log4j.RollingFileAppender  
log4j.appender.file.File=${catalina.home}/logs/ddoMsg.log  
#log4j.appender.file.File=D:/SmgpAppService/logs/smgpApp.log  
log4j.appender.file.MaxFileSize=1024KB  
log4j.appender.file.MaxBackupIndex=100  
log4j.appender.file.layout=org.apache.log4j.PatternLayout  
log4j.appender.file.layout.ConversionPattern= %d{yyyy-MM-dd HH:mm:ss} %5p %c %t: - %m%n  #INFO WARN ERROR DEBUG  
log4j.rootLogger=WARN, file, stdout  
#log4j.rootLogger=INFO,stdout  
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog  
#org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester=debug  
#org.apache.commons.logging.simplelog.log.org.apache.commons.digester.ObjectCreateRule=debug  
#org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester.sax=info 
log4j.logger.com.jason.ddoMsg=debug 

logger配置说明

log4j.rootLogger=INFO, stdout , R
此句为将等级为INFO的日志信息输出到stdout和R这两个目的地,stdout和R的定义在下面的代码,可以任意起名。
等级可分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF则不打出任何信息,如果配置为INFO这样只显示INFO, WARN, ERROR的log信息,而DEBUG信息不会被显示

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
此句为定义名为stdout的输出端是哪种类型,可以是

org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
此句为定义名为stdout的输出端的layout是哪种类型,可以是

org.apache.log4j.HTMLLayout(以HTML表格形式布局)
org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n
如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下:

格式 描述
[QC] log信息的开头,可以为任意字符,一般为项目简称
%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数

输出的信息
[TS] DEBUG [main] AbstractBeanFactory.getBean(189) | Returning cached instance of singleton bean ‘MyAutoProxy’

log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
此句与第3行一样。定义名为R的输出端的类型为每天产生一个日志文件

log4j.appender.R.File=D:\\Tomcat 5.5\\logs\\qc.log
此句为定义名为R的输出端的文件名为D:\\Tomcat 5.5\\logs\\qc.log可以自行修改

log4j.appender.R.layout=org.apache.log4j.PatternLayout
与第4行相同

log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
与第5行相同

log4j.logger.com. neusoft =DEBUG
指定com.neusoft包下的所有类的等级为DEBUG。
可以把com.neusoft改为自己项目所用的包名。

log4j.logger.com.opensymphony.oscache=ERROR
log4j.logger.net.sf.navigator=ERROR

这两句是把这两个包下出现的错误的等级设为ERROR,如果项目中没有配置EHCache,则不需要这两句

log4j.logger.org.apache.commons=ERROR
log4j.logger.org.apache.struts=WARN

这两句是struts的包

log4j.logger.org.displaytag=ERROR
这句是displaytag的包。(QC问题列表页面所用)

log4j.logger.org.springframework=DEBUG
此句为spring的包

log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=DEBUG

此两句是hibernate的包

以上这些包的设置可根据项目的实际情况而自行定制

log4j.rootLogger=DEBUG,console  #以下是rootLogger的配置,子类默认继承,但是子类重写下面配置=rootLogger+自己配置
#输出到控制台     
log4j.appender.console=org.apache.log4j.ConsoleAppender    
#设置输出样式     
log4j.appender.console.layout=org.apache.log4j.PatternLayout   
#日志输出信息格式为  
log4j.appender.console.layout.ConversionPattern=[%-d{yyyy-MM-dd HH:mm:ss}]-[%t-%5p]-[%C-%M(%L)]: %m%n   

SLF4J和Logback

Commons Logging和Log4j(Simple logging这一对好基友,它们一个负责充当日志API,一个负责实现日志底层,搭配使facade for Java)用非常便于开发
SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4j,是一个日志的实现

SLF4J对Commons Logging的接口有何改进。在Commons Logging中,要打印日志,有时候得这么写:

Log log = LogFactory.getLog(Main.class);  // Main为本类
int score = 99;
p.setScore(score);
log.info("Set score " + score + " for Person " + p.getName() + " ok.");

拼字符串是一个非常麻烦的事情,所以SLF4J的日志接口改进成这样了:

Logger log = LoggerFactory.getLogger(Main.class);  // Main为本类
int score = 99;
p.setScore(score);
logger.info("Set score {} for Person {} ok.", score, p.getName());

SLF4J的日志接口传入的是一个带占位符的字符串,用后面的变量自动替换占位符,所以看起来更加自然

SLF4J的接口实际上和Commons Logging几乎一模一样

Commons Logging SLF4J
org.apache.commons.logging.Log org.slf4j.Logger
org.apache.commons.logging.LogFactory org.slf4j.LoggerFactory
不同之处就是Log变成了Logger,LogFactory变成了LoggerFactory

logback同log4j相比具有众多优势
(1)更快的实现
(2)自动重新装载日志配置文件
(3)更好的过滤器(filter)
(4)自动压缩归档的日志文件
(5)堆栈跟踪里包括了Java包(jar文件)的信息
(6)自动删除旧日志归档文件

SLF4J:http://www.slf4j.org/download.html
Logback:https://logback.qos.ch/download.html

Java日志处理

使用SLF4J的Logger和LoggerFactory即可。和Log4j类似,仍然需要一个Logback的配置文件,把logback.xml放到classpath下

<?xml version="1.0" encoding="UTF-8"?>
<configuration><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern><charset>utf-8</charset></encoder><file>log/output.log</file><rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"><fileNamePattern>log/output.log.%i</fileNamePattern></rollingPolicy><triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"><MaxFileSize>1MB</MaxFileSize></triggeringPolicy></appender><root level="INFO"><appender-ref ref="CONSOLE" /><appender-ref ref="FILE" /></root>
</configuration>

从目前的趋势来看,越来越多的开源项目从Commons Logging加Log4j转向了SLF4J加Logback

(1)SLF4J和Logback可以取代Commons Logging和Log4j
(2)始终使用SLF4J的接口写入日志,使用Logback只需要配置,不需要修改代码

<!--   configuration为根元素,包含三个属性:  debug,指是否开启logback内部日志,没有设置此属性或设置其值为空、null或false时,表示不开启,否则,开启;scan,设置是否定时扫描配置文件scanPeriod,设置扫描周期,与scan属性配合使用,默认是60000毫秒,如果该属性值没有带单位,则单位为毫秒,可带的单位有milli/millisecond/second/seconde/minute/hour/day,可忽略大小写  
-->  
<configuration debug="true" scan="true" scanPeriod="600 seconds">  <!--   appender表示日志输出的方式,该元素必须包含name、classs属性;  name,表示appender的唯一标识  class一般常见有ch.qos.logback.core.FileAppender、ch.qos.logback.core.rolling.RollingFileAppender、ch.qos.logback.core.ConsoleAppender  -->  <!-- 下面appender表示输出到控制台 -->  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">  <!-- 设置级别过滤器 -->  <filter class="ch.qos.logback.classic.filter.LevelFilter">  <!-- level级别的日志才会被处理,其他级别不处理 -->  <level>DEBUG</level>  <!-- 用于配置符合条件的操作 -->  <onMatch>ACCEPT</onMatch>  <!-- 用于配置不符合条件的操作 -->  <onMismatch>DENY</onMismatch>  </filter>  <!-- encoder指定编码格式,并根据指定的pattern输出日志信息 -->  <encoder charset="UTF-8">  <!-- pattern指定日志的输出格式 -->  <pattern>%d{HH:mm:ss.SSS}[%-5level][%thread][%logger{20}]-%msg%n</pattern>  </encoder>  </appender>  <!-- 下面是将日志输入到指定的文件中 -->  <appender name="file" class="ch.qos.logback.core.FileAppender">    <!-- 指定的日志文件名 -->  <file>logFile.log</file>  <!-- 是否追加到文件末尾,默认true -->  <append>true</append>  <encoder>  <pattern>%-4r [%thread] %-5level %logger{35} - %msg%n</pattern>  </encoder>  </appender>  <!-- 下面是以滚动的方式生成日志文件 -->  <appender name="rollingFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">  <!-- 下面是设置的临界值过滤器 -->  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">  <!-- 小于level级别的日志会被过滤掉 -->  <level>INFO</level>  </filter>  <!-- rollingPolicy表示滚动策略,下面表示以时间来指定滚动策略 -->  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  <!-- 如果用TimeBasedRollingPolicy,则fileNamePattern元素必须包含,指定日志的名称 -->  <fileNamePattern>E:/demo-%d{yyyy-MM-dd}.log</fileNamePattern>  <!-- 文件的最大保留数,超过此数字会自动删除 -->  <maxHistory>90</maxHistory>  </rollingPolicy>  <encoder>  <pattern>%d{HH:mm:ss.SSS}[%-5level][%thread][%logger{20}]-%msg%n</pattern>  <!-- 是否立即清空输出流,设置为false可以提高性能,但日志可能会丢失 -->  <immediateFlush>false</immediateFlush>  </encoder>  </appender>  <!-- root与logger类似,它表示根logger,只有大于等于level级别的日志才交由appender处理,level默认为DEBUG -->  <root level="INFO">  <appender-ref ref="stdout" />  <appender-ref ref="rollingFile" />  <appender-ref ref="file" />  </root>  <!--   logger元素用来设置某包或者类的日志输出级别  name:表示某包或类名称  level:表示日志级别,如果没有此属性,那么当前的logger会继承上级的日志级别  -->  <logger name="com.erayt" level="INFO" />  <logger name="com.erayt.solar2" level="DEBUG" />  <logger name="com.googlecode" level="WARN" />  <logger name="com.ibatis" level="WARN" />  <logger name="com.opensymphony" level="WARN" />  <logger name="com.opensymphony.xwork2" level="WARN" />  <logger name="net.sf" level="WARN" />  <logger name="org.apache" level="INFO" />  <logger name="org.apache.struts2" level="WARN" />  <logger name="org.codehaus" level="WARN" />  <logger name="org.jgroups" level="WARN" />  <logger name="org.springframework" level="WARN" />  <logger name="java.sql.Connection" level="WARN" />  <logger name="java.sql.PreparedStatement" level="WARN" />  <logger name="java.sql.ResultSet" level="WARN" />  
</configuration> 


上一篇:异常(throwable)                        下一篇:IDEA中使用SVN