使用Quartz Worker线程拆分log4j输出

时间:2020-03-06 14:49:03  来源:igfitidea点击:

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

根据来自其来源的国家(例如,美国,英国,法国等),该应用程序触发一个作业线程来运行每个国家的处理周期,因此会有一个英国工人线程,一个用于美国,法国等。将输出格式化为log4j时,我使用的是thread参数,因此它会发出[ApplicationName_Worker-1],[ApplicationName_Worker-2]等。请尝试一下,我找不到命名线程的方法,因为它们重新退出了Quartz的线程池。尽管我可以扩展Quartz,但我想提出一种不同的解决方案,而不是弄乱标准库。

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

因此,这里有一个问题:一种建议的方法是允许应用程序中的许多下属类在登录时都知道它们在特定国家/地区线程的上下文中,而每个下属类用于每个不同的线程来处理输入?

祝我们好运,请问清楚问题!我希望有人能够帮助我找到解决这个问题的一种不错的方法。欢迎所有建议。

解决方案

我希望我能对此有所帮助,但我们可能想使用一些过滤器进行调查?也许日志记录可以输出国家/地区代码,并且我们可以基于此匹配过滤器?

一个StringMatchFilter应该可以为我们匹配它。

无法获得以下地址作为链接可以正常工作,但是如果我们查看它,它会在使用过滤器的单独文件记录中有一些东西。

http://mail-archives.apache.org/mod_mbox/logging-log4j-user/200512.mbox/<1CC26C83B6E5AA49A9540FAC8D35158B01E2968E@pune.kaleconsultants.com>(只需删除>之前的空格)

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/spi/Filter.html

我可能对我们要完成的工作完全不了解,但是我会尝试解决方案。听起来好像我们要为要处理电子邮件的每个国家/地区使用单独的日志文件。基于这种理解,下面是一个可能的解决方案:

  • 在我们要分别登录的每个国家(我们提供美国示例)的log4j配置中设置一个添加程序:log4j.appender.usfile = org.apache.log4j.FileAppender log4j.appender.usfile.File = us.log log4j。 appender.usfile.layout = org.apache.log4j.PatternLayout log4j.appender.usfile.layout.ConversionPattern =%m%n
  • 为每个国家/地区创建一个记录器,并将每个记录器定向到适当的添加程序(提供美国示例):log4j.logger.my-us-logger = debug,usfile
  • 在代码中,根据要处理电子邮件的国家/地区来创建Logger:Logger logger = Logger.getLogger(" my-us-logger");
  • 确定如何完成后续方法调用的第3步。我们可以在每个类/方法中重复第3步;或者,我们可以修改方法签名以接受Logger作为输入;或者或者我们可能使用ThreadLocal在方法之间传递Logger。

额外信息:如果我们不希望将日志语句发送给父记录器(例如rootLogger),则可以将其可加性标志设置为false(提供了美国示例):

log4j.additivity.my-us-logger=false

在每个国家/地区的处理线程的顶部,将国家/地区代码放入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);
  }
}
...

编写自定义过滤器:

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>

为什么在工作开始将名称设置为Thread时不只调用Thread.setName()?如果存在访问问题,请将quartz配置为使用我们自己的线程池。