一、用途
程序中记录日志,打印到控制台、文件等方式,记录过程可根据日志级别做筛选,日志格式可以自定义。
大概结构如下所示:
简要说明各个模块:
(1) LogManager:管理LoggerContext及Logger,可以添加、获取Logger。
(2) LoggerContext: 保存所有Logger,用于添加及获取Logger,由LoggerManager管理。LoggerContext中同时保存了Logger的弱引用,在释放Logger时使用到。
(3) Logger: 记录日志的对象,提供接口实现对日志消息以不同的日志级别进行记录,管理Handler、Formatter、Filter等。
(4) Handler: 记录日志真正的对象,包括FileHandler(记录到文件)、ConsoleHandler(记录到控制台)、StreamHandler(记录到流)、SocketHandler(通过socket发送日志?)等。
(5) Formatter: 处理日志的格式,包括SimpleFormatter(简单格式记录,显示时间、级别、线程、日志信息等)、XMLFormatter(以xml格式记录日志)等。
(6) Filter: 实现日志的过滤功能,通过自定义Filter,实现对某些日志的过滤处理。
(7) ErrorManager: 记录日志出错时的处理对象?
(8) LogRecord: 对日志消息的抽象。
二、详细介绍(结合源码)
详细描述如下:
(1)日志管理:
LoggerManager主要用来管理LogContext以及logger。
LoggerManager维护变量主要有:
(a) LogManager manager
manager对象单例,用于LogManager内部其他方法调用。在静态构造函数中实例化,在getLogManager()方法被调用时初始化:
1 public staticLogManager getLogManager() {2 if (manager != null) {3 manager.ensureLogManagerInitialized();4 }5 returnmanager;6 }7
8
9 public voidensureLogManagerInitialized() //简要描述10 {11 //若未初始化
12 synchronized(this)13 {14 owner.readPrimordialConfiguration(); //读配置文件初始化manager
15 owner.rootLogger = owner.new RootLogger(); //添加rootLogger
16 owner.addLogger(owner.rootLogger);17 final Logger global = Logger.global; //添加globalLogger
18 owner.addLogger(global);19 }20 }
(b) Properties props
用于读配置文件内容,配合完成初始化。上述readPrimordialConfiguration()函数即使用了Props获取配置。
主要针对配置文件中对Logger及Handler等定义,进行动态设置。
默认的配置文件如下:
handlers=java.util.logging.ConsoleHandler
.level=INFO
java.util.logging.FileHandler.pattern= %h/java%u.log
java.util.logging.FileHandler.limit= 50000java.util.logging.FileHandler.count= 1java.util.logging.FileHandler.formatter=java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
com.xyz.foo.level= SEVERE
主要对默认Handler进行配置(ConsoleHanlder,默认只打印日志到控制台),设置FileHandler的一些配置(文件名格式、写文件大小限制、使用文件数量、日志格式为xml),设置ConsoleHandler控制台打印的格式为普通格式(SimpleFormatter)、打印日志级别最低为INFO。
(c) LggerContext userContext
LogContext是LogManager的内部类。
LogManager中包含默认两个LoggerContext: userContext与systemContext,用户添加logger皆是放入userContext。
LoggerContext内部,Logger被放入LogNode root中以链表形式存储。
该类对LogManager开放接口:
添加Logger: addLogger
synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { //简要说明
final LogManager owner = getOwner(); //设置logmanager
logger.setLogManager(owner);
LoggerWeakRef ref = owner.new LoggerWeakRef(logger);
namedLoggers.put(name, ref); //保存弱引用
Level level = owner.getLevelProperty(name + ".level", null); //设置Level
if (level != null && !logger.isLevelInitialized()) {
doSetLevel(logger, level);
}
processParentHandlers(logger, name); //设置父Logger(rootLogger)
Logger parent = getParentLogger();
if (parent != null) {
doSetParent(logger, parent);
}
return true;
}
查找Logger: findLogger
(d) logManager.addLogger(Logger logger)方法
void addLogger(Logger logger)
{
final String name = logger.getName();
if (name == null) {
throw new NullPointerException();
}
LoggerContext cx = getUserContext();
if (cx.addLocalLogger(logger)) {
loadLoggerHandlers(logger, name, name + ".handlers"); //根据配置文件加载logger对应handler
}
}
(e) logManager.demandLogger
调用loggerContext接口,构造logger并添加至loggerContext。
(2) 日志打印
(a) name: String
logger的名称。
例如:Logger logger = Logger.getLogger("test"); 则,logger名称为"test";
(b) handlers: List、addHanlder(Handler) //设置日志处理对象
Handler是日志处理类的基类,子类主要有ConsoleHandler、FileHandler、SocketHandler、StreamHandler、MemoryHandler。
Handler负责将日志消息写到指定位置。
该变量负责维护logger上的handlers,通过addHandler(Handler)添加,removeHandler(Handler)移除
一个logger具备多个handlers,因此,一条日志可依据定制情况打印到多个位置。
例如,通过这样,可以为logger添加具备特定formatter、loglevel的handlers:
Logger logger = Logger.getLogger(loggerName);
logger.setLevel(Level.INFO);
//添加文件输出xml格式
Handler fileXMLhandler = new FileHandler("E:\\logging_XMLFormat.file");
Formatter xmlFormatter = new XMLFormatter();
fileXMLhandler.setFormatter(xmlFormatter);
handler.setLevel(Level.WARNING);
logger.addHandler(handler);
//添加文件输出普通格式
Handler fileSimpleHandler = new FileHandler("E:\\logging_simpleFormat.file");
Formatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
handler.setLevel(Level.INFO);
logger.addHandler(handler);
//添加控制台输出
Handler consoleHandler = new ConsoleHandler();
logger.addHandler(consoleHandler);
(c) Filter、Handler.setFilter(Filter) //设置过滤器
用于过滤logRecord。
接口包含:
boolean isLoggable(in LogRecord);
实现Filter自定义设置过滤规则。
(d) Logger.getLogger(String name) //获取logger
获取logger(若无,则先添加后返回)
eg.
Logger logger = Logger.getLogger("testLogger");
logger.info("this is a test msg");
(e) Logger.log(LogRecord record) //日志打印过程
负责将指定log内容记录到日志。
此处LogRecord是对日志消息的抽象,结构如下:
主要包含日志内容String msg、日志级别Level level
日志打印函数如下:
public void log(LogRecord record) {
//检查level
if (!isLoggable(record.getLevel())) {
return;
}
//检查过滤
Filter theFilter = filter;
if (theFilter != null && !theFilter.isLoggable(record)) {
return;
}
//处理日志打印
Logger logger = this;
while (logger != null) {
//获取所有Handlers
final Handler[] loggerHandlers = Logger.getHandlers();
//遍历所有Handlers,处理logRecord
for (Handler handler : loggerHandlers) {
handler.publish(record);
}
//判断是否使用parentLogger处理,若否,结束流程
final boolean useParentHdls = logger.useParentHandlers;
if (!useParentHdls) {
break;
}
logger = isSystemLogger ? logger.parent : logger.getParent(); //通过parentLogger继续处理
}
}
至此,java 的logging工具中的主要模块描述结束。
有问题敬请反馈,多谢!