一、用途
程序中记录日志,打印到控制台、文件等方式,记录过程可根据日志级别做筛选,日志格式可以自定义。
大概结构如下所示:
简要说明各个模块:
(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 static LogManager getLogManager() { 2 if (manager != null) { 3 manager.ensureLogManagerInitialized(); 4 } 5 return manager; 6 } 7 8 9 public void ensureLogManagerInitialized() //简要描述 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 = 50000 java.util.logging.FileHandler.count = 1 java.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<Handler>、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工具中的主要模块描述结束。
有问题敬请反馈,多谢!