• sofa-bolt源码阅读(5)-日志


    6.1 日志类型

    bolt的日志文件默认位于{user.home}/logs/bolt目录下

    文件名 说明
    common-default.log 普通日志
    common-error.log 错误级别日志
    connection-event.log 连接相关的日志
    remoting-rpc.log rpc协议相关的日志

    6.2 日志实现

    bolt只依赖SLF4j门面框架,可以支持三种类型的日志实现:log4j, log4j2, 和 logback。运行时根据具体项目引入的日志框架,实现日志的自适应打印。

    日志隔离的功能是工具包sofa-common-tools实现的。

    日志的入口类是BoltLoggerFactory

    private static final Logger logger = BoltLoggerFactory.getLogger("CommonDefault");
    

    获取名称为CommonDefault的日志,这个名称是在日志配置文件中配置的。

    <AsyncLogger name="CommonDefault" level="${LOG_LEVEL}" additivity="false">
        <appender-ref ref="CommonDefaultAppender"/>
        <appender-ref ref="ERROR-APPENDER"/>
    </AsyncLogger>
    

    日志空间 space

    public static Logger getLogger(String name) {
        if (name == null || name.isEmpty()) {
            return null;
        }
        return LoggerSpaceManager.getLoggerBySpace(name, BOLT_LOG_SPACE);
    }
    

    spaceName参数BOLT_LOG_SPACE的值是com.alipay.remoting,表示的是日志空间的名称。

    不同的space使用不同的配置文件。例如space名称为com.alipay.remoting时,bolt会尝试从classpath下的com.alipay.remoting.log目录下寻找配置文件。resouces下有三种日志框架的配置文件,如图

    自适应日志框架

    com.alipay.sofa.common.log.LoggerSpaceManager#getLoggerBySpace
    
    public static Logger getLoggerBySpace(String name, String spaceName) {
        return getLoggerBySpace(name, new SpaceId(spaceName),
        Collections.<String, String> emptyMap());
    }
    
    public static Logger getLoggerBySpace(String name, SpaceId spaceId,
                                          Map<String, String> properties) {
        //init first
        init(spaceId, properties);
        return MultiAppLoggerSpaceManager.getLoggerBySpace(name, spaceId);
    }
    

    MultiAppLoggerSpaceManager中实现了自适应逻辑

    public static Logger getLoggerBySpace(String name, SpaceId spaceId, ClassLoader spaceClassloader) {
        //创建日志工厂类
        AbstractLoggerSpaceFactory abstractLoggerSpaceFactory = getILoggerFactoryBySpaceName(
            spaceId, spaceClassloader);
        return abstractLoggerSpaceFactory.getLogger(name);
    }
    

    创建日志工厂方法已经返回的是具体的日志工厂实现类(由构造器LoggerSpaceFactoryBuilder的实现类构建)

    • log4j -> LoggerSpaceFactory4Log4jBuilder
    • log4j2 -> LoggerSpaceFactory4Log4j2Builder
    • logback -> LoggerSpaceFactory4LogbackBuilder
    • commons-log -> LoggerSpaceFactory4CommonsLoggingBuilder

    从工厂类中获取日志对象Logger并返回

    创建日志工厂

    private static AbstractLoggerSpaceFactory createILoggerFactory(SpaceId spaceId,
                                               ClassLoader spaceClassloader) {
        if (System.getProperty(SOFA_MIDDLEWARE_LOG_DISABLE_PROP_KEY) != null
            && Boolean.TRUE.toString().equalsIgnoreCase(
                System.getProperty(SOFA_MIDDLEWARE_LOG_DISABLE_PROP_KEY))) {
            ReportUtil.reportWarn("Sofa-Middleware-Log is disabled!  -D"
                                  + SOFA_MIDDLEWARE_LOG_DISABLE_PROP_KEY + "=true");
            return NOP_LOGGER_FACTORY;
        }
        // 设置日志的配置文件
        LogEnvUtils.processGlobalSystemLogProperties();
    
        try {
            if (LogEnvUtils.isLogbackUsable(spaceClassloader)) {
                String isLogbackDisable = System
                    .getProperty(LOGBACK_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
                if (isLogbackDisable != null
                    && Boolean.TRUE.toString().equalsIgnoreCase(isLogbackDisable)) {
                    ReportUtil.reportWarn("Logback-Sofa-Middleware-Log is disabled!  -D"
                                          + LOGBACK_MIDDLEWARE_LOG_DISABLE_PROP_KEY + "=true");
                } else {
                    ReportUtil.report("Actual binding is of type [ " + spaceId.toString()
                                      + " Logback ]");
                    LoggerSpaceFactoryBuilder loggerSpaceFactory4LogbackBuilder = new LoggerSpaceFactory4LogbackBuilder(
                        spacesMap.get(spaceId));
    
                    return loggerSpaceFactory4LogbackBuilder.build(spaceId.getSpaceName(),
                        spaceClassloader);
                }
            }
    
            if (LogEnvUtils.isLog4j2Usable(spaceClassloader)) {
                String isLog4j2Disable = System.getProperty(LOG4J2_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
                if (isLog4j2Disable != null
                    && Boolean.TRUE.toString().equalsIgnoreCase(isLog4j2Disable)) {
                    ReportUtil.reportWarn("Log4j2-Sofa-Middleware-Log is disabled!  -D"
                                          + LOG4J2_MIDDLEWARE_LOG_DISABLE_PROP_KEY + "=true");
                } else {
                    ReportUtil.report("Actual binding is of type [ " + spaceId.toString()
                                      + " Log4j2 ]");
                    LoggerSpaceFactoryBuilder loggerSpaceFactory4Log4j2Builder = new LoggerSpaceFactory4Log4j2Builder(
                        spacesMap.get(spaceId));
    
                    return loggerSpaceFactory4Log4j2Builder.build(spaceId.getSpaceName(),
                        spaceClassloader);
                }
            }
    
            if (LogEnvUtils.isLog4jUsable(spaceClassloader)) {
                String isLog4jDisable = System.getProperty(LOG4J_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
                if (isLog4jDisable != null
                    && Boolean.TRUE.toString().equalsIgnoreCase(isLog4jDisable)) {
                    ReportUtil.reportWarn("Log4j-Sofa-Middleware-Log is disabled!  -D"
                                          + LOG4J_MIDDLEWARE_LOG_DISABLE_PROP_KEY + "=true");
                } else {
                    ReportUtil.report("Actual binding is of type [ " + spaceId.toString()
                                      + " Log4j ]");
                    LoggerSpaceFactoryBuilder loggerSpaceFactory4Log4jBuilder = new LoggerSpaceFactory4Log4jBuilder(
                        spacesMap.get(spaceId));
    
                    return loggerSpaceFactory4Log4jBuilder.build(spaceId.getSpaceName(),
                        spaceClassloader);
                }
            }
    
            if (LogEnvUtils.isCommonsLoggingUsable(spaceClassloader)) {
                //此种情形:commons-logging 桥接到 log4j 实现,默认日志实现仍然是 log4j
                String isLog4jDisable = System
                    .getProperty(LOG4J_COMMONS_LOGGING_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
                if (isLog4jDisable != null
                    && Boolean.TRUE.toString().equalsIgnoreCase(isLog4jDisable)) {
                    ReportUtil
                        .reportWarn("Log4j-Sofa-Middleware-Log(But adapter commons-logging to slf4j) is disabled!  -D"
                                    + LOG4J_COMMONS_LOGGING_MIDDLEWARE_LOG_DISABLE_PROP_KEY
                                    + "=true");
                } else {
                    ReportUtil.report("Actual binding is of type [ " + spaceId.toString()
                                      + " Log4j (Adapter commons-logging to slf4j)]");
    
                    LoggerSpaceFactoryBuilder loggerSpaceFactory4Log4jBuilder = new LoggerSpaceFactory4CommonsLoggingBuilder(
                        spacesMap.get(spaceId));
    
                    return loggerSpaceFactory4Log4jBuilder.build(spaceId.getSpaceName(),
                        spaceClassloader);
                }
            }
    
            ReportUtil.reportWarn("No log util is usable, Default app logger will be used.");
        } catch (Throwable e) {
            ReportUtil.reportError("Build ILoggerFactory error! Default app logger will be used.",
                e);
        }
    
        return NOP_LOGGER_FACTORY;
    }
    

    LogEnvUtils用来判断是否存在相应的类包,例如判断类路径下是否存在log4j2的jar包

    public static boolean isLog4j2Usable(ClassLoader spaceClassloader) {
        AssertUtil.notNull(spaceClassloader);
        try {
            return (spaceClassloader.loadClass("org.apache.logging.slf4j.Log4jLoggerFactory") != null);
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
    

    如果存在多个日志jar包,代码中可以看出优先级为

    1. logback
    2. log4j2
    3. log4j
    4. commons-log
    5. default

    按顺序一旦找到某一日志jar包,就调用相应构造器的build方法进行构建。

    创建Log4j2工厂

    @Override
    public AbstractLoggerSpaceFactory build(String spaceName, ClassLoader spaceClassloader) {
        AssertUtil.hasText(spaceName);
        AssertUtil.notNull(spaceClassloader);
    
        //load config file
        URL configFileUrl = getSpaceLogConfigFileURL(spaceClassloader, spaceName);
    
        // set default logging.level
        specifySpaceLogConfigProperites(spaceName);
    
        return doBuild(spaceName, spaceClassloader, configFileUrl);
    }
    
    1. 加载配置文件

      private URL getSpaceLogConfigFileURL(ClassLoader spaceClassloader, String spaceName) {
          String suffix = LogEnvUtils.getLogConfEnvSuffix(spaceName);
      
          //TODO avoid this pattern "log-conf.xml.console"
          String logConfigLocation = spaceName.replace('.', '/') + "/" + LOG_DIRECTORY + "/"
                                     + getLoggingToolName() + "/" + LOG_XML_CONFIG_FILE_NAME + suffix;
          URL configFileUrl = spaceClassloader.getResource(logConfigLocation);
      
          //recommend this pattern "log-conf-console.xml"
          if (configFileUrl == null && suffix != null && !suffix.isEmpty()) {
              //try again with another env profile file pattern;
              logConfigLocation = spaceName.replace('.', '/')
                                  + "/"
                                  + LOG_DIRECTORY
                                  + "/"
                                  + getLoggingToolName()
                                  + "/"
                                  + String.format(LOG_XML_CONFIG_FILE_ENV_PATTERN,
                                      suffix.substring(1));
              configFileUrl = spaceClassloader.getResource(logConfigLocation);
          }
          AssertUtil.state(configFileUrl != null, this + " build error: No " + getLoggingToolName()
                                                  + " config file (" + configFileUrl + ") found!");
          return configFileUrl;
      }
      

      日志文件名后缀

      // 指定特定spaceName的环境配置,值格式: spaceName1:dev&spaceName2:test&spaceName3:product
      String LOG_ENV_SUFFIX                                        = "log.env.suffix";
      

      日志配置文件路径

      参数
      suffix 未配置,空字符串
      LOG_DIRECTORY log
      getLoggingToolName() logback
      log4j2
      log4j
      LOG_XML_CONFIG_FILE_NAME log-conf.xml

      日志配置文件可能的地址是

      • com/alipay/remoting/log/log4j2/log-conf.xml
      • com/alipay/remoting/log/log4j2/log-conf.xml.console
      • com/alipay/remoting/log/log4j2/log-conf-console.xml
    2. 设置默认日志级别

      private void specifySpaceLogConfigProperites(String spaceName) {
          //如果system.properties 与 properites 都含有某分配置,那么以 system.properties 为准,同时WARN警告,properties中重复定义会被抛弃;
          Iterator<Map.Entry<Object, Object>> iterator = spaceInfo.properties().entrySet().iterator();
          while (iterator.hasNext()) {
              Map.Entry<Object, Object> entry = iterator.next();
              if (System.getProperties().containsKey(entry.getKey())) {
                  iterator.remove();
                  logger.warn(
                      "Props key {}  is also already existed in System.getProps ({}:{}),so use it!",
                      entry.getKey(), entry.getKey(), System.getProperty((String) entry.getKey()));
              }
          }
      
          /**
           * == 1.space's logger path
           */
          String loggingPathKey = LOG_PATH_PREFIX + spaceName;
          if (System.getProperty(loggingPathKey) == null && System.getProperty(LOG_PATH) != null
              && spaceInfo.properties().getProperty(loggingPathKey) == null) {
              spaceInfo.properties().setProperty(loggingPathKey, System.getProperty(LOG_PATH));
          }
      
          /**
           * == 2.space's logger level
           */
          String loggingLevelKey = LOG_LEVEL_PREFIX + spaceName;
          if (System.getProperty(loggingLevelKey) == null
              && spaceInfo.properties().getProperty(loggingLevelKey) == null) {
              spaceInfo.properties().setProperty(loggingLevelKey,
                  Constants.DEFAULT_MIDDLEWARE_SPACE_LOG_LEVEL);
          }
      
      }
      
    3. 构建

      com.alipay.sofa.common.log.factory.LoggerSpaceFactory4Log4j2Builder#doBuild
      @Override
      public AbstractLoggerSpaceFactory doBuild(String spaceName, ClassLoader spaceClassloader,
                                                URL url) {
      
          try {
              final LoggerContext context = new LoggerContext(spaceName, null, url.toURI());
              Configuration config = null;
              ConfigurationFactory configurationFactory = ConfigurationFactory.getInstance();
              try {
                  //log4j-core 2.3 version
                  Class[] parameterTypes = new Class[3];
                  parameterTypes[0] = String.class;
                  parameterTypes[1] = URI.class;
                  parameterTypes[2] = ClassLoader.class;
                  Method getConfigurationMethod = configurationFactory.getClass().getMethod(
                      "getConfiguration", parameterTypes);
                  config = (Configuration) getConfigurationMethod.invoke(configurationFactory,
                      spaceName, url.toURI(), spaceClassloader);
              } catch (NoSuchMethodException noSuchMethodException) {
                  //log4j-core 2.8 version
                  Class[] parameterTypes = new Class[4];
                  parameterTypes[0] = LoggerContext.class;
                  parameterTypes[1] = String.class;
                  parameterTypes[2] = URI.class;
                  parameterTypes[3] = ClassLoader.class;
                  Method getConfigurationMethod = configurationFactory.getClass().getMethod(
                      "getConfiguration", parameterTypes);
                  config = (Configuration) getConfigurationMethod.invoke(configurationFactory,
                      context, spaceName, url.toURI(), spaceClassloader);
              }
              if (config == null) {
                  throw new RuntimeException("No log4j2 configuration are found.");
              }
              for (Map.Entry entry : getProperties().entrySet()) {
                  //from Map<String,String>
                  config.getProperties().put((String) entry.getKey(), (String) entry.getValue());
              }
      
              for (Map.Entry entry : System.getProperties().entrySet()) {
                  //from Map<String,String>
                  config.getProperties().put((String) entry.getKey(), (String) entry.getValue());
              }
      
              context.start(config);
      
              return new AbstractLoggerSpaceFactory(getLoggingToolName()) {
      
                  private ConcurrentMap<String, org.apache.logging.slf4j.Log4jLogger> loggerMap = new ConcurrentHashMap<String, org.apache.logging.slf4j.Log4jLogger>();
      
                  @Override
                  public Logger setLevel(String loggerName, AdapterLevel adapterLevel)
                                                                                      throws Exception {
                      org.apache.logging.slf4j.Log4jLogger log4jLoggerAdapter = (org.apache.logging.slf4j.Log4jLogger) this
                          .getLogger(loggerName);
                      final String key = Logger.ROOT_LOGGER_NAME.equals(loggerName) ? LogManager.ROOT_LOGGER_NAME
                          : loggerName;
                      org.apache.logging.log4j.core.Logger log4j2Logger = context.getLogger(key);
                      //level
                      org.apache.logging.log4j.Level log4j2Level = this.toLog4j2Level(adapterLevel);
                      log4j2Logger.setLevel(log4j2Level);
                      return log4jLoggerAdapter;
                  }
      
                  @Override
                  public org.slf4j.Logger getLogger(String name) {
                      final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME
                          : name;
                      org.apache.logging.log4j.core.Logger log4jLogger = context.getLogger(key);
                      Log4jLogger oldInst = this.loggerMap.get(key);
                      if (oldInst != null) {
                          return oldInst;
                      }
                      Log4jLogger newInst = new Log4jLogger(log4jLogger, key);
                      oldInst = this.loggerMap.putIfAbsent(key, newInst);
                      return oldInst == null ? newInst : oldInst;
                  }
      
                  private org.apache.logging.log4j.Level toLog4j2Level(AdapterLevel adapterLevel) {
                      if (adapterLevel == null) {
                          throw new IllegalStateException(
                              "AdapterLevel is NULL when adapter to log4j2.");
                      }
                      switch (adapterLevel) {
                          case TRACE:
                              return Level.TRACE;
                          case DEBUG:
                              return Level.DEBUG;
                          case INFO:
                              return Level.INFO;
                          case WARN:
                              return Level.WARN;
                          case ERROR:
                              return Level.ERROR;
                          default:
                              throw new IllegalStateException(adapterLevel
                                                              + " is unknown when adapter to log4j2.");
                      }
                  }
              };
          } catch (Throwable e) {
              throw new IllegalStateException("Log4j2 loggerSpaceFactory build error!", e);
          }
      }
      

      调用ConfigurationFactory解析log4j2的配置文件,然后返回匿名类。匿名类的getLogger方法就是前面生成工厂类后获取Logger对象时调用的。

  • 相关阅读:
    bzoj 2138: stone
    LOJ #6062. 「2017 山东一轮集训 Day2」Pair
    bzoj 5341: [Ctsc2018]暴力写挂
    UOJ #356. 【JOI2017春季合宿】Port Facility
    UOJ #357. 【JOI2017春季合宿】Sparklers
    UOJ #349. 【WC2018】即时战略
    bzoj 3600: 没有人的算术
    Codeforces 960G. Bandit Blues
    codeforces524E
    codeforces193B
  • 原文地址:https://www.cnblogs.com/huiyao/p/12436780.html
Copyright © 2020-2023  润新知