历史背景:这个问题最终完全没有我认为的那样。原因和解决方案如下,但原始发布仅供参考。
我正在开发一个简单的框架,用于定期轮询.properties文件的目录,然后执行SQL查询并根据其配置发送电子邮件。因为每个.properties文件具有相同的操作范围,所以它们都由同一个Task类解释。但由于它们各自代表不同的逻辑操作,因此它们各自获得单独的日志文件。
这是通过共享log4j RollingFileAppender的一个实例,并根据.properties文件中的值动态更改其输出文件来实现的。由于这是一个单线程应用程序,因此工作正常。
但是,我注意到在某些情况下,这个RollingFileAppender将被关闭,应用程序将继续不经意地继续,除了现在没有进行日志记录。由于控制台输出,我只能设法捕获这一次,因为通常此服务作为Linux服务器上的后台进程运行。这是发生的事情:
1)主类StartScheduler每分钟创建一个新的TaskPoller实例。
2)TaskPoller扫描目录,从每个.properties文件中加载一些信息,并确定是否应该运行它。它还有自己独立的RollingFileAppender,它通过Logger.getLogger(TaskPoller.class)检索。如果应该运行一个Task,那么它会实例化一个Task对象,传入要运行的特定.properties文件。
3)Task获取其RollingFileAppender,然后调用fileAppender.setFile(“newtaskname.log”)和fileAppender.activateOptions()来更改输出文件位置。然后,在执行期间,会发生类似这样的事情:
[TaskPoller]
...
task = new Task(fileName); //Points RollingFileAppender to the right place
if (!task.Execute())
logger.warn(fileName + " returned with an error code."); //Succeeds
[Task.Execute]
...
try {
dbDAO.Connect();
} catch (Exception e) {
logger.fatal{"Database connection error.", e}; //Different RFA; Fails
return false;
}
[DBDAO.Connect throws SQLException, ClassNotFoundException]
...
try {
Class.forName(dbDriver); //Dynamically loaded jdbc driver class name
connection = DriverManager.getConnection(urlString, userName, password);
} catch (SQLException e) {
if (connection != null)
try { connection.close(); } catch (Exception e2) { ; }
throw e;
}
正在发生的事情是,在DBDAO.Connect()期间,有时我会得到一个com.mysql.jdbc.exceptions.jdbc4.CommunicationsException(或者从加载的任何jdbc类中获得一些其他意外的异常)。这不会被Connect()捕获,但它将被Execute()捕获。
不知何故,此过程导致Task的RollingFileAppender关闭。对于这种情况,我唯一可以想到的是,与其一致且稳定的正常操作相反,抛出的异常不会被Connect()声明为抛出。但我不认为这会导致log4j Appender关闭。
所以我的问题是,是什么原因导致这个appender在与其配置无关的方法中意外关闭?
- Edit-- 看起来我完全被误导了;问题出在Quartz之间的交互中,我每分钟都会使用TaskPoller和log4j。我还没有完全理解它的原因,但[这个解决方案] [1]似乎解决了这个问题。它直到现在才表现为观察到的问题,所以我认为它与最近发生的事情有关。
答案 0 :(得分:0)
这个问题的真正原因是Quartz调度程序和我使用log4j的方式之间的交互。事实证明,如果你在Quartz工作线程上修改log4j的属性(我通过调用fileAppender.setFile(fileName)和fileAppender.activateOptions()来做)(即使Quartz配置为一次只运行一个线程) ),事情崩溃了。这是通过在使用它之前在工作线程的每个新实例上重新加载log4j属性来解决的,我完成了这样做:
[Task() Constructor]
Properties props = new Properties();
URL url = ClassLoader.getSystemResource("log4j.properties");
try {
props.load(url.openStream());
PropertyConfigurator.configure(props);
} catch (Exception e) {
//The logger that never got renamed never stopped working.
Logger.getLogger(TaskPoller.class).error("Diagnostics!");
}
logger = Logger.getLogger(Task.class);