<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot日志源碼解析:日志監(jiān)聽器的執(zhí)行

          共 18374字,需瀏覽 37分鐘

           ·

          2022-04-25 07:53


          LoggingApplicationListener 的執(zhí)行


          LoggingApplicationListener 的主要作用是配置LoggingSystem, 如果 環(huán)境 包含 loggingconfig 屬性,LoggingApplicationListener 將用于引導 日志記錄系統(tǒng),否則使用默認配置。

          如果環(huán)境包含 logging.level.*和日志記錄組,則可以使用 logging.group 定義日志記錄級別。

          關(guān)于 LoggingApplicationL istener 的重點功能我們后面章節(jié)再進行講解。


          LoggingApplicationListener 實現(xiàn)自 GenericApplicationListener 接口,具有監(jiān)聽器的特性。

          因此,執(zhí)行
          EventPublishingRunListener 廣播事件之后,LoggingApplicationListener 便會監(jiān)聽到對應(yīng)的事件并執(zhí)行 onApplicationEvent 方法中的邏輯判斷,有針對性地處理不同的事件,相關(guān)代碼如下。

          @Override
          public void onApplicationEvent(ApplicationEvent event)
          {
          if (event instanceof ApplicationStartingEvent) {
          // springboot 啟動時觸發(fā)
          onApplicationStartingEvent( (ApplicationStartingEvent) event);
          } else if (event instanceof ApplicationEnvironmentPreparedEvent) {
          // Environment 環(huán)境準備初級階段觸發(fā)
          onApplicationEnvironmentPreparedEvent( (Applicat ionEnvironmentPrepared-
          Event) event);
          } else if (event instanceof ApplicationPreparedEvent) {//應(yīng)用上下文準備完成,但未刷新時觸發(fā)
          onApplicationPreparedEvent( (Applicat ionPreparedEvent) event);
          else if (event instanceof ContextClosedEvent
          && ((ContextClosedEvent) event). getApplicationContext() . getPar
          ent()
          == null) {
          //容器關(guān)閉時處理
          onContextClosedEvent( );
          } else if (event instanceof ApplicationFailedEvent) {
          //啟動失敗時處理
          onApplicationFailedEvent();
          }
          }

          以上代碼中的事件處理基本涵蓋了 Spring Boot 啟動的不同階段和不同狀況,比如 SpringBoot 剛剛啟動階段、環(huán)境準備初級階段、應(yīng)用上下文準備完成階段、容器關(guān)閉階段、應(yīng)用程序啟動失敗等。后面章節(jié)我們會對這些過程中日志系統(tǒng)是如何處理的進行詳解介紹。


          ApplicationStartingEvent 事件處理

          在 Spring Boot 的啟動過程中,通過
          SpringApplicationRunListeners 類間接的調(diào)用了EventPublishingRunListener 中 的 各 類 事 件 的 發(fā) 布 方 法 , 最 終 被 LoggingApplicationListener 監(jiān)聽并進行處理。在后續(xù)的講解中,我們省略這個中間調(diào)用過程,直接講解 Logging-ApplicationL istener 接收到事件后的處理。

          Spring Boot 剛 剛 啟 動 時 發(fā) 布 了 ApplicationStartingEvent 事 件 ,
          LoggingApplication-Listener 中的 onApplicationStartingEvent 方法便被調(diào)用了,該方法源碼如下。

          private void onApplicat ionStartingEvent(ApplicationStartingEvent event)
          this .loggingSystem
          = LoggingSystem. get(event . getSpringApplication(). getCl
          assLoader())
          ;
          this. loggingSystem. beforeInitialize();
          }


          onApplicationStartingEvent 方法中,首先獲得一個 LoggingSystem 對象,然后調(diào)用對象的 beforelnitialize 方 法進行預(yù)初始化操作。也就是說在 Spring Boot 開始啟動時,日志系統(tǒng)做了兩件事:創(chuàng)建 LoggingSystem 對象和預(yù)初始化操作。

          LoggingSystem 為日志系統(tǒng)的通用抽象類,其中也提供了獲取 LoggingSystem 對象的靜態(tài)方法。上面 LoggingSystem 的創(chuàng)建便 是調(diào)用其 get 方法獲得,相關(guān)代碼如下。

          public static LoggingSystem get(ClassLoader classLoader) {
          //從系統(tǒng)變量中獲得 L oggingSystem 的類名String loggingSystem = System. getProperty(SYSTEM PROPERTY);
          f (StringUtils . hasLength(loggingSystem)) {
          if (NONE . equals(loggingSystem))
          return new NoOpLoggingSystem();
          /如果存在,則通過反射進行對象的初始化
          return get(classLoader, loggingSystem);
          // MSYSTEMS 篩選并初始化 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"));
          }

          該方法首先判斷系統(tǒng)中是否配置了 LoggingSystem 的配置,存在且不為“none”時,則利用反射機制進行初始化;如果明確配置為"none”,則返回 NoOpL oggingSystem 對象。實例化配置的 L oggingSystem 相關(guān)代碼如下。

          private static LoggingSystem get(ClassLoader classLoader, String loggingSys
          temClass) {
          try
          Class systemClass = ClassUtils . forName( loggingSystemClass, classLoad
          er);
          Constructor constructor = systemClass . getDeclaredConstructor(Class-
          Loader.
          class
          );
          constructor . setAccessible(true);
          return (LoggingSystem) constructor. newInstance(classLoader);
          } catch (Exception ex) {
          throw new IllegalStateException(ex);
          }
          }

          以上代碼就是通過獲取指定類的構(gòu)造器,調(diào)用其 newInstance 方法來創(chuàng)建 LoggingSystem對象的。

          如果系統(tǒng)中不存在該對象的配置,則從 SYSTEMS 篩選獲取第一個符合條件的值,然后進行初始化。SYSTEMS 為 L oggingSystem 的靜態(tài)變量,通過靜態(tài)代碼塊進行初始化,相關(guān)代碼如下。

          private static final Map<String, String> SYSTEMS;static {
          Map<String, String> systems = new LinkedHashMap<>();
          systems . put("ch. qos . logback. core . Appender"
          "org. springframework . boot . logging . logback. LogbackLoggingSyste
          m"
          );
          systems . put("org. apache. logging. log4j . core. impl. Log4jContextFactory"
          "org. springframework. boot. logging . log4j2 . Log4J2LoggingSyste
          m"
          );
          systems . put("java .util. logging. LogManager" ,
          "org. springframework . boot . logging . java . Javal oggingSystem");
          SYSTEMS = Collections . unmodifiableMap(systems);
          }

          常 量 SYSTEMS 是 Map 結(jié) 構(gòu) , 其 中 key 為 對 應(yīng) 日 志 系 統(tǒng) 的 核 心 類 ( 類 似@ConditionalOn-Class 注解中指定的類), value 的值 是 LoggingSystem 的具體實現(xiàn)類。

          在靜態(tài)代碼塊中,初始化分別添加了 LogbackL oggingSystem、Log4J2L oggingSystem 和JavaLoggingSystem,這也是 Spring Boot 默認內(nèi)置的 3 個日志實現(xiàn)類。而且 SYSTEMS 被初始化之后便不可被修改了。

          其中從 SYSTEMS 中篩選出符合條件的 L oggingSystem 實現(xiàn)類,這里采用了 Java 8 新增的 Stream 語法來實現(xiàn),基本處理過程是這樣的:遍歷 SYSTEMS 中的值,通過 ClassUtils的 isPresent 方 法過濾符合條件的值(key 對應(yīng)的類存在于 classpath 中) ;然后通過上面提到的反射方法創(chuàng)建篩選過后的值的對象;最后獲取第一個對象并返回。如果未獲取到則拋出異常。

          由于 SYSTEMS 是基于 LinkedHashMap 實現(xiàn)的,因此,這里可以看出默認情下 SpringBoot優(yōu)先采用
          org.springframework.bot.logging.logback.LogbackL oggingSystem 實現(xiàn)類。

          也就是說,默認情況下使用 L ogback 進行日志配置。

          完成 LoggingSystem 初始化之后,程序便調(diào)用其 beforelnitialize 方法進行初始化前的準備工作。在 L oggingSystem 中 beforelnitialize 為抽象方法,由子類實現(xiàn)。該方法在 LogbackLoggingSystem 中的源碼實現(xiàn)如下。

          public void beforeInitialize() {
          //獲得 LoggerContext
          LoggerContext loggerContext = getLoggerContext();
          //如果 LoggerContext 中已經(jīng)配置有 oggingSystem 對應(yīng)的 Logger,則直接返回
          if (isAlreadyInitialized(loggerContext)) {
          return;
          11 調(diào)用父類的初始化方法
          super . beforeInitialize();//向 LoggerContext 中的 TurboFilterL ist 添加 1 個 TurboFilter
          // 目的是在 L oggerContext 沒有初始化之前對應(yīng)打印的日志的請求全部拒絕
          loggerContext . getTurboFilterList() . add(FILTER);
          }

          該方法的主要功能是獲得 LoggerContext 并校驗是否存在對應(yīng)的 logger,如果不存在則調(diào)用父類的初始化方法,并拒絕在 L oggerContext 沒有初始化之前對應(yīng)打印的日志的全部請求。

          LogbackL oggingSystem 的 父 類 為 SIf4JL oggingSystem, 因 此 方 法 中 調(diào) 用 了 SIf4JLogging-System 的 beforelnitialize 方法,相關(guān)源碼如下。

          public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
          private static final String BRIDGE_ HANDLER = "org. slf4j . bridge . SLF4JBridg
          eHandler"
          ;
          @Override
          publi
          ic void beforeInitialize()
          //調(diào)用父類的 beforeInitialize, 默認父類實現(xiàn)為空
          super . beforeInitialize();
          //配置 jdk 內(nèi)置日志與 SLF4J 直接的嬌接 Handler
          configureJdkL oggingBridgeHandler();
          private void configureJdkl oggingBridgeHandler() {
          //判斷是否需要將 JUL 橋接為 SLf4j
          if (isBridgeJulIntoSlf4j()) {
          //刪除 jdk 內(nèi)置日志的 Handler
          removeJdk oggingBridgeHandler();
          //添加 SLF4J 的 Handler
          SLF4JBridgeHandler . install();
          catch (Throwable ex) {
          //忽略異常。沒有 java. util. Logging 橋接被安裝
          }
          //根據(jù)兩個條件判斷是否將 JUL 橋接為 SLF4J
          protected final boolean isBridgeJulIntoSlf4j() {
          return isBridgeHandlerAvailable() && isJulUsingASingleConsoleHandler-
          AtMost();
          /判斷 org. slf4j. bridge. SLF4JBridgeHandler 是否存在于類路徑下
          protected final boolean isBridgeHandlerAvailable()
          return ClassUtils. isPresent(BRIDGE_ HANDLER, getClassLoader());//判斷是否不存在 HandLer 或只存在-個 Consol eHandler
          private boolean isJulUsingASingleConsoleHandlerAtMost() {
          Logger rootLogger = LogManager . getLogManager(). getLogger("");
          Handler[] handlers = rootlogger. getHandlers();
          return handlers.length == 0 | | (handlers.length == 1 && handlers[0] ins
          tanceof-
          ConsoleHandler);
          //移除 Handler
          private void removeJdkL oggingBridgeHandler() {
          try {
          removeDefaultRootHandler();
          //移除 SLF4J 相關(guān) Handler
          SLF4JBridgeHandler . uninstall();
          } catch (Throwable ex) {
          //忽略并繼續(xù)
          //移除 ConsoleHandler
          private void removeDefaultRootHandler() {
          try {
          Logger rootLogger = LogManager . getLogManager().getLogger(" );
          Handler[] handlers = rootl ogger. getHandlers( );
          if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
          rootLogger . removeHandler(handlers[0]);
          } catch (Throwable ex) {
          //忽略并繼續(xù)
          }
          }
          }

          上述代碼涵蓋了 SIf4JL oggingSystem 中大多數(shù)的功能,其主要目的就是處理內(nèi)置日志(JUL)與 SLF4J 的 Handler 的橋接轉(zhuǎn)換操作。

          基本判斷邏輯如下:如果類路徑下存在 SLF4JBridgeHandler 類,并且根 Logger 中不包含或僅包含 ConsoleHandler 時,說明需要將內(nèi)置日志轉(zhuǎn)換為 SLF4J。

          基本轉(zhuǎn)換過程分兩步:刪除原有 Handler、新增指定的 Handler; 如果滿足條件,先刪除內(nèi)置日志的Handler,然后再刪除SLF4J的Handler,最后再將SLF4J對應(yīng)的SLF4JBridgeHandler添加到根 L ogger 中。

          具體的方法實現(xiàn)參考上述代碼中的注解,關(guān)于 SLF4J 的 uninstall 方法和 install 方法均在SLF4JBridgeHandler 類中,實現(xiàn)比較簡單,在此不再進行拓展。不過建議讀者朋友閱讀一下 SLF4JBridgeHandler 中的源代碼, 其內(nèi)部還提供了轉(zhuǎn)換過程中各層級日志級別對應(yīng)等處理。

          至此,針對 Spring Boot 啟動階段發(fā)出的 ApplicationStartingEvent 事件及日志系統(tǒng)所做的相應(yīng)操作已經(jīng)講解完畢。


          ApplicationEnvironmentPreparedEvent 事件處理

          當 SpringBoot 繼續(xù)啟動操作便會廣播
          ApplicationEnvironmentPreparedEvent 事件,此時便會調(diào)用 LoggingApplicationListener 的 onApplicationEnvironmentPreparedEvent 方法,該方法源碼如下。

          private void onApplicationEnvironmentPreparedEvent(
          ApplicationEnvironmentPreparedEvent event
          )
          {
          if (this.loggingSystem == null)
          this. loggingSystem = LoggingSystem
          .get(event
          getSpringApplication()
          initialize(event. getEnvironment(), event . getSpringApplication(). getClassL
          oader())
          ;
          }

          在上一節(jié)中,ApplicationStartingEvent 事件觸發(fā)時 ,loggingSystem 已經(jīng)初始化賦值了,在該方法中會再次判斷l(xiāng)oggingSystem是否為null,如果為null,則通過LoggingSystem的 get方法進行對象創(chuàng)建。

          完成 loggingSystem 的再次判斷并創(chuàng)建之后,調(diào)用 initialize 方法進行初始化操作,主要完成了初始參數(shù)的設(shè)置、日志文件、日志級別設(shè)置以及注冊 ShutdownHook 等操作,相關(guān)代碼如下。

          protected void initialize(ConfigurableEnvironment environment, ClassLoader
          classLoader
          )
          {
          //創(chuàng)建 LoggingSystemProperties 對象,并 沒置默認屬性
          new LoggingSystemProperties( environment) . apply();
          //獲取 LogFile,如果 LogFile 存在, 則向系統(tǒng)屬性寫入 LogFile 配置的文件路徑
          this.logFile = LogFile . get( environment);
          if (this.logFile != null) {
          this . logFile . applyToSystemProperties();
          this. loggerGroups = new LoggerGroups(DEFAULT_ GROUP_ LOGGERS);
          !1 rsrinooogging
          initializeEarlyLoggingL evel(environment)
          ;
          //初始化 L oggingSysteminitializeSystem(environment, this . loggingSystem, this. logFile);
          //最終沒置日志級別
          initializeFinaloggingL evels(environment, this . loggingSystem) ;
          //注冊 ShutdownHook
          registerShutdownHookIfNecessary(environment, this . loggingSystem);
          }

          上述代碼中,創(chuàng)建 LoggingSystemProperties 對象之 后主要是通過調(diào)用其 apply 方法來獲取默認的日志配置參數(shù)(在配置文件中以"logging.”開頭的屬性),并設(shè)置到系統(tǒng)屬性中。

          LogFile的get方法主要是獲取日志文件的路徑和名稱,并作為參數(shù)創(chuàng)建Logfile對象。LogFile中 get 方法相關(guān)代碼如下。

          public static LogFile get(PropertyResolver propertyResolver) {
          Strinp file = petI OpFi ] ePronertv( nronertvResolver. FTI F NAMF PROPERTY. F T
          LE_ PROPERTY);
          String path = getLogFileProperty(propertyResolver, FILE_ PATH PROPERTY, PA
          if (StringUtils.haslength(file) | StringUtils.haslength(path))
          {
          return new LogFile(file, path);
          }
          return null;
          }

          從上述代碼可以看出,通過獲取屬性名為"logging.ile.path"的值得到了 日志的路徑,通過獲取屬性名為"logging.file.name "的值得到了日志文件名。其中 getLogFileProperty 的第 3 個參數(shù)為兼容歷史版本中的配置屬性名。當然,程序會優(yōu)先獲取當前版本的屬性配置,當查找不到值時才會獲取歷史版本的值。

          緊接著,initialize 方法中判斷當 LogFile 不為 null 時,調(diào)用它的 apply ToSystemProperties方法,也就是將上述獲得的日志文件路徑和名稱存入系統(tǒng)屬性當中。

          initializeEarlyL oggingl .evel 方法用于早期設(shè)置 springBootl ogging 的值和 LoggingSystem的初始化,代碼如下。

          private void initializeEarlyLoggingl evel (Conf igurableEnvironment environmen
          if (this. parseArgs && this . springBootLogging == null) {
          f (isSet(environment, "debug")) {
          this . springBootLogging = LogLevel. DEBUG;
          this. springBootlogging = Loglevel. TRACE;
          }
          }
          }

          上述代碼主要根據(jù) parseArgs 參數(shù)(默認為 true )和 springBootLogging 是否為 null,在早期階段中設(shè)置 springBootL ogging 的值,也就是日志級別。

          在 parseArgs 為 true, 并 且 springBootLogging 值 為 null 的 情 況 下 , 如 果Configurable-Environment 中 debug 的值存在且為 true, 則設(shè)置 springBootL ogging 為DEBUG。同樣,如果 trace 的值存在且為 true,則設(shè)置 springBootLogging 為 TRACE。

          初始化 LoggingSystem 的代碼如下。

          private void initializeSystem(Conf igurableEnvironment environment,
          LoggingSystem system, LogFile logFile)
          {
          //實例化 LoggingInitial izat ionContext
          LoggingInitializationContext initializationContext = new LoggingInitializ
          ationContext (
          environment)
          ;
          String 1ogConfig 二 enviroment .getProperty(CONFITG PROPERTY);
          if (ignoreLogConfig(logConfig))
          //如果 logging. config 沒有配置或者配置的值是 D 開頭的,則調(diào)用 L oggingSystem
          的方法進行初始化
          system . initialize(initializationContext, null, logFile);
          } else {
          11 通過 ResourceUtils 進行加載判斷其文件是否存在,如果不存在,則拋出 Illega
          lStateException
          ResourceUtils .getURL(logConfig) .openStream().close();
          //存在則調(diào)用 L oggingSystem 的方法進行實例化
          system. initialize(initializationContext, logConfig, logFile);
          } catch (Exception ex) {
          private boolean ignoreLogConfig(String logConfig) {
          return !StringUtils.hasLength(logConfig) 11 logConfig. startsWith("-D");
          }

          initializeSystem 方法中代碼的邏輯主要是圍繞 L oggingSystem 的初始化來進行的,首先為其初始化準備了
          LogginghitializationContext 對象。然后獲取 logging.config 的參數(shù)并賦值給 logConfig,如果 logConfig 未配置 或者配置的值以 D 開頭,則調(diào)用 LoggingSystem 的initialize 方法進行初始化; 其他情況則通過 ResourceUtil 加載判斷對應(yīng)配置文件是否存在,如果不存在,則拋出llegalStateException;如果存在,則同樣調(diào)用L oggingSystem的initialize方法進行初始化。

          下面我們來看 LoggingSystem 的 itialize 方法的源代碼。該方法在 LoggingSystem 類中的實現(xiàn)為空,而在其子類 AbstractL oggingSystem 中提供了如下的實現(xiàn)。

          @Overridepublic void initialize(LoggingInitializationContext initializationContext,
          String configLocation, LogFile logFile)
          {
          if (StringUtils . hasLength(configLocation))
          initializeWi thSpecificConfig(initializationContext, configLocation, log
          File)
          ;
          return;
          }
          initializeWithConvent ions (initializationContext, logFile);
          }

          .上述代碼中,如果用戶指定了配置文件,則加載指定配置文件中的屬性進行初始化操作;如果未指定配置,則加載默認的配置,比如 log4j2 的 log4j2 .properties 或 log4j2.xml。其中默認加載日志配置文件名稱及文件格式由具體的子類實現(xiàn)。

          下面重點講解 SpringBoot 中默認查找配置文件路徑的實現(xiàn),該部分在 LoggingSystem的抽象子類 AbstractL oggingSystem 的 initializeWithConventions 中實現(xiàn)。

          private void initializeWithConventions(
          LoggingInitializationContext initializationContext, LogFile logFile
          )
          String config
          = getSelfInitializat ionConfig();
          if (config != null && logFile == null) {
          //自我初始化操作,屬性變更時會重新初始化
          reinitialize(initializationContext);
          return;
          }
          if (config == null) {
          config = getSpringInitializationConfig();
          }
          if (config != null) {
          loadConfiguration(initializationContext, config, logFile);
          return;
          loadDefaults(initializationContext, logFile);
          }

          該方法的基本流程是:首先,獲得默認的日志配置文件(比如 logback.xml 等), 當配置文件不為 null,且 logFile 為 null 時, 進行自我初始化,具體實現(xiàn)由不同的日志框架來執(zhí)行,主要就是重置數(shù)據(jù)并加載初始化;然后,如果默認的配置文件不存在,則嘗試獲取包含“-spring”的名稱的配置文件(比如 logback-spring.xmI 等),如果獲得對應(yīng)的配置文件,則直接加載初始化;最后,如果上述兩種類型的配置文件均未找到,則調(diào)用 loadDefaults 方法采用默認值進行加載配置。


          getSelflnitializationConfig 方法和 getSpringlnitializationConfig 都是用來獲取默認配置文件名稱的,不同之處在于獲得的配置文件的名稱中是否多了“一 spring"。這兩個方法都是先調(diào)用 getStandardConfig ocations 方法獲得默認的配置文件名稱數(shù)組,然后再調(diào)用 findConfig來驗證獲取符合條件的值。

          我們先看不同之處,
          getSpringhnitializationConfig 方法通過 getSpringConfigL ocations 來獲得配置文件名稱數(shù)組。

          protected String[] getSpringConfigLocations() {
          //獲得默認配置文件的路徑
          String[] locations = getStandardConfigLocations();
          for (int i = 0; i < locations.length; i++) {
          String extension = StringUtils. getFilenameExtens ion(locations[i]);
          locations[i] = locations[i]. substring(0,
          locations[i].length() - extensio
          n.1ength() - 1) + "-spring.
          t extension;
          return locations;
          }

          上述代碼中,通過
          getStandardConfigLocations 獲得 了默認配置文件名稱數(shù)組,然后對路徑中的文件名進行兼容處理,比如默認配置文件名稱為 logback.xml ,當我們配置為logback-spring.xml 時 , 通 過 getSelfInitializationConfig 方 法 無 法 加 載 到 , 但 通 過getStandard-ConfigLocations 方法則可以加載到。上述方 法核心處理就是將默認的配置文件名截取之后拼接上了“-spring"。

          其中兩個方法都調(diào)用了
          getStandardConfigLocations 方法為 AbstractLoggingSystem 的抽象方法,具體實現(xiàn)由具體日志框架的子類來完成,比如在 L ogbackL oggingSystem 中,該方法的實現(xiàn)如下。

          protected String[] getStandardConfigLocations() {
          return new String[] { "logback- test . groovy", "logback-test . xml", "logbac
          k. groovy"

          "logback.xml" };
          }

          也就是說,LogbackLoggingSystem 默認支持以 logback-test.groovy、logback-test.xml、logback.groovy、logback.xm 以及 上述名稱擴展了“-spring”(比如 logback-spring.xml)的配置文件。

          無論是通過配置指定配置文件名稱,還是通過上述默認方式獲得配置文件名稱,當獲得之后,都會調(diào)用 loadConfiguration 方法進行配置的加載。loadConfiguration 方法由子類來實現(xiàn),比如,LogbackLoggingSystem 中實現(xiàn)的源碼如下。

          @Override
          protected void loadConfiguration(LoggingInitializat ionContext initializat
          String location, LogFile logFile)
          {
          super . loadConfiguration(initializationContext, location, logFile);
          //獲得 LoggerContext, 這里實際上是 ILoggerFac tory 的具體實現(xiàn)
          LoggerContext loggerContext = getLoggerContext();
          stopAndReset(loggerContext);
          configureByResourceUrl(initializationContext, loggerContext,
          } catch (Exception ex) {
          ResourceUtils.getURL(location));
          }
          }
          }

          上述代碼中首先會調(diào)用父類的 loadConfiguration 方法,該方法的最終操作還是調(diào)用了前面講到的 LoggingSystemProperties#apply 方法進行參數(shù)的獲取,并設(shè)置到系統(tǒng)屬性中。

          getL oggerContext 獲得了 LoggerContext 對象,本質(zhì)上 LoggerContext 是 ILoggerFactory的具體實現(xiàn)類。隨后通過 stopAndReset 方法對日志相關(guān)的參數(shù)、監(jiān)聽器等進行停止和重置。

          configureByResourceUrl 方法重點實現(xiàn)了針對 xml 格式的配置文件和其他格式(比如 groovy后綴)的配置文件的解析和具體配置,相關(guān)操作由對應(yīng)的日志框架內(nèi)部提供的類來實現(xiàn)。

          最 后 , 再 回 到 AbstractL
          oggingSystemtinitializeWithConventions 方 法 中 調(diào) 用 的IloadDefaults 方法,看看當未查找到配置文件時是如何處理的。

          loadDefaults 方法同樣是抽象方法,在 LogbackLoggingSystem 中的具體實現(xiàn)如下。

          @Override
          protected void loadDefaults(LoggingInitializationContext initializationCo
          ntext, LogFile
          //獲得 L oggerContext(ILoggerFactory)并進行重置操作
          secrerecntxt BetLossercontext();
          //獲得是否為 debug 模式
          boolean debug = Boolean.getBoolean("logback. debug");
          //如果為 debug 模式則添加控制臺監(jiān)聽器
          StatusL istenerConfi gHelper . addOnConsoleL istenerInstance(context, new On
          Statuslistener
          //根據(jù)是否為 debug 模式創(chuàng)建不同的 L ogbackConfigurator
          LogbackConfigurator configurator = debug ? new Debugl ogbackConfigurator(c
          : new LogbackConfigurator(context);i 配置白志級別將( LEVEL PATTERNE 默認 5setnmironomnti
          context . putProperty(LoggingSystemProperties.L0G_ LEVEL_ PATTERN,
          environment . resolvePlaceholders("${logging. pattern.le
          vel:${LOG_ LEVEL_
          PATTERN:%5p}}"));
          //配置日志中時間格式
          (LOG DATEFORMAT_ PATTERN), 默認為 y>y -M-dd HH: m:ss.sssS
          oggingSystemProperties.
          LOG
          DATEFORMAT_
          PATTERN,
          environment.
          resolvePlacehold
          0BB1
          "${logging . pattern.
          dateformat:${LOG_ DATEFORMAT_ PATTERN:yyy-MM-dd HH:
          m.SS}"));
          //配置文件名稱格式
          (ROLLING FILE_ _NAME_ PATTERN),默認為${LOG _FILE}. %d{yyy -MM-dd}. %i.gz}
          (logingSystemProperties . RLLING FILE NANE PATTERN, environment
          .resolvePlaceholders("${logging. pattern. rolling-file-name:${LOG_ FILE} .
          %yy--d}- .%i.g}));
          new DefaultLogbackConfiguration(initializationContext, logFile). apply(confi
          gurator);
          context . setPackagingDataEnabled(true);
          }

          以 上 代 碼 主 要 進 行 了 LoggerContext 重 置 、 日志輸出格 式 、 日志文 件 名 、Logback-Configurator (編程風格配置 logback)等設(shè)置,具體操作的作用可對照上述代碼中的注解進行了解。

          回到最初 L oggingApplicationListener 的 initializeSystem 方法,值得注意的是,在通常情況下,該方法中往往不是直接采用 LoggingSystem 的抽象類 AbstractL oggingSystem 中的initialize 方法實現(xiàn),而是通過不同日志框架重新實現(xiàn),在恰當?shù)臅r機會調(diào)用 AbstractL oggingSystem 的 initialize 方法。這樣做的好處是可以根據(jù)不同的日志框架進行定制化的擴展。比如LogbackL oggingSystem 中 initialize 方法的實現(xiàn)如下。

          @Override
          public void initialize(LoggingInitializationContext initializationContex
          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(LogbackL oggingSystem. class . getName()) .warn(
          "Ignoring '” + CONFIGURATION_ FILE_ PROPERTY +”' system property.'
          + "
          Please use ' logging. config' instead.");
          }
          }

          我們可以看到,在 LogbackL oggingSystem 的實現(xiàn)類中不僅在調(diào)用父類的 initialize 方法之前進行了是否已經(jīng)初始化的判斷,還在調(diào)用父類 initialize 方法之后,實現(xiàn)了它自己的一些業(yè)務(wù)邏輯,比如移除 LoggerContext 的 TurboFilterList 中添加的 TurboFilter、標記初始化狀態(tài)。

          在完成了以上步驟之后,日志系統(tǒng)已經(jīng)正式啟動,可以進行正常的日志輸至此,針 對
          LoggingApplicationListener 中 ApplicationEnvironmentPreparedEvent 事件的處理已經(jīng)講解完畢。


          小結(jié)

          本章詳細介紹了 Spring Boot 啟動過程中日志事件的觸發(fā),以及事件發(fā)布之后,日志系統(tǒng)所對應(yīng)的處理。在
          LoggingApplicationListener 的 onApplicationEvent 方法中還有其他事件的處理,比如:

          ApplicationPreparedEvent、ContextClosedEvent 、ApplicationFailedEvent 等,但相對于上述過程,這些事件的日志處理比較簡單,讀者可自行閱讀。當然,如果對日志系統(tǒng)感興趣,可針對具體的技術(shù)框架進行更加深入地學習。

          本文給大家講解的內(nèi)容是Spring Boot日志源碼解析:LoggingApplicationListener的執(zhí)行

          1. 下篇文章給大家講解的是創(chuàng)建SpringBoot自動配置項目;

          2. 覺得文章不錯的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;

          3. 感謝大家的支持!


          本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學習更多的話可以到微信公眾號里找我,我等你哦。

          瀏覽 99
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天操夜夜操 | 黄色电影免费看片 | 香蕉久久草 | 亚洲AV日韩AV永久无码网站 | 中文乱伦字幕 |