- 浏览: 1850774 次
- 性别:
- 来自: 深圳
-
文章分类
- 全部博客 (665)
- 闲话 (17)
- ruby (1)
- javascript (40)
- linux (7)
- android (22)
- 开发过程 (11)
- 哥也读读源代码 (13)
- JVM (1)
- ant (2)
- Hibernate (3)
- jboss (3)
- web service (17)
- https (4)
- java基础 (17)
- spring (7)
- servlet (3)
- 杂记 (39)
- struts2 (10)
- logback (4)
- 多线程 (2)
- 系统诊断 (9)
- UI (4)
- json (2)
- Java EE (7)
- eclipse相关 (4)
- JMS (1)
- maven (19)
- 版本管理 (7)
- sso (1)
- ci (1)
- 设计 (18)
- 戒烟 (4)
- http (9)
- 计划 (4)
- HTML5 (3)
- chrome extensions (5)
- tomcat源码阅读 (4)
- httpd (5)
- MongoDB (3)
- node (2)
最新评论
-
levin_china:
勾选了,还是找不到
用spring annotation声明的bean,当打包在jar中时,无法被扫描到 -
GGGGeek:
我用的maven-3.5.0,还没有遇到这种情况,使用jar ...
用spring annotation声明的bean,当打包在jar中时,无法被扫描到 -
GGGGeek:
受益匪浅,从组织项目结构,到技术细节,讲的很到位,只是博主不再 ...
一个多maven项目聚合的实例 -
Aaron-Joe-William:
<?xml version="1.0" ...
hibernate逆向工程 -
li272355201:
http://archive.apache.org/dist/ ...
tomcat源码阅读(一)——环境搭建
上一篇博客介绍了logback的StaticLoggerBinder类怎么初始化并创建LoggerContext,这篇博客准备接下来介绍一下,LoggerContext怎么创建日志框架真正的核心类Logger。为什么logback框架提供的Logger是树形结构的呢?这就是在LoggerContext里实现的
还是先上图,对LoggerContext和周边类的关系有一个整体上的认识
从图中可以看到,LoggerContext类除了ILoggerFactory接口之外,还实现了LifeCycle接口,并继承自ContextBase类
LoggerContext最核心的方法是getLogger(String name),这个方法放在最后介绍,先介绍一下其它字段和方法,这些字段和方法,大部分都是为了getLogger()方法服务的
首先需要澄清一点,虽然LoggerContext没有显式地采用单例模式,但实际上应用从来不会直接获取到LoggerContext类的实例,都是通过StaticLoggerBinder内的defaultLoggerContext字段来间接引用LoggerContext的实例,而StaticLoggerBinder本身是单例的,所以程序中用到的LoggerContext也始终是同一个实例,这点很重要
看看LoggerContext内的字段
1、root是根Logger
2、size用来表示LoggerContext一共创建了几个Logger
3、loggerCache的名字起得有点误导性,实际上不止是一个cache,LoggerContext创建的所有Logger,都保存在这里,另外这里用的容器是Hashtable,我感到有点疑问,一般来说Hashtable是不推荐使用的,不知道最新版本的logback里,作者是否已经修改
4、loggerContextRemoteView是一个LoggerContext的VO对象,保存了LoggerContext的一些值,比如name、birthTime等,大家可以自己看看
5、turboFilterList保存所有的TurboFilter,TurboFilter顾名思义,是一种快速过滤器,对是否记录日志有一票通过和一票否决的权力,后面的博客在介绍Filter时,会专门介绍
6、resetCount是用来统计该LoggerContext调用过几次reset()方法
7、其它几个字段,我暂时不太清楚是干什么的
介绍完了字段,来看看LoggerContext的构造方法吧
这个方法基本上是只会调用一次的,在StaticLoggerBinder里
在构造LoggerContext的时候,对上面提到的字段进行了初始化,并创建了根Logger
上面的syncRemoteView()方法,是在LoggerContext的name字段和propertyMap发生变化的时候调用的,目的是创建新的LoggerContextVO,实际中不太会被调用到,不是核心方法
这2个方法都是和size字段相关的,很好理解
然后是一系列命名很奇怪的方法,只举一个最常用的例子
这个方法命名很烂,从命名上完全看不出这个方法的意图。该方法主要是在Logger要记录日志之前,用TurboFilter做一下过滤先,如果没有配置TurboFilter,就跳过过滤;否则的话,用TurboFilter做一下过滤。这部分内容涉及到fitler过滤,会在后面的博客里详细介绍
这个类中还有一系列侦听器方法,这篇博客先略过
接下来就是核心的getLogger()方法
这个方法实际上调用的是下面这个方法
上面的getLogger(String name)方法,就是创建Logger的关键,详细解读一下:
1、如果name是null,抛出异常,不过这个情况基本是不会发生的
2、如果请求的是ROOT Logger,那么就直接返回root
3、然后检查一下请求的Logger是否已经创建过了,如果已经创建过,就直接从loggerCache中返回
4、如果还没创建过,那就开始逐层创建,比如请求的Logger的name是com.company.package.ClassName,那么一共会创建4个Logger,分别是Logger[com]、Logger[com.company]、Logger[com.company.package]、Logger[com.company.package.ClassName]
上面代码比较清晰,就是根据"."来解析name,然后创建Logger,每创建一个Logger,都放到loggerCache中,并且把size++
创建Child Logger是有点讲究的,除了创建Logger实例之外,还有维护父子关系,并且处理Level继承的问题,这个是在Logger类的createChildByName(String childName)方法里实现的
首先检查一下name是否是合法的。然后创建childLogger
在上面的this.parent = parent里,设置了父Logger
然后在childrenList.add(childLogger)里,将新创建的Logger加到子Logger列表里
最后把子Logger的生效级别设置为当前Logger的生效级别
总结一下创建Logger的完整流程:
1、如果请求ROOT logger,则直接返回root
2、如果请求的Logger已经存在,则直接返回
3、如果请求的Logger尚未创建,则从ROOT开始,级联创建所有Logger
4、每创建一个Logger,都要设置父子关系,继承生效级别(至于Appender是怎么继承的,在后面的博客里介绍)
5、每创建一个Logger,都将其放入loggerCache,并将size++
关于在每次调用LoggerFactory.getLogger(String name)时,logback是怎么返回Logger的,这篇博客就介绍完了。下一篇博客会介绍,当调用Logger.info(String msg)时,会发生的事情
还是先上图,对LoggerContext和周边类的关系有一个整体上的认识
![](http://dl.iteye.com/upload/attachment/551197/68ec5f35-b281-39ac-abee-672e78dd3d21.jpg)
从图中可以看到,LoggerContext类除了ILoggerFactory接口之外,还实现了LifeCycle接口,并继承自ContextBase类
LoggerContext最核心的方法是getLogger(String name),这个方法放在最后介绍,先介绍一下其它字段和方法,这些字段和方法,大部分都是为了getLogger()方法服务的
首先需要澄清一点,虽然LoggerContext没有显式地采用单例模式,但实际上应用从来不会直接获取到LoggerContext类的实例,都是通过StaticLoggerBinder内的defaultLoggerContext字段来间接引用LoggerContext的实例,而StaticLoggerBinder本身是单例的,所以程序中用到的LoggerContext也始终是同一个实例,这点很重要
看看LoggerContext内的字段
final Logger root; private int size; private int noAppenderWarning = 0; final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>(); // We want loggerCache to be synchronized so Hashtable is a good choice. In // practice, it performs a little faster than the map returned by // Collections.synchronizedMap at the cost of a very slightly higher memory // footprint. private Hashtable<String, Logger> loggerCache; private LoggerContextVO loggerContextRemoteView; private final TurboFilterList turboFilterList = new TurboFilterList(); private boolean packagingDataEnabled = true; private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH; boolean started = false; int resetCount = 0;
1、root是根Logger
2、size用来表示LoggerContext一共创建了几个Logger
3、loggerCache的名字起得有点误导性,实际上不止是一个cache,LoggerContext创建的所有Logger,都保存在这里,另外这里用的容器是Hashtable,我感到有点疑问,一般来说Hashtable是不推荐使用的,不知道最新版本的logback里,作者是否已经修改
4、loggerContextRemoteView是一个LoggerContext的VO对象,保存了LoggerContext的一些值,比如name、birthTime等,大家可以自己看看
5、turboFilterList保存所有的TurboFilter,TurboFilter顾名思义,是一种快速过滤器,对是否记录日志有一票通过和一票否决的权力,后面的博客在介绍Filter时,会专门介绍
6、resetCount是用来统计该LoggerContext调用过几次reset()方法
7、其它几个字段,我暂时不太清楚是干什么的
介绍完了字段,来看看LoggerContext的构造方法吧
public LoggerContext() { super(); this.loggerCache = new Hashtable<String, Logger>(); this.loggerContextRemoteView = new LoggerContextVO(this); this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this); this.root.setLevel(Level.DEBUG); loggerCache.put(Logger.ROOT_LOGGER_NAME, root); initEvaluatorMap(); size = 1; }
这个方法基本上是只会调用一次的,在StaticLoggerBinder里
private LoggerContext defaultLoggerContext = new LoggerContext();
在构造LoggerContext的时候,对上面提到的字段进行了初始化,并创建了根Logger
/** * A new instance of LoggerContextRemoteView needs to be created each time the * name or propertyMap (including keys or values) changes. */ private void syncRemoteView() { loggerContextRemoteView = new LoggerContextVO(this); for (Logger logger : loggerCache.values()) { logger.buildRemoteView(); } }
上面的syncRemoteView()方法,是在LoggerContext的name字段和propertyMap发生变化的时候调用的,目的是创建新的LoggerContextVO,实际中不太会被调用到,不是核心方法
private void incSize() { size++; } int size() { return size; }
这2个方法都是和size字段相关的,很好理解
然后是一系列命名很奇怪的方法,只举一个最常用的例子
final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format, final Object[] params, final Throwable t) { if (turboFilterList.size() == 0) { return FilterReply.NEUTRAL; } return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t); }
这个方法命名很烂,从命名上完全看不出这个方法的意图。该方法主要是在Logger要记录日志之前,用TurboFilter做一下过滤先,如果没有配置TurboFilter,就跳过过滤;否则的话,用TurboFilter做一下过滤。这部分内容涉及到fitler过滤,会在后面的博客里详细介绍
这个类中还有一系列侦听器方法,这篇博客先略过
接下来就是核心的getLogger()方法
public final Logger getLogger(final Class clazz) { return getLogger(clazz.getName()); }
这个方法实际上调用的是下面这个方法
public final Logger getLogger(final String name) { if (name == null) { throw new IllegalArgumentException("name argument cannot be null"); } // if we are asking for the root logger, then let us return it without // wasting time if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) { return root; } int i = 0; Logger logger = root; // check if the desired logger exists, if it does, return it // without further ado. Logger childLogger = (Logger) loggerCache.get(name); // if we have the child, then let us return it without wasting time if (childLogger != null) { return childLogger; } // if the desired logger does not exist, them create all the loggers // in between as well (if they don't already exist) String childName; while (true) { int h = Logger.getSeparatorIndexOf(name, i); if (h == -1) { childName = name; } else { childName = name.substring(0, h); } // move i left of the last point i = h + 1; synchronized (logger) { childLogger = logger.getChildByName(childName); if (childLogger == null) { childLogger = logger.createChildByName(childName); loggerCache.put(childName, childLogger); incSize(); } } logger = childLogger; if (h == -1) { return childLogger; } } }
上面的getLogger(String name)方法,就是创建Logger的关键,详细解读一下:
1、如果name是null,抛出异常,不过这个情况基本是不会发生的
if (name == null) { throw new IllegalArgumentException("name argument cannot be null"); }
2、如果请求的是ROOT Logger,那么就直接返回root
// if we are asking for the root logger, then let us return it without // wasting time if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) { return root; }
3、然后检查一下请求的Logger是否已经创建过了,如果已经创建过,就直接从loggerCache中返回
// check if the desired logger exists, if it does, return it // without further ado. Logger childLogger = (Logger) loggerCache.get(name); // if we have the child, then let us return it without wasting time if (childLogger != null) { return childLogger; }
4、如果还没创建过,那就开始逐层创建,比如请求的Logger的name是com.company.package.ClassName,那么一共会创建4个Logger,分别是Logger[com]、Logger[com.company]、Logger[com.company.package]、Logger[com.company.package.ClassName]
int i = 0; Logger logger = root; // if the desired logger does not exist, them create all the loggers // in between as well (if they don't already exist) String childName; while (true) { int h = Logger.getSeparatorIndexOf(name, i); if (h == -1) { childName = name; } else { childName = name.substring(0, h); } // move i left of the last point i = h + 1; synchronized (logger) { childLogger = logger.getChildByName(childName); if (childLogger == null) { childLogger = logger.createChildByName(childName); loggerCache.put(childName, childLogger); incSize(); } } logger = childLogger; if (h == -1) { return childLogger; } }
上面代码比较清晰,就是根据"."来解析name,然后创建Logger,每创建一个Logger,都放到loggerCache中,并且把size++
创建Child Logger是有点讲究的,除了创建Logger实例之外,还有维护父子关系,并且处理Level继承的问题,这个是在Logger类的createChildByName(String childName)方法里实现的
Logger createChildByName(final String childName) { int i_index = getSeparatorIndexOf(childName, this.name.length() + 1); if (i_index != -1) { throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName + " passed as parameter, may not include '.' after index" + (this.name.length() + 1)); } if (childrenList == null) { childrenList = new ArrayList<Logger>(DEFAULT_CHILD_ARRAY_SIZE); } Logger childLogger; childLogger = new Logger(childName, this, this.loggerContext); childrenList.add(childLogger); childLogger.effectiveLevelInt = this.effectiveLevelInt; return childLogger; }
首先检查一下name是否是合法的。然后创建childLogger
Logger(String name, Logger parent, LoggerContext loggerContext) { this.name = name; this.parent = parent; this.loggerContext = loggerContext; buildRemoteView(); instanceCount++; }
在上面的this.parent = parent里,设置了父Logger
然后在childrenList.add(childLogger)里,将新创建的Logger加到子Logger列表里
最后把子Logger的生效级别设置为当前Logger的生效级别
总结一下创建Logger的完整流程:
1、如果请求ROOT logger,则直接返回root
2、如果请求的Logger已经存在,则直接返回
3、如果请求的Logger尚未创建,则从ROOT开始,级联创建所有Logger
4、每创建一个Logger,都要设置父子关系,继承生效级别(至于Appender是怎么继承的,在后面的博客里介绍)
5、每创建一个Logger,都将其放入loggerCache,并将size++
关于在每次调用LoggerFactory.getLogger(String name)时,logback是怎么返回Logger的,这篇博客就介绍完了。下一篇博客会介绍,当调用Logger.info(String msg)时,会发生的事情
发表评论
-
小读spring ioc源码(五)——BeanDefinitionDocumentReader
2012-07-28 13:11 2827上一篇博客说到,BeanDefinition的解析,已经走到了 ... -
小读spring ioc源码(四)——BeanDefinitionReader
2012-07-24 19:02 2448上一篇博客说到,ApplicationContext将解析Be ... -
小读spring ioc源码(三)——XmlWebApplicationContext初始化的整体过程
2012-07-12 16:54 2933上一篇说到,ContextLoader ... -
小读spring ioc源码(二)——ContextLoaderListener
2012-06-25 18:41 2880实际开发中,比较多的项目是web项目,这时候加载spring, ... -
小读spring ioc源码(一)——整体介绍
2012-06-18 22:59 2618最近在读spring ioc的源码,用EA画了几张比较清楚的类 ... -
读logback源码系列文章(八)——记录日志的实际工作类Encoder
2011-10-17 20:34 3023本系列的博客从logback怎么对接slf4j开始,逐步介绍了 ... -
读logback源码系列文章(七)——配置的实际工作类Action
2011-10-11 22:59 3574上篇博客介绍了ContextInitializer类如何把框架 ... -
读logback源码系列文章(六)——ContextInitializer
2011-10-09 21:46 6861放了一个长假回来啦,继续写本系列博客 这篇博客我们接着上一篇 ... -
读logback源码系列文章(五)——Appender
2011-09-16 21:03 13860明天要带老婆出国旅游 ... -
读logback源码系列文章(四)——记录日志
2011-09-12 02:22 4450今天晚上本来想来写一下Logger怎么记录日志,以及Appen ... -
读logback源码系列文章(二)——提供ILoggerFactory
2011-09-07 20:07 6680上篇博客介绍了logback是 ... -
读logback源码系列文章(一)——对接slf4j
2011-08-29 02:43 7562以前也读过一些开源项目的源码,主要是spring和ant,不过 ...
相关推荐
标题"Logback的使用和logback.xml详解"暗示了我们要讨论的是一个日志管理框架——Logback,以及它的配置文件`logback.xml`。Logback是Java社区广泛使用的日志处理系统,由Ceki Gülcü创建,作为Log4j的后继者。它...
本篇文章将深入探讨的是JAVA日志组件中的一个经典组合——logback 1.0.13 和 SLF4J 1.7.5。这两者都是由Ceki Gülcü开发的,他是Java世界中著名的日志框架Log4j的创始人。SLF4J(Simple Logging Facade for Java)...
接下来,我们创建Logback的配置文件——`logback.xml`。这个文件位于`src/main/resources`目录下,它是Logback的日志级别、输出目的地、格式等设置的中心位置。以下是一个基本的`logback.xml`配置示例: ```xml ...
**SpringBoot 源码解析:日志基础服务** 在SpringBoot框架中,日志服务是应用程序不可或缺的一部分,它提供了一种记录应用运行时详细信息的机制,包括方法的输入参数、运行时的特殊记录等。SpringBoot为开发者提供...
在本文中,我们将深入探讨这两个关键领域,并基于给出的"Spring学习心得(二)——日志和数据库"这个标题进行详细的阐述。 首先,让我们从日志开始。在Java应用程序中,日志系统扮演着调试、监控和问题排查的关键角色...
《深入理解Log4j——基于源码与工具的探索》 Log4j,作为Java世界里最著名的日志记录框架之一,一直以来都是开发者们必备的工具。它以其强大的功能、灵活的配置和高效的性能,赢得了广大开发者的青睐。这篇长文将...
这篇博客“【MyBatis入门】—— MyBatis日志”旨在帮助初学者理解如何在MyBatis中配置和使用日志功能,以便于调试和优化应用程序。 在MyBatis中,日志系统对于跟踪SQL语句和参数,以及检查执行效率至关重要。...
5. **使用第三方日志库**:虽然JDK的日志框架能满足基本需求,但功能更强大且被广泛使用的日志库如Log4j、Logback和SLF4J提供了更多的灵活性和性能优化。通过桥接器(如Log4j的JDK14LoggerAdapter或SLF4J的...
- `@Log4j`, `@Log4j2`, `@Logback`, `@SLF4J`:自动导入对应日志库的Logger实例,简化日志记录。 5. **其他实用注解**: - `@Value`:与@Data类似,但生成的类是final的,字段也是private且final,适合创建不可...
- Logback:由Log4j的创始人Ceki Gülcü创建的下一代日志框架,比Log4j更现代,性能更优。 - Log4j2:Log4j的升级版,提供了更多特性,如异步日志记录,性能进一步提升。 6. **安全性考虑**: - 日志信息可能...
《TarenaLogs——Java学习中的日志记录与分析》 在Java编程中,日志记录是不可或缺的一部分,它能够帮助开发者在程序运行过程中追踪错误、调试代码,甚至用于性能分析。"TarenaLogs"这个主题,显然聚焦于Java学习中...
Logback是由log4j的创始人Ceki Gülcü创建的,它旨在提供更高的性能和更多的功能。Logback是SLF4J的推荐日志实现,因为它与SLF4J有紧密的集成,而且性能优于许多其他日志框架。 在使用桥接器时,通常需要做以下...