Разделение выходных данных log4j с помощью Рабочих потоков Quartz

StackOverflow https://stackoverflow.com/questions/142845

Вопрос

Я работаю над приложением, которое состоит из общего планировщика на основе Quartz и запуска "CycledJob" с использованием CronTriggers.Целью приложения является обработка входных данных из разных почтовых ящиков электронной почты в зависимости от страны-источника.

В зависимости от страны, из которой он поступает (т. е.US, UK, FR и т.д.) Приложение запускает один поток заданий для запуска цикла обработки каждой страны, так что будет рабочий поток для Великобритании, один для США, Франции и т.д.При форматировании выходных данных в log4j я использую параметр thread, поэтому он выдает [ApplicationName_Worker-1], [ApplicationName_Worker-2] и т.д.Как я ни стараюсь, я не могу найти способ назвать потоки, поскольку они извлекаются из пулов потоков Quartz.Хотя я мог бы зайти так далеко, что расширил Quartz, я бы хотел разработать другое решение вместо того, чтобы возиться со стандартной библиотекой.

Вот в чем проблема:При использовании log4j я бы хотел, чтобы все элементы журнала из потока US выводились в файл только для США, аналогично для каждого из потоков country.Меня не волнует, останутся ли они в одном унифицированном консольном приложении, разделение FileAppender - это то, что мне нужно здесь.Я уже знаю, как указать несколько отправителей файлов и тому подобное, моя проблема в том, что я не могу дифференцироваться в зависимости от страны.В приложении более 20 классов, которые могут находиться в цепочке выполнения, очень немногие из которых я хочу обременить знаниями о передаче дополнительного параметра "context" через КАЖДЫЙ метод...Я рассмотрел шаблон стратегии, расширяющий класс-оболочку log4j, но если я не могу позволить каждому классу в цепочке знать, в каком потоке он находится для параметризации вызова регистратора, это кажется невозможным.Отсутствие возможности назвать поток также создает проблему (иначе это было бы легко!).

Итак, вот в чем вопрос:Каков был бы предлагаемый подход, позволяющий множеству подчиненных классов в приложении, каждый из которых используется для каждого отдельного потока для обработки входных данных, знать, что они находятся в контексте потока конкретной страны, когда они регистрируются?

Удачи в понимании и, пожалуйста, задавайте уточняющие вопросы!Я надеюсь, что кто-нибудь сможет помочь мне найти достойный способ справиться с этим.Все предложения приветствуются.

Это было полезно?

Решение

В верхней части потока обработки каждой страны поместите код страны в сопоставленный диагностический контекст Log4j (MDC).При этом используется переменная ThreadLocal, так что вам не нужно явно передавать country вверх и вниз по стеку вызовов.Затем создайте пользовательский фильтр, который просматривает MDC и отфильтровывает все события, которые не содержат кода страны текущего приложения.

В вашем Job:

...
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> 

Другие советы

Я хотел бы быть немного более полезным, чем это, но, возможно, вы захотите провести расследование, используя какие-нибудь фильтры?Возможно, в вашем логировании можно было бы вывести код страны, и вы могли бы подобрать свой фильтр на основе этого?

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

Почему бы просто не вызвать Thread.setName(), когда ваша работа начнет устанавливать имя потока?Если возникла проблема с доступом, настройте quartz на использование вашего собственного пула потоков.

Возможно, я полностью ошибаюсь в своем понимании того, чего вы пытаетесь достичь, но я попробую найти решение.Похоже, вам нужен отдельный файл журнала для каждой страны, для которой вы обрабатываете электронную почту.Основываясь на этом понимании, вот возможное решение:

  1. Настройте приложение в вашей конфигурации log4j для каждой страны, для которой вы хотите войти в систему отдельно (приведен пример США).:

    log4j.appender.usfile=org.apache.log4j.FileАппендер

    log4j.appender.usfile.Файл=us.log

    log4j.добавляющий файл.us.макет=org.apache.log4j.PatternLayout

    log4j.appender.usfile.макет.ConversionPattern=%m%n

  2. Создайте регистратор для каждой страны и направьте каждый из них в соответствующее приложение (приведен пример из США).:

    log4j.logger.my-us-logger=отладка,usfile

  3. В вашем коде создайте свой регистратор на основе страны, для которой обрабатывается электронное письмо:

    Logger logger = Logger.getLogger("мой-us-logger");

  4. Определите, как вы будете выполнять шаг 3 для последующих вызовов метода.Вы могли бы повторить шаг 3 в каждом классе / методе;или вы могли бы изменить сигнатуры методов, чтобы принимать Регистратор в качестве входных данных;или вы могли бы, возможно, использовать Локальный поток передавать регистратор между методами.

Дополнительная информация:Если вы не хотите, чтобы инструкции журнала передавались родительским регистраторам (напримеррутлоггер), вы можете установить для их флагов аддитивности значение false (приведенный НАМИ пример).:

log4j.additivity.my-us-logger=false
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top