zoukankan      html  css  js  c++  java
  • 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工具中的主要模块描述结束。

    有问题敬请反馈,多谢!

  • 相关阅读:
    【Dubbo 源码解析】08_Dubbo与Spring结合
    【Dubbo 源码解析】07_Dubbo 重试机制
    【Dubbo 源码解析】06_Dubbo 服务调用
    【Dubbo 源码解析】05_Dubbo 服务发现&引用
    【Dubbo 源码解析】04_Dubbo 服务注册&暴露
    【Dubbo 源码解析】03_Dubbo Protocol&Filter
    【Dubbo 源码解析】02_Dubbo SPI
    Hadoop(十五)MapReduce程序实例
    Hadoop(十四)MapReduce原理分析
    Hadoop(十三)分析MapReduce程序
  • 原文地址:https://www.cnblogs.com/xinxinBlog/p/9906287.html
Copyright © 2011-2022 走看看