问题描述:
某个SDK(有一定历史的祖传代码)中自定义了一个logger,并且在应用初始化时调用log4j为该logger配置了Appender、Layout,filePath,logName命名为logA.log。而使用了这个SDK的应用,正好在需要查看这个log时,发现logA.log是存在的,但是文件内容却为空。
结论:翻了一下源码,结合当前使用这个SDK的应用的日志配置,得出结论:
1) SDK中自定义的那个logger的声明,使用的日志框架为common-logging+log4j,虽然通过代码指定了使用log4j为其配置Appender、filePath,但是该应用采用的日志框架为slf4j+logback,SDK中的所有log在运行时其实是使用的logback,以logback配置为输出标准。所以,自然不会输出到log4j指定的file中。
2)在SDK中指定某个日志的具体实现,并配置日志名称、输出路径,理解这种做法的初衷,但是,该log的level、appender、filepath最好是由用户自己按需配置。
场景还原:
抽象出代码如下,sdk-a中指定logName=“log4jLog”,并通过log4j进行配置,本意是想把Log4jTest中的日志单独打印到一个文件中。
package com.sdk.a;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import java.io.File;
import java.util.Enumeration;
import java.util.Properties;
public class Log4jInit {
private static final Log logger = LogFactory.getLog(Log4jInit.class);
static private volatile boolean initOK = false;
public static final String LOG_NAME = "log4jLog";
static private Properties defaultProperties = new Properties();
static {
defaultProperties.put("log4j.logger.log4jLog", "info, D");
defaultProperties.put("log4j.additivity.log4jLog", "false");
defaultProperties.put("log4j.appender.D", "org.apache.log4j.DailyRollingFileAppender");
defaultProperties.put("log4j.appender.D.DatePattern", "'.'yyyy-MM-dd");
defaultProperties.put("log4j.appender.D.File", "log4j_data.log");
defaultProperties.put("log4j.appender.D.layout", "org.apache.log4j.PatternLayout");
defaultProperties.put("log4j.appender.D.layout.ConversionPattern","%d{MM-dd HH:mm:ss} - %m%n");
defaultProperties.put("log4j.appender.D.Append", "true");
}
public static void initLog() {
if (initOK) {
return;
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(Log4jInit.class.getClassLoader());
try {
PropertyConfigurator.configure(defaultProperties);
FileAppender bizFileAppender = new FileAppender();
bizFileAppender.setFile("/app/weblogic/logs/log-demo/log4j.log");
setFileAppender(bizFileAppender, LOG_NAME);
initOK = true;
}
finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
private static void setFileAppender(FileAppender bizFileAppender, String logName) {
FileAppender fileAppender = getFileAppender(Logger.getLogger(logName));
String bizLogDir = new File(bizFileAppender.getFile()).getParent();
File newLogFile = new File(bizLogDir, fileAppender.getFile());
fileAppender.setFile(newLogFile.getAbsolutePath());
fileAppender.activateOptions();
logger.info(logName + ",appender-path:" + newLogFile.getAbsolutePath());
}
}
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Log4jTest {
static {
Log4jInit.initLog();
}
//LOG_NAME在Log4jInit.initLog()中配置
private static final Log log4j = LogFactory.getLog(Log4jInit.LOG_NAME);
public void printLog() {
log4j.info("log4j.info");
}
}
应用使用sdk-a,如果想要特殊处理Logger("log4jLog"),可以自定义file、appender。
<appender name="otherAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/other.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_BAK_DIR}/other.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>${LOG_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_TOTAL_SIZE_CAP}</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] %logger{50}\(%file:%line\) - %msg%n</pattern>
</encoder>
</appender>
<!--可以重新配置log4jLog,若不配置,使用root输出也可以-->
<logger name="log4jLog" level="info" additivity="false">
<appender-ref ref="otherAppender"/>
</logger>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
<appender-ref ref="appAppender"/>
</root>
启动应用,打印sdk-a的Logger(log4jLog),查看日志,可以看出:
1)确实输出了sdk-a中定义的/app/weblogic/logs/log-demo/log4j.log,并且内容为空。
2)Logger(log4jLog)由应用配置的logback,输出到了otherAppender定义的file中。
package com.ls.demo.log.logdemo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.sdk.a.Log4jTest;
@SpringBootApplication
public class LogDemoApplication {
public static void main(String[] args) {
SpringApplication.run(LogDemoApplication.class, args);
//使用sdk-a
Log4jTest log4jTest = new Log4jTest();
log4jTest.printLog();
Logger logger = LoggerFactory.getLogger(LogDemoApplication.class);
logger.info("logback.info");
}
}