前言:
在工作中,日志功能是必不可少的组件,它为我们提供了方便快捷的日志记录功能。借此机会,总结下java项目中常用的第三方日志接口与实现。
1.commons-logging:
commons-logging 是Apache基金会下开发的一款日志系统接口,通过其API可以整合不同的日志系统实现,其主要用于为整个软件系统提供统一抽象的日志操作,避免由于不同日志系统实现所带来的学习与迁移成本。从广义的角度来说,即是面向接口开发理念。
既然谈到commons-logging是一个接口,那么我们如何在java项目中为其指定对应的日志实现呢。从官方文档中,我们可以看到commons-logging一共提供了五种查找日志实现的策略,优先级从上到下依次递减:
- 查找LogFactory(后文提到,其是一个日志对象工场)名为org.apache.commons.logging.Log的配置属性。针对该属性可以通过Java代码显式设置配置属性,但是更常见的是通过在类路径中放置一个名为commons-logging.properties的文件来进行设置。当存在这样的文件时,属性文件中的每个条目都将成为LogFactory的“属性”。如果类路径中有多个此类文件,则1.1版本之前的commons-logging版本仅使用找到的第一个文件。从版本1.1开始,每个文件都可以定义优先级密钥,并且使用优先级最高的文件(没有优先级定义表示优先级为零)。当多个文件具有相同的优先级时,将使用找到的第一个文件。
建议在commons-logging.properties文件中定义此属性,以明确选择Log实现。
- 查找一个名为org.apache.commons.logging.Log的系统属性 ,并以该属性值作为日志实现。
- 查看类路径中是否有 Log4j 的包,如果发现,则自动使用 Log4j 作为日志实现类。
- 如果应用程序在JDK 1.4系统上执行,则使用相应的包装器类(即采用Java语言自带的日志实现)。
- 退回到默认的简单日志记录包装器(Simple日志,commons-logging自带的日志实现)。
通过以上5种方式,commons-logging便完成了接口与实现的衔接。
接着,我们来谈下commons-logging接口的使用方法。由于其是第三方组件,所以第一件事便是通过Maven将其引入到项目依赖中。其配置如下所示:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
然后,便可以采用LogFactory创建一个Log对象,开启日志之旅,如下所示:
package org.example;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Commons-logging
*
*/
public class App
{
public static void main( String[] args )
{
Log log = LogFactory.getLog(App.class);
log.info("this is info!");
}
}
Commons Logging定义了6个日志级别:
- FATAL
- ERROR
- WARNING
- INFO
- DEBUG
- TRACE
默认级别是INFO,在项目中,我们可以根据实际需求选择我们需要的日志登记。
2.log4j:
log4j 全称为log for(4) java, 即一个java日志组件。从使用者角度来看,log4j主要由三大部分组成:
Logger, Appender, Formatter。
为了使用log4j,同样需要在maven中引入其依赖:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
-
Logger:简而言之,logger就是一个用于获取日志对象,并写入日志的类,获取该对象的方法也有多种,如下所示:
//Shorthand for getLogger(clazz.getName()). static Logger getLogger(Class clazz) //Retrieve a logger named according to the value of the name parameter. static Logger getLogger(String name) //Like getLogger(String) except that the type of logger instantiated depends on the type returned by the static Logger getLogger(String name, LoggerFactory factory) // method of the factory parameter. LoggerFactory.makeNewLoggerInstance(java.lang.String) //Return the root logger for the current logger repository. static Logger getRootLogger()
Logger类是日志包的核心,Logger的名称是大小写敏感的,并且名称之间有继承关系。子名由父名做前缀,用点号“.”分隔,如x.y是x.y.z的父亲Logger。
Logger系统中有个根logger,是所有logger的祖先,它总是存在的,并且不可以通过名字获取,可以通过Logger.getRootLogger()来获取。
并且一个Logger可以拥有多个Appender,这样同一条日志消息可以输出到多个地方。每个logger都有一个日志级别,用来控制日志的 输出。未分配级别的logger将自动继承它最近的父logger的日志级别。Logger的由低到高级别如下:
ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF
-
Appender:在编程语言中,append方法往往出现在容器类中,用于给容器添加特定对象。同样,在log4j中,可以将不同的日志输出目的地看作不同的日志容器,比如控制台、文件、邮箱、数据库等。这样当给一个logger对象指定相应的appender时,便可以将相关日志输出到对应的目的地中。Log4j提供了以下几个实现:
org.apache.log4j.ConsoleAppender(控制台) org.apache.log4j.FileAppender(文件) org.apache.log4j.DailyRollingFileAppender(每天都产生一个日志文件) org.apache.log4j.RollingFileAppender(文件大小达到指定尺寸时产生一个新的日志文件,文件名称上会自动添加数字序号。) org.apache.log4j.WriterAppender(将日志信息以流的格式发送到任意指定的地方)
默认情况下,子logger将继承父logger的所有appenders。rootlogger拥有目标为system.out的consoleAppender,故默认情况下,所有的logger都将继承该appender。
-
Formatter:为了便于阅读,那么格式化便是必不可少的工作,而这部分功能便由Formatter来提供。log4j提供了以下几种formatter实现:
org.apache.log4j.HTMLLayout(以HTML表格形式布局) org.apache.log4j.PatternLayout(可以灵活地指定布局模式) org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串) org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
那么用户该怎么配置来获取自己想要的日志输出对象呢?Log4j为我们提供来一个叫做log4j.properties文件(需要放在class-path下)让我们配置我们想要的日志功能。首先来看看一个典型的配置文件:
1 ### logger的配置 ###
2 #配置根logger
3 log4j.rootLogger=INFO,stdout
4 #配置子logger:org.lavasoft(在org.lavasoft包中类的日志在没有指定子logger名的情况下使用这个logger)
5 log4j.logger.org.lavasoft=ERROR,file
6 #配置子logger:org.lavasoft.test(在org.lavasoft.test包中类的日志在没有指定子logger名的情况下使用这个logger)
7 log4j.logger.org.lavasoft.test=ERROR,file1,stdout
8
9 ### direct log messages to stdout ### (标准的终端输出)
10 #控制台输出
11 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
12 #自定义输出布局
13 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
14 #输出的格式
15 log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
16
17 ### direct messages to file ttt.log ### (输入到文件ttt.log的配置)
18 #输出到滚动文件
19 log4j.appender.file=org.apache.log4j.RollingFileAppender
20 #输出文件最大为10M
21 log4j.appender.file.MaxFileSize=10MB
22 #输出文件最大序号为10
23 log4j.appender.file.MaxBackupIndex=10
24 #输出文件路径
25 log4j.appender.file.File=C:/ttt.log
26 #自定义输出布局
27 log4j.appender.file.layout=org.apache.log4j.PatternLayout
28 #输出格式
29 log4j.appender.file.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x - %m%n
30
31 ### direct messages to file ttt.log ### (输入到文件ttt1.log的配置)
32 #输出到滚动文件
33 log4j.appender.file1=org.apache.log4j.RollingFileAppender
34 #输出文件最大为10M
35 log4j.appender.file1.MaxFileSize=10MB
36 #输出文件最大序号为10
37 log4j.appender.file1.MaxBackupIndex=10
38 #输出文件路径
39 log4j.appender.file1.File=C:/ttt1.log
40 #自定义输出布局
41 log4j.appender.file1.layout=org.apache.log4j.PatternLayout
42 #输出格式
43 log4j.appender.file1.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x - %m%n
下面对以上的文件解说,并给出一个应用例子:
logger的配置语法为:级别,输入终端1,输出终端2,…
根logger的配置项为:log4j.rootLogger
子logger的配置项为:
log4j.logger.<子logger名>
在上面的配置项中,配置了一个根logger和两个子logger,名称为:org.lavasoft和org.lavasoft.test,通过该名称,利用Logger.getLogger(String name)可以获取这两个logger对象。
子logger的名字一般都以包名来配置,这样当在程序通过类名获取logger的对象就是与本类包名相同的子logger,这样可以方便控制某个包下面logger的输出。
但是一般不建议设置子logger,一个通用的根logger足够用了。由于继承关系,并且可以在任何地方通过子logger的名字获取logger对象,这样容易导致混乱,比如在org.lavasoft包下的类直接获取了名为org.lavasoft.test子logger的对象,这是不合理的(虽然日志可以打印出来),因为如果直接在类中用Logger.getLogger(Class clazz)方式来获取Logger对象,则获取到的应该是名为org.lavasoft的logger对象,而不会得到名为org.lavasoft.test的logger对象。
以上的配置文件纯粹是为了说明问题才那么写的。
最后再列出一个典型的配置文件,以供大家实际项目中使用:
### 配置根 debug level,后面均为定义的appender###
log4j.rootLogger = debug,console ,fileAppender,dailyRollingFile,ROLLING_FILE,MAIL,DATABASE
### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名,这样logger的appender继承于rootlogger ###
log4j.logger.org.apache=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug
### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n
### 配置输出到文件 ###
log4j.appender.fileAppender = org.apache.log4j.FileAppender
log4j.appender.fileAppender.File = logs/log.log
log4j.appender.fileAppender.Append = true
log4j.appender.fileAppender.Threshold = DEBUG
log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 配置输出到文件,并且每天都创建一个文件 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = DEBUG
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n### 配置输出到文件,且大小到达指定尺寸的时候产生一个新的文件 ###log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.Threshold=ERROR log4j.appender.ROLLING_FILE.File=rolling.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=10KB log4j.appender.ROLLING_FILE.MaxBackupIndex=1 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
### 配置输出到邮件 ###
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=chenyl@yeqiangwei.com
log4j.appender.MAIL.SMTPHost=mail.hollycrm.com
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=chenyl@yeqiangwei.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
### 配置输出到数据库 ###
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=
log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=SampleMessages.log4j
log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout