java - 使用 Quartz 工作线程拆分 log4j 输出

标签 java multithreading log4j quartz-scheduler scheduler

我正在开发一个应用程序,该应用程序由基于 Quartz 的整体调度程序和使用 CronTriggers 运行的“CycledJob”组成。该应用程序的目的是根据来源国家/地区处理来自不同电子邮件收件箱的输入。

根据它来自的国家(即美国、英国、法国等),应用程序触发一个作业线程来运行每个国家/地区的处理周期,因此会有一个英国工作线程,一个用于美国、法国等。当将输出格式化为 log4j 时,我使用线程参数,因此它发出 [ApplicationName_Worker-1]、[ApplicationName_Worker-2] 等。尽我所能,我找不到命名线程的方法因为他们被拉出 Quartz 的线程池。尽管我可能会扩展 Quartz,但我想制定一个不同的解决方案而不是弄乱标准库。

这就是问题所在:使用 log4j 时,我希望将所有日志项从美国线程输出到仅限美国的文件,对于每个国家/地区线程也是如此。我不在乎他们是否留在一个统一的 ConsoleAppender 中,FileAppender 拆分是我在这里所追求的。我已经知道如何指定多个文件附加程序等,我的问题是我无法根据国家/地区进行区分。应用程序中有 20 多个类可以在执行链上,其中很少有我想承担通过每个方法传递额外“上下文”参数的知识......我考虑过一个策略模式扩展log4j 包装器类,但除非我能让链中的每个类都知道它在哪个线程上来参数化记录器调用,否则这似乎是不可能的。无法命名线程也会带来挑战(否则这很容易!)。

所以这里的问题是:允许应用程序中的许多从属类的建议方法是什么,每个从属类都用于每个不同的线程来处理输入,知道它们在记录日志时处于特定国家/地区线程的上下文中?

祝您理解顺利,请提出澄清问题!我希望有人能够帮助我找到解决这个问题的好方法。欢迎所有建议。

最佳答案

在每个国家/地区的处理线程的顶部,将国家/地区代码放入 Log4j 的映射诊断上下文 (MDC) 中。这使用了一个 ThreadLocal 变量,这样您就不必显式地在调用堆栈中上下传递国家/地区。然后创建一个查看 MDC 的自定义过滤器,并过滤掉任何不包含当前附加程序国家代码的事件。

在你的工作中:

...
public static final String MDC_COUNTRY = "com.y.foo.Country";
public void execute(JobExecutionContext context)
  /* Just guessing that you have the country in your JobContext. */
  MDC.put(MDC_COUNTRY, context.get(MDC_COUNTRY));
  try {
    /* Perform your job here. */
    ...
  } finally {
    MDC.remove(MDC_COUNTRY);
  }
}
...

写一个自定义Filter :

package com.y.log4j;

import org.apache.log4j.spi.LoggingEvent;

/**
 * This is a general purpose filter. If its "value" property is null, 
 * it requires only that the specified key be set in the MDC. If its 
 * value is not null, it further requires that the value in the MDC 
 * is equal.
 */
public final class ContextFilter extends org.apache.log4j.spi.Filter {

  public int decide(LoggingEvent event) {
    Object ctx = event.getMDC(key);
    if (value == null)
      return (ctx != null) ? NEUTRAL : DENY;
    else
      return value.equals(ctx) ? NEUTRAL : DENY;
  }

  private String key;
  private String value;

  public void setContextKey(String key) { this.key = key; }
  public String getContextKey() { return key; }
  public void setValue(String value) { this.value = value; }
  public String getValue() { return value; }

}

在你的 log4j.xml 中:

<appender name="fr" class="org.apache.log4j.FileAppender">
  <param name="file" value="france.log"/>
  ...
  <filter class="com.y.log4j.ContextFilter">
    <param name="key" value="com.y.foo.Country" />
    <param name="value" value="fr" />
  </filter>
</appender> 

关于java - 使用 Quartz 工作线程拆分 log4j 输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/142845/

相关文章:

java - 使用 LinkedList 和 ListIterator 按字母顺序返回元素

java - 为什么我的 run() 方法没有从我的 Actionlistener 中正确调用

c++ - 如何在 C++ 中实现线程安全引用计数

multithreading - 等待 Scala Future 完成并继续下一个

log4j设置默认日志目录

从属性文件读取时出现 java.util.ConcurrentModificationException

java - 可以将 IntelliJ 配置为在不使用快捷方式的情况下调用 SmartType 代码完成吗?

java - 'APPLICATION_JSON' 和 'APPLICATION_JSON_VALUE' 之间的区别

java - 如何使用joda time在java中将字符串转换为日期时间

java - Spring 中的动态 Apache log4j