Spring Boot Logback启动流程

news2024/11/27 10:29:16

Spring Boot 默认使用的是 Logback 的日志框架、Logback 的组件主要通过 Spring Boot ApplicationListener 启动的

// LoggingApplicationListener
@Override
public void onApplicationEvent(ApplicationEvent event) {
   if (event instanceof ApplicationStartingEvent) {
      onApplicationStartingEvent((ApplicationStartingEvent) event);
   }
   else if (event instanceof ApplicationEnvironmentPreparedEvent) {
      onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
   }
   else if (event instanceof ApplicationPreparedEvent) {
      onApplicationPreparedEvent((ApplicationPreparedEvent) event);
   }
   else if (event instanceof ContextClosedEvent
         && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
      onContextClosedEvent();
   }
   else if (event instanceof ApplicationFailedEvent) {
      onApplicationFailedEvent();
   }
}

onApplicationStartingEvent

创建 LoggingSystem

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
   this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
   this.loggingSystem.beforeInitialize();
}

LoggingSystem 是 Spring Boot 抽象的日志系统接口

public static LoggingSystem get(ClassLoader classLoader) {
   String loggingSystem = System.getProperty(SYSTEM_PROPERTY);
   if (StringUtils.hasLength(loggingSystem)) {
      if (NONE.equals(loggingSystem)) {
         return new NoOpLoggingSystem();
      }
      return get(classLoader, loggingSystem);
   }
   return SYSTEMS.entrySet().stream().filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader))
         .map((entry) -> get(classLoader, entry.getValue())).findFirst()
         .orElseThrow(() -> new IllegalStateException("No suitable logging system located"));
}

private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) {
  try {
    Class<?> systemClass = ClassUtils.forName(loggingSystemClass, classLoader);
    Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
    constructor.setAccessible(true);
    return (LoggingSystem) constructor.newInstance(classLoader);
  }
  catch (Exception ex) {
    throw new IllegalStateException(ex);
  }
}

SYSTEMS 的值如下、默认情况下创建的就是 LogbackLoggingSystem

private static final Map<String, String> SYSTEMS;

static {
   Map<String, String> systems = new LinkedHashMap<>();
   systems.put("ch.qos.logback.classic.LoggerContext",
         "org.springframework.boot.logging.logback.LogbackLoggingSystem");
   systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
         "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
   systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
   SYSTEMS = Collections.unmodifiableMap(systems);
}

接下来进入到 LogbackLoggingSystem#beforeInitialize 方法

@Override
public void beforeInitialize() {
   LoggerContext loggerContext = getLoggerContext();
   if (isAlreadyInitialized(loggerContext)) {
      return;
   }
   super.beforeInitialize();
   loggerContext.getTurboFilterList().add(FILTER);
}

通过 StaticLoggerBinder.getSingleton() 创建 LoggerContext

private LoggerContext getLoggerContext() {
   ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
   Assert.isInstanceOf(LoggerContext.class, factory,
         String.format(
               "LoggerFactory is not a Logback LoggerContext but Logback is on "
                     + "the classpath. Either remove Logback or the competing "
                     + "implementation (%s loaded from %s). If you are using "
                     + "WebLogic you will need to add 'org.slf4j' to "
                     + "prefer-application-packages in WEB-INF/weblogic.xml",
               factory.getClass(), getLocation(factory)));
   return (LoggerContext) factory;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UzJLNIVn-1652367835486)(https://tva1.sinaimg.cn/large/e6c9d24egy1h0k3conp2qj214x0u0jvx.jpg)]

进入到静态代码块的 init 方法

void init() {
    try {
        try {
            new ContextInitializer(defaultLoggerContext).autoConfig();
        } catch (JoranException je) {
            Util.report("Failed to auto configure default logger context", je);
        }
        // logback-292
        if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
            StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
        }
        contextSelectorBinder.init(defaultLoggerContext, KEY);
        initialized = true;
    } catch (Exception t) { // see LOGBACK-1159
        Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
    }
}

最终会执行下面的代码、尝试获取默认配置文件、如果不存在则通过 SPI 获取 Configurator 实现类、如果还是没有、则使用默认的配置 BasicConfigurator

public void autoConfig() throws JoranException {
    StatusListenerConfigHelper.installIfAsked(loggerContext);
    URL url = findURLOfDefaultConfigurationFile(true);
    if (url != null) {
        configureByResource(url);
    } else {
        Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
        if (c != null) {
            try {
                c.setContext(loggerContext);
                c.configure(loggerContext);
            } catch (Exception e) {
                throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass()
                                .getCanonicalName() : "null"), e);
            }
        } else {
            BasicConfigurator basicConfigurator = new BasicConfigurator();
            basicConfigurator.setContext(loggerContext);
            basicConfigurator.configure(loggerContext);
        }
    }
}

onApplicationEnvironmentPreparedEvent

初始化阶段

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
   if (this.loggingSystem == null) {
      this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
   }
   initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
   new LoggingSystemProperties(environment).apply();
   this.logFile = LogFile.get(environment);
   if (this.logFile != null) {
      this.logFile.applyToSystemProperties();
   }
   this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
   initializeEarlyLoggingLevel(environment);
   initializeSystem(environment, this.loggingSystem, this.logFile);
   initializeFinalLoggingLevels(environment, this.loggingSystem);
   registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

主要就是从 environment 对象中设置相关属性值到 System 中、然后判断是否设置了 log file

最主要的 initializeSystem

private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
   LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
   String logConfig = environment.getProperty(CONFIG_PROPERTY);
   if (ignoreLogConfig(logConfig)) {
      system.initialize(initializationContext, null, logFile);
   }
   else {
      try {
         system.initialize(initializationContext, logConfig, logFile);
      }
      catch (Exception ex) {
         Throwable exceptionToReport = ex;
         while (exceptionToReport != null && !(exceptionToReport instanceof FileNotFoundException)) {
            exceptionToReport = exceptionToReport.getCause();
         }
         exceptionToReport = (exceptionToReport != null) ? exceptionToReport : ex;
         // NOTE: We can't use the logger here to report the problem
         System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
         exceptionToReport.printStackTrace(System.err);
         throw new IllegalStateException(ex);
      }
   }
}

正常情况下、我们都没有配置 logConfig 进入到下面的方法

	@Override
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		LoggerContext loggerContext = getLoggerContext();
		if (isAlreadyInitialized(loggerContext)) {
			return;
		}
		super.initialize(initializationContext, configLocation, logFile);
		loggerContext.getTurboFilterList().remove(FILTER);
		markAsInitialized(loggerContext);
		if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
			getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
					+ "' system property. Please use 'logging.config' instead.");
		}
	}

进入到父类的 initialize 方法中

@Override
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
   if (StringUtils.hasLength(configLocation)) {
      initializeWithSpecificConfig(initializationContext, configLocation, logFile);
      return;
   }
   initializeWithConventions(initializationContext, logFile);
}
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
   String config = getSelfInitializationConfig();
   if (config != null && logFile == null) {
      // self initialization has occurred, reinitialize in case of property changes
      reinitialize(initializationContext);
      return;
   }
   if (config == null) {
      config = getSpringInitializationConfig();
   }
   if (config != null) {
      loadConfiguration(initializationContext, config, logFile);
      return;
   }
   loadDefaults(initializationContext, logFile);
}

正常建议使用的是 xxx-spring.xml 的配置文件、那么我们进入到 loadConfiguration 中

@Override
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
      LogFile logFile) {
   super.loadConfiguration(initializationContext, location, logFile);
   LoggerContext loggerContext = getLoggerContext();
   stopAndReset(loggerContext);
   try {
      configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));
   }
   catch (Exception ex) {
      throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
   }
   List<Status> statuses = loggerContext.getStatusManager().getCopyOfStatusList();
   StringBuilder errors = new StringBuilder();
   for (Status status : statuses) {
      if (status.getLevel() == Status.ERROR) {
         errors.append((errors.length() > 0) ? String.format("%n") : "");
         errors.append(status.toString());
      }
   }
   if (errors.length() > 0) {
      throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors));
   }
}
private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
      URL url) throws JoranException {
   if (url.toString().endsWith("xml")) {
      JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
      configurator.setContext(loggerContext);
      configurator.doConfigure(url);
   }
   else {
      new ContextInitializer(loggerContext).configureByResource(url);
   }

我们发现了一个 SpringBootJoranConfigurator 这个配置一看就是 Spring Boot 相关的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-st2p63uA-1652367835487)(https://tva1.sinaimg.cn/large/e6c9d24egy1h0k3nimm0uj21el0u043j.jpg)]

增加了一些 Spring Boot 的规则、非常明显、用于从 environment 中获取属性填充到 logback 配置文件中

微信公众号:CoderLi

一起看下 logback-spring.xml 文件就能感受到了

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">

    <springProperty scope="context" name="logName" source="spring.application.name" defaultValue="localhost.log"/>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/${logName}.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/${logName}-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>7</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%logger:%line] --%mdc{client} %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
        </filter>
    </appender>
    <!-- Console output -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                [ %-5level] [%date{yyyy-MM-dd HH:mm:ss}] %logger{96} [%line] - %msg%n
            </pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>

    <springProfile name="dev,test">
        <root level="DEBUG">
            <appender-ref ref="FILE"/>
            <appender-ref ref="STDOUT"/>
        </root>
    </springProfile>

    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="FILE"/>
            <appender-ref ref="STDOUT"/>
        </root>
    </springProfile>

    <logger name="org.springframework" level="INFO"/>
    <logger name="com.netflix" level="WARN"/>
    <logger name="org" level="INFO"/>
    <logger name="springfox.documentation" level="INFO"/>

</configuration>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

测试工作中的测试用例设计

测试工作中的测试用例设计 测试工作的最核心的模块&#xff0c;在执行任何测试之前&#xff0c;首先必须完成测试用例的编写。测试用例是指导你执行测试&#xff0c;帮助证明软件功能或发现软件缺陷的一种说明。 进行用例设计&#xff0c;需要对项目的需求有清晰的了解&#xf…

Excel找回打开密码过程

Excel文件设置了打开密码&#xff0c;但是忘记了打开密码或者不知道这份文件的打开密码都没办法打开excel文件了。可是文件的打开密码&#xff0c;一旦忘记了&#xff0c;想要再打开文件&#xff0c;都是需要找回密码的。网上的一些绕过密码、直接删除密码都是无效的解决方法。…

C# 文件压缩解压与sqlite存储文件数据

文章目录环境压缩nugetUI代码资源链接&#xff08;下载地址&#xff09;ZipFile 类方法环境 .netframerwork4.8sqlite3 压缩 nuget <package id"System.IO.Compression" version"4.3.0" targetFramework"net48" /><package id"…

四嗪-五聚乙二醇-羧基,1682653-79-7,Tetrazine-PEG5-COOH 水溶性和稳定性怎么样?

●中文名&#xff1a;四嗪-五聚乙二醇-羧基 ●英文&#xff1a;Tetrazine-PEG5-COOH ●外观以及性质&#xff1a;Tetrazine-PEG5-COOH为红色固体&#xff0c;四嗪目前被广泛应用于蛋白质特定位点功能阐释、亚细胞结构选择性标记。四嗪PEG衍生物用于与 TCO&#xff08;反式环辛烯…

【Linux初阶】操作系统概念与定位 | 操作系统管理硬件方法、系统调用和库函数概念

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;操作系统的基础概念、设计OS的目的&#xff0c;操作系统的定位&#xff0c;操作系统管理硬件方法&#xff0c;…

【正厚软件干货】我推荐你的入门编程语言选python

By——正厚技术极客陈多多 当友友看到这篇文章的时候&#xff0c;心里一定有一个学习编程的想法&#xff0c;但是又不知道挑选哪个作为入门语言&#xff01;我写这篇文章就是为了帮有困难的你做出选择&#xff01;&#xff08;作者本人有选择困难症&#xff0c;当时也纠结了好久…

图神经网络关系抽取论文阅读笔记(五)

1 依赖驱动的注意力图卷积网络关系抽取方法&#xff08;Dependency-driven Relation Extractionwith Attentive Graph Convolutional Networks&#xff09; 论文&#xff1a;Dependency-driven Relation Extraction with Attentive Graph Convolutional Networks.ACL 2021 1.1 …

Win11系统禁止关机键关机的方法教学

Win11系统禁止关机键关机的方法教学。在操作电脑的时候&#xff0c;有用户经常出现自己误触关键按键导致电脑关机的情况。对于这个情况&#xff0c;我们可以去开启电脑禁止关机按键关机的设置。这样就可以不用担心误触导致关机的问题了。一起看看设置的方法吧。 操作方法&#…

OFFER狂魔成长指南

OFFER OFFER狂魔成长指南 前言 本文章是总结了我春招的经历&#xff0c;博主背景为211某流计算机专业&#xff0c;考研失败转春招。因为计划会再考一年因此主要面试的偏国企专业&#xff08;现在放弃了&#xff0c;因为公司里面校招生就我一个本科&#xff0c;其他都是21198…

基于VDI2230规范的螺栓评估(上)

作者&#xff1a;王庆艳&#xff0c;安世中德工程师&#xff0c;仿真秀科普作者 一、写在前面 【螺栓评估准则】&#xff1a;VDI2230&#xff08;高强度螺栓连接系统计算&#xff09;规范是德国工程师协会负责编写整理&#xff0c;包括Part1及Part2两部分&#xff0c;是目前针…

(5)点云数据处理学习——其它官网例子2

1、主要参考 &#xff08;1&#xff09;官方稳定地址 Point cloud — Open3D 0.16.0 documentation 2、相关功能 2.1凸包&#xff08;Convex hull&#xff09; &#xff08;1&#xff09;函数 compute_vertex_normals create_from_triangle_mesh &#xff08;2&#xff09;…

Three.js教程之在网页快速实现 3D效果(教程含源码)

介绍 本文概述了与使用 Three.js 在常规 Web 浏览器中直接在 Web 上制作 3D 图形相关的术语和概念。对于 3D,就像任何主题一样,如果您深入了解所有细节,事情会很快变得复杂。我将尝试做相反的事情,并简单概述您在学习如何在常规 Web 浏览器中制作 3D 时会遇到的所有概念。 …

手把手教你用站长工具综查询网站域名在各个平台的权重情况 站长工具综查询

网站权重是根据流量值来判断网站的权重值。权重值是属于第三方评估就有参考的意义。 了解网站的权重可以助于SEO专员找到工作的方向。使用站长工具综合查询可以看到网站在各个平台的权重情况。 用站长工具综合查询网站在各个平台的权重情况的操作步骤吧&#xff01; 第一步、打…

德云一哥岳云鹏,准备录制河南和东方卫视节目,央视春晚还参加吗

时间如白驹过隙&#xff0c;转眼马上又要过年了&#xff0c;央视春晚也备受关注&#xff0c;不知道今年春晚究竟有哪些明星登台演出。说起央视春晚舞台&#xff0c;就不得不说起相声和小品&#xff0c;这两个节目是春晚的台柱子&#xff0c;也一直深受大家的喜爱。 尤其是最近几…

Linux基础概念,目录文件操作命令,压缩命令:

Linux基础概念&#xff1a; 1&#xff0c;linux登录方式&#xff0c;本地登录&#xff0c;远程登录&#xff08;借助xshell等&#xff09; 登录后是shell交互界面&#xff0c;用c编写的程序。常见的就是bash&#xff0c;sh&#xff0c;csh&#xff0c;ksh等。 2&#xff0c;li…

项目实战——Web自动化测试

目录 一、前言及测试用例设计 二、 首页测试&#xff08;未登录&#xff09; 三、注册测试 四、对局列表测试 五、排行榜测试 六、对战测试 七、Bot测试 八、测试套件Suite 一、前言及测试用例设计 整个项目已经部署完成&#xff0c;我们历经九九八十一难&#xff0c;…

英国Top20名校更偏爱IB申请党?

IB的全球超高认可度相信大家都有共识&#xff0c;英国大学对于A-Level、IB成绩都持认可态度。 但多数人会认为A-Level成绩申请英国名校会比IB更有优势&#xff0c;事实果真如此吗&#xff1f;接下来通过官方给出的数据&#xff0c;我们来一探究竟~ 01英国Top20名校更偏爱IB学生…

Linux的十个常用命令

目录 1、ls 2、pwd 3、cd 4、touch 5、cat 6、echo 7、mkdir 8、rm 9、mv 10、cp 1、ls ls命令用于显示目录中的文件信息. 格式&#xff1a;ls [选项] [文件] 参数&#xff1a; -a 显示所有文件及目录 (. 开头的隐藏文件也会列出)-l 除文件名称外&#xff0c;亦将文件型…

点云缩放(附open3d python代码)

1/ numpy 数组方法 通过将点云数组乘以一个缩放因子来改变大小, 同时通过加法运算实现质心平移。points = points/2.0#缩小到原来的一半 points[:, 0] = points[:, 0] + 20#质心平移到x=20处2/ open3d的缩放函数为scale,包含两个参数。 第一个参数是缩放的比例,即放大的倍数…

Kafka怎样完成建立和Broker之间的连接?

文章目录NetworkClient初始化1.基于Java NIO SocketChannel封装KafkaChannel2.Kafka提供的Selector是如何初始化跟Broker之间的连接的2.1 初始化SocketChannel&#xff0c;发起连接请求2.2 将发起连接请求后的SocketChannel缓存起来3.万能poll()方法之前的准备工作4. 通过不断轮…