常见的java logging
System.out与System.err
java.util.logging
apache log4j
apache commons logging
SLF4J
其中System.out与System.err就不多说了。我们学习的第一个程序可能就是它们两个。
Logging的通用概念
Logging拥有通用的概念,在logging中,包含以下几个元素:
logger - 产生log record,并设置record的level
level - 两个地方可以设置过滤信息,1)logger的level, 2)handler的level.
logRecord
formatter/render/layout - 将log record转换为格式化的语言
handler/appender - 将message输出到console, file, stream...
以及隐藏的logger属性的继承概念。所有的logger总会有一个rootLogger,在其每个path下的logger总会继承其父路径的level与handler.
Java Util Logging
调用方法
Java logging是java自带的logging工具包,在java.util.logging包中。使用方法很简单
public static Logger logger = LogManager.getLogger(A.class)
public void test() {
logger.info("A.info");
logger.warning("A.warning");
logger.severe("A.servere");
logger.fine("A.fine");
}
配置方法
默认的配置文件为:jre/lib/logging.properties
可以通过4种方法覆盖默认配置:
java -Djava.util.logging.config.file=...
java -Djava.util.logging.config.class=...
书写自己的LogManager, 在logManager初始化中使用readConfiguration()来读入新的配置。
在运行时,动态的new xxxHandler和logger.setHandler()来配置logger.
在logging.properties中可以配置Level,handler,formatter
Java自带Logging的Level有SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, OFF, ALL。
logger的level与handler的level共同控制着消息的输出。只有当两个level都满足,才会真正的输出。
两个标准的Formatter: java.util.logging.XMLFormatter, java.util.logging.SimpleFormatter
五个标准Handler: ConsoleHandler, FileHandler, StreamHandler, SocketHandler, MemoryHandler
此外还可以自己复写handler与formatter.
一个logging.properties的例子:
注意handlers不是overwrite的,而是累加的。例如,handlers声明的所有handler都应用于每个类。而每个类再次声明的handler将累加到前面的handler上。
handlers =
.level =
config =
"logger".handlers =
"logger".useParentHandlers =
"logger".level =
java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
java.util.logging.FileHandler.level = WARNING
java.util.logging.FileHandler.filter =
java.util.logging.FileHandler.formatter =
java.util.logging.FileHandler.encoding =
java.util.logging.FileHandler.limit =
java.util.logging.FileHandler.count =
java.util.logging.FileHandler.append = false
java.util.logging.FileHandler.pattern = log.%u.%g.txt
java.util.logging.ConsoleHandler.level = WARNING
java.util.logging.ConsoleHandler.filter =
java.util.logging.ConsoleHandler.formatter =
java.util.logging.ConsoleHandler.encoding =
Apache log4j
log4j2很快就会出来了,它将取代Log4j. 但log4j还是在很多地方被使用。Maven引入log4j的方法是
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
在log4j中,没有LogFactory, LoggerFactory或LogManager,直接调用Logger.getLogger()即可初始化logger。
import org.apache.log4j.Logger;
public class App {
private static Logger logger = Logger.getLogger(App.class);
public static void main( String[] args ) {
if (logger.isInfoEnabled()) {
logger.info("app.info");
}
if (logger.isDebugEnabled()) {
logger.info("app.debug");
}
}
}
log4j跟其它logger机制一样,存在着基本的logger所有基本概念,只是它的fomatter叫做patternLayout, 而handler则叫做appender. 另外log4j和java util logging在level的划分上有所不同,log4j的level分为:TRACE,DEBUG,INFO,WARN,ERROR,FATAL,ALL。
log4j的所有配置可以放在一个log4j.properties中:
#log4j.logger.[package name]=level, appender1, appender2...
#define rootlogger
log4j.rootLogger=info, RF
log4j.appender.RF=org.apache.log4j.RollingFileAppender
log4j.appender.RF.File=example.log
log4j.appender.RF.MaxFileSize=100KB
# Keep one backup file
log4j.appender.RF.MaxBackupIndex=1
log4j.appender.RF.layout=org.apache.log4j.PatternLayout
log4j.appender.RF.layout.ConversionPattern=%p %t %c - %m%n
#define logger for com.my.test
log4j.logger.com.my.test=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
#com.my.test log messages do not transfer to parent's appenders.
log4j.additivity.com.my.test=false
将log4j.properties放在classpath下面,就可以被自动加载了。如果log4j.properties不在classpath下,或者其名字叫other.properties, 则可以指定system property:
java -Dlog4j.configuration=other.properties ....
Apache Commons Logging
也叫Jakarta Commons logging, 简称JCL, 其的目的是为所有流行的logging提供一个通用接口。用户在开发的时候只需要调用commons logging的接口。而运行环境中具体使用的是log4j,util logging或其它的logging, 则由commons logging配置文件或者commons logging的自动探测来决定。
Commons Logging包含三个jar:
commons-logging-api.jar 这里面包含了commons logging的通用接口以及两个logger的实现:SimpleLog和NoOpLog.
commons-logging.jar 这里面包含了通用接口,以及Log4j,JDK logger等的适配器。这个是最常用的包。
commons-logging-adapters.jar 只包含了第三方logger的适配器。必须和上面两个包中的一个搭配使用。
Maven引入commons-logging.jar的方法:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
Commons logging采用的是自动探测加载logging工具。在复杂的环境中,可能因为classpath中的某个文件,导致Commons logging加载跟我们的预期不一致,我们可以启动诊断功能将其探测决断的过程输出到STDOUT或STDERR中
java -Dorg.apache.commons.logging.diagnostics.dest=STDERR ...
Commons logging的使用方法:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class App {
private static Log log = LogFactory.getLog(App.class);
public static void main( String[] args ) {
if (log.isInfoEnabled()) {
log.info("app.info");
}
if (log.isDebugEnabled()) {
log.debug("app.debug");
}
}
}
在初始化Abstract LogFactory时,系统需要探测真正LogFactory Implementation, 其的探测顺序:
Java System property: org.apache.commons.logging.LogFactory
Commons-logging.properties中的org.apache.commons.logging.LogFactory
使用类org.apache.commons.logging.impl.LogFactoryImpl
LogFactoryImpl对依次试图初始化
commons-logging.properties中指定的org.apache.commons.logging.Log
Java system properties中指定的org.apache.commons.logging.Log
Log4j
JDK4Logger
SimpleLog
对于上面的代码,如果没有任何配置,则使用的是JDK4Logger. 如果将log4j放入classpath中,根据上面探测优先顺序,将会优先使用log4j。
Log的level与log4j的level分类相同。
SLF4J
Simple logging facades for java也是一种通用的logging接口。它跟commons logging类似,区别在于commons logging是运行时运用classLoader动态探测实际logging,而SLF4J是开发时使用import class静态声明binding。这解决了在类似OSGI下,commons logging工作存在的问题。SLF4J的产生,就是为了改变commons logging的动态探测。
Maven引入SLF4J接口的方法
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
</dependency>
slf4j-api.jar主要包含了接口,它并不包含logging的实现类,也不包含其它logger的装饰器。它必须和它的bindings一起工作。它的bindings里面包含了其它logger的装饰器。bindings有以下几个:
slf4j-log4j12 包含了log4j的装饰器
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.6</version> </dependency>
slf4j-jdk14 顾名思义
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.6</version> </dependency>
slf4j-nop no operation的意思是忽略丢弃所有的logging message
slf4j-simple 包含了simple logging的实现。
slf4j-jcl 包含了commons logging的装饰类。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jcl</artifactId> <version>1.7.6</version> </dependency>
slf4j的调用方法:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static Logger logger = LoggerFactory.getLogger(App.class);
public static void main( String[] args ) {
if (logger.isInfoEnabled()) {
logger.info("app.info");
}
if (logger.isDebugEnabled()) {
logger.debug("app.debug");
}
}
}
而配置上非常简单。如果想使用log4j,只需要在pom.xml中引入slf4j-log4j12的binding,binding自动引入依赖模块log4j.还需要在classpath下面创建log4j.properties就可以使log4j工作。同样的,可以方法可以使用commons logging或jdk14logging.
建议使用SLF4J。对于旧的系统,无法改动为SLF的,可以使用SLF的桥接功能。SLF4J提供了几个桥接包,可以是对log4j,jdk14log,commons logging的调用,转发给SLF4J. 这几个桥接包就是jcl-over-slf4j.jar,log4j-over-slf4j.jar以及jul-over-slf4j.jar.
Tomcat 8 logging
顺带说下tomcat自身代码的logging,tomcat 8使用的是自己开发的org.apache.juli.logging. 它类似于commons-logging. 是在commons logging的帮助下开发的。 我猜之所以不直接用commons logging,可能是tomcat不想在自己的最小发布包中包含其它的依赖。
juli logging默认支持的是jdk14 logging. 如果想支持Log4J,则需要使用juli adapter。在adapter中包含了对外的适配器。
以上是tomcat8自身的logging系统。接下来,对于开发的web应用,可以使用javax.servlet.ServletContext.log(...)把log写到tomcat的log中。