SpringBoot logging -3

企业级的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 的到文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容