Разделение выхода log4j кварцевыми рабочими нитями - PullRequest
4 голосов
/ 27 сентября 2008

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

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

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

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

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

Ответы [ 4 ]

5 голосов
/ 27 сентября 2008

В верхней части потока обработки каждой страны поместите код страны в отображенный диагностический контекст Log4j (MDC). При этом используется переменная ThreadLocal, поэтому вам не нужно явно передавать страну вверх и вниз по стеку вызовов. Затем создайте пользовательский фильтр, который просматривает 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> 
2 голосов
/ 27 сентября 2008

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

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

1 голос
/ 28 сентября 2008

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

1 голос
/ 27 сентября 2008

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

  1. Установите appender в конфигурации 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 =% м% п

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

    log4j.logger.my-нам-логгер = отлаживать usfile

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

    Logger logger = Logger.getLogger ("my-us-logger");

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

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

log4j.additivity.my-us-logger=false
...