Java源码初探_logging日志模块实现

 一、用途

程序中记录日志,打印到控制台、文件等方式,记录过程可根据日志级别做筛选,日志格式可以自定义。

大概结构如下所示:

简要说明各个模块:

(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工具中的主要模块描述结束。

有问题敬请反馈,多谢!

原文地址:https://www.cnblogs.com/xinxinBlog/p/9906287.html