1. Logging配置
目录:$JAVA_HOME/jre/lib/logging.properties
# 默认配置
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
com.xyz.foo.level = SEVERE
sun.rmi.transport.tcp.logLevel = FINE
第一行:Handler。用逗号分隔每个Handler,包括ConsoleHandler(控制台)、FileHandler(文件)、MemoryHandler(内存缓冲区)、SocketHandler(网络流)、StreamHandler(流,常作为其他Handler的基类)。
举个栗子:
handlers = java.util.logging.ConsoleHandler,java.util.logging.FileHandler,java.util.MemoryHandler,java.util.logging.SocketHandler,java.util.logging.StreamHandler
第二行:.level是root logger的日志级别。
第三行至第八行: .xxx是配置具体某个handler的属性,以FileHandler为例:
- java.util.logging.FileHandler.level 为 Handler 指定默认的级别(默认为 Level.ALL)。
- java.util.logging.FileHandler.filter 指定要使用的 Filter 类的名称(默认为无 Filter)。
- java.util.logging.FileHandler.formatter指定要使用的Formatter类的名称(默认为java.util.logging.XMLFormatter)。
- java.util.logging.FileHandler.encoding 指定要使用的字符集编码的名称(默认使用默认的平台编码)。
- java.util.logging.FileHandler.limit 指定要写入到任意文件的近似最大量(以字节为单位)。如果该数为 0,则没有限制(默认为无限制)。
- java.util.logging.FileHandler.count 指定有多少输出文件参与循环(默认为 1)。
- java.util.logging.FileHandler.pattern 为生成的输出文件名称指定一个模式。有关细节请参见以下内容(默认为 “%h/java%u.log”)。
java.util.logging.FileHandler.append指定是否应该将FileHandler追加到任何现有文件上(默认为 false)。
- ”/” 本地路径名分隔符
- “%t” 系统临时目录
- “%h” “user.home” 系统属性的值
- “%g” 区分循环日志的生成号
- “%u” 解决冲突的唯一号码
- “%%” 转换为单个百分数符号”%”
第九行:logger的配置,所有以[.level]结尾的属性皆被认为是对某个logger的级别的定义。
2.logging的执行过程
Logger的获取###参照
首先是调用Logger的getLogger()方法获得一个logger:
public static synchronized Logger getLogger(String name) {
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
}
上面的调用会触发java.util.logging.LoggerManager的类初始化工作,LoggerManager有一个静态化初始化块(这是会先于LoggerManager的构造函数调用的~_~):
static {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
String cname = null;
try {
cname = System.getProperty("java.util.logging.manager");
if (cname != null) {
try {
Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
manager = (LogManager) clz.newInstance();
} catch(ClassNotFoundException ex) {
Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
manager = (LogManager) clz.newInstance();
}
}
} catch (Exceptionex) {
System.err.println("Could not load Logmanager \"" + cname+ "\"");
ex.printStackTrace();
}
if (manager == null) {
manager = newLogManager();
}
manager.rootLogger = manager.newRootLogger();
manager.addLogger(manager.rootLogger);
Logger.global.setLogManager(manager);
manager.addLogger(Logger.global);
return null;
}
});
}
从静态初始化块中可以看出LoggerManager是可以使用系统属性java.util.logging.manager指定一个继承自java.util.logging.LoggerManager的类进行替换的,比如Tomcat启动脚本中就使用该机制以使用自己的LoggerManager。
不管是JDK默认的java.util.logging.LoggerManager还是自定义的LoggerManager,初始化工作中均会给LoggerManager添加两个logger,一个是名称为””的root logger,且logger级别设置为默认的INFO;另一个是名称为global的全局logger,级别仍然为INFO。
LogManager”类”初始化完成之后就会读取配置文件(默认为$JAVA_HOME/jre/lib/logging.properties),把配置文件的属性名<->属性值这样的键值对保存在内存中,方便之后初始化logger的时候使用。
第一步中Logger类发起的getLogger操作将会调用java.util.logging.LoggerManager的如下方法:
Logger demandLogger(String name) {
Logger result = getLogger(name);
if (result == null) {
result = newLogger(name, null);
addLogger(result);
result = getLogger(name);
}
return result;
}