03.spring-boot与日志

spring-boot与日志

使用slf4j

如何让系统中所有的日志都统一到slf4j

  1. 将系统中其他日志框架先排除出去。
  2. 用中间包来替换原有的日志框架。
  3. 我们导入slf4j其他的实现。
spring-boot的日志依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐logging</artifactId>
</dependency>

总结:

  1. spring-boot底层选用的是slf4j+logback进行日志记录。
  2. spring-boot把其他的日志都替换成了slf4j。
  3. 如果我们要引入其他框架,需要把默认的日志依赖移除掉。
spring-boot的日志使用

导入slf4j的jar和logback的实现jar

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RunWith(SpringRunner.class)
@SpringBootTest
public class LoggerApplicationTests {

    @Test
    public void contextLoads() {

        Logger logger = LoggerFactory.getLogger(LoggerApplicationTests.class);
        logger.trace("trace");
        logger.debug("debug");
        logger.info("info");
        logger.warn("warn");
        logger.error("error");

    }

}
日志级别

spring-boot默认日志级别是info

1. trace
2. debug
3. info
4. warn
5. error
日志默认配置修改
日志输出格式: 
    %d:表示日期时间,
    %thread:表示线程名,
    %‐5level:级别从左显示5个字符宽度
    %logger{50}:表示logger名字最长50个字符,否则按照句点分割。 
    %msg:日志消息,
    %n:是换行符
例子:
    %d{yyyy‐MM‐dd HH:mm:ss} [%thread] %-5level %logger- %msg%n

控制台:

//修改默认日志级别,level后面跟着包名
logging.level.com.bafan.springboot.logger=trace
//修改控制台
logging.pattern.console=%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n

指定文件输出:

在实际的项目开发中,我们习惯将日志输出到服务器的某个文件下
  • 需要将日志的配置文件放在类路径下(spring-boot就不使用他默认的配置了)。
  • logback.xml:直接被日志框架识别。
  • logback-spring.xml:日志框架不直接加载日志的配置项,由spring-boot解析日志配置,可以使用spring-boot的高级profile功能。(推荐)。(<springProfile name="dev">可以根据环境来决定功能)

spring-boot识别logback.xml文件

1--LoggingApplicationListener的initialize方法

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
    (new LoggingSystemProperties(environment)).apply();
    LogFile logFile = LogFile.get(environment);
    if (logFile != null) {
        logFile.applyToSystemProperties();
    }

    this.initializeEarlyLoggingLevel(environment);
    //进入
    this.initializeSystem(environment, this.loggingSystem, logFile);
    this.initializeFinalLoggingLevels(environment, this.loggingSystem);
    this.registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

2--LoggingApplicationListener的initializeSystem方法

private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
    LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
    String logConfig = environment.getProperty("logging.config");
    if (this.ignoreLogConfig(logConfig)) {
        //进入
        system.initialize(initializationContext, (String)null, logFile);
    } else {
        try {
            ResourceUtils.getURL(logConfig).openStream().close();
            system.initialize(initializationContext, logConfig, logFile);
        } catch (Exception var7) {
            System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
            var7.printStackTrace(System.err);
            throw new IllegalStateException(var7);
        }
    }

}

3--LogbackLoggingSystem的initialize方法

public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
    LoggerContext loggerContext = this.getLoggerContext();
    if (!this.isAlreadyInitialized(loggerContext)) {
        //调用父类的初始化方法
        super.initialize(initializationContext, configLocation, logFile);
        loggerContext.getTurboFilterList().remove(FILTER);
        this.markAsInitialized(loggerContext);
        if (StringUtils.hasText(System.getProperty("logback.configurationFile"))) {
            this.getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring 'logback.configurationFile' system property. Please use 'logging.config' instead.");
        }

    }
}

4--AbstractLoggingSystem的initialize方法

public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
    if (StringUtils.hasLength(configLocation)) {
        this.initializeWithSpecificConfig(initializationContext, configLocation, logFile);
    } else {
        //进入
        this.initializeWithConventions(initializationContext, logFile);
    }
}

5--AbstractLoggingSystem的initializeWithConventions方法

private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
    //查找自己的配置(logback.xml)
    String config = this.getSelfInitializationConfig();
    if (config != null && logFile == null) {
        this.reinitialize(initializationContext);    
    } else { //如果没有找到,就去找spring的文件
        if (config == null) {
            //查找spring的配置(logback-spring.xml)
            config = this.getSpringInitializationConfig();
        }

        if (config != null) {
            this.loadConfiguration(initializationContext, config, logFile);
        } else { //如果都没有找到,只使用properties默认的配置
            this.loadDefaults(initializationContext, logFile);
        }
    }
}

6--AbstractLoggingSystem的getSelfInitializationConfig方法,执行结束后到7

protected String getSelfInitializationConfig() {
    //获取spring-boot标准的配置
    return this.findConfig(this.getStandardConfigLocations());
}
protected String[] getStandardConfigLocations() {
    return new String[]{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"};
}

7--AbstractLoggingSystem的findConfig方法,执行结束后到5

private String findConfig(String[] locations) {
    String[] var2 = locations;
    int var3 = locations.length;

    //如果能够找到配置文件的位置,则返回配置文件位置,否则返回null
    for(int var4 = 0; var4 < var3; ++var4) {
        String location = var2[var4];
        ClassPathResource resource = new ClassPathResource(location, this.classLoader);
        if (resource.exists()) {
            return "classpath:" + location;
        }
    }

    return null;
}

8--AbstractLoggingSystem的getSpringConfigLocations方法,执行结束后到7

protected String[] getSpringConfigLocations() {   
    String[] locations = this.getStandardConfigLocations();

    for(int i = 0; i < locations.length; ++i) {
        //加上-spring后缀再在跟路径下查找
        String extension = StringUtils.getFilenameExtension(locations[i]);
        locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring." + extension;
    }

    return locations;
}

logback常用配置

1--根节点configuration

  • 通常不加任何属性。

2--configuration的子节点

2.1 property
//用来定义变量
<property name="FILE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %c{35} %M %L - %msg%n"/>
...
<encoder>
    <pattern>${FILE_PATTERN}</pattern>
</encoder>
2.2 logger
  • name:用来指定受此logger约束的某一个包或者具体的某一个类。
  • level:用来打印日志级别。(root<append<logger)
  • additivity:是否向上级传递打印信息,默认是true,项目中都用false
<logger name="com.mogujie.raptor" level="@root.log.level@" additivity="false">
    <appender-ref ref="RAPTOR_FILE"/>
</logger>
2.3 root
  • 也属于logger标签,只有一个属性,定义日志级别,默认是全路径。
<root level="INFO">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="FILE" />
</root>

3--appender和它的子节点

  • appender是configuration的子节点。
  • name:指定appender名称,class指定appender的全限名。
3.1 ConsoleAppender
  • 控制台日志
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>${CONSOLE_LOG_PATTERN}</pattern>
    </encoder>
</appender>
3.2 RollingFileAppender
滚动日志文件:先将日志记录到指定文件,当符合某个条件的时候,将日志记录到 其它文件。FileAppender没有滚动功能,要么追加要么覆盖,项目中几乎不用。
  • file:写入文件的名字。
  • append:默认是true、追加;false是覆盖。
  • encoder:对记录事件进行格式化。一是把日志信息转成字节数组,二是把字节数组写入输出流。
  • rollingPolicy:发生滚动时,涉及文件移动和重命名。
rollingPolicy:
  • TimeBasedRollingPolicy:最常用的滚动方式,根据时间制定滚动策略,既负责滚动,也负责触发滚动。
  1. fileNamePattern:包含文件名及“%d”转换符。
  2. maxHistory:保留最大时间,根据fileNamePattern的时间决定单位是年月日。
  3. timeBasedFileNamingAndTriggeringPolicy:当文件大小超过多少时触发滚动,里面配置maxFileSize,例如500MB。
<appender name="errorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <encoder>
        <pattern>${FILE_PATTERN}</pattern>
    </encoder>
    <Encoding>UTF-8</Encoding>
    <file>${CATALINA_APPLOG}/finance_mall_error.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${CATALINA_APPLOG}/finance_mall_error_%d{yyyy_MM_dd}-%i.log</fileNamePattern>
        <MaxHistory>10</MaxHistory>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>500MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
</appender>
4. filter
属于appender下的标签,一般用于日志级别过滤
  • level:日志级别
  • onMatch:如果匹配上,接受(ACCEPT)
  • onMismatch:如果没有匹配上,拒绝(DENY)
<filter class="ch.qos.logback.classic.filter.LevelFilter"> 
  <level>INFO</level> 
  <onMatch>ACCEPT</onMatch> 
  <onMismatch>DENY</onMismatch> 
</filter> 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359