一.使用JDK Logging
为什么要使用日志
-
在编写程序的过程中,发现程序运行结果与预期不符,可以用
System.out.println()打
印出执行过程中的某些变量,观察每一步的结果与代码逻辑是否符合,然后有针对性地修改代码。 -
代码改好了怎么办?当然是删除没有用的
System.out.println()
语句了。 -
如果改代码又改出问题怎么办?再加上
System.out.println()
。 -
反复这么搞几次,很快大家就发现使用
System.out.println()
非常麻烦。
怎么办?解决方法是使用日志。
那什么是日志?
日志就是Logging,它的目的是为了取代System.out.println()
。
输出日志的好处
输出日志,而不是用System.out.println(),有以下几个好处:
- 可以设置输出样式,避免自己每次都写"ERROR: " + var;
- 可以设置输出级别,禁止某些级别输出。例如,只输出错误日志;
- 可以被重定向到文件,这样可以在程序运行结束后查看日志;
- 可以按包名控制日志级别,只输出某些包打的日志;
- 日志可以存档,便于追踪问题;
- 可以根据配置文件调整日志,无需修改代码;
那如何使用日志?
Java标准库内置了日志包java.util.logging
,我们可以直接用
import java.util.logging.Level;
import java.util.logging.Logger;
public class Hello {
public static void main(String[] args) {
Logger logger = Logger.getGlobal();
logger.info("start process...");
logger.warning("memory is running out...");
logger.fine("ignored.");
logger.severe("process will be terminated...");
}
}
使用日志最大的好处是,它自动打印了时间、调用类、调用方法
等很多有用的信息。
再仔细观察发现,4条日志,只打印了3条,logger.fine()没
有打印。这是因为,日志的输出可以设定级别。JDK的Logging定义了7个日志级别,从严重到普通:
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
因为默认级别是INFO
,因此,INFO级别以下的日志,不会被打印出来。
使用日志级别的好处在于,调整级别,就可以屏蔽掉很多调试相关的日志输出。
Java标准库内置的Logging的局限
-
Logging系统在·JVM启动时读取配置文件·并完成初始化,一旦开始运行main()方法,就无法修改配置;
-
配置不太方便,需要在
JVM启动时传递参数
-Djava.util.logging.config.file=<config-file-name>
。- 因此,Java标准库内置的Logging使用并不是非常广泛。更方便的日志系统下面介绍。
二.使用Commons Logging
什么是Commons Logging
- 和Java标准库提供的日志不同,
Commons Logging是一个第三方日志库
,它是由Apache创建的日志模块
。 - Commons Logging的特色是,它可以
挂接不同的日志系统
,并通过配置文件指定挂接的日志系统
。 默认情况下
,Commons Loggin自动搜索并使用Log4j
(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Logging
。
Commons Logging 具体用法
common-logging组件:
-
Commons Logging (JCL)提供的是一个
日志(Log)接口(interface)
,同时兼顾轻量级和不依赖于具体的日志实现工具。它提供给中间件/日志工具开发者一个简单的日志操作抽象,允许程序开发人员使用不同的具体日志实现工具。
-
了解包里情况,可以查看它的API文档:
http://www.oschina.net/uploads/doc/commons-logging-1.1.1/index.html
, 其中Log(基本记录器)
和LogFactory(负责创建Log实例)
是两个基类。 -
该API直接提供对下列底层日志记录工具的支持:
Jdk14Logger,Log4JLogger,LogKitLogger,NoOpLogger (直接丢弃所有日志信息),还有一个SimpleLog
。 有必要详细说明一下调用LogFactory.getLog()时发生的事情。 -
调用该函数会启动一个发现过程,即找出必需的底层日志记录功能的实现,具体的发现过程在下面列出: ( 换句话说就是, ·有这么多工具,common-logging该使用哪一个呢?· 这取决于系统的设置,common-logging将按以下顺序决定使用哪个日志记录工具:)
-
首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则
使用其中定义的Log实现类; -
如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量
org.apache.commons.logging.Log,找到则使用其定义的Log实现类; -
否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;
-
否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);
-
否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog;
所以,在前面程序的基础上,只要导入log4j的包,则commons-logging会自动使用 log4j作为输出类,程序不需要任何改动,- 但是注意:log4j的配置文件 log4j.properties对Log4j来说是必须的。如果classpath中没有该配置文件,或者配置 不对,将会引发运行时异常。所以下面介绍log4j.properties的配置。
- 根据不同的性质,日志信息通常被分成不同的级别,从低到高依次是:
“调试( DEBUG )” ,“信息( INFO )” ,“警告( WARN )”,“错误(ERROR )” ,“致命错误( FATAL )”
。
- 导入依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 配置common-logging.properties文件
只需要一行即可,放在classpath
下,如果是Maven中就在src/resources
下
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
- 配置log4j.properties文件
放在classpath下,如果是Maven中就在src/resources下
### set log levels ###
log4j.rootLogger = debug , stdout , D , E
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
## 输出INFO级别以上的日志
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E:/logs/log.log
log4j.appender.D.Append = true
## 输出DEBUG级别以上的日志
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 保存异常信息到单独文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
## 异常日志文件名
log4j.appender.E.File = E:/logs/error.log
log4j.appender.E.Append = true
## 只输出ERROR级别以上的日志!!!
log4j.appender.E.Threshold = ERROR
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
测试
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Created by kun on 2016/5/13.
*/
public class LogTest {
private static Log logger = LogFactory.getLog(LogTest.class);
public static void main(String[] args) {
logger.trace("我是trace信息");
logger.debug("我是debug信息");
logger.info("我是info信息");
logger.warn("我是warn信息");
logger.error("我是error信息");
logger.fatal("我是fatal信息");
}
}
- 基于common-logging的运行方式:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class TestCommLogger {
public static Log log = LogFactory.getLog(TestCommLogger.class);
public static void main(String[] args) {
log.debug("111");
log.info("125");
log.warn("485");
log.error("error");
}
}
- 基于log4j的运行方式
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class TestLog4j {
static Logger logger = Logger.getLogger(TestLog4j.class);
public static void main(String args[]) {
PropertyConfigurator.configure("log4j.properties");
logger.debug("Here is some DEBUG");
logger.info("Here is some INFO");
logger.warn("Here is some WARN");
logger.error("Here is some ERROR");
logger.fatal("Here is some FATAL");
}
}
三.使用Log4j
什么是log4j
-
Commons Logging,可以作为
“日志接口”
来使用。而真正的“日志实现”
可以使用Log4j。 -
Log4j的1.x版本虽然已经被广泛使用于很多应用程序中,但由于出现内存泄漏等bug,代码难以维护,以及需要使用老版本的jdk等劣势,在2015年8月已经玩完。目前log4j的升级版本为
重构后的log4j2
-
Log4j是一个
组件化设计
的日志系统,它的架构大致如下:
当我们使用Log4j输出一条日志时,Log4j自动通过不同的Appender
把同一条日志输出到不同的目的地。例如:- console:输出到屏幕;
- file:输出到文件;
- socket:通过网络输出到远程计算机;
- jdbc:输出到数据库
在输出日志的过程中,通过Filter
来过滤哪些log需要被输出
,哪些log不需要被输出
。
- 例如,仅输出ERROR级别的日志。
最后,通过Layout来格式化日志信息
- 例如,自动添加日期、时间、方法名称等信息。
上述结构虽然复杂,但我们在实际使用的时候,并不需要关心Log4j的API,而是通过配置文件
来配置它
Log4j的架构
Log4j系统的三大板块:日志写入器
、日志输出终端
、日志布局模式
Logger类
Logger类是日志包的核心,Logger的名称是大小写敏感的,并且名称之间有继承关系。子名由父名做前缀,用点号“.”分隔,如x.y是x.y.z的父亲Logger。
- Logger系统中有个根logger,是所有logger的祖先,它总是存在的,并且不可以通过名字获取,可以通过Logger.getRootLogger()来获取。
- 获取Logger对象的方法很多,可以参考API文档,在某对象中,用该对象所属的类作为参数,调用
Logger.getLogger(Class clazz)
以获取logger对象被认为是目前所知最理智的命名logger方法。
Log4j的日志级别(Level)
每个logger都有一个日志级别,用来控制日志的输出。未分配级别的logger将自动继承它最近的父logger的日志级别。Logger的由低到高级别如下:
ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF
Log4j的输出终端(Appender接口)
Log4j提供了以下几个实现
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天都产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小达到指定尺寸时产生一个新的日志文件,文件名称上会自动添加数字序号。)
org.apache.log4j.WriterAppender(将日志信息以流的格式发送到任意指定的地方)
Log4j的输出布局模式(Layout接口)
Log4j提供Layout有以下几种
org.apache.log4j.HTMLLayout(以HTML表格形式布局)
org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
Log4j采用类似C语言中的printf函数的打印格式格式化日志信息
。打印参数如下:
%r:输入自应用启动到输出该log信息耗费的毫秒数。
%c:输出所属的类目,通常就是所在类的全名。
%n:输出一个回车换行符。Windows平台为“\r\n”,UNIX为“\n”。
%l:输出日志事件发生的位置,包括类名、线程名,以及所在代码的行数。
%c:输出所属的类目,通常就是所在类的全名。
%t:输出产生该日志线程的线程名。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,推荐使用“%d{ABSOLUTE}”,这个输出格式形如:“2007-05-07 18:23:23,500”,符合中国人习惯。
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd HH:mm:ss },输出类似:2002-10-18- 22:10:28
%f 输出日志信息所属的类的类名
%m 输出代码中指定的信息,如log(message)中的message
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL。如果是调用debug()输出的,则为DEBUG,依此类推
Log4j的properties配置
- 在实际使用中,Log4j一般是通过配置文件配置使用的。配置文件有两种,
Java properties和XML文件
。一般都选用properties文件来配置,因为简洁易读
。下面只介绍Java properties的配置方式。 - 对Log4j的配置就是对
rootLogger
和子Logger
的配置。主要的配置项为:rootLogger
、输出终端
、输出布局模式
,所有的配置项都必须以log4j
开头。
##Log4J的配置之简单使它遍及于越来越多的应用中了
##Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了。
##此文件(log4j.properties)内容来自网络,非本文作者liigo原创。
log4j.rootLogger = DEBUG, CONSOLE,A1
log4j.addivity.org.apache = true
# 应用于控制台
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
log4j.appender.Threshold = DEBUG
log4j.appender.CONSOLE.Target = System.out
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n
#log4j.appender.CONSOLE.layout.ConversionPattern = [start] % d {DATE} [DATE] % n % p[PRIORITY] % n % x[NDC] % n % t[THREAD] n % c[CATEGORY] % n % m[MESSAGE] % n % n
#应用于文件
log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.File = file.log
log4j.appender.FILE.Append = false
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n
# Use this layout for LogFactor 5 analysis
# 应用于文件回滚
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
#应用于socket
log4j.appender.SOCKET = org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost = localhost
log4j.appender.SOCKET.Port = 5001
log4j.appender.SOCKET.LocationInfo = true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout = org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern = [start] % d {DATE} [DATE] % n % p[PRIORITY] % n % x[NDC] % n % t[THREAD] % n % c[CATEGORY] % n % m[MESSAGE] % n % n
# Log Factor 5 Appender
log4j.appender.LF5_APPENDER = org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords = 2000
# 发送日志给邮件
log4j.appender.MAIL = org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold = FATA
log4j.appender.MAIL.BufferSize = 10
log4j.appender.MAIL.From = web@www.wuset.com
log4j.appender.MAIL.SMTPHost = www.wusetu.com
log4j.appender.MAIL.Subject = Log4J Message
log4j.appender.MAIL.To = web@www.wusetu.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
#自定义Appender
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
log4j.appender.im.host = mail.cybercorlin.net
log4j.appender.im.username = username
log4j.appender.im.password = password
log4j.appender.im.recipient = corlin@cybercorlin.net
log4j.appender.im.layout = org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n
# 结束
log4j使用
- 引入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 在classPath下面log4j.properties编写配置文件
### set log levels ###
log4j.rootLogger = debug , stdout , D , E
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
## 输出INFO级别以上的日志
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E:/logs/log.log
log4j.appender.D.Append = true
## 输出DEBUG级别以上的日志
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 保存异常信息到单独文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
## 异常日志文件名
log4j.appender.E.File = E:/logs/error.log
log4j.appender.E.Append = true
## 只输出ERROR级别以上的日志!!!
log4j.appender.E.Threshold = ERROR
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
- web.xml中进行配置
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
- 测试
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class TestLog4j {
static Logger logger = Logger.getLogger(TestLog4j.class);
public static void main(String args[]) {
PropertyConfigurator.configure("log4j.properties");
logger.debug("Here is some DEBUG");
logger.info("Here is some INFO");
logger.warn("Here is some WARN");
logger.error("Here is some ERROR");
logger.fatal("Here is some FATAL");
}
}
什么是log4j2
Log4j和Log4j2的区别
-
log4j2是log4j 1.x 的升级版,2015年5月,Apache宣布
log4j1.x
停止更新。最新版为1.2.17
-
log4j是
Apache
的一个开源项目,log4j2和log4j是一个作者
,只不过log4j2是重新架构的一款日志组件
,他抛弃了之前log4j的不足
,以及吸取了优秀的logback的设计重新推出的一款新组件
。log4j2的社区活跃很频繁而且更新的也很快。
-
Log4j2虽然在各个方面都与logback非常相似,但是却提供了更强的性能和并发性,下一代异步logger,易于拓展自定义需求的架构,是目前使用十分广泛的日志框架
- 配置文件类型
log4j是通过一个.properties
的文件作为主配置文件的,而现在的log4j 2则已经弃用了这种方式,采用的是.xml,.json或者.jsn
这种方式来做,可能这也是技术发展的一个必然性,毕竟properties文件的可阅读性真的是有点差。
- 核心JAR包
log4j只需要引入一个jar包即可,
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
而log4j 2则是需要2个核心
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
log4j和log4j 2的包路径是不同的,Apache为了区分,包路径都更新了
- 文件渲染
log4j想要生效,我们需要在web.xml中进行配置,
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:config/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
- 这段配置目的在于告诉工程去哪加载log4j的配置文件和定义一个扫描器,这样可以随心所欲的放置log4j配置文件。
- log4j2就比较简单,以maven工程为例,我们只需要把
log4j2.xml
放到工程resource目录
下就行了。大家记住一个细节点,是log4j2.xml,而不是log4j.xml,xml名字少个2都不行!!
- Log调用
log4j
import org.apache.log4j.Logger;
private final Logger LOGGER = Logger.getLogger(Test.class.getName());
log4j2:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private static Logger logger = LogManager.getLogger(Test.class.getName());
- 配置文件方式
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error">
<!-- 先定义所有的appender -->
<appenders>
<!-- 这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 这个都知道是输出日志的格式 -->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!-- 文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 -->
<!-- append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true -->
<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>
<!-- 添加过滤器ThresholdFilter,可以有选择的输出某个级别以上的类别 onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否则直接拒绝 -->
<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} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
<RollingFile name="RollingFile" fileName="logs/web.log"
filePattern="logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="2MB"/>
</RollingFile>
</appenders>
<!-- 然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<loggers>
<!-- 建立一个默认的root的logger -->
<root level="trace">
<appender-ref ref="RollingFile"/>
<appender-ref ref="Console"/>
<appender-ref ref="ERROR" />
<appender-ref ref="log"/>
</root>
</loggers>
</configuration>
log4j2的输出终端(Appender接口)
Appender负责将LogEvents传递到目的地。 每个Appender都必须实现Appender接口。 大多数Appender继承自AbstractAppender,它增加了Lifecycle和Filterable支持。 生命周期允许组件在配置完成后完成初始化并在关闭期间执行清理。 Filterable接口允许组件附加过滤器,在事件处理期间对其进行筛选。Appender通常只负责将事件数据写入目标目标。 在大多数情况下,他们将格式化事件的责任委托给布局。 一些appender包装其他appender,以便他们可以修改LogEvent,处理Appender中的故障,根据高级Filter条件将事件路由到从属Appender
(1)AsyncAppender:引用其他Appender,被引用的Appender可以做到异步输出日志。
(2)CassandraAppender:可以将消息写入Cassandra数据库。
(3)ConsoleAppender:日志写入到标准输出,如System.out或System.error
(4)FailoverAppender:引用一组Appender,如果主的Appender失败则备用Appender开始起作用,直到主Appender恢复正常。
(5)FileAppender:将日志写入文件,比较常用。
(6)FlumeAppender:将日志以event的形式写入flume。
有三种模式:
a.远程客户端模式:模拟flume远程客户端,以avro_event的方式向agent发送消息。
b.内置flume agent模式:直接将event写入flume channel。
c.persist模式:将event写入本地BerkeleyDB,然后通过异步的方式将event发送到flume。
(7)JDBCAppender:使用JDBC连接将数据写入传统数据库。
(8)JMS Appender:将格式化后的日志写入JMS Destination。
(9)HttpAppender:发送日志到一个Http服务,必须使用Layout来格式化日志。
(10)KafkaAppender:将数据发送到kafka的topic,log4j的event对应kafka的record。
(11)MemoryMappedFileAppender:是一种特殊的日志写入方式,将日志写入内存以减少读写磁盘带来的IO开销,提升性能。
(12)NoSQLAppender:可以将数据写入nosql数据库,目前支持MongoDB和CouchDb。
(13)RandomAccessFileAppender:和FileAppender类似,但是使用了ByteBuffer+RandomAccessFile的方式来代替BufferedOutputStream
(14)RewriteAppender:允许LogEvent在其他appender处理之前先由RewriteAppender处理。
(15)RollingFileAppender:配置文件滚动生成策略,按照策略生成新的日志文件。
(16)RollingRandomAccessFileAppender:和RollingFileAppender类似,使用了ByteBuffer+RandomAccessFile的方式代替BufferedOutputStream。
(17)RoutingAppender:路由appender,可以分发Logevent到多个子Appender。
(18)SMTPAppender:将日志以邮件的形式发送,用在错误监控或者报警上。
(19)SocketAppender:将logevent发送到远程机器上,可以使用TCP或者UDP协议。
Log4j2的输出布局模式(Layout接口)
layout是指输出Logevent的布局,常见的比如输出日志的级别、时间、类名、线程等信息。Log4j2支持的Layout有如下几种:
(1)CSV Layouts。日志输出为csv文件,如log.info(a,b,c)会输出到csv文件对应的三列。
(2)HTML Layout。将日志输出为html页面,每个Logevent对应table里面的一行。
(3)JSON Layout。将日志输出为json格式。
(4)Pattern Layout。较为常用,通过使用一些匹配规则来确定日志输出格式。
(5)RFC5424 Layout。消息型的Appender经常用这种layout。
(6)Serialized Layout。使用java自身的序列化工具将Logevent序列化成byte array,但因为java固有的安全性问题,这种方式不再被推荐。
(7)Syslog Layout。将日志格式化为BSD syslog格式。
(8)XML Layout。格式化为xml。
Log4j2配置文件
如果没有配置文件,log4j会使用自身的最小化配置,但是只能输出error级别的日志,并且在运行过程中会提示没有找到配置文件。log4j2支持xml,json,yaml,propertie
s这几种格式的配置文件。配置文件应该放到项目的classpath下面,一般都放在resources
目录下。
读取配置文件的优先级由高到低依次为:
log4j2.properties>log4j2.yaml>log4j2.json>log4j2.xml>defaultConfiguration。
Log4j2使用
- 引入pom依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
- 配置log4j2.xml
<?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="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
- 测试代码
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class MyApp {
private static final Logger logger = LogManager.getLogger(MyApp.class);
public static void main(final String... args) {
logger.info("log4j test.");
logger.error("this is error message .");
}
}
四.使用SLF4J和Logback
SLF4J和Logback简介
-
Commons Logging和Log4j这一对好基友,它们一个负责充当
日志API
,一个负责实现日志底层
,搭配使用非常便于开发。 -
SLF4J类似于Commons Logging
,也是一个日志接口
,而Logback类似于Log4
,是一个日志的实现
-
slf4j是
接口抽象层
,logback是slf4j的具体实现
,在程序中使用slf4j的api进行日志的记录。使用logback来配置日志的输出规则- SLF4J是众多日志系统的内核,提供统一的接口,不提供具体实现,不是具体可使用可配置的日志系统。
- Logback提供了对SLF4J具体实现的日志系统,相比Log4j1.x,Logback的性能,使用场景,内存使用等方面的优化要远远强于Log4j1.x。
为什么有了Commons Logging和Log4j,又会蹦出来SLF4J和Logback?
- Java有着非常悠久的开源历史,不但OpenJDK本身是开源的,而且我们用到的第三方库,几乎全部都是开源的。开源生态丰富的一个特定就是,同一个功能,可以找到若干种互相竞争的开源库。
- 因为对Commons Logging的接口不满意,有人就搞了SLF4J。因为对Log4j的性能不满意,有人就搞了Logback。
SLF4J对Commons Logging的接口有何改进。在Commons Logging中,我们要打印日志,有时候得这么写
int score = 99;
p.setScore(score);
log.info("Set score " + score + " for Person " + p.getName() + " ok.");
拼字符串是一个非常麻烦的事情,所以SLF4J的日志接口
改进成这样了:
int score = 99;
p.setScore(score);
logger.info("Set score {} for Person {} ok.", score, p.getName());
SLF4J的日志接口传入的是一个带占位符的字符串,用后面的变量自动替换占位符,所以看起来更加自然。
如何使用SLF4J?
它的接口实际上和Commons Logging几乎一模一样
:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Main {
final Logger logger = LoggerFactory.getLogger(getClass());
}
对比一下Commons Logging和SLF4J的接口:
Commons Logging | SLF4J |
---|---|
org.apache.commons.logging.Log | org.slf4j.Logger |
org.apache.commons.logging.LogFactory | org.slf4j.LoggerFactory |
不同之处就是Log变成了Logger
,LogFactory变成了LoggerFactory
。
slf4j和logback的使用
使用SLF4J和Logback
和前面讲到的使用Commons Logging加Log4j
是类似的
- 增加对slf4j和logback的依赖,此处如果是引用了spring boot则不需要再引一下的jar包了,spring boot中已集成了这些包
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
- logback.xml放到classpath的下,配置如下
- logback 会在
类路径classpath
下寻找名为logback-test.xml
的文件。 - 如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
- 如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
- 如果没有找到,将会通过 JDK 提供的 ServiceLoader 工具在类路径下寻找文件
META-INFO/services/ch.qos.logback.classic.spi.Configurator
,该文件的内容为实现了 Configurator 接口的实现类的全限定类名。 - 如果以上都没有成功,logback 会通过 BasicConfigurator 为自己进行配置,并且日志将会
全部在控制台打印出来。
- logback 会在
<?xml version="1.0" encoding="utf-8" ?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,
默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志文件 输入位置 -->
<property name="logPath" value="D:/test_log" />
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="30"/>
<!-- 配置项, 通过此节点配置日志输出位置(控制台、文件、数据库)、输出格式等-->
<!-- ConsoleAppender代表输出到控制台 -->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!-- layout代表输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</layout>
</appender>
<!-- 日志输出文件 -->
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</encoder>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
<!-- 滚动策略,它根据时间来制定滚动策略.既负责滚动也负责触发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 输出路径 -->
<fileNamePattern>${logPath}/info/%d.log</fileNamePattern>
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6,
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${logPath}/%d{yyyy-MM-dd}/.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy> -->
<!-- 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy> -->
</appender>
<!-- 特殊记录Error日志 -->
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 只记录ERROR级别日志,添加范围过滤,可以将该类型的日志特殊记录到某个位置 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/error/%d.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<!-- 根节点,表名基本的日志级别,里面可以由多个appender规则 -->
<!-- level="info"代表基础日志级别为info -->
<root level="info">
<!-- 引入控制台输出规则 -->
<appender-ref ref="consoleLog" />
<appender-ref ref="fileInfoLog" />
<appender-ref ref="fileErrorLog" />
</root>
</configuration>
- 测试
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestLog {
static Logger logger = LoggerFactory.getLogger(TestLog.class);
public static void main(String[] arge){
logger.debug("taiyonghai debug");
logger.info("taiyonghai info");
logger.error("taiyonghai error");
logger.warn("taiyonghai warn");
}
}
发现没有了debug日志的输出,那是因为root节点中我们记录的日志级别是info,他就只会记录等于或高于info级别的日志,其他的都忽略避免生产环境产生过多日志