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。