Агрегация логов с нескольких серверов средставами log4j

  • Tutorial
log4j широкоизвестная библиотека логирования, нашедшая своё применение во многих проектах. Её возможности не ограничиваются «добавлением строчек в лог-файлы». На базе log4j можно организовать сложную систему агрегации логов на центральный сервер. Кроме того, сообщество располагает GUI утилитами для анализа логов, которые удобно подключать к центральному серверу для анализа логов.

Когда в компании появляется несколько серверов или россыпь разнообразных приложений логирующих данные в разные файлы, становится крайне не удобным отслеживать все события происходящие в приложениях. Иногда это становится не возможным ввиду отсутствия прав доступа к тому или иному серверу. Именно в таких системах возникает необходимость агрегирования данных на одном центральном сервере. Рассмотрим наиболее простой способ реализации такой системы с использованием библиотеки log4j.



log4j

Интеграция log4j в Java проект чрезвычайна проста. Нужно подключить саму библиотеку и создать файл конфигуарции log4j.properties или log4j.xml. Если рассматривать maven проект, то его конфигурация будет примерно следующая.

./pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.github.caiiiycuk</groupId>
	<artifactId>log4j-app</artifactId>
	<version>1.0</version>
	<name>log4j app</name>

	<dependencies>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
	</dependencies>
</project>


./src/main/java/Log4JApp.java
import org.apache.log4j.Logger;

public class Log4JApp {

	private final static Logger LOGGER = Logger.getLogger(Log4JApp.class);

	public static void main(String[] args) throws InterruptedException {
		while (true) {
			LOGGER.debug("I'm doing science and I'm still alive.");
			LOGGER.info("I feel fantastic and I'm still alive.");
			LOGGER.warn("While you're dying I'll be still alive.");
			LOGGER.error("And when you're dead I will be, still alive.");
			LOGGER.fatal("Still alive, still alive.");
			Thread.sleep(1000);
		}
	}

}


./src/main/resources/log4j.properties
log4j.rootLogger=debug, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Шаблон лог сообщения
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n



Файл log4j.properties должен находиться в classpath, что бы log4j смог обнаружить его. Возможно явно указать расположения файла конфигурации с помощью аргумента командной строки java -Dlog4j.configuration=pathToFile, подробнее о конфигурации log4j.

В log4j есть понятие appender, он определяет обработчиков событий, в примере мы использовали стандартный ConsoleAppender который логирует все события в консоль. По счастью в стандартный набор входят так же SocketAppender и SocketHubAppender.

SocketAppender создаёт подключение к удаленному лог серверу и отправляет события на этот сервер. Причем посылаются сериализованные LoggingEvent, т. е. передаётся вся информация о событии а не строка. В случае если удалённый сервер не доступен, сообщения будут отбрасываться, когда же сервер заработает вновь соеденение будет востановленно автоматически.

SocketHubAppender похож на SocketAppender, но работает наоборот. SocketHubAppender создаёт сокет на который могут подключаться удалённые клиенты и при возникновении событий они отправляются всем подключенным клиентам.

Что бы настроить log4j на работу с удалённым сервером в конфигурацию нужно добавить следующее:
log4j.rootLogger=DEBUG, stdout, server
(...)
log4j.appender.server=org.apache.log4j.net.SocketAppender
log4j.appender.server.Port=4560
log4j.appender.server.RemoteHost=localhost
log4j.appender.server.ReconnectionDelay=10000
log4j.appender.server.Application=Log4JApp
log4j.appender.server.LocationInfo=true
  • Port — порт сервера на который посылать события, по умолчанию 4560
  • RemoteHost — хост сервера на который посылать события
  • ReconnectionDelay — интервал ожидания для переподключения
  • Application — имя приложения генерирующего логи
  • LocationInfo — включать ли информацию о расположении


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

java -classpath log4j.jar org.apache.log4j.net.SimpleSocketServer 4560 log4j-server.properties


Первый параметр это порт сервера, а второй конфигурация логера сервера. Все сообщения которые поступают на сервер будут обрабатываться так, как будто сам сервер сгенерировал эти события, т.е. так как вы сконфигурируете log4j-server.properties так и будут сообщения выводиться. Так можно организовывать сложные цепочки пересылки сообщения от сервера к серверу.

Если одновременно запустить сервер и клиент, то в консоле сервера будут печататься все сообщения которые произошли на клиенте (при условии что вы добавили ConsoleAppender).

Пример работы сервера
Конфигурация
log4j.rootLogger=debug, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Шаблон лог сообщения
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

Вывод в консоль
 INFO [main] (SimpleSocketServer.java:60) - Listening on port 4560
 INFO [main] (SimpleSocketServer.java:63) - Waiting to accept a new client.
 INFO [main] (SimpleSocketServer.java:65) - Connected to client at /127.0.0.1
 INFO [main] (SimpleSocketServer.java:66) - Starting new socket node.
 INFO [main] (SimpleSocketServer.java:63) - Waiting to accept a new client.
DEBUG [main] (Log4JApp.java:9) - I'm doing science and I'm still alive.
 INFO [main] (Log4JApp.java:10) - I feel fantastic and I'm still alive.
 WARN [main] (Log4JApp.java:11) - While you're dying I'll be still alive.
ERROR [main] (Log4JApp.java:12) - And when you're dead I will be, still alive.
 INFO [SimpleSocketServer-4560] (SocketNode.java:94) - Caught java.io.EOFException closing conneciton.



Ошибка Caught java.io.EOFException closing conneciton
Эта ошибка возникает при разрыве соединения между сервером и клиентом, она возникает всегда, в ней нет ничего страшного.


Анализ логов

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

log4j-server.properties
log4j.rootLogger=debug, stdout, server

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) — %m%n

log4j.appender.server=org.apache.log4j.net.SocketHubAppender
log4j.appender.server.Port=4561
log4j.appender.server.LocationInfo=true

С такой конфигурацией можно подключаться к серверу программами для анализа логов.

chainsaw v2

chainsaw — программа зародившаяся в недрах log4j, позволяет подключаться к SocketHub и отображать информацию о событиях в реальном времени. Кажется что chainsaw давно умер, т.к. последний билд был аж в 2006 году. Тем неменее с задачей своей справляется.

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

chainsaw v2



otroslogviewer

otroslogviewer — более современная программа, которая активно развивается. Подключение к SocketHub выполняется через пункт меню File -> Connect to Log4J SocketHub. Точно так же указываете хост и порт. Отображение событий будет происходить в новой закладке в главной панели.

otroslogviewer



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

lilith

lilith — автор заявляет, что данная программа может работать с SocketHub. Но я так и не понял как её настроить.

Вопросы безопасности

SocketHubAppender не предусматривает возможность какой-либо авторизации, т.е. теоретически к нему могут подключаться любые пользователи. Поэтому открыть в глобальный доступ такого рода сервер логов апрометчиво. Самый простой способ обезопаситься — пробрасывать порты через ssh тунель. Т.е. запускаем сервер на удаленной машине на порту 4561, но закрываем доступ на этот порт с помощью фаервола. Когда же появляется необходимость посмотреть последнии события пробрасываем порты командой.

ssh -L 4561:localhost:4561 remote-host

После этого можно подключаться анализаторами логов к localhost:4561.

P. S. Существуют и другие технологии решающие эту задачу например scribe от facebook, но как мне кажется, для простых проектов его преимущества не значительны, а настроить его сложнее.
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 18
  • +1
    Спасибо за статью.

    Позвольте немного конструктивной критики:

    SocketAppender создаёт подключение к удаленному лог серверу и отправляет события на этот сервер. Причем посылаются сериализованные LoggingEvent, т. е. передаётся вся информация о событии а не строка. В случае если удалённый сервер не доступен, сообщения будут отбрасываться, когда же сервер заработает вновь соеденение будет востановленно автоматически.

    Сервер для хранения логов действительно может быть недоступен по каким-либо причинам. И что тогда? Мы теряем логи? Если эта информация представляет для вас какую-то ценность, то такой подход неприемлем.

    Более надежный способ — хранение логов локально с ротацией по дням, скажем за последнюю неделю (один из стандартных appender-ов log4j) и ежедневная заливка данных на центральный сервер (log rotation). Если сервер недоступен, ну что же, зальем чуть позже. Логи не пропадут.

    Что касается SimpleSocketServer: а есть что-нибудь посерьезнее? Скажем, реализация, которая выстраивает логи в правильном хронологическом порядке? Или такие вещи проще решить через консоль при помощи простого sort? :)

    За программы для анализа логов — спасибо, покопаюсь. Давно ищу что-нибудь удобное для анализа, правда больше интересуют программы, которые способны работать с архивами логов, а не с runtime потоком.

    Вообще, я сам интересовался вопросом централизации логов с нескольких серверов и дальнейшим их анализом, но до конца построить готовый прототип руки так и не дошли. Буду очень благодарен, если кто-то опишет готовое надежное решение этой задачи.
    • +1
      Более надежный способ — хранение логов локально с ротацией по дням, скажем за последнюю неделю (один из стандартных appender-ов log4j) и ежедневная заливка данных на центральный сервер (log rotation). Если сервер недоступен, ну что же, зальем чуть позже. Логи не пропадут.


      Полностью согласен не стоит складывать все яйца в одну корзину. Логи обязательно должны хранится в файловой системе как вы сказали. Центральный сервер в первую очередь нужен для удобства, в 99% случаев он работает и к нему можно подключиться что бы посмотреть логи, но если что-то пошло не так всегда есть локальные логи на сервере.

      Что касается SimpleSocketServer: а есть что-нибудь посерьезнее? Скажем, реализация, которая выстраивает логи в правильном хронологическом порядке? Или такие вещи проще решить через консоль при помощи простого sort? :)


      В LoggingEvent есть информация о времени возникновения события. Те программы которые я описал сами выстраивают все в верном порядке. На более навороченные реализации сервера не натыкался.
    • +1
      А почему бы не использовать для этих целей готовое решение slf4j + logback + стандартные логбэковские аппендеры (SocketAppender, DBAppender)?
      Log4j, как мне кажется, уже устарел. И его используют только по инерции.
      • –1
        Как раз используем устаревающий log4j. logback пока только в планах :)
        • 0
          Log4j 2 во всю сейчас пилится
          • 0
            Но он пока сыроват и только в beta версии… Выйдет полноценная версия — можно будет потрогать. В большинстве случаев, лучше использовать slf4j и не зависеть от конечной системы логирования.
        • 0
          Есть такое решение:
          при помощи flume и его exec source отправлять логи с серверов в HDFS.
          Подключить Timestamp Interceptor и настроить Flume HDFS sink так, чтоб он писал с партиционированием по по часам:
          /landing/source/mysuper_distirubuted_system/2013/03/08/12
          Затем запускать pig и при помощи него выполнять аналитические операции над логами. Ведь мало собрать логи в одном месте, важно иметь способ быстро что-то по ним поискать.
          • 0
            У Log4J очень хорошо с расширяемостью. Когда-то работал над внутренним проектом одной компании: писали аппендер, который по сообщениям уровня ERROR автоматически заводил багрепорты в Jira.
            • 0
              Функциональность у Log4j и Logback практически идентичная. Logback в свое время «учился» на ошибках Log4j, исправлял их. Теперь, судя по заявлениям разработчиков Log4j2, они занимаются исправлением «ошибок» архитектуры Logback'а.
              • 0
                Блин, но вот за что нам это наказание?! Опять новый революционный логгер! )))
                • 0
                  А в чем проблема то? Проект не должен зависеть от системы логирования. Сами по себе логгеры похожи. Не думаю, что в Log4j2 будет что то революционное. Возможно он будет незначительно быстрее… Возможно будет иметь несколько удобных, но не везде необходимых фич. Проблема перехода с одной системы логирования на другую — проблема конфигурирования. Затруднения могут вызвать только кастомные аппендеры — не частая встречающаяся задача.
                  То, что есть конкуренция — это хорошо. Технология не застаивается, есть альтернатива, можно выбрать именно то, что подойдет для твоего конкретного проекта.
                  • 0
                    Имхо, смотря какой проект. Если, к примеру, библиотека или фреймворк, то зависимости однозначно не должно быть. В противном случае получается проект, в котором для удовлетворения зависимостей одновременно приходится держать несколько библиотек логгирования. Ситуация, кстати, вполне реальная. Именно из-за этого я напрягаюсь когда слышу, что еще одним логгером стало больше. Ну а если конечное бизнес-приложение, то почему нет?
                    • 0
                      Библиотека уж тем более должна использовать slf4j, что бы не зависеть от конкретной системы логирования. Да и в любом проекте лучше через нее работать.
                      • 0
                        Боюсь что перестал понимать моего оппонента. Независимость библиотек от систем журналирования обеспечивается использованием библиотек-обёрток типа Apache Commons Logging. SLF4J — это конкретная реализация системы журналирования, равно как и Log4j, и Logback. Привязка Commons Logging к конкретному логгеру производится через файл commons-logging.properties. Пример привязки:

                        org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger

                        Прописал такую строку, и вот уже Log4J рулит сообщениями Commons Logging.

                        Использование SLF4J в библиотеках противоречит тезису «Проект не должен зависеть от системы логирования». Хотя на практике оно случается. Могу привести реальный пример того, почему это плохо. SLF4J перестаёт вести журналирование, если приложение запускает само себя под управлением встроенной Jetty. Когда понял почему, хотел избавиться от SLF4J, но не мог, потому что от этой библиотеки зависит Apache Wicket. Если бы Wicket использовал Apache Commons Logging, то проблемы со сменой системы журналирования не возникло бы.
                        • 0
                          SLF4J — это не конкретная реализация системы логирования, это фасад логирования. Под который можно легко подложить любую другую, конечную систему логирования, без каких либо конфигов со стороны slf4j. Чем этот подход отличается от Apache Commons Logging? Практически ничем. При этом, Apache Common Logging имеет ряд проблем, из-за которых многие отказываются от него в пользу slf4j.

                          Что касается описанной проблемы с Jetty, Wicket и вашим приложением… На вскидку могу предположить, что имели место конфигурационные проблемы, возможно не было необходимых slf4j бриджей. В моей практике slfj4 не доставлял проблем. Хотелось бы взглянуть на проблемный пример в живую.
                          • 0
                            Приношу извинения, я напутал, так как не использовал SLF4J уже много лет. Но суть проблемы в общих чертах помню. У SLF4J есть одна особенность, которая описана здесь:

                            www.slf4j.org/codes.html#multiple_bindings

                            SLF4J делает binding при первом запуске. Но если в приложение встроен Jetty, который запускает то же самое приложение, но уже как web-приложение, то это приводит к повторной инициализации SLF4J. Результат:

                            SLF4J: Class path contains multiple SLF4J bindings.

                            Надо сказать, это был полный fail. Но через некоторое время я выкинул Wicket, выкинул SLF4J, а позже Jetty заменил на еще более легковесный Winstone. И теперь я счастлив, используя старый добрый Log4J. Мне не шашечки, мне ехать.
            • 0
              Graylog2, logstash вам в помощь.
              • 0
                Спасибо большое, это именно то что я и хотел!

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.