slf4j
slf4j是一个日志接口,相比之前Commons Logging的日志接口增加了输出日志占位符的支持。
logback
logback是一种slf4j接口的实现,目前比较流行,使用较多。
优点
支持分级别的日志输出
同一个类中可以包含不同的日志
配置动态加载,修改配置后实时生效
日志有层级结构,层级之间输出等级可以继承和覆盖
支持同步,异步输出(默认是同步)
支持多目的源的日志输出,包括控制台,文件,数据库,网络等
日志占位符的支持使得字符串的拼接在确定日志输出之前,减少字符串构建成本
支持立即清空缓冲区或缓冲区满后输出(缓冲区满后输入速度会快,但日志关闭不好处理,可能会丢日志)
原理解析 —— 主要组件
Logger 日志的实例类,提供trace,debug, info, warn, error 方法供业务调用,内部含有日志输出等级,和所绑定的appender list,以及该logger的父级,孩子logger。
LoggingEvent 日志事件,包括输出日志等级,日志字符串,日志参数,输入日志的线程名称等。
Appender 代表日志的输出目的地,其中AsyncAppender为异步输出的appender,使用时需指定appender-ref属性来绑定实际输出的同步appender。
Layout 负责将日志事件转换为字符串。
原理解析 —— 初始化
使用Joran 对配置文件进行解析,将配置文件解析,动态映射进入LoggerContext。
LoggerContext为logback中最顶层的配置管理容器。其中包括:
loggerCache 缓存所有的Logger, key为Logger 的字符串名称
以下为初始化步骤:
1. LoggerFactory.getLogger 获取 ILoggerFactory实例(实现了ILoggerFactory接口的LoggerContext对象),判断初始化状态,若未初始化则进入初始化流程,初始化流程会在LoggerFactory对象上上锁。
2. 在ClassLoader里找org.slf4j.impl.StaticLoggerBinder.class的实现,判断是否有多个(一个项目里有可能载入多个slf4j的实现框架),如果只有一个,则调用StaticLoggerBinder.getSingleton() 进行初始化。
3. StaticLoggerBinder 为单例模式,其中有LoggerContext实例,在StaticLoggerBinder init时,通过ContextInitializer类加载配置对LoggerContext实例初始化。
4.在初始化完成后,接1步骤,返回StaticLoggerBinder的LoggerContext实例,在LoggerContext实例实例中获取日志实例,进行返回。
5.从LoggerContext获取logger对象,若没有则级联创建。这里需要注意LoggerContext初始化完成后只有配置文件配置过的logger对象,新创建的logger对象会继承父级logger对象的输出等级,但是并不继承父级的appender list。
原理解析 —— 调用
1. 获取TurboFilter过滤链,如果未过滤则进入下一步
2.比较当前调用的日志级别和logger对象本身的日志输出级别,如果本条日志不需要输出,则过滤
3.创建一个LoggingEvent 对象,包含了日志请求所有相关的参数,请求的 logger,日志请求的级别,日志信息,与日志一同传递的异常信息,当前时间,当前线程,以及当前类的各种信息和 MDC。
4.调用当前logger对象绑定的appender 的 doAppend方法,如果当前对象没有绑定或调用完成再调用父级logger绑定的appender 的 doAppend方法,知道最顶级,或additive属性为false。
5.Appender委托对应的Layout对象(最常用的是PatternLayout)对日志进行字符串格式化,然后输出。
其它
1.多线程下是怎么保证日志输出不乱:同步模式是在 write 之前加锁,异步模式是通过阻塞队列缓冲 + 单线程 write。