System.out与System.err
java.util.logging
apache log4j
apache commons logging
SLF4J
其中System.out与System.err就不多说了。我们学习的第一个程序可能就是它们两个。
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 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 =
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 ....
也叫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分类相同。
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自身代码的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中。