Fork me on GitHub
21 May 2019

在使用“httpclient”组件的项目中,当项目的日志级别为”DEBUG”时它会持续输出连接超时关闭的日志,干扰正常业务日志,因此需要关闭PoolingHttpClientConnectionManager的”DEBUG”日志。

以“log4j-1.2.17”为例,如下配置即可。

log4j.logger.org.apache.http.impl.conn.PoolingHttpClientConnectionManager=INFO

接下来我们来看看为什么如此配置。

log4j 通过PropertyConfigurator类载入配置文件。

// 配置文件的 logger 前缀
static final String      LOGGER_PREFIX   = "log4j.logger.";

// 解析配置元素
protected void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
    Enumeration enumeration = props.propertyNames();
    while(enumeration.hasMoreElements()) {
        String key = (String) enumeration.nextElement();
        if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
        String loggerName = null;
        if(key.startsWith(CATEGORY_PREFIX)) {
            loggerName = key.substring(CATEGORY_PREFIX.length());
        } else if(key.startsWith(LOGGER_PREFIX)) {
            // 解析配置的 loggerName
            loggerName = key.substring(LOGGER_PREFIX.length());
        }
        String value =  OptionConverter.findAndSubst(key, props);
        Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
        
        // ...
        }
    }
}

从上可得出可通过”loggerName”配置该”logger”的日志级别。PoolingHttpClientConnectionManager类通过LogFactory.getLog(getClass())获取”logger”实例,”loggerName”即为该类的类全限定名。所以,以上配置即可。

对于”Spring Boot”项目,可以如下配置:

# 定义日志组
logging.group.httpLog=org.apache.http.impl.conn.PoolingHttpClientConnectionManager
# 设置该组的输出级别
logging.level.httpLog=info

代码解析:

LoggingApplicationListener类负责解析配置。

// 日志级别, 配置项前缀
private static final ConfigurationPropertyName LOGGING_LEVEL = ConfigurationPropertyName
			.of("logging.level");

// 日志组, 配置项前缀
private static final ConfigurationPropertyName LOGGING_GROUP = ConfigurationPropertyName
			.of("logging.group");

// 解析日志相关配置
protected void setLogLevels(LoggingSystem system, Environment environment) {
    if (!(environment instanceof ConfigurableEnvironment)) {
        return;
    }
    Binder binder = Binder.get(environment);
    Map<String, String[]> groups = getGroups();
    binder.bind(LOGGING_GROUP, STRING_STRINGS_MAP.withExistingValue(groups));
    Map<String, String> levels = binder.bind(LOGGING_LEVEL, STRING_STRING_MAP)
            .orElseGet(Collections::emptyMap);
    // 配置日志组的输出级别
    levels.forEach((name, level) -> {
        String[] groupedNames = groups.get(name);
        if (ObjectUtils.isEmpty(groupedNames)) {
            setLogLevel(system, name, level);
        }
        else {
            setLogLevel(system, groupedNames, level);
        }
    });
}