14.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抽象类,日志工厂,获取日志类
LogFactoryImplLogFactory的实现类,真正获取日志对象的地方
Log4JLogger对log4j的日志对象封装
Jdk14Logger对JDK1.4的日志对象封装
Jdk13LumberjackLogger对JDK1.3以及以前版本的日志对象封装
SimpleLogcommons-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是一种非常流行的日志框架,是一个组件化设计的日志系统,它的架构大致如下
在这里插入图片描述

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

Log4j也是一个第三方库,需要从这里下载Log4j
在这里插入图片描述

下载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%n

log4j.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 LoggingSLF4J
org.apache.commons.logging.Logorg.slf4j.Logger
org.apache.commons.logging.LogFactoryorg.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

在这里插入图片描述

使用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> 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值