log4j:ERROR Failed to rename
重命名日誌文件時錯誤。
log4j.properties配置如下:
log4j.rootLogger=INFO
log4j.logger.access= INFO,reg
log4j.appender.reg=org.apache.log4j.DailyRollingFileAppender
log4j.appender.reg.layout=org.apache.log4j.PatternLayout
log4j.appender.reg.File=D:\\secureCRT\\sync.log
log4j.appender.reg.DatePattern='.'yyyy-MM-dd
log4j.appender.reg.layout.ConversionPattern=%d - [%p] - [%F\:%L] %m%n
log4j.logger.access2= INFO,A3
log4j.appender.A3=org.apache.log4j.RollingFileAppender
log4j.appender.A3.layout=org.apache.log4j.PatternLayout
log4j.appender.A3.File=D:\\secureCRT\\sync.log
log4j.appender.A3.MaxFileSize=500KB
log4j.appender.A3.layout.ConversionPattern=%d - [%p] - [%F\:%L] %m%n
運行時只使用了reg,沒有使用A3。
原因:reg日誌記錄器配置的是DailyRollingFileAppender,每天產生新文件,而產生新文件是用的File.renameTo(),
在這出錯了。google發現,在windows下原因是有其他的進程在佔用該日誌文件,檢查發現我用記事本打開了這個文件(D:\\secureCRT\\sync.log),於是關掉記事本,再次運行,仍然報這個錯誤。有點奇怪了,除了reg還會有什麼進程在佔用這個文件呢?
然後又檢查log4j.properties,發現reg和A3的日誌文件時配置的同一個(見上面配置文件),會不會這個導致的?
於是,把A3暫且註釋掉,運行通過,產生了新的文件!
也就是說,雖然沒有用到A3,但log4j仍然會加載該配置並實例化了。
爲了驗證,查看源碼(版本1.2.16):
log4j的使用是從如下地方開始的:
Logger logger = Logger.getLogger(Test.class);
進入Logger.getLogger(Test.class):
static public Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
}
進入LogManager類,注意有一段static代碼區,是用於在第一次加載該類時初始化log4j的:
static {
// By default we use a DefaultRepositorySelector which always returns 'h'.
// 以DEBUG級別生成RootLogger
Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
......
//加載系統變量"log4j.configuration",已不推薦使用
String configurationOptionStr = OptionConverter.getSystemProperty(DEFAULT_CONFIGURATION_KEY, null);
String configuratorClassName = OptionConverter.getSystemProperty( CONFIGURATOR_CLASS_KEY, null);
URL url = null;
// 先加載log4j.xml,如果失敗,則加載log4j.properties(推薦使用xml)
// if the user has not specified the log4j.configuration
// property, we search first for the file "log4j.xml" and then
// "log4j.properties"
if(configurationOptionStr == null) {
url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
if(url == null) {
url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
}
} else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException ex) {
// so, resource is not a URL:
// attempt to get the resource from the class path
url = Loader.getResource(configurationOptionStr);
}
}
......
// 加載配置文件
OptionConverter.selectAndConfigure(url, configuratorClassName,
LogManager.getLoggerRepository());
......
}
進入OptionConverter.selectAndConfigure():
void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
Configurator configurator = null;
String filename = url.getFile();
if(clazz == null && filename != null && filename.endsWith(".xml")) {
clazz = "org.apache.log4j.xml.DOMConfigurator";
}
if(clazz != null) {
LogLog.debug("Preferred configurator class: " + clazz);
configurator = (Configurator) instantiateByClassName(clazz,
Configurator.class,
null);
if(configurator == null) {
LogLog.error("Could not instantiate configurator ["+clazz+"].");
return;
}
} else {
configurator = new PropertyConfigurator();
}
configurator.doConfigure(url, hierarchy);
}
根據配置文件類型,選擇不同的加載類,xml則爲DOMConfigurator,properties則爲PropertyConfigurator,
然後調用doConfigure()來加載。
以PropertyConfigurator來看,進入PropertyConfigurator.doConfigure():
public void doConfigure(String configFileName, LoggerRepository hierarchy) {
Properties props = new Properties();
FileInputStream istream = null;
try {
istream = new FileInputStream(configFileName);
props.load(istream);
istream.close();
}
......
// 加載propertie配置文件
// If we reach here, then the config file is alright.
doConfigure(props, hierarchy);
}
進入加粗的方法裏,最後會看到三行代碼:
configureRootCategory(properties, hierarchy);//加載RootLogger
configureLoggerFactory(properties);// 加載LoggerFactory(用於後面生成Logger)
parseCatsAndRenderers(properties, hierarchy);//加載其他日誌記錄器(自定義的)
從上面的源碼可以看出,log4j在第一次調用時,即加載整個配置文件並初始化,所有就可以理解上面這個問題了,雖然A3沒有使用,
但仍然加載初始化了,並佔有日誌文件,導致reg無法rename。