日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

log4j 2.x 架构(源码)

發(fā)布時間:2024/4/13 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 log4j 2.x 架构(源码) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

1.概述

1.1.組件概覽

1.2.靈活的配置

1.2.1.插件發(fā)現(xiàn)機制

1.2.2.插件裝配機制

1.2.3.配置文件基本元素與對象的映射關(guān)系

事件級別

2.屬性占位符

2.1.概述

2.2.Interpolator插值器

2.3.默認屬性配置

3.Logger

3.1.配置示例

3.1.1寫日志邏輯

3.1.2 Additive

3.2.配置詳解

3.3.Logger繼承機制

3.4構(gòu)建LoggerConfig樹

4.Appender

4.1.概述

4.2.框架支持的Appender實現(xiàn)

4.3.常用Appender詳解

4.3.1.ConsoleAppender

4.3.2.RollingFileAppender

5.Layout

5.1.概述

5.2.PatternLayout

5.2.1.模式字符串

6.Manager

7.Filter

?AbstractFilterBuilder

BurstFilter

DynamicThresholdFilter

MapFilter

LevelRangeFilter

MarkerFilter

RegexFilter

?ThresholdFilter

TimeFilter

ScriptFilter

CompositeFilter

StructuredDataFilter

ThreadContextMapFilter



1.概述

1.1.組件概覽

在log4j2中,LogManager就是日志的門面,相當于slf4j-api中的LoggerFactory.
框架為每個類加載分配了一個單獨的LoggerContext,用于管理所有創(chuàng)建出來的Logger實例.
ContextSelector則負責管理類加載器到對應的LoggerContext實例之間的映射關(guān)系.
log4j2中,有5個關(guān)鍵概念:

  • LoggerConfig:日志配置,用于整合多個Appender,進行日志打印.
  • Appender:追加器,用于操作Layout和Manager,往單一目的地進行日志打印.
  • Layout:布局,用于把LogEvent日志事件序列化成字節(jié)序列,不同Layout實現(xiàn)具有不同的序列化方式.
  • Manager:管理器,用于管理輸出目的地,如:RollingFileManager用于管理文件滾動以及將字節(jié)序列寫入到指定文件中.
  • Filter:過濾器,用于對LogEvent日志事件加以過濾,LoggerConfig和Appender都可以配置過濾器,也就是說日志事件會經(jīng)過一總一分兩層過濾.

組件架構(gòu)如下:

?

組件架構(gòu)

1.2.靈活的配置

1.2.1.插件發(fā)現(xiàn)機制

在log4j2中,一切皆插件,框架通過PluginRegistry掃描并發(fā)現(xiàn)插件配置.

PluginRegistry支持兩種掃描方式

  • 一種是使用指定的ClassLoader讀取classpath下所有的META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat文件,產(chǎn)生PluginType;
  • 另一種是掃描classpath下指定的packageName,內(nèi)省帶有@Plugin注解的類文件,產(chǎn)生PluginType.

插件配置以PluginType的形式保存在插件注冊表中,PluginType的作用類似于spring中BeanDefinition,定義了如何創(chuàng)建插件實例.
插件類通過@PluginFactory注解或者@PluginBuilderFactory注解配置插件實例的實例化和屬性注入方式.

1.2.2.插件裝配機制

log4j2知道如何實例化插件后,我們就可以通過編寫配置文件(如:log4j2.xml),進行插件的實例化和屬性注入了.
Configuration全局配置對象負責保存所有解析到的配置.
通過ConfigurationFactory.getConfiguration()可以使用不同的工廠生產(chǎn)不同的配置對象,不同的Configuration實現(xiàn)可以解析不同格式的配置,如:xml,yaml,json等.

以xml文件為例,文件中每個元素都會最終對應一個插件實例,元素名稱實際就是PluginType中的name,實例的屬性可以從子元素對應的實例獲取,也可以從自身元素的屬性配置獲取.

因此,xml中dom樹的元素嵌套關(guān)系,也就是log4j組件實例的引用嵌套關(guān)系.

xml,yaml,json格式文件都可以描述這種嵌套關(guān)系,因此log4j2中定義了與文件格式無關(guān)的數(shù)據(jù)結(jié)構(gòu),Node來抽象配置.

AbstractConfiguration.setup()負責提取配置,形成Node樹.
AbstractConfiguration.doConfigure()負責根據(jù)Node樹,進行插件實例化和屬性注入.

1.2.3.配置文件基本元素與對象的映射關(guān)系

序號xml元素工廠方法(類名.方法名)對象類型
1<Properties>PropertiesPlugin.configureSubstitutor()StrLookup
2<Property>Property.createProperty()Property
3<Loggers>LoggersPlugin.createLoggers()Loggers
4<Logger>LoggerConfig.createLogger()LoggerConfig
5<Root>RootLogger.createLogger()LoggerConfig
6<AppenderRef>AppenderRef.createAppenderRef()AppenderRef
7<Filters>CompositeFilter.createFilters()CompositeFilter
8<Appenders>AppendersPlugin.createAppenders()ConcurrentMap<String, Appender>

?

事件級別

public enum StandardLevel {OFF(0),FATAL(100),ERROR(200),WARN(300),INFO(400),DEBUG(500),TRACE(600),ALL(Integer.MAX_VALUE); }public final class Level {public boolean isMoreSpecificThan(final Level level) {return this.intLevel <= level.intLevel;}public boolean isInRange(final Level minLevel, final Level maxLevel) {return this.intLevel >= minLevel.intLevel && this.intLevel <= maxLevel.intLevel;}}

2.屬性占位符

2.1.概述

在log4j2中,環(huán)境變量信息(鍵值對)被封裝為StrLookup對象,該對象作用類似于spring框架中的PropertySource.

在配置文件中,基本上所有的值的配置都可以通過參數(shù)占位符引用環(huán)境變量信息,格式為:${prefix:key}.

2.2.Interpolator插值器

Interpolator內(nèi)部以Map<String,StrLookup>的方式,封裝了很多StrLookuo對象,key則對應參數(shù)占位符${prefix:key}中的prefix.

同時,Interpolator內(nèi)部還保存著一個沒有prefix的StrLookup實例,被稱作默認查找器,它的鍵值對數(shù)據(jù)來自于log4j2.xml配置文件中的<Properties>元素的配置.

當參數(shù)占位符${prefix:key}帶有prefix前綴時,Interpolator會從指定prefix對應的StrLookup實例中進行key查詢,

當參數(shù)占位符${key}沒有prefix時,Interpolator則會從默認查找器中進行查詢.

Interpolator中默認支持的StrLookup查找方式如下(StrLookup查找器實現(xiàn)類均在org.apache.logging.log4j.core.lookup包下):

序號prefix插件類型描述
1(*)sysSystemPropertiesLookup從jvm屬性中查找
2(*)envEnvironmentLookup從操作系統(tǒng)環(huán)境變量中獲取value
3markerMarkerLookup判斷以指定key為名稱的Marker標簽是否存在,存在則返回key,否則返回null
4jvmrunargsJmxRuntimeInputArgumentsLookup獲取jmx的運行時輸入?yún)?shù)
5bundleResourceBundleLookup通過ResourceBundle查找value,格式:${prefix:bundleName:bundleKey}
6javaJavaLookup獲取jvm進程信息,只指定固定的key值,包括:(1)version:jdk版本(2)runtime:運行環(huán)境信息(3)vm:虛擬機名稱(4)os:操作系統(tǒng)信息(5)hw:硬件信息(6)locale:地區(qū)信息
7mainMainMapLookup暫未啟用
8log4jLog4jLookup只支持兩個key:(1)configLocation:獲取log4j2配置文件的絕對路徑(2)configParentLocation:獲取配置文件所在目錄的絕對路徑
9dateDateLookup以指定格式,獲取當前系統(tǒng)時間或LogEvent的時間戳,通過key來指定日期格式字符串
10sdStructuredDataLookup從LogEvent中引用的StructuredDataMessage中獲取value
11ctxContextMapLookup從ThreadContext中獲取value
12mapMapLookup暫未啟用
13jndiJndiLookup使用jndi(javax.naming)獲取value

2.3.默認屬性配置

注意:Properties元素一定要配置在最前面,否則不生效.

<?xml version="1.0" encoding="UTF-8"?> <Configuration><Properties><Property name="customKey_1">customValue_1</Property><Property name="customKey_2">customValue_2</Property></Properties> </Configuration>

3.Logger

?

3.1.配置示例

<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn" dest="err" verbose="false"><Appenders><Console name="console"><PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %C.%M %message%n"/></Console></Appenders><Loggers><Root additivity="true" level="error" includeLocation="true" ><AppenderRef ref="console" level="info"><ThresholdFilter level="warn" onMatch="NEUTRAL" onMismatch="DENY"/></AppenderRef><Property name="customeKey">customeValue</Property><ThresholdFilter level="warn" onMatch="NEUTRAL" onMismatch="DENY"/></Root><Logger name="com.lixin" additivity="true" level="info" includeLocation="true"><AppenderRef ref="console" level="info"><ThresholdFilter level="warn" onMatch="NEUTRAL" onMismatch="DENY"/></AppenderRef><Property name="customeKey">customeValue</Property><ThresholdFilter level="warn" onMatch="NEUTRAL" onMismatch="DENY"/></Logger></Loggers></Configuration>

?3.1.1寫日志邏輯

//AbstractLogger public void debug(final Marker marker, final MessageSupplier msgSupplier, final Throwable t) {logIfEnabled(FQCN, Level.DEBUG, marker, msgSupplier, t);}//AbstractLoggerpublic void logIfEnabled(final String fqcn, final Level level, final Marker marker,final MessageSupplier msgSupplier, final Throwable t) {if (isEnabled(level, marker, msgSupplier, t)) {logMessage(fqcn, level, marker, msgSupplier, t);}} //具體 Loggerpublic boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {return privateConfig.filter(level, marker, message, params);} //privateConfigboolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {final Filter filter = config.getFilter();if (filter != null) {final Filter.Result r = filter.filter(logger, level, marker, msg, p1);if (r != Filter.Result.NEUTRAL) {return r == Filter.Result.ACCEPT;}}return level != null && intLevel >= level.intLevel();}//AbstractLoggerprotected void logMessage(final String fqcn, final Level level, final Marker marker, final String message,final Throwable t) {logMessage(fqcn, level, marker, messageFactory.newMessage(message), t);}//Loggerpublic void logMessage(final String fqcn, final Level level, final Marker marker, final Message message,final Throwable t) {final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();strategy.log(this, getName(), fqcn, marker, level, msg, t);}//具體 DefaultReliabilityStrategypublic void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn, final Marker marker, final Level level,final Message data, final Throwable t) {loggerConfig.log(loggerName, fqcn, marker, level, data, t);}//LoggerConfigprivate void processLogEvent(final LogEvent event, LoggerConfigPredicate predicate) {event.setIncludeLocation(isIncludeLocation());if (predicate.allow(this)) {callAppenders(event);}logParent(event, predicate);} //LoggerConfigprotected void callAppenders(final LogEvent event) {final AppenderControl[] controls = appenders.get();//noinspection ForLoopReplaceableByForEachfor (int i = 0; i < controls.length; i++) {controls[i].callAppender(event);}}//AppenderControlpublic void callAppender(final LogEvent event) {if (shouldSkip(event)) {return;}callAppenderPreventRecursion(event);}//AppenderControlprivate void callAppenderPreventRecursion(final LogEvent event) {try {recursive.set(this);callAppender0(event);} finally {recursive.set(null);}}//AppenderControlprivate void callAppender0(final LogEvent event) {ensureAppenderStarted();if (!isFilteredByAppender(event)) {tryCallAppender(event);}}//AppenderControlprivate void tryCallAppender(final LogEvent event) {try {appender.append(event);} catch (final RuntimeException ex) {handleAppenderError(ex);} catch (final Exception ex) {handleAppenderError(new AppenderLoggingException(ex));}}//Appendervoid append(LogEvent event);//AbstractOutputStreamAppender@Overridepublic void append(final LogEvent event) {try {tryAppend(event);} catch (final AppenderLoggingException ex) {error("Unable to write to stream " + manager.getName() + " for appender " + getName() + ": " + ex);throw ex;}}private void tryAppend(final LogEvent event) {if (Constants.ENABLE_DIRECT_ENCODERS) {directEncodeEvent(event);} else {writeByteArrayToManager(event);}}protected void directEncodeEvent(final LogEvent event) { // getLayout().encode(event, manager);getLayout().encode(event, manager);if (this.immediateFlush || event.isEndOfBatch()) {manager.flush();}}//PatternLayoutpublic void encode(final LogEvent event, final ByteBufferDestination destination) {if (!(eventSerializer instanceof Serializer2)) {super.encode(event, destination);return;}final StringBuilder text = toText((Serializer2) eventSerializer, event, getStringBuilder());final Encoder<StringBuilder> encoder = getStringBuilderEncoder();encoder.encode(text, destination);trimToMaxSize(text);}//PatternLayoutprivate StringBuilder toText(final Serializer2 serializer, final LogEvent event,final StringBuilder destination) {return serializer.toSerializable(event, destination);}protected void writeByteArrayToManager(final LogEvent event) { //getLayout().toByteArray(event),layout格式化數(shù)據(jù)final byte[] bytes = getLayout().toByteArray(event);if (bytes != null && bytes.length > 0) {manager.write(bytes, this.immediateFlush || event.isEndOfBatch());}}//OutputStreamManagerprotected void write(final byte[] bytes, final boolean immediateFlush) {write(bytes, 0, bytes.length, immediateFlush);}protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) {if (immediateFlush && byteBuffer.position() == 0) {writeToDestination(bytes, offset, length);flushDestination();return;}if (length >= byteBuffer.capacity()) {// if request length exceeds buffer capacity, flush the buffer and write the data directlyflush();writeToDestination(bytes, offset, length);} else {if (length > byteBuffer.remaining()) {flush();}byteBuffer.put(bytes, offset, length);}if (immediateFlush) {flush();}}protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {try {getOutputStream().write(bytes, offset, length);} catch (final IOException ex) {throw new AppenderLoggingException("Error writing to stream " + getName(), ex);}}

3.1.2 Additive

private void processLogEvent(final LogEvent event, LoggerConfigPredicate predicate) {event.setIncludeLocation(isIncludeLocation());if (predicate.allow(this)) {callAppenders(event);}logParent(event, predicate);}private void logParent(final LogEvent event, final LoggerConfigPredicate predicate) {if (additive && parent != null) {parent.log(event, predicate);}}

3.2.配置詳解

  • additivity:日志可加性,如果配置為true,則在日志打印時,會通過Logger繼承關(guān)系遞歸調(diào)用父Logger引用的Appender進行日志打印.
    注意:該屬性默認為true.在遞歸打印日志時,會忽略父Logger的level配置
  • level:用于控制允許打印的日志級別上線,在配置示例中,只有級別<=info的LogEvent才會被放行,級別優(yōu)先級順序為OFF<FATAL<ERROR<WARN<INFO<DEBUG<TRACE<ALL
    注意:level屬性的配置時可選的,在獲取level時會通過Logger繼承關(guān)系遞歸獲取,RootLogger的級別默認為error,其他默認為null.也就是說,如果全都不配置level的話,則所有Logger級別都默認為error.
  • includeLocation:如果配置為true,則打印日志時可以附帶日志點源碼位置信息輸出.同步日志上下文默認為true,異步默認為false.
  • LoggerConfig元素下可以單獨配置Property元素,添加屬性鍵值對,這些屬性會在每次打印日志時,被追加到LogEvent的contextData中
  • LoggerConfig支持配置過濾器,在判斷是否打印日志時,先過濾器判斷過濾,然后再級別判斷過濾.
  • AppenderRef:顧名思義,就是配置當前Logger引用的Appender.同時,AppenderRef也支持配置level和Filter,進行更細粒度的日志過濾
  • LoggerConfig等于總開關(guān),AppenderRef則為各個子開關(guān),兩個開關(guān)都通過才能打印日志

3.3.Logger繼承機制

log4j2框架會根據(jù)LoggerConfig的name建立對象之間的繼承關(guān)系.這種繼承機制與java的package很像,name以點進行名稱空間分割,子名稱空間繼承父名稱空間.
名稱空間可以是全限定類名,也可以是報名.整個配置樹的根節(jié)點就是RootLogger.
舉例:假如我們的配置的Logger如下:

<Root/> <Logger name="com"/> <Logger name="com.lixin.DemoClass"/> <Logger name="org"/> <Logger name="org.springframework"/>

配置樹

當通過LogManager.getLogger(name)獲取Logger實例時,會根據(jù)name逐級遞歸直到找到匹配的LoggerConfig,或者遞歸到Root根節(jié)點為止.

3.4構(gòu)建LoggerConfig樹

//AbstractConfiguration,在初始化配置時設(shè)置層次。//通過key查找,如果未找到自己的,往上找祖先。public LoggerConfig getLoggerConfig(final String loggerName) {LoggerConfig loggerConfig = loggerConfigs.get(loggerName);if (loggerConfig != null) {return loggerConfig;}String substr = loggerName;while ((substr = NameUtil.getSubName(substr)) != null) {loggerConfig = loggerConfigs.get(substr);if (loggerConfig != null) {return loggerConfig;}}return root;}public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, final Filter filter) {final String loggerName = logger.getName();final LoggerConfig lc = getLoggerConfig(loggerName);if (lc.getName().equals(loggerName)) {//如果是自己的配置lc.addFilter(filter);} else {//不是自己的配置,則新創(chuàng)建一個配置,設(shè)置parentfinal LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive());nlc.addFilter(filter);nlc.setParent(lc);loggerConfigs.putIfAbsent(loggerName, nlc);setParents();logger.getContext().updateLoggers();}}public synchronized void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger,final Appender appender) {final String loggerName = logger.getName();appenders.putIfAbsent(appender.getName(), appender);final LoggerConfig lc = getLoggerConfig(loggerName);if (lc.getName().equals(loggerName)) {lc.addAppender(appender, null, null);} else {final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive());nlc.addAppender(appender, null, null);nlc.setParent(lc);loggerConfigs.putIfAbsent(loggerName, nlc);setParents();logger.getContext().updateLoggers();}}public synchronized void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger, final boolean additive) {final String loggerName = logger.getName();final LoggerConfig lc = getLoggerConfig(loggerName);if (lc.getName().equals(loggerName)) {lc.setAdditive(additive);} else {final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), additive);nlc.setParent(lc);loggerConfigs.putIfAbsent(loggerName, nlc);setParents();logger.getContext().updateLoggers();}}private void setParents() {for (final Map.Entry<String, LoggerConfig> entry : loggerConfigs.entrySet()) {final LoggerConfig logger = entry.getValue();String key = entry.getKey();if (!key.isEmpty()) {//根據(jù)點號(.)按層次分別設(shè)置parentfinal int i = key.lastIndexOf('.');if (i > 0) {key = key.substring(0, i);LoggerConfig parent = getLoggerConfig(key);if (parent == null) {parent = root;}logger.setParent(parent);} else {logger.setParent(root);}}}}

4.Appender

4.1.概述

追加器,負責控制Layout進行LogEvent的序列化,以及控制Manager對序列化后的字節(jié)序列進行輸出.

在log4j2.xml配置文件中,配置方式如下:

<Appenders><具體的Appender插件名稱></具體的Appender插件名稱> </Appenders>

4.2.框架支持的Appender實現(xiàn)

序號工廠方法xml元素具體類作用
1NullAppender.createAppender<Null>NullAppender?
2ConsoleAppender.newBuilder<Console>ConsoleAppender控制臺輸出
3FileAppender.newBuilder<File>FileAppender往一個固定文件,流式追加日志
5RollingFileAppender.newBuilder<RollingFile>RollingFileAppender日志文件可滾動,滾動策略可配置,可按時間,文件大小等方式.流式追加日志
6AsyncAppender.newBuilder<Async>AsyncAppender內(nèi)部引用一組appender,通過異步線程+隊列方式調(diào)用這些appender
7RollingRandomAccessFileAppender.newBuilder<RollingRandomAccessFile>RollingRandomAccessFileAppender日志文件可滾動,使用RandomAccessFile追加日志
8RandomAccessFileAppender.newBuilder<RandomAccessFile>RandomAccessFileAppender往一個固定文件,使用RandomAccessFile追加日志
8OutputStreamAppender.newBuilder<OutputStream>OutputStreamAppender?
9MemoryMappedFileAppender.newBuilder<MemoryMappedFile>MemoryMappedFileAppender比RandomAccessFile性能高
10JdbcAppender.newBuilder<JDBC>JdbcAppender?
11JpaAppender.createAppender<JPA>JpaAppender?
12JeroMqAppender.createAppender<JeroMQ>JeroMqAppender?
13KafkaAppender.newBuilder<Kafka>KafkaAppender?
14JmsAppender.newBuilder<JMS>
<JMSQueue>
<JMSTopic>
JmsAppender?
15Rewrite.createAppender<Rewrite>RewriteAppender?
16RoutingAppender.newBuilder<Routing>RoutingAppender路由追加器
可根據(jù)pattern模式字符串,路由到內(nèi)部管理的其他Appender實例,
支持LogEvent事件重寫和Appender定期清理
17CountingNoOpAppender.createAppender<CountingNoOp>CountingNoOpAppender?
18FailoverAppender.createAppender<Failover>FailoverAppender?
19ScriptAppenderSelector<ScriptAppenderSelector>??
20SmtpAppender.createAppender<SMTP>SmtpAppender?
21SocketAppender.newBuilder<Socket>SocketAppender?
22SyslogAppender.newBuilder<Syslog>SyslogAppender?
23WriterAppender.newBuilder<Writer>WriterAppender?

4.3.常用Appender詳解

4.3.1.ConsoleAppender

控制臺追加器,用于把日志輸出到控制臺,一般本地調(diào)試時使用.
配置示例如下:

<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn" dest="err" verbose="false"><Appenders><!-- follow和direct不能同時為true,如果follow為true則會跟隨底層輸出流的變化,direct為true則固定指向輸出流 --><Console name="console" target="SYSTEM_OUT" follow="false" direct="true"><PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %C.%M %message%n"/></Console></Appenders><Loggers><Root additivity="true" level="error" includeLocation="true" ><AppenderRef ref="console" level="info"/></Root></Loggers></Configuration>

4.3.2.RollingFileAppender

文件滾動追加器,用于向本地磁盤文件中追加日志,同時可以通過觸發(fā)策略(TriggeringPolicy)和滾動策略(RolloverStrategy)控制日志文件的分片,避免日志文件過大.
線上環(huán)境常用.

常用的觸發(fā)策略包含兩種:

  • TimeBasedTriggeringPolicy:基于時間周期性觸發(fā)滾動,一般按天滾動
  • SizeBasedTriggeringPolicy:基于文件大小觸發(fā)滾動,可以控制單個日志文件的大小上限

滾動策略的實現(xiàn)包含兩種:

  • DefaultRolloverStrategy:默認滾動策略
    該策略內(nèi)部維護一個最小索引和最大索引,每次滾動時,會刪除歷史文件,之后剩余文件全部進行一輪重命名,最后創(chuàng)建新的不帶有索引后綴的文件進行日志追加

    ?

    默認策略

  • DirectWriteRolloverStrategy:直接寫滾動策略
    該策略內(nèi)部會維護一個一直自增的文件索引,每次滾動時直接創(chuàng)建新的帶有索引后綴的文件進行日志追加,同步清理歷史的文件.

    ?

    直接寫策略

配置示例如下:

<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn" dest="err" verbose="false"><Properties><Property name="logDir">/Users/lixin46/workspace/demo/logdemo/logs</Property><Property name="pattern">%date{yyyy-MM-dd HH:mm:ss.SSS} %C.%M %message%n</Property></Properties><Appenders><Console name="console" ><PatternLayout pattern="${pattern}"/></Console><!-- 使用DirectWriteRolloverStrategy策略時,不需要配置fileName --><RollingFile name="fileAppender" fileName="${logDir}/request.log" filePattern="${logDir}/request.log-%d-%i"><PatternLayout pattern="${pattern}"/><!-- 所有策略中,只要任意策略滿足就會觸發(fā)滾動 --><Policies><!-- 滾動時間周期,只有數(shù)量,單位取決于filePattern中%d的配置 --><TimeBasedTriggeringPolicy interval="1"/><SizeBasedTriggeringPolicy size="10b"/></Policies><!-- 限制最多保留5個文件,索引自增 --><!--<DirectWriteRolloverStrategy maxFiles="5"/>--><!-- 限制最多保留5個文件,索引從2到6 --><DefaultRolloverStrategy fileIndex="max" min="2" max="6"/></RollingFile></Appenders><Loggers><Root level="info"><AppenderRef ref="console" level="info"/><AppenderRef ref="fileAppender" level="info" /></Root></Loggers></Configuration>

5.Layout

5.1.概述

布局對象,職責是把指定的LogEvent轉(zhuǎn)換成可序列化對象(如:String),或者直接序列化成字節(jié)數(shù)組.

log4j2支持很多的序列化格式,如:普通模式字符串,JSON字符串,yaml字符串,XML格式字符串,HTML字符串等等.

類體系如下:

?

layout類體系

5.2.PatternLayout

模式布局是我們最常使用的,它通過PatternProcessor模式解析器,對模式字符串進行解析,得到一個List<PatternConverter>轉(zhuǎn)換器列表和List<FormattingInfo>格式信息列表.

在PatternLayout序列化時,會遍歷每個PatternConverter,從LogEvent中取不同的值進行序列化輸出.

5.2.1.模式字符串

模式字符串由3部分組成,格式為:%(格式信息)(轉(zhuǎn)換器名稱){選項1}{選項2}...

  • 格式信息
    數(shù)據(jù)結(jié)構(gòu)如下:
public final class FormattingInfo {// 默認配置,右對齊,長度不限,左側(cè)截斷private static final FormattingInfo DEFAULT = new FormattingInfo(false, 0, Integer.MAX_VALUE, true);// 字段最小長度private final int minLength;// 字段最大長度private final int maxLength;// 是否左對齊,默認為false,長度過短時,左側(cè)填充空白private final boolean leftAlign;// 是否左側(cè)截斷,默認為true,長度過長時,刪除左側(cè)內(nèi)容private final boolean leftTruncate; }

模式字符串的格式為:
%-(minLength).-(maxLength)(轉(zhuǎn)換器名稱){選項字符串}
minLength代表字段的最小長度限制,當字段內(nèi)容長度小于最小限制時,會進行空格填充.
minLength前面的-負責控制對齊方式,默認為右對齊(左邊空格填充),如果加上-,則會切換為左對齊方式(右邊空格填充)
maxLength代表字段的最大長度限制,當字段內(nèi)容長度大于最大限制時,會進行內(nèi)容階段
maxLength前面的-負責控制階段方向,默認為左側(cè)階段,如果加上-,則會切換為右側(cè)階段
minLength和maxLength之間用點分隔.
格式信息中所有屬性都是可選的,不配置,則使用默認值

  • 轉(zhuǎn)換器名稱

log4j2會通過PluginManager收集所有類別為Converter的插件,同時分析插件類上的@ConverterKeys注解,獲取轉(zhuǎn)換器名稱,并建立名稱到插件實例的映射關(guān)系.
PatternParser識別到轉(zhuǎn)換器名稱的時候,會查找映射.

框架支持的所有轉(zhuǎn)換器如下:

序號名稱類型描述
1d
date
DatePatternConverter日志的時間戳
2p
level
LevelPatternConverter日志級別
3m
msg
message
MessagePatternConverter日志中的消息內(nèi)容
4C
class
ClassNamePatternConverter日志打印點所在類的類名
注意:需要給LoggerincludeLocation="true"屬性開啟位置
5M
method
MethodLocationPatternConverter日志打印點所在方法的方法名
注意:需要給LoggerincludeLocation="true"屬性開啟位置
6c
logger
LoggerPatternConverterLogger實例的名稱
7nLineSeparatorPatternConverter專門追加換行符
8propertiesLog4j1MdcPatternConverter?
9ndcLog4j1NdcPatternConverter?
10enc
encode
EncodingPatternConverter?
11equalsIgnoreCaseEqualsIgnoreCaseReplacementConverter?
12equalsEqualsReplacementConverter?
13xEx
xThroable
xException
ExtendedThrowablePatternConverter?
14F
file
FileLocationPatternConverter注意:需要給LoggerincludeLocation="true"屬性開啟位置
15l
location
FullLocationPatternConverter相當于%C.%M(%F:%L)
注意:需要給LoggerincludeLocation="true"屬性開啟位置
16highlightHighlightConverter?
17L
line
LineLocationPatternConverter日志打印點的代碼行數(shù)
注意:需要給LoggerincludeLocation="true"屬性開啟位置
18K
map
MAP
MapPatternConverter?
19markerMarkerPatternConverter打印完整標記,格式如:標記名[父標記名[祖父標記名]],一個標記可以有多個父標記
20markerSimpleNameMarkerSimpleNamePatternConverter只打印標記的名稱
21maxLength
maxLen
MaxLengthConverter?
22X
mdc
MDC
MdcPatternConverterLogEvent.getContextData()映射診斷上下文
23N
nano
NanoTimePatternConverter?
24x
NDC
NdcPatternConverterLogEvent.getContextStack()嵌套診斷上下文
25replaceRegexReplacementConverter?
26r
relative
RelativeTimePatternConverter?
27rEx
rThrowable
rException
RootThrowablePatternConverter?
28styleStyleConverter?
29T
tid
threadId
ThreadIdPatternConverter線程id
30t
tn
thread
threadName
ThreadNamePatternConverter線程名稱
31tp
threadPriority
ThreadPriorityPatternConverter線程優(yōu)先級
32ex
throwable
Exception
ThrowablePatternConverter異常
33u
uuid
UuidPatternConverter生成一個uuid,隨日志一起打印,用于唯一標識一條日志
34notEmpty
varsNotEmpty
variablesNotEmpty
VariablesNotEmptyReplacementConverter?
  • 選項字符串

有時我們需要對特定的轉(zhuǎn)換器進行特殊的配置,如:給DatePatternConverter配置時間格式,這個時候需要通過選項字符串配置.
PatternParser會提取模式字符串中的所有選項,保存在一個List<String>中,每個{}包裹的內(nèi)容作為一個選項.
當創(chuàng)建轉(zhuǎn)換器時,框架會自動掃描轉(zhuǎn)換器類中聲明的靜態(tài)工廠方法newInstance,同時支持兩種可選的形參,一種是Configuration,另一種String[]則會注入選項列表.
選項列表的識別由不同的轉(zhuǎn)換器各自定義.

最后,以一個實際的例子解釋配置:
日志會輸出時間,類名,方法名,消息以及一個換行符.
同時,我們給DatePatternConverter指定了了時間格式,并且限制全限定類名最小長度為5,右截斷,最大為10,左對齊.

<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %-5.-10C.%M %message%n"/>

6.Manager

管理器的職責主要是控制目標輸出流,以及把保存在ByteBuffer字節(jié)緩沖區(qū)中的日志序列化結(jié)果,輸出到目標流中.
如:RollingFileManager需要在每次追加日志之前,進行滾動檢查,如果觸發(fā)滾動還會創(chuàng)建新的文件輸出流.
manager繼承體系如下:

?

manager繼承體系

7.Filter

過濾器的核心職責就是對LogEvent日志事件進行匹配,匹配結(jié)果分為匹配和不匹配,結(jié)果值有3種:接受,拒絕,中立.可由用戶自定義匹配和不匹配的行為結(jié)果.

所有實現(xiàn)了Filterable接口的組件都可以引用一個過濾器進行事件過濾,包含LoggerConfig和AppenderControl等.

框架實現(xiàn)的過濾器如下:

序號工廠方法(類名.方法名)xml元素作用
1BurstFilter.newBuilder<BurstFilter>?
2DynamicThresholdFilter.createFilter<DynamicThresholdFilter>?
3LevelRangeFilter.createFilter<LevelRangeFilter>?
4MapFilter.createFilter<MapFilter>?
5MarkerFilter.createFilter<MarkerFilter>?
6RegexFilter.createFilter<RegexFilter>?
7ScriptFilter.createFilter<ScriptFilter>?
8StructuredDataFilter.createFilter<StructuredDataFilter>?
9ThreadContextMapFilter.createFilter<ThreadContextMapFilter><ContextMapFilter>?
10ThresholdFilter.createFilter<ThresholdFilter>根據(jù)LogEvent的級別進行過濾,如果LogEvent.level<=ThresholdFilter.level,則返回匹配的結(jié)果,否則返回不匹配的結(jié)果.如:過濾器為info,日志為error,則error<=info返回匹配結(jié)果
11TimeFilter.createFilter<TimeFilter>判斷日志時間是否在指定的時間區(qū)間內(nèi)
enum Result {/*** The event will be processed without further filtering based on the log Level.*/ACCEPT,/*** No decision could be made, further filtering should occur.*/NEUTRAL,/*** The event should not be processed.*/DENY;public static Result toResult(final String name) {return toResult(name, null);}public static Result toResult(final String name, final Result defaultResult) {return EnglishEnums.valueOf(Result.class, name, defaultResult);} }

?AbstractFilterBuilder

用于根據(jù)配置文件中設(shè)置創(chuàng)建Filter

public static abstract class AbstractFilterBuilder<B extends AbstractFilterBuilder<B>> {public static final String ATTR_ON_MISMATCH = "onMismatch";public static final String ATTR_ON_MATCH = "onMatch";@PluginBuilderAttribute(ATTR_ON_MATCH)private Result onMatch = Result.NEUTRAL;@PluginBuilderAttribute(ATTR_ON_MISMATCH)private Result onMismatch = Result.DENY;public Result getOnMatch() {return onMatch;}public Result getOnMismatch() {return onMismatch;}/*** Sets the Result to return when the filter matches. Defaults to Result.NEUTRAL.* @param onMatch the Result to return when the filter matches.* @return this*/public B setOnMatch(final Result onMatch) {this.onMatch = onMatch;return asBuilder();}/*** Sets the Result to return when the filter does not match. The default is Result.DENY.* @param onMismatch the Result to return when the filter does not match. * @return this*/public B setOnMismatch(final Result onMismatch) {this.onMismatch = onMismatch;return asBuilder();}@SuppressWarnings("unchecked")public B asBuilder() {return (B) this;}}

?

BurstFilter

頻率控制過濾器

<BurstFilter level="INFO" rate="16" maxBurst="100"/>

level :BurstFilter過濾的事件級別
rate :每秒允許的 log 事件的平均值
maxBurst:當BurstFilter過濾的事件超過 rate 值,排隊的 log 事件上限。超過此上限的 log ,將被丟棄。默認情況下 maxBurst = 100*rate
按以上配置,假定每個 log 事件的執(zhí)行時間較長,輸出 117 個 log 事件( INFO級別)到RollingFileAppenders,BurstFilter會過濾得到INFO級別的 log 事件,之后會發(fā)生: 16 個 log 事件在執(zhí)行, 100 個等待執(zhí)行, 1 個被丟棄。

private static final long NANOS_IN_SECONDS = 1000000000; //默認private static final int DEFAULT_RATE = 10;private static final int DEFAULT_RATE_MULTIPLE = 100;private static final int HASH_SHIFT = 32;private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch,final Result onMismatch) {super(onMatch, onMismatch);this.level = level;this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate));//構(gòu)造maxBurst個令牌for (int i = 0; i < maxBurst; ++i) {available.add(createLogDelay(0));}}private Result filter(final Level level) {if (this.level.isMoreSpecificThan(level)) { //DelayQueue<LogDelay> history = new DelayQueue<>();LogDelay delay = history.poll();while (delay != null) {available.add(delay);delay = history.poll();}delay = available.poll();if (delay != null) { //獲取到令牌,則表示matchdelay.setDelay(burstInterval);history.add(delay);return onMatch;}return onMismatch;}return onMatch;}public static class Builder extends AbstractFilterBuilder<Builder> {public BurstFilter build() {if (this.rate <= 0) {this.rate = DEFAULT_RATE;}if (this.maxBurst <= 0) { //默認為100 * ratethis.maxBurst = (long) (this.rate * DEFAULT_RATE_MULTIPLE);}return new BurstFilter(this.level, this.rate, this.maxBurst, this.getOnMatch(), this.getOnMismatch());} }

DynamicThresholdFilter

可以過濾具有特定的屬性某一級別的日志

<DynamicThresholdFilter key="loginId" defaultThreshold="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL"><KeyValuePair key="User1" value="DEBUG"/> </DynamicThresholdFilter>

如果用戶的登錄ID被捕獲在ThreadContext的Map中則可以啟用debug級的日志

private Result filter(final Level level, final ReadOnlyStringMap contextMap) { //<DynamicThresholdFilter key="loginId"final String value = contextMap.getValue(key);if (value != null) {Level ctxLevel = levelMap.get(value);if (ctxLevel == null) {ctxLevel = defaultThreshold;}return level.isMoreSpecificThan(ctxLevel) ? onMatch : onMismatch;}return Result.NEUTRAL;}

MapFilter

MapFilter可以對Map中的信息進行過濾,進而記錄特定事件,比如登入、退出

<MapFilter onMatch="ACCEPT" onMismatch="NEUTRAL" operator="or"><KeyValuePair key="eventId" value="Login"/><KeyValuePair key="eventId" value="Logout"/> </MapFilter> protected boolean filter(final Map<String, String> data) {boolean match = false;for (int i = 0; i < map.size(); i++) { //map.getKeyAt(i) key="eventId"final String toMatch = data.get(map.getKeyAt(i)); //map.getValueAt(i) value="Login"match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);if ((!isAnd && match) || (isAnd && !match)) {break;}}return match;}

LevelRangeFilter

private Result filter(final Level level) {return level.isInRange(this.minLevel, this.maxLevel) ? onMatch : onMismatch;}

MarkerFilter

對LogEvent中的Marker 進行過濾

<MarkerFilter marker="FLOW" onMatch="ACCEPT" onMismatch="DENY"/> private Result filter(final Marker marker) {return marker != null && marker.isInstanceOf(name) ? onMatch : onMismatch;}LogManager.getLogger((Class<?>)caller[0]).error(MarkerManager.getMarker("FATALMARKER"), caller[1] + ": " + value);

RegexFilter

對格式化消息和非格式化消息進行正則匹配過濾

<RegexFilter regex=".* test .*" onMatch="ACCEPT" onMismatch="DENY"/> private Result filter(final String msg) {if (msg == null) {return onMismatch;}final Matcher m = pattern.matcher(msg);return m.matches() ? onMatch : onMismatch;}

?ThresholdFilter

對level進行過濾

<ThresholdFilter level="TRACE" onMatch="ACCEPT" onMismatch="DENY"/> private Result filter(final Level testLevel) {return testLevel.isMoreSpecificThan(this.level) ? onMatch : onMismatch;}

TimeFilter

基于時間段的日志過濾

<TimeFilter start="05:00:00" end="05:30:00" onMatch="ACCEPT" onMismatch="DENY"/> Result filter(final long currentTimeMillis) {if (currentTimeMillis >= midnightTomorrow || currentTimeMillis < midnightToday) {initMidnight(currentTimeMillis);}return currentTimeMillis >= midnightToday + start && currentTimeMillis <= midnightToday + end //? onMatch // within window: onMismatch;}

ScriptFilter

<Scripts><ScriptFile name="filter.js" language="JavaScript" path="src/test/resources/scripts/filter.js" charset="UTF-8" /><ScriptFile name="filter.groovy" language="groovy" path="src/test/resources/scripts/filter.groovy" charset="UTF-8" /></Scripts><ScriptFilter onMatch="ACCEPT" onMisMatch="DENY"><ScriptRef ref="filter.groovy" /></ScriptFilter>

?

public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,final Throwable t) {final SimpleBindings bindings = new SimpleBindings();bindings.put("logger", logger);bindings.put("level", level);bindings.put("marker", marker);bindings.put("message", msg);bindings.put("parameters", null);bindings.put("throwable", t);bindings.putAll(configuration.getProperties());bindings.put("substitutor", configuration.getStrSubstitutor());final Object object = configuration.getScriptManager().execute(script.getName(), bindings);return object == null || !Boolean.TRUE.equals(object) ? onMismatch : onMatch;}

CompositeFilter

組合過濾器

<Filters><MarkerFilter marker="EVENT" onMatch="ACCEPT" onMismatch="NEUTRAL"/><DynamicThresholdFilter key="loginId" defaultThreshold="ERROR"onMatch="ACCEPT" onMismatch="NEUTRAL"><KeyValuePair key="User1" value="DEBUG"/></DynamicThresholdFilter></Filters>

?

?

StructuredDataFilter

ThreadContextMapFilter




參考鏈接:https://www.jianshu.com/p/0c882ced0bf5
?

總結(jié)

以上是生活随笔為你收集整理的log4j 2.x 架构(源码)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。