如何看源码及原理分析 原理及源码查看是相辅相成。只能了解原理才能更好的了解源码;只有相应的了解源码才能知道是怎么原理实现。
原理:
自已设计一个。配合源码查看,验证框架设计者是怎么设计的。
源码:
只要知道入口。使用时分几步。分别查看。
logback简单使用 加入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependencies > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > <version > 1.7.25</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-core</artifactId > <version > 1.1.11</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.1.11</version > </dependency > </dependencies >
使用:
1 2 3 4 5 6 7 8 public class Test { public static void main (String[] args) { Logger logger = LoggerFactory.getLogger(Test.class); logger.debug("Hello world." ); } }
这时运行会在控制台打印日志。
程序在IO上会有标准输入、标准输出、错误输出。
配置文件 logback.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <configuration > <appender name ="FILE" class ="ch.qos.logback.core.FileAppender" > <file > myApp.log</file > <encoder > <pattern > %msg%n</pattern > </encoder > </appender > <root > <appender-ref ref ="FILE" /> </root > </configuration >
如果添加这个文件到资源目录下,会把日志写入myApp.log文件。
源码分析 从使用上来看。如果没有配置文件,框架会使用默认配置。有配置文件加载配置文件。根据提供的LoggerFactory类获取Logger,Logger调用方法打印日志。
LoggerFactory 看名字就是一个工厂类。又是一个入口类。这个类肯定会有默认配置和加配置文件的功能。生成Logger。
Logger是提供打印日志的方法。
加载配置文件源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Logger logger = LoggerFactory.getLogger(Test.class ); Logger logger = getLogger(clazz.getName()); ILoggerFactory iLoggerFactory = getILoggerFactory(); performInitialization(); bind(); staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); #这时会找到org/slf4j/impl/StaticLoggerBinder.class StaticLoggerBinder.getSingleton(); SINGLETON.init ();new ContextInitializer(defaultLoggerContext).autoConfig(); # 看到autoConfig了应该就到目标了。 URL url = findURLOfDefaultConfigurationFile(true );# 看系统参数logback.configurationFile 是否有设置 URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);# logback.groovy 配置文件 url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);# logback-test.xml 配置文件 url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);# logback.xml 配置文件 return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus); 最后url 是null 时说明没有配置文件 BasicConfigurator basicConfigurator = new BasicConfigurator(); basicConfigurator.setContext(loggerContext); basicConfigurator.configure(loggerContext);#进入configure方法 这里配置了控制台输出的Appender、Encoder、Layout
这时就有Appender、Encoder、Layout要知道是啥
Appender是输出到哪去。
Encoder是输出的编码。
Layout是打印日志的格式。
Logger上下文 1 2 3 contextSelectorBinder.getContextSelector () return contextSelectorBinder.getContextSelector ().getLoggerContext (); ch.qos .logback .classic .LoggerContext
Logger 1 2 3 4 5 return iLoggerFactory.getLogger(name); LoggerContext类 childLogger = logger.createChildByName(childName); childLogger = new Logger(childName, this , this .loggerContext); loggerCache.put(childName, childLogger);
logger的结构为: 比如有一个com.test.a的类,这时会生成如下logger的父子结构
1 2 3 4 root com com .test com .test.a
打印日志源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 logger.debug("Hello world." ); filterAndLog_0_Or3Plus(FQCN, null , Level.DEBUG, msg, null , null ); buildLoggingEventAndAppend(localFQCN, marker, level, msg, params , t); LoggingEvent le = new LoggingEvent(localFQCN, this , level, msg, t, params ); callAppenders(le); writes += l.appendLoopOnAppenders(event );return aai.appendLoopOnAppenders(event ); final Appender<E>[] appenderArray = appenderList.asTypedArray(); appenderArray[i].doAppend(e);# 这时会调用具体的Appender的实现类 this .append(eventObject); UnsynchronizedAppenderBase.doAppend(E eventObject) this .append(eventObject); OutputStreamAppender.subAppend(E event )lock .lock ();try { writeOut(event ); } finally { lock .unlock(); }this .encoder.doEncode(event ); String txt = layout.doLayout(event ); outputStream.write(convertToBytes(txt));if (immediateFlush) outputStream.flush(); System.out .write(b);
这时发现consoleAppender使用ReentrantLock, system.out.write。
结构 Logger
1 ch.qos .logback .classic .Logger
Appender 在root的logger中AppenderAttachableImpl aai
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Appender (ch . qos. logback. core) UnsynchronizedAppenderBase (ch . qos. logback. core) AsyncAppenderBase (ch . qos. logback. core) #异步写日志 AsyncAppender (ch . qos. logback. classic) OutputStreamAppender (ch . qos. logback. core) # 同步写控制台 ConsoleAppender (ch . qos. logback. core) # 同步写文件 FileAppender (ch . qos. logback. core) # 滚动同步写文件 RollingFileAppender (ch . qos. logback. core. rolling) DBAppenderBase (ch . qos. logback. core. db ) DBAppender (ch . qos. logback. classic. db ) AppenderBase (ch . qos. logback. core) NOPAppender (ch . qos. logback. core. helpers) AbstractServerSocketAppender (ch . qos. logback. core. net. server) SSLServerSocketAppenderBase (ch . qos. logback. core. net. server) SSLServerSocketAppender (ch . qos. logback. classic. net. server) ServerSocketAppender (ch . qos. logback. classic. net. server) JMSAppenderBase (ch . qos. logback. core. net) JMSTopicAppender (ch . qos. logback. classic. net) JMSQueueAppender (ch . qos. logback. classic. net) SyslogAppenderBase (ch . qos. logback. core. net) SyslogAppender (ch . qos. logback. classic. net) SMTPAppenderBase (ch . qos. logback. core. net) SMTPAppender (ch . qos. logback. classic. net) SiftingAppenderBase (ch . qos. logback. core. sift) SiftingAppender (ch . qos. logback. classic. sift) CyclicBufferAppender (ch . qos. logback. core. read) ListAppender (ch . qos. logback. core. read) AbstractSocketAppender (ch . qos. logback. core. net) AbstractSSLSocketAppender (ch . qos. logback. core. net) SSLSocketAppender (ch . qos. logback. classic. net) SocketAppender (ch . qos. logback. classic. net)
常用的 ConsoleAppender、FileAppender、RollingFileAppender、AsyncAppender
Encoder
1 2 3 4 5 6 7 8 9 10 Encoder (ch.qos.logback.core.encoder) EncoderBase (ch.qos.logback.core.encoder) # 输出不变只加换行符 EchoEncoder (ch.qos.logback.core.encoder) # 布局包装 LayoutWrappingEncoder (ch.qos.logback.core.encoder) PatternLayoutEncoderBase (ch.qos.logback.core.pattern) # 图案布局 PatternLayoutEncoder (ch.qos.logback.classic.encoder) ObjectStreamEncoder (ch.qos.logback.core.encoder)
一般使用LayoutWrappingEncoder、PatternLayoutEncoder
Layout
1 2 3 4 5 6 7 8 9 10 11 12 13 Layout (ch.qos.logback.core) LayoutBase (ch.qos.logback.core) # xml布局 XMLLayout (ch.qos.logback.classic.log4j) # 固定格式的布局 %d{HH:mm:ss.SSS} [%thread] %-5 level %logger{36 } - %msg%n TTLLLayout (ch.qos.logback.classic.layout) HTMLLayoutBase (ch.qos.logback.core.html) # html 布局 HTMLLayout (ch.qos.logback.classic.html) EchoLayout (ch.qos.logback.core.layout) PatternLayoutBase (ch.qos.logback.core.pattern) # 使用模式字符串配置的灵活布局。 PatternLayout (ch.qos.logback.classic)
格式 1 2 3 4 5 6 7 8 <configuration > 多个appender <appender > </appender > 多个logger <logger > </logger > 一个root <root > </root > </configuration >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <configuration > <property name ="log.path" value ="/Users/qichuanhan/logs" /> <property name ="log.pattern" value ="%d{HH:mm:ss.SSS} -[%X{traceId}]- [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> <appender name ="console" class ="ch.qos.logback.core.ConsoleAppender" > <encoder > <pattern > ${log.pattern}</pattern > </encoder > </appender > <appender name ="file_info" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <file > ${log.path}/my-info.log</file > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <fileNamePattern > ${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern > <maxHistory > 60</maxHistory > </rollingPolicy > <encoder > <pattern > ${log.pattern}</pattern > </encoder > <filter class ="ch.qos.logback.classic.filter.LevelFilter" > <level > INFO</level > <onMatch > ACCEPT</onMatch > <onMismatch > DENY</onMismatch > </filter > </appender > <appender name ="file_error" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <file > ${log.path}/my-error.log</file > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <fileNamePattern > ${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern > <maxHistory > 60</maxHistory > </rollingPolicy > <encoder > <pattern > ${log.pattern}</pattern > </encoder > <filter class ="ch.qos.logback.classic.filter.LevelFilter" > <level > ERROR</level > <onMatch > ACCEPT</onMatch > <onMismatch > DENY</onMismatch > </filter > </appender > <root level ="info" > <appender-ref ref ="console" /> <appender-ref ref ="file_info" /> <appender-ref ref ="file_error" /> </root > </configuration >
AsyncAppender 消费者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Worker extends Thread { public void run () { AsyncAppenderBase<E> parent = AsyncAppenderBase.this ; AppenderAttachableImpl<E> aai = parent.aai; while (parent.isStarted()) { try { E e = parent.blockingQueue.take(); aai.appendLoopOnAppenders(e); } catch (InterruptedException ie) { break ; } } addInfo("Worker thread will flush remaining events before exiting. " ); for (E e : parent.blockingQueue) { aai.appendLoopOnAppenders(e); parent.blockingQueue.remove(e); } aai.detachAndStopAllAppenders(); } }
1 2 BlockingQueue<E> blockingQueue;public static final int DEFAULT_QUEUE_SIZE = 256 ;
生产者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 UnsynchronizedAppenderBase.doAppend(E eventObject);this .append(eventObject);@Override protected void append (E eventObject) { if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) { return ; } preprocess(eventObject); put(eventObject); }private void put (E eventObject) { if (neverBlock) { blockingQueue.offer(eventObject); } else { try { blockingQueue.put(eventObject); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }