1 minute read

Facade Pattern

Facade封装了一个子系统中的所有功能,外界的调用先经过facade,然后被facade委派到子系统中。

fac

什么是slf4j,有什么用

一个facade,可以允许用户使用同样的接口调用不同的日志框架实现。

例子:

某项目使用logback,但是其依赖的库中使用的是log4j。如果没有slf4j就得再加载并维护log4j。

但如果大家都用slf4j,就只要维护自己选择的那个日志框架。slf4j会将调用委托给你指定的日志框架实现。

slf4j如何实现委托

getLogger的时候会去classpath下找STATIC_LOGGER_BINDER_PATH,STATIC_LOGGER_BINDER_PATH值为”org/slf4j/impl/StaticLoggerBinder.class”,即所有slf4j的实现,在提供的jar包路径下,一定是有”org/slf4j/impl/StaticLoggerBinder.class”存在的

slfbinding

一般来说选择一个日志框架,slf4j就会bind到它。

当然你也可以include多个日志框架。slf4j会随便选一个使用然后给出警告。

使用Lombok中的@slf4j注解

相当于帮你写了Logger log = LoggerUtils.getLogger(class名.class);这句话。

之后只要调用log.info()等方法就可以了。

Logback

先来一个sample。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <property name="FILE_ERROR_PATTERN"
            value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} %file:%line: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
		<filter class="ch.qos.logback.classic.filter.LevelFilter">
			<level>INFO</level>
		</filter>
		<encoder>
			<pattern>${CONSOLE_LOG_PATTERN}</pattern>
			<charset>UTF-8</charset>
		</encoder>
	</appender>

	<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高, 所以我们使用下面的策略,可以避免输出 Error 的日志-->
		<filter class="ch.qos.logback.classic.filter.LevelFilter">
			<!--过滤 Error-->
			<level>ERROR</level>
			<!--匹配到就禁止-->
			<onMatch>DENY</onMatch>
			<!--没有匹配到就允许-->
			<onMismatch>ACCEPT</onMismatch>
		</filter>
		<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
		<!--<File>logs/info.demo-logback.log</File>-->
		<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
			<FileNamePattern>logs/demo-logback/info.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
			<!--只保留最近90天的日志-->
			<maxHistory>90</maxHistory>
			<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
			<!--<totalSizeCap>1GB</totalSizeCap>-->
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
				<maxFileSize>2MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
		<!--<maxFileSize>1KB</maxFileSize>-->
		<!--</triggeringPolicy>-->
		<encoder>
			<pattern>${FILE_LOG_PATTERN}</pattern>
			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
		</encoder>
	</appender>

	<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter-->
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>Error</level>
		</filter>
		<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
		<!--<File>logs/error.demo-logback.log</File>-->
		<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
			<FileNamePattern>logs/demo-logback/error.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
			<!--只保留最近90天的日志-->
			<maxHistory>90</maxHistory>
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
				<maxFileSize>2MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder>
			<pattern>${FILE_ERROR_PATTERN}</pattern>
			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
		</encoder>
	</appender>

	<root level="info">
		<appender-ref ref="CONSOLE"/>
		<appender-ref ref="FILE_INFO"/>
		<appender-ref ref="FILE_ERROR"/>
	</root>
  <logger name="template-logger">
    <appender-ref ref="CONSOLE"/>
  </logger>
</configuration>

Quick Start

在resource文件夹中创建logback-spring.xml就可以了。不需要做任何其他操作。

<property>

可以自定义一些常量,比如:

<property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg %n"/>

后面就可以使用${pattern}来引用该常量。

<appender>

appender是logback的核心,because it does the real work。appender标签定义一个渲染器。class属性表示它向哪里输出。常用的有 向控制台输出&向文件输出。

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

子标签中比较重要的有<encoder>. 它指定了输出的格式。

<encoder>
    <pattern>${pattern}</pattern>
</encoder>

如果是向文件输出,还有一个<rollingPolicy>元素。自己看上面例子就好了。

<logger>

可以理解为一个或多个appender的封装。调用一个logger时会触发它引用的所有appender。

<logger name="com.example.logbackdemo.IndexAction" level="info" additivity="false">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="CONSOLE-COLORS"/>
</logger>

LoggerFactory.getLogger(A) 拿到的就是配置文件中name属性为“A”的logger。

<root>是一个特殊的logger。所有没有单独logger的信息都会交给root。

例如,前面的例子中,除了目标为template-logger的调用,其他都会被root处理。

个人经验

我实习的时候,原来只有一个general的logger,记录了一些debug信息。我的任务是为程序流程中的两个关键部分记录日志。为了更加清晰,我定义了两个logger和两个appender,将两个部分的日志分别写到两个文件中。

由于只需要定义两个logger,我就新建了一个工具类,里面只有两个static Logger对象。任何其他类要拿logger直接从工具类里面拿就可以了。

最后的效果大概是这样:

public class LoggerUtils {
    public enum LoggerType {
        TEMPLATE,
        ACTION
    }
    static Logger templateLogger = LoggerFactory.getLogger("template-logger");
    static Logger actionLogger = LoggerFactory.getLogger("action-logger");
    public static Logger getLogger(LoggerType type) {
        if (type == LoggerType.TEMPLATE) {
            return templateLogger;
        } else return actionLogger;
    }
}

调用:

Logger logger = LoggerUtils.getLogger(LoggerUtils.LoggerType.TEMPLATE);

References

https://www.cnblogs.com/xrq730/p/8619156.html

https://blog.csdn.net/shiyong1949/article/details/78844342

https://juejin.cn/post/6844903822687485965

https://www.cnblogs.com/xrq730/p/8628945.html