Java必备知识--日志框架
框架说明
-
log4j:Log4j是Apache的一个Java的日志库,通过使用Log4j,我们可以控制日志信息输送的目的地(控制台、文件、数据库等)
-
logback:一个“可靠、通用、快速而又灵活的Java日志框架”。logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日志系统
-
log4j2:Apache Log4j 2是对Log4j的升级,它比其前身Log4j 1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些问题。现在最优秀的Java日志框架是Log4j2

Slf4j
Simple Logging Facade for Java,提供了一个日志入口,称作"门面日志",即它不负责写日志,而是提供用一个统一的接口,通过jar来决定使用的日志框架,这样就不要再更换框架的时候再修改代码。
上图是官方SLF4J依赖关系图
-
和log4j配合,需要导入log4j.jar,桥接包slf4j-log412.jar。
-
和log4j2配合,需要导入log4j2的log4j-api.jar、log4j-core.jar,桥接包log4j-slf4j-impl.jar。
-
logback只需要导入logback-classic.jar和logback-core.jar,不需要桥接包
门面日志原理就是就是让ClassLoader从依赖的jar中找到StaticLoggerBinder,绑定到实际的日志框架,log4j或者logback中的Logger。所谓的桥接包,就是实现StaticLoggerBinder类,用来连接slf4j和日志框架。
实践
SpringBoot与Log4j2
<?xml version="1.0" encoding="UTF-8" ?> <Configuration status="INFO" monitorInterval="60"> <Properties> <Property name="LOG_HOME" value="${sys:user.home}/logs/spring-boot-log4j2/logs/"/> <Property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%t] (%F:%L) - %m%n"/> </Properties> <Appenders> <Console name="ConsoleAppender" target="SYSTEM_OUT" follow="true"> <PatternLayout pattern="${PATTERN}"/> </Console> <RollingFile name="APPENDER_ALARM" fileName="${LOG_HOME}/alarm.log" filePattern="${LOG_HOME}/alarm.%d{yyyy-MM-dd}.%i.log"> <Filters> <LogAlarmFilter/> <LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT"/> </Filters> <PatternLayout pattern="${PATTERN}"/> <Policies> <TimeBasedTriggeringPolicy modulate="true" interval="1"/> <SizeBasedTriggeringPolicy size="200MB"/> </Policies> <DefaultRolloverStrategy max="30"> <Delete basePath="${LOG_HOME}/" maxDepth="1"> <IfFileName glob="alarm.*.log"/> <IfLastModified age="7d"/> </Delete> </DefaultRolloverStrategy> </RollingFile> <RollingFile name="APPENDER_APPLICATION" fileName="${LOG_HOME}/application.log" filePattern="${LOG_HOME}/application.%d{yyyy-MM-dd}.%i.log"> <Filters> <LevelRangeFilter minLevel="FATAL" maxLevel="DEBUG" onMatch="ACCEPT"/> </Filters> <PatternLayout pattern="${PATTERN}"/> <Policies> <TimeBasedTriggeringPolicy modulate="true" interval="1"/> <SizeBasedTriggeringPolicy size="200MB"/> </Policies> <DefaultRolloverStrategy max="30"> <Delete basePath="${LOG_HOME}/" maxDepth="1"> <IfFileName glob="application.*.log"/> <IfLastModified age="7d"/> </Delete> </DefaultRolloverStrategy> </RollingFile> <Async name="ASYNC_APPLICATION" includeLocation="true"> <AppenderRef ref="APPENDER_APPLICATION"/> </Async> <RollingFile name="APPENDER_SERVICE" fileName="${LOG_HOME}/service.log" filePattern="${LOG_HOME}/service.%d{yyyy-MM-dd}.%i.log"> <Filters> <LevelRangeFilter minLevel="FATAL" maxLevel="DEBUG" onMatch="ACCEPT"/> </Filters> <PatternLayout pattern="${PATTERN}"/> <Policies> <TimeBasedTriggeringPolicy modulate="true" interval="1"/> <SizeBasedTriggeringPolicy size="200MB"/> </Policies> <DefaultRolloverStrategy max="30"> <Delete basePath="${LOG_HOME}/" maxDepth="1"> <IfFileName glob="service.*.log"/> <IfLastModified age="7d"/> </Delete> </DefaultRolloverStrategy> </RollingFile> <Async name="ASYNC_SERVICE" includeLocation="true"> <AppenderRef ref="APPENDER_SERVICE"/> </Async> </Appenders> <Loggers> <Root level="INFO"> <AppenderRef ref="ASYNC_APPLICATION"/> <AppenderRef ref="APPENDER_ALARM"/> <AppenderRef ref="ConsoleAppender"/> </Root> <Logger name="cn.tla001" level="INFO" additivity="false"> <AppenderRef ref="ASYNC_SERVICE"/> <AppenderRef ref="APPENDER_ALARM"/> <AppenderRef ref="ConsoleAppender"/> </Logger> </Loggers> </Configuration>
自定义Filter
@Plugin(name = "LogAlarmFilter", category = "Core", elementType = "filter", printObject = true) public class LogAlarmFilter extends AbstractFilter { private static final Logger logger = LoggerFactory.getLogger(LogAlarmFilter.class); private static Set<String> logFilterItems = Sets.newConcurrentHashSet(); private static String regExStr = ""; private static Pattern pattern = Pattern.compile(regExStr); public LogAlarmFilter() { super(Result.NEUTRAL, Result.NEUTRAL); } private Result filter(Level level, String msg){ if (level == null || msg == null){ return Result.NEUTRAL; } if ("ERROR".equalsIgnoreCase(level.toString())){ Matcher matcher = pattern.matcher(msg); if (matcher.find()){ return Result.DENY; }else { //如果不处理,一定要返回NEUTRAL,否则会影响其他Filter的执行 return Result.NEUTRAL; } } return Result.NEUTRAL; } @PluginFactory public static LogAlarmFilter createFilter(){ return new LogAlarmFilter(); } private static synchronized void compileFilterItems(){ regExStr = Joiner.on("|").join(logFilterItems); pattern = Pattern.compile(regExStr); logger.info("Update regExStr:[{}]", regExStr); } public static Boolean delFilterItem(String item){ if (StringUtils.isBlank(item)){ return Boolean.FALSE; } if (!logFilterItems.contains(item)){ return Boolean.TRUE; } logFilterItems.remove(item); compileFilterItems(); return Boolean.TRUE; } public static Boolean addFilterItem(String item){ if (StringUtils.isBlank(item)){ return Boolean.FALSE; } if (logFilterItems.contains(item)){ return Boolean.TRUE; } logFilterItems.add(item); compileFilterItems(); return Boolean.TRUE; } @Override public Result filter(LogEvent event) { return filter(event.getLevel(), event.getMessage().getFormattedMessage()); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, Message msg, Throwable t) { return filter(level, msg.getFormattedMessage()); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, Object msg, Throwable t) { return filter(level, msg.toString()); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object... params) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8) { return filter(level, msg); } @Override public Result filter(org.apache.logging.log4j.core.Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9) { return filter(level, msg); } }
SpringBoot与Logback
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <springProperty name="spring.application.name" scope="context" source="spring.application.name"/> <property name="LOG_PATH" value="${user.home}/logs/${spring.application.name}/logs"/> <!-- 日志格式 --> <property name="CONSOLE_LOG_PATTERN" value="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%c){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/> <property name="FILE_LOG_PATTERN" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %c : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/> <!--输出到控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> </encoder> </appender> <!--输出到文件--> <appender name="APPENDER_APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_PATH}/application.log</file> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/application.%d{yyy-MM-dd}.%i.log</fileNamePattern> <maxHistory>3</maxHistory> <maxFileSize>100MB</maxFileSize> <totalSizeCap>10GB</totalSizeCap> </rollingPolicy> </appender> <appender name="ASYNC_APPLICATION" class="ch.qos.logback.classic.AsyncAppender"> <discardingThreshold>0</discardingThreshold> <queueSize>1024</queueSize> <neverBlock>true</neverBlock> <appender-ref ref="APPENDER_APPLICATION"/> </appender> <appender name="APPENDER_SERVICE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_PATH}/service.log</file> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/service.%d{yyy-MM-dd}.%i.log</fileNamePattern> <maxHistory>3</maxHistory> <maxFileSize>100MB</maxFileSize> <totalSizeCap>10GB</totalSizeCap> </rollingPolicy> </appender> <appender name="ASYNC_SERVICE" class="ch.qos.logback.classic.AsyncAppender"> <discardingThreshold>0</discardingThreshold> <queueSize>1024</queueSize> <neverBlock>true</neverBlock> <appender-ref ref="APPENDER_SERVICE"/> </appender> <appender name="APPENDER_ALARM" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="cn.tla001.spring.demo.plugin.LogAlarmFilter"/> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <file>${LOG_PATH}/alarm.log</file> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/alarm.%d{yyy-MM-dd}.%i.log</fileNamePattern> <maxHistory>3</maxHistory> <maxFileSize>100MB</maxFileSize> <totalSizeCap>10GB</totalSizeCap> </rollingPolicy> </appender> <logger name="cn.tla001" additivity="false"> <appender-ref ref="APPENDER_ALARM"/> <appender-ref ref="ASYNC_SERVICE"/> </logger> <logger name="alarm" additivity="false"> <appender-ref ref="APPENDER_ALARM"/> <appender-ref ref="ASYNC_SERVICE"/> </logger> <!-- (多环境配置日志级别)根据不同的环境设置不同的日志输出级别 --> <springProfile name="default,local"> <root level="DEBUG"> <appender-ref ref="console"/> <appender-ref ref="ASYNC_APPLICATION"/> </root> </springProfile> <springProfile name="product,pre"> <root level="INFO"> <appender-ref ref="console"/> <appender-ref ref="ASYNC_APPLICATION"/> </root> </springProfile> </configuration>
自定义Filter
public class LogAlarmFilter extends Filter<ILoggingEvent> { private static final Logger logger = LoggerFactory.getLogger(LogAlarmFilter.class); private static Set<String> logFilterItems = Sets.newConcurrentHashSet(); private static String regExStr = ""; private static Pattern pattern = Pattern.compile(regExStr); private FilterReply filter(Level level, String msg){ if (level == null || msg == null){ return FilterReply.NEUTRAL; } if ("ERROR".equalsIgnoreCase(level.toString())){ Matcher matcher = pattern.matcher(msg); if (matcher.find()){ return FilterReply.DENY; }else { //如果不处理,一定要返回NEUTRAL,否则会影响其他Filter的执行 return FilterReply.NEUTRAL; } } return FilterReply.NEUTRAL; } private static synchronized void compileFilterItems(){ regExStr = Joiner.on("|").join(logFilterItems); pattern = Pattern.compile(regExStr); logger.info("Update regExStr:[{}]", regExStr); } public static Boolean delFilterItem(String item){ if (StringUtils.isBlank(item)){ return Boolean.FALSE; } if (!logFilterItems.contains(item)){ return Boolean.TRUE; } logFilterItems.remove(item); compileFilterItems(); return Boolean.TRUE; } public static Boolean addFilterItem(String item){ if (StringUtils.isBlank(item)){ return Boolean.FALSE; } if (logFilterItems.contains(item)){ return Boolean.TRUE; } logFilterItems.add(item); compileFilterItems(); return Boolean.TRUE; } @Override public FilterReply decide(ILoggingEvent iLoggingEvent) { return filter(iLoggingEvent.getLevel(), iLoggingEvent.getFormattedMessage()); } }