Чуть ранее мы рассмотрели библиотеку логирования Pantheios. Пришла очередь ознакомится с более известной Apache log4cxx. Библиотека является имплементаций принципов и механизмов из log4j (библиотека логирования Java) на ��зыке C++. Проект выглядит «мертвым», по крайней мере круных изменений в течение последнего года не наблюдается, впрочем это не повод не посмотреть (а может быть и использовать) эту библиотеку.


Общие замечания


Я рассматриваю процесс сборки и работы с библиотекой на платформе Windows, используя VisualStudio 2010 SP1. Логи как обычно хочется иметь как на консоли, так и в файле. Особо стоит подчеркнуть что логи могут содержать симолы национальных алфавитов (читай unicode).

Статья расчитана на начинающих и содержит материал для быстрого старта, а не глубокое изучение библиотеки.

Получение библиотеки




Сборка


Для начала нам надо скачать зависимости и собственно саму библиотеку. По хорошему, надо бы скачать её с SVN проекта, т.к. судя по коммитам исправлены баги, но я почему-то решил не рисковать и попытать счастья с стабильной версией 0.10.0.

Итак, поехали!
Качаем:
исходники: www.apache.org/dyn/closer.cgi/logging/log4cxx/0.10.0/apache-log4cxx-0.10.0.tar.gz
APR: apr.apache.org/download.cgi (качать надо APR 1.4.2, APR-util 1.3.10 , APR iconv 1.2.1)
Sed: sourceforge.net/projects/gnuwin32/files//sed/4.2.1/sed-4.2.1-bin.zip/download

и зависимости для Sed:
sourceforge.net/projects/gnuwin32/files/libintl/0.14.4/libintl-0.14.4-bin.zip/download
sourceforge.net/projects/gnuwin32/files/libiconv/1.9.2-1/libiconv-1.9.2-1-bin.zip/download
sourceforge.net/projects/gnuwin32/files/regex/2.7/regex-2.7-bin.zip/download

фуф, самое сложное позади…

Распаковываем:
  • исходники в C:\temp\log4cxx_test\apache-log4cxx-0.10.0\
  • APR в C:\temp\log4cxx_test\apr\ (переименовать папку архивную не забудьте)
  • APR Util в C:\temp\log4cxx_test\apr-util\ (переименовать папку архивную не забудьте)
  • APR в C:\temp\log4cxx_test\apr-util\ (переименовать папку архивную не забудьте)


Мне sed на машине не нужен как таковой, но он нужен для скрипта конфигурирования, его надо бы бросить сюда: C:\temp\log4cxx\apache-log4cxx-0.10.0\ и все dll файлы, от которых он зависит рядом положить. Эстеты могут бросить и sed и dll-ки в %SystemRoot%\System32\, если sed им в дальнейшем нужен.

Далее: запускаем командную строку VS2010 и перейдя в каталог C:\temp\log4cxx\apache-log4cxx-0.10.0\ делаем последовательно:
>configure
>configure-aprutil


Ошибок быть не должно.

Настала очередь собственно сборки. Тут стоит запустить саму студию (devenv) и открыть в ней c:\temp\log4cxx\apache-log4cxx-0.10.0\projects\log4cxx.dsw. Студия попросит переконвертировать проекты, что нам и надо.

Нажимаем build, но чай пить не идем. Процесс сборки достаточно быстро завершится ~300 ми ошибками. Ругаться будет на LOG4CXX_LIST_DEF макрос. Все такие макросы надо вынести перед классом (они объявлены внутри класса).

После убирания этих ощибок будут ошибки, связанные с неизвестностью std::insert_iterator — просто добавьте #include <iterator> вначале файла.

Далее появится ошибки об неопределенности «KeySet» — на все такие ошибки надо поменять (пример):
LoggingEvent::KeySet set;
на
KeySet set;

(т.к. мы вынесли макрос, формирующий эти самые KeySet вне классов).

После этого «шаманства» сборка пройдет успешно, а вот линковка «завалится» с сообщениями об неопределнности некоторых функций. Это просто студия при конвертации забыла сделать ссылки на нужные файлы в проекте.
Их надо попросту внести в свойства проекте log4cxx (я захардкодил пути для простоты абсолютными, т.к. мне это надо только на период одноразовой сборки):



после чего и линковка заработает.

Кратко об архитектуре


log4cxx оперирует понятиями appender-ов, layout-ов и target-ами.

appender — определяет набор параметров для получателя (target) потока сообщений (лог строк по сути), содержит в себе ссылку на layout, который в свою очередь определяет правила форматирования сообщения.

Подробнее об архитектуре можно прочесть на сайте проекта.

Тестируем работу


Я предпочитаю затягивать в свои программы подобные фундаментальные библиотеки через переменные среды (но т.к. работаю с разными версиями библиотек стараюсь не определять их как переменные среды для пользователя). Поэтому запустив коммандную строку VS2010 вбиваем примерно следующее:
>SET LOG4CXX=C:\temp\log4cxx\apache-log4cxx-0.10.0\src\main\include\
>SET LOG4CXXLIB=C:\temp\log4cxx\apache-log4cxx-0.10.0\projects\Debug\
>devnenv

(я собирал в дебаг режиме, для release понятное дело пути немного иные).

Далее создадим простой проект C++ Console Application — это и будет наш тестовый проект.
В нём доопределим пути до общих include файлов и библиотек в настройке проекта, согласно нашим переменным среды:

и для линковки:




Пришла очередь добавить нужные нам include-ы:
//apache log4cxx
#pragma warning(push)
#pragma warning(disable: 4231 4250)
	//warning C4231: nonstandard extension used : 'extern' before template explicit instantiation	c:\libs\apache\apache-log4cxx-0.10.0\src\main\include\log4cxx\rolling\timebasedrollingpolicy.h	221	p2jbTestApp
	//warning C4250: 'log4cxx::rolling::TimeBasedRollingPolicy' : inherits 'log4cxx::rolling::RollingPolicyBase::log4cxx::rolling::RollingPolicyBase::setOption' via dominance
	#include <log4cxx/rolling/rollingfileappender.h>
	#include <log4cxx/logger.h>
	#include <log4cxx/consoleappender.h>
	#include <log4cxx/logmanager.h>
	#include <log4cxx/patternlayout.h>
	#include <log4cxx/rolling/timebasedrollingpolicy.h>
	#include <log4cxx/helpers/simpledateformat.h>
	#include <log4cxx/helpers/stringhelper.h>
	#include <log4cxx/xml/domconfigurator.h>
#pragma warning(pop)


Теперь надобно сделать конфигурационный файл. Я сделал его таким:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- debug="true" -->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" >

  <appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ABSOLUTE} [%t] %level %c{2}#%M %F:%L - %m%n"/>
    </layout>
  </appender>


  <appender name="DailyRollingFileAppender" class="org.apache.log4j.rolling.DailyRollingFileAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ABSOLUTE} [%t] %level %c{2}#%M %F:%L - %m%n"/>
    </layout>
    
    <param name="Encoding" value="UTF-16" />
    <param name="file" value="logfile.log"/>
    <param name="DatePattern" value="'.'yyyy-MM-dd" />
    <param name="append" value="true"/>
  </appender>

  <root>
    <priority value="all" />
    <appender-ref ref="DailyRollingFileAppender"/>
    <appender-ref ref="ConsoleAppender"/>
  </root>

</log4j:configuration>


Подробнее можно прочесть в документации к log4j, т.к. форматы файлов «почти совпадают».

Ну и ниже тестовая программа:
void foo(void*)
{
	log4cxx::LoggerPtr logger = log4cxx::Logger::getRootLogger();
	for(int i=0;i<10;++i){
		LOG4CXX_DEBUG((logger),  L"debug" << L"other debug message");
		LOG4CXX_TRACE((logger),  L"trace");
		LOG4CXX_INFO((logger),  L"привет, мир!");
		LOG4CXX_WARN((logger),  L"WARN");
		LOG4CXX_ERROR((logger),  L"error");
		LOG4CXX_FATAL((logger),  L"FATAL");
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	setlocale(LC_ALL, "Russian_Russia.OCP");	//иначе русский не будет нормально работать

	TCHAR selfName[MAX_PATH];
	HMODULE hModule = GetModuleHandle(NULL);
	GetModuleFileName(hModule, selfName, _countof(selfName));
	std::wstring configFile(selfName);
	configFile.append(L".logconfig");
	log4cxx::xml::DOMConfigurator::configure(configFile);

	for(int i=0;i<10;++i){
		_beginthread(foo,0,NULL);
	}

	std::cin.get();

	return 0;
}


Замечу «хитрость» использования:
LOG4CXX_DEBUG((logger),  L"debug" << L"other debug message");

из цикла «как это работает». Дело в том, что второй аргумент макроса передается на вход в stream, и именно поэтому сюда можно писать несколько сообщений (не только текст), используя operator <<.

Заключение


Мы посмотрели сборку и базовую настройку log4cxx на простом примере.
Из плюсо�� библиотеки стоит выделить настраиваемость через конфигурационный файл, поддержку unicode «из коробки» (включая различные кодировки выходных файлов).

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

Вне этого топика осталось: advanced конфигурирование, сравнение с другими логгерами, проверка «мифа о течах».

Приятного логирования!