6.1 日志类型
bolt的日志文件默认位于/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包,代码中可以看出优先级为
- logback
- log4j2
- log4j
- commons-log
- 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); }
加载配置文件
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
log4jLOG_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
设置默认日志级别
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); } }
构建
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对象时调用的。
来源:https://www.cnblogs.com/huiyao/p/12436780.html