Open Joker-Qian opened 9 years ago
类图时序图需要介绍啊,要不别人怎么看懂?是否有更详尽的介绍文档?
还有一个框架来说,首先明白如何使用是最重要的,马上拿来就说源码,对自己对别人都不是一个更好的理解方式,因为看官听众根本脑中就无上下文
还有就是不必分要苛求自己从架构角度去说一个东西,因为目前来说能力限制,自身本身就没理解到那个程度,那么就务实一步步来,先把使用说明搞好,自己懂怎么用,可以工程化,用起来有什么优点缺点,然后把使用经验分享给别人,自己懂了才能身教给别人。
嗯, 了解了, 今天就改正一下文档.
Slf4j与Log4j学习分享
首先看的是slf4j, 因为我对slf4j一直很好奇, 虽然看过许多介绍说什么slf4j是日志的门面, 但是我实在不明白 '门面' 是什么意思, 于是先来看它. 在官网下载了最新的 zip 包之后, 解压得到这样的一堆内容: 里面各种jar包一大堆, 经过一系列尝试之后, 知道了要用 slf4j + log4j 需要使用这三个包:
slf4j-api-1.7.12.jar
,slf4j-log4j12-1.7.12.jar
,log4j-1.2.17.jar
当使用slf4j + log4j之后, 日志配置和原来一模一样( 关于配置见[log4j] ), 但是在获取日志对象时发生了改变,查看源码之后得到类图如下: LoggerFactory这个名字一看就知道肯定是获得Logger对象的工厂类了, 它里面有静态方法
getLogger(name : String)
这个方法其实是调用了ILoggerFactory
的一个实现类的getLogger()
方法, 这个具体的实现类就在每个不通的slf4j-xxxx-1.7.12.jar
中; 在这个jar包中一定会在org.slf4j.impl
这个路径下面有StaticLoggerBinder
类, 它继承了api中的LoggerFactoryBinder
接口, 里面做了一些验证和具体日志实现的初始化工作( 详情见[slf4j + log4j] ); 然后有Log4jLoggerAdapter
实现了api中的Logger
接口, 它作为了真正进行日志记录工作的log4j-Logger
的代理; 将日志消息做了简单处理之后, 就调用log4j-Logger
来的方法进行日志记录; 这样, slf4j的层次就很清楚了:slf4j-api
是通用的日志接口, 这些接口有各种不同的实现策略, 每种策略同时是对应的具体的日志工具的代理, 这样我们只要在程序中使用slf4j-api
给出的接口, 在根据需要选用代理和日志工具, 同时可以方便切换日志工具;上回配置相关的介绍一点都没有, 这次一并补充, 在
log4j.properties
配置文件里, 配置主要分三种, 如图: 虽然rootLogger
也属于Logger
, 但是rootLogger
为必配项,rootLogger
作为所有Logger
的父亲, 所有的Logger
都会继承它的输出源, 通常rootLogger
配置为控制台输出:如果自己配置的Logger并不想继承父Logger的输出源, 可添加下面的配置:
配置中Appender的各项参数与选用的日志输出策略相关, 例如:
最常用的就是 DailyRollingFileAppender 了, 它的配置如下:
我尝试了一下JDBCAppender, 日志记录速度简直惊人的慢:
如果换成常用的文件记录, 时间只用了
33 ms
, 从网上看资料说一般会为JDBCAppender配置连接池池, 如果使用HicariCP
为它管理连接池, 记录速度应该会大大提升(待测). 关于日志输出格式Layout
, 它也有许多种实现策略, 为了控制日志输出格式, 一般会采用PatternLayout
可以自己指定布局, 关于其它各种布局方式, 以下为网上摘录 :在自己选用布局格式时, 各种占位符含义如下:
2.API
首先当然需要获取日志对象:
Logger对外暴露了很多接口, 除了记录日志的接口如
info(), log(), l7dlog()
; 还能获得改日志对象的一些信息, 如getName(), getLevel(), getParent()
; 甚至还能用addAppender(), removeAppender()
手动添加或删除日志的输出源, 如果自己包装了日志事件LoggingEvent
能够直接调用callAppenders()
来记录日志; 各种接口看log4j的API就会很明白; 在slf4j中, Logger的代理在记录日志时, 调用的都是log()
接口, 使用了Adapter的FQCN;l7dlog()
这个接口我没有用过, 在使用它的之前, 必须先为该logger设置一个ResourceBundle
, 具体用处没有深入了解;3.类图时序图
结合上面的配置和API再来看一看类图, 核心是
Category
, 它是Logger
的父类, 它的对象里会持有配置给它的所有Appender
的一个管理器的引用; 还有所有Logger的仓库LoggerRepository
;Appender
又会聚合Layout
;log4j的初始化时序图如下:
当选用log4j作为slf4j的具体日志实现时, 中间加入了代理层, 类图见[slf4j], 我们使用slf4j获得的
Logger
对象其中的接口就很简单了, 只有两种: 1. 用来记录日志, 例如:info(String format, Object arg), debug(String msg)
; 2. 用来判断对象是否记录该level的日志, 例如:isInfoEnabled(), isWarnEnabled()
; 相比Log4j的Logger, 这个实在是清晰舒爽多了; 而且在单独使用log4j时, 一般会先进行一下判断, 当等级不够时, 会少做许多日志内容处理操作, 提高一些效率;而使用slf4j的接口就不必这样了, 它自己会先检测日志级别, 然后才拼接字符串; 同时slf4j使用
{}
作为占位符, 省的使用+
连接字符串或是String.format(format, Object...args)
, 并且能够自动解析参数中的数组, 打印其中的元素; 这个优点我个人觉得还好, 因为在项目中一般都会对日志进行一层封装, 这些操作其实都可以自己来实现; 使用slf4j后, 日志记录时调用过程如下:待续