- PVSM.RU - https://www.pvsm.ru -

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

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

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

Агрегация логов с нескольких серверов средставами 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 [1].

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

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

SocketHubAppender [3] похож на 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 [4] — программа зародившаяся в недрах log4j, позволяет подключаться к SocketHub и отображать информацию о событиях в реальном времени. Кажется что chainsaw давно умер, т.к. последний билд был аж в 2006 году. Тем неменее с задачей своей справляется.

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

chainsaw v2

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

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

otroslogviewer

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

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

lilith

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

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

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

ssh -L 4561:localhost:4561 remote-host

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

P. S. Существуют и другие технологии решающие эту задачу например scribe [7] от facebook, но как мне кажется, для простых проектов его преимущества не значительны, а настроить его сложнее.

Автор: Caiiiycuk

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/29150

Ссылки в тексте:

[1] log4j: http://logging.apache.org/log4j/1.2/manual.html

[2] SocketAppender: http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/SocketAppender.html

[3] SocketHubAppender: http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/SocketHubAppender.html

[4] chainsaw: http://logging.apache.org/chainsaw/

[5] otroslogviewer: http://code.google.com/p/otroslogviewer/

[6] lilith: http://lilith.huxhorn.de/

[7] scribe: https://github.com/facebook/scribe

[8] Источник: http://habrahabr.ru/post/172075/