Лучшие практики для ведения журнала Java из нескольких потоков? - PullRequest
25 голосов
/ 19 февраля 2009

Я хочу иметь диагностический журнал, который создается несколькими задачами, управляющими данными. Эти задачи могут быть в нескольких потоках. Каждая задача должна записать элемент (возможно, с подэлементами) в журнал; войти и выйти быстро. Если бы это была ситуация с одной задачей, я бы использовал XMLStreamWriter , так как это кажется наилучшим соответствием для простоты / функциональности без необходимости хранить всплывающий XML-документ в памяти.

Но это не единственная задача, и я не уверен, как лучше всего убедиться, что это «потокобезопасный», где «потокобезопасность» в этом приложении означает, что каждый элемент журнала должен записываться в журнал правильно и последовательно (один за другим, без чередования).

Есть предложения? У меня есть смутное представление о том, что нужно использовать очередь элементов журнала (каждый из которых может быть быстро создан: мое приложение занято реальной работой, чувствительной к производительности), и у меня есть отдельный поток, который обрабатывает журнал. элементы и отправляет их в файл, чтобы регистрация не прерывала производителей.

Ведение журнала не обязательно должно быть XML, но я хочу, чтобы оно было структурированным и машиночитаемым.

edit: я помещаю "threadsafe" в кавычки. Log4j кажется очевидным выбором (новым для меня, но старым для сообщества), зачем изобретать велосипед ...

Ответы [ 12 ]

22 голосов
/ 19 февраля 2009

Я думаю, что вы на неправильном пути. Вы говорите «потокобезопасный», но на самом деле имеете в виду «сериализованный». Безопасный поток означает, что один поток не будет мешать данным из другого потока. В большинстве случаев проблемы с потоками решаются заранее, и вам не следует беспокоиться об этом только ради ведения журнала. Например, если вы пишете:

myVariableSum = 0 + myVariable;
//here comes other thread - Not very likely!
logger.info("Log some INFO; myVariable has value" + myVariable.toString());

Вы должны убедиться, что myVariable не был изменен каким-либо другим потоком с момента выполнения вычисления (первая строка), но до вызова метода регистрации. Если это произойдет, вы запишете грязное значение, которое не использовалось для выполнения операции, а значение, назначенное другим потоком. Об этом обычно заботятся; например, локальная (уровень метода) переменная не может быть изменена другим потоком. В любом случае, если вам приходится беспокоиться об этом при регистрации, то на 99% ваша программа уже имеет серьезные проблемы с многопоточностью.
Все основные каркасы журналирования сами по себе «потокобезопасны», что означает, что они могут быть развернуты в многопоточных средах и не будут отображать проблемы, аналогичные описанным выше, для внутренних целей.
Получение следов в журнале в порядке их появления обычно называется «сериализацией» вызовов. Сериализация записей в журнале будет основным узким местом производительности в любом многопоточном приложении. Если вы используете каркас журналирования, такой как log4j, следы от всех потоков будут отображаться в одном месте более или менее в порядке их появления. Однако один столбец обычно является именем потока, поэтому вы можете легко фильтровать данные журнала по потокам; каждый поток регистрирует свои данные в хронологическом порядке. Проверьте эту ссылку: http://logging.apache.org/log4j/1.2/faq.html#1.7
Наконец, если вам действительно нужна сериализация записей журнала, вы можете использовать некую структуру, например java.util.concurrent.BlockingQueue, для маршрутизации ваших сообщений.

21 голосов
/ 19 февраля 2009

Использование каркаса ведения журнала, например Log4j .

9 голосов
/ 28 мая 2009

Использовать logback-classic. Это более новая и лучшая реализация log4j.

5 голосов
/ 20 февраля 2009

Я склонен использовать SLF4J поверх Log4J. Функциональность параметризованного ведения журнала особенно привлекательна, если у вас будет много операторов ведения журнала, которые могут быть отключены в производственной среде.

Он также может работать поверх java.util.logging или использовать собственный простой вывод.

4 голосов
/ 19 февраля 2009

Используйте каркас ведения журнала, который реализует некоторую форму шаблона NDC , например Log4J .

4 голосов
/ 19 февраля 2009

Используйте каркас журналирования, такой как Log4.

и если вас не устраивает вывод, вы можете написать свой собственный Appender, Filter, что угодно, чтобы настроить его, просто напишите. Таким образом, вы могли бы даже сделать некоторое кеширование, чтобы изменить порядок записей, хотя я не говорю, что это хорошая идея.

4 голосов
/ 19 февраля 2009

Вы можете использовать механизмы синхронизации (например, монитор или семафору), чтобы убедиться, что один запрос журнала обрабатывается до принятия следующего. Все это может быть скрыто от кода, вызывающего процедуры регистрации.

3 голосов
/ 19 февраля 2009

log4j является и был стандартом для регистрации Java в течение многих лет. Но если вам не нравится внешняя зависимость, то пакет java.util.logging предоставляет приемлемое решение.

2 голосов
/ 11 ноября 2016

У меня была похожая проблема и требования к реализации только для специальных журналов. Мое решение было:

  1. Я взял blockinglinkedqueue с размером *2 трафика приложения / мин.

  2. Все потоки помещают объект в очередь и заканчивают работу.

  3. Отдельный поток Log-Writer, извлекающий объект head из очереди и записывающий его в файл log4j с помощью отдельного приложения. Этот appender не использовался для системных журналов.

Это гарантирует, что журналы записываются последовательно и всегда в порядке.

Это не повлияет на производительность приложения, так как запись журнала является совершенно отдельным процессом и не создает узкого места.

Вы также можете использовать aysncappender из log4j.

1 голос
/ 31 мая 2009

Если вам нужно, вы можете свернуть свои собственные .. используя FIFO с одним записывающим / считывающим устройством или очередями.

...