美文网首页
SpringBoot logging -3

SpringBoot logging -3

作者: 程序员札记 | 来源:发表于2023-12-03 09:38 被阅读0次

企业级的logging 通常有自己特殊的需求,因此就需要覆盖default spring boot 的logging 配置。 通常有三方面的需求

  • 向后兼容性。 由于历史原因,企业用的log 配置文件通常不会是springboot 支持的名字, 那么在migrate 到spring boot 后,需要向后兼容。也要求springboot 能支持同样名字的log file。
  • 动态变化logging level。 在启动阶段,期望能看到更低level的 log 比如info, 在启动结束后,为了避免太多的log, 需要动态改变log 的level到更高的level 比如warning。
  • 在不同环境, 比如在本地开发和线上运行,需要的appender 是不一样的。 因此也需要根据情况来加载不同配置的log 文件

根据前文的分析,如果LoggingApplicationListener在initialize的时候,如果环境变量已经设置。则就用设置过的log文件。 那么要覆盖LoggingApplicationListener 的逻辑,必须在它之前运行并且设置环境变量。
LoggingApplicationListener的顺序定义为
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 20;

那么我们可以设计一个listener 顺序为Ordered.HIGHEST_PRECEDENCE + 19

public class LogbackInitializer implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

  @Override
  public int getOrder() {
    return Ordered.HIGHEST_PRECEDENCE + 19;
  }
}

然后ApplicationEnvironmentPreparedEvent 时候来根据环境加载log文件同时设置环境变量


  @Override
  public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    init(event.getEnvironment());
  }
  private void init(Environment environment) {
    /*
     * Check to see if Spring Boot's "logging.config" property has already been set.
     * If it is has, we do not configure Raptor's logger.
     */
    String springLogbackConfigLocation = environment.getProperty(SPRING_LOGGING_CONFIG_PROPERTY);

    /*
     * If the "logging.config" property was not set, check the Logger component configuration.
     * Fall-back to the default logback configuration if no external configuration was specified.
     */

    if (springLogbackConfigLocation != null) {
      initLogback(springLogbackConfigLocation, environment);
    } else {
      // Reset Logback's status manager to re-initialize.
      resetLoggerContext();

      String logbackConfigLocation = environment.getProperty(_LOGGER_CONFIG_PROPERTY);
      initLogback(logbackConfigLocation != null ? logbackConfigLocation : getDefaultLogbackConfigLocation(environment), environment);
    }
  void initLogback(String logbackConfigLocation, Environment environment) {
    //protection log setting it should not be set on logback-default.xml other than dev by default
    if (DEFAULT_DEV_LOGBACK_CONFIG.equals(logbackConfigLocation) && !isDev(environment)) {
      System.out.println(
          "logback-default.xml configuration found in non-dev environment, override to default logback-raptor.xml!");
      logbackConfigLocation = getDefaultLogbackConfigLocation(environment);
    }

    System.out.println("Initializing Logback with configuration: " + logbackConfigLocation);

    // Always set both configuration properties to keep everything in sync.
    System.setProperty(_LOGGER_CONFIG_PROPERTY, logbackConfigLocation);
    System.setProperty(SPRING_LOGGING_CONFIG_PROPERTY, logbackConfigLocation);

    initLogHome(environment);
  }
  }

在LoggingApplicationListener加载之后 需要动态改变log level

需要再写一个listener

public class StartupLogCapturer implements ApplicationListener<SpringApplicationEvent>, Ordered {
  @Override
  public int getOrder() {
    return Ordered.HIGHEST_PRECEDENCE + 21;
  }
}

在不同阶段来改变log level


  @Override
  public void onApplicationEvent(SpringApplicationEvent springEvent) {
    if (springEvent instanceof ApplicationEnvironmentPreparedEvent) {
      lowerRootLoggerLevel((ApplicationEnvironmentPreparedEvent) springEvent);
    } else if (springEvent instanceof ApplicationReadyEvent) {
      restoreRootLoggerLevel((ApplicationReadyEvent) springEvent);
    }
  }

具体逻辑

  private void lowerRootLoggerLevel(ApplicationEnvironmentPreparedEvent event) {
    Logger rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    if (rootLogger instanceof ch.qos.logback.classic.Logger) {
      ch.qos.logback.classic.Logger logbackRootLogger = (ch.qos.logback.classic.Logger) rootLogger;
      originalLevel = logbackRootLogger.getLevel();
      if (originalLevel.toInt() > STARTUP_LOG_LEVEL.toInt()) {
        logbackRootLogger.setLevel(STARTUP_LOG_LEVEL);
        LOGGER.info("Lowering the ROOT logger level from {} to {} to capture Raptor's startup logs.", originalLevel,
            STARTUP_LOG_LEVEL);
      }
    }
  }

  private void restoreRootLoggerLevel(ApplicationReadyEvent event) {
    Logger rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    if (rootLogger instanceof ch.qos.logback.classic.Logger) {
      ch.qos.logback.classic.Logger logbackRootLogger = (ch.qos.logback.classic.Logger) rootLogger;
      if (originalLevel != null && originalLevel.toInt() > STARTUP_LOG_LEVEL.toInt()) {
        LOGGER.info("Raptor's startup has completed. Restoring the ROOT logger level back to {}.", originalLevel);
        logbackRootLogger.setLevel(originalLevel);
      }
      logbackRootLogger.detachAppender(STARTUP_APPENDER_NAME);
      redirectStandout();
    }
  }

  private void redirectStandout() {
    Logger stdoutLogger = LoggerFactory.getLogger(STDOUT_LOGGER_NAME);
    if (stdoutLogger != null && stdoutLogger instanceof ch.qos.logback.classic.Logger) {
      ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) stdoutLogger;
      if (logger.getAppender("STDOUT-APPENDER") != null) {
        try {
          System.setOut(new PrintStream(new LogbackOutputStream(logger, Level.toLocationAwareLoggerInteger(Level.INFO)),
              true, "UTF-8"));
          System.setErr(new PrintStream(
              new LogbackOutputStream(logger, Level.toLocationAwareLoggerInteger(Level.ERROR)), true, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
          LOGGER.error("Failed to redirect standout and standerr.", e);
        }
      }
    }
  }

其中

      logbackRootLogger.detachAppender(STARTUP_APPENDER_NAME);
      redirectStandout();

目的就是在运行时候把console logger 去掉,取而代之就是输出Error level 的到文件。

相关文章

网友评论

      本文标题:SpringBoot logging -3

      本文链接:https://www.haomeiwen.com/subject/acukgdtx.html