3

历史背景:这个问题最终不是我想象的那样。原因及解决方法如下,原贴留作参考。

我正在开发一个简单的框架,用于定期轮询目录中的 .properties 文件,然后执行 SQL 查询并根据它们的配置发送电子邮件。因为每个 .properties 文件都有相同的操作范围,所以它们都由同一个 Task 类解释。但由于它们各自代表不同的逻辑操作,它们各自获得单独的日志文件。

这是通过共享 log4j RollingFileAppender 的一个实例,并根据 .properties 文件中的值动态更改其输出文件来实现的。由于这是一个单线程应用程序,因此可以正常工作。

但是,我注意到在某些情况下,此 RollingFileAppender 将关闭,并且应用程序将不经意地继续运行,除非现在不进行日志记录。由于控制台输出,我只成功地捕捉到了一次,因为该服务通常作为 Linux 服务器上的后台进程运行。这是发生的事情:

1)StartScheduler,主类,每分钟创建一个新的TaskPoller实例。

2) TaskPoller 扫描目录,从每个 .properties 文件中加载一些信息,并确定是否应该运行它。它还有自己独立的 RollingFileAppender,它通过 Logger.getLogger(TaskPoller.class) 检索。如果应该运行一个任务,那么它会实例化一个任务对象,并传入要运行的特定 .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 关闭。

所以我的问题是,什么可能导致这个附加程序意外关闭与其配置无关的方法?

--Edit-- 看起来我完全被误导了;问题出在 Quartz 和 log4j 之间的交互中,我用它来让 TaskPoller 每分钟触发一次。我还不完全理解它的原因,但是 [this solution][1] 似乎解决了这个问题。它只是直到现在才表现为一个观察到的问题,所以我认为它与最近发生的事情有关。

4

1 回答 1

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);
于 2010-10-26T18:21:22.327 回答