Сравнение библиотек логирования



В сети огромное количество площадок формата Q&A где задаются вопросы из разряда:

  • Предложите С++ логер? (C++ logging framework suggestions)
  • Какой наиболее эффективный потоко-безопасный С++ логер? (What is the most efficient thread-safe C++ logger)
  • Библиотека логирования для игр (Logging library for c games)
  • Асинхронный потоко-безопасный С++ логер? (Asynchronous thread-safe logging in C++)

Люди делятся своим опытом и знаниями, но формат таких площадок позволяет лишь показать личные предпочтения отвечающего. К примеру, одним из самых производительных логеров чаще всего называют Pantheios, который даже по тестам производителя тратит больше 100 секунд на запись 1M строк лога, на современном железе это около 30 секунд, быстро ли это?

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

Мотивация


Почти в каждом известном мне проекте рано или поздно появлялось логирование и инженеры спрашивали себя «а как решить эту задачу?», кто-то рылся на площадках Q&A и получал ответы «Мне подошел логер X, вроде полет нормальный» (1), кто-то писал свой логер (2), а кто-то запасался терпением и штудировал целый пучок логеров на предмет своих интересов и спустя неделю другую делал свой выбор (3).

Эта статья для всех 3х групп:

  1. Для первой группы эта статья даст более обширное сравнение логеров, чем рекомендация «логер Х для меня лучший»
  2. Для второй группы статья даст представление о текущем уровне технологий и возможно склонит чашу весов в сторону «проще использовать готовый» или «ого, работы много, но я смогу лучше» и кто знает, может через год другой мы увидим прорыв в этой области.
  3. Для третьей и самой педантичной группы я надеюсь, эта статья сохранит как минимум одну из 3х недель изысканий, в масштабах всей индустрии это может быть внушительное количество человеко-часов

N.B. Статья достаточно объемная, поэтому если решитесь на чтение, пожалуйста, запаситесь терпением!

Логеры и их основные параметры


Выбор логеров для сравнения дело хлопотное и не простое, в любом случае возникнут вопросы «А почему логер Х не был рассмотрен?» Что тут можно сказать, логика была проста – взять 4 известных логера и 4 сравнительно юных и «голодных».

Но даже сравнение этих 8 кандидатов заняло более 3х недель выкуривания доков, issues, чтения форумов, написания тестов и сбора результатов.

Так или иначе, если какой-то очень важный логер был пропущен – статью можно обновить. В обзор попали:

  1. Pantheios
  2. Glog
  3. log4cpp
  4. P7
  5. G3log
  6. Spdlog
  7. Easylogging
  8. Boost.Log (часть огромной библиотеки Boost)

Общие характеристики выбранных логеров
Лиц. Языки Обнов. Платф. Комп.
Pantheios BSD C++ 2010 Windows, *nix, OS-X VC++, GCC, Intel, Borland, Comeau,Digital Mars,Metrowerks
Glog 3-clause BSD С++ 2016 Windows, *nix, QNX VC++, GCC, clang, intel
log4cpp LGPL С++ 2016 Windows, *nix, Solaris VC++, GCC, Sun CC, OpenVMS
P7 LGPL С++, С, C#, Python 2016 Windows, *nix VC++, GCC, clang, MinGW
G3log Public Domain C++11 2016 Windows, *nix VC++, GCC, clang
Spdlog MIT C++11 2016 Windows, Linux, Solaris, OS-X, Android VC++, GCC, Clang
Easylogging MIT C++11 2016 Windows, Linux, Solaris, OS-X, Android VC++, GCC, Clang, Intel
Boost.Log Boost(1) C++ 2016 Windows, Linux(2) VC++, GCC, Clang(3)

  1. Собственная лицензия www.boost.org/LICENSE_1_0.txt
  2. www.boost.org/doc/libs/1_62_0/libs/log/doc/html/log/installation.html
  3. Есть целый ряд других платформ и компиляторов, на которых можно скомпилировать Boost, но официально они не поддерживаются и вероятно потребуют дополнительных усилий

Документация и зависимости


Трудно оспаривать важность документации для сложных проектов, современные логеры простыми проектами назвать можно с большой натяжкой и наличие хорошей документации порой существенно ускоряет внедрение и исправление ошибок:
Документация Зависимости
Pantheios Полная (API + использование) STLSoft
Glog Рудиментарная, почти отсутствует Google gflags, слабая зависимость (1)
log4cpp Генерируемая (Doxygen) (API только) Boost, слабая зависимость (1)
P7 Полная (API + использование) Нет
G3log Базовая (общие методы использования) Нет
Spdlog Базовая (общие методы использования) Нет, только заголовочный файл (2)
Easylogging Базовая (общие методы использования) Нет, только заголовочный файл (2)
Boost.Log Базовая (общие методы использования) плюс базовое описание некоторых классов и их методов Boost

  1. Слабая зависимость – часть кода зависит от сторонних решений, но не препятствует компиляции, а лишь частично ограничивает функционал.

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

Тип логера, контроль потребления памяти и потокобезопасность


На данный момент широко распространены 2 подхода в логировании:

  • Синхронный – вызов Log(..) функции и запись в файл происходят синхронно из одного потока
  • Асинхронный – вызов Log(..) функции лишь обновляет очередь сообщений, а другой поток занимается записью, таким образом, уменьшая время вызова Log(..) функции из пользовательского потока, и как следствие достигается максимальная производительность пользовательского потока и минимизируются задержки на вызов Log(..) функции.

Правда есть нюансы понимания асинхронности разными производителями библиотек логирования.

  1. Тип №1: Одни считают, что вызов Log(..) функции должен быть атомарным и таким образом порядок лог сообщений в файле будет последовательным во времени 00:00 -> 00:01 -> 00:02 и тд.

  2. Тип №2: Другие считают, что в целях достижения максимальной производительности можно пожертвовать атомарностью вызова Log(..) функции и смириться с тем, что лог сообщения в файле будут перемежающимися, например вот так 00:00 -> 00:05 -> 00:01.

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

Другим важным аспектом асинхронного логирования является контроль за выделением памяти, так как, чтоб ваш логер был асинхронен — вы должны сохранять данные в буфера, а писать уже из другого потока. И вот тут кроется важная мелочь — какой размер буферов будет оптимален и может ли пользователь влиять на этот параметр? Вопрос совсем не праздный, так как некоторые из протестированных логеров выделяли сотни мегабайт под свои нужды.
Тип Контроль памяти Потоко-безопасность
Pantheios Синхронный нет да
Glog Синхронный нет да
log4cpp Синхронный нет да
P7 Асинхронный, (тип 1) (2) точное (с шагом 1Kb) да
G3log Асинхронный, (тип 2) (3) нет (5) да
Spdlog Асинхронный, (тип 2) (3) частичное (6) да
Easylogging Синхронный (1) нет нет (4)
Boost.Log Синхронный, Асинхронный, (тип 2) (7) нет по умолчанию (8) да

  1. Асинхронный режим находится в экспериментальном состоянии
  2. Временные метки и сообщения не перемешаны
  3. Временные метки и сообщения могут быть перемешаны
  4. По умолчанию не доступно, необходимо активировать макросом ELPP_THREAD_SAFE
  5. Неконтролируемое выделение памяти, при высоких нагрузках может выделять сотни мегабайт, комментарий автора: kjellkod.wordpress.com/2014/08/16/presenting-g3log-the-next-version-of-the-next-generation-of-loggers
    In the case of g2log or g3log a std::queue is used, internally that is a std::deque. It’s unbounded but much more memory tolerant than a std::vector. Internally the queue is wrapped inside the shared_queue.hpp.

  6. Можно задавать длину очереди в элементах, размер элемента в начальном виде – 88 байт + текстовое сообщение (30-160 байт). Автор рекомендует задавать размер очереди в 1 миллион сообщений для оптимальной производительности, что выливается в расходы памяти от 120 мегабайт до 250 мегабайт только на библиотеку логирования.
  7. Очередность сообщений не гарантируется библиотекой по умолчанию, временные метки могут быть перемешаны в итоговом файле при логировании из разных потоков: “Why log records are weakly ordered in a multithreaded application?” Автор библиотеки предлагает использовать «unbounded_ordering_queue» для преодоления этой проблемы совместно с использованием спец. атрибута «RecordID» по которому будет производиться сортировка
  8. По умолчанию библиотека использует «unbounded_fifo_queue» что выливается в неконтролируемый рост потребления памяти при интенсивном логировании. Возможно использовать при дополнительной настройке «bounded_fifo_queue». В таком случае можно задать длину очереди в элементах. Каждый элемент (record в терминологии библиотеки) занимает около 1KB

Обработка сбоев процесса


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

Распространены 3 подхода в перехвате падений:

  • Автоматический, библиотека сама настраивает все векторы и сделает все сама, большим минусом такого решения является скудность перехватываемых сигналов, а так же помехи для приложения, которое возможно хотело бы само обрабатывать падение в целях сохранения crash dump файла или сброса буферов.
  • Ручной – библиотека предоставляет примитивы для перехвата падений и сброса буферов, а приложение уже само решает, когда и как установить перехватчик и что делать в случае падения.
  • Пусть падает, дело житейское

Pantheios Нет
Glog автоматический, только под Linux (1)
log4cpp Нет
P7 ручной и автоматический (2)
G3log автоматический, только под Linux (3)
Spdlog Нет (4)
Easylogging автоматический, только под Linux (5)
Boost.Log Нет (6)

  1. Перехватывается следующие сигналы: SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, SIGTERM.
  2. Перехватывается следующие сигналы: SIGSEGV, SIGILL, SIGFPE, SIGINT, SIGABRT, SIGBUS, SIGTERM, SIGBUS, PureVirtualCall, VectoredException, Newhandler, InvalidParameterHandler, status_access_, exception_array_bounds_exceeded, exception_datatype_misalignment, exception_flt_divide_by_zero, exception_flt_stack_check, exception_illegal_instruction, exception_int_divide_by_zero, exception_noncontinuable_exception, exception_priv_instruction, exception_stack_overflow
  3. Перехватывается следующие сигналы: SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, SIGTERM, Div by zero, illegal printf, out of bounds, access violation, std::future_error
  4. github.com/gabime/spdlog/issues/55 — Дефект был «закрыт» в 2015, т.е. работ не предвидится по этому направлению.
  5. Перехватывается следующие сигналы: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGINT
  6. Даже для синхронного режима есть риск потери данных в случае сбоя процесса, в случае использования асинхронного режима риск потери данных крайне высок

Стиль логирования и вывод (sink)


Основная масса библиотек поддерживает 2 хорошо зарекомендовавших себя стиля логирования:

  • Функции со списком переменных аргументов (prinf стиль)
  • Перегрузка оператора “<<”

Стиль Вывод (Sink)
Pantheios Template + перегруженные функции
F(A), F(A,A), F(A,A,A), … F(A, <-64->, A)
Printf
File, syslog, console, speech, ACE, COMerror, WinEventLog
Glog Log() << Message File, syslog, console
log4cpp Printf, Log() << Message File, syslog, console, NT log, IDS/A, OsStream, StringQueue, Win32Debug
P7(6) Printf Binary file, console, syslog, text file (Linux: UTF8, Windows: UTF-16) network (собств. протокол и сервер(2)), null
G3log Printf, Log() << Message File (3)
Spdlog Printf (4) File, syslog, console
Easylogging Printf (1), Log() << Message File, syslog, console
Boost.Log Log() << Message(5) File, syslog, console, Win32Debug, WinEventLog, IPC

  1. В случае использования «Printf» генерировал exception, вероятно это временная проблема.
  2. Свой сервер, используется для достижения максимальной производительности. Сервер бесплатен, но к сожалению поддерживает только Windows, судя по всему основан на Qt, запрос об поддержке Linux был направлен автору. Скорость отправки логов по сети в тестовой конфигурации была около 3.5 миллионов в секунду при загрузке CPU – 13%. Подробно тесты производительности обсуждаются в следующих главах.
  3. В официальную поставку не входит Sink с поддержкой ротации файлов и консоль, но эти расширения можно скачать github.com/KjellKod/g3sinks
  4. Формат строка для Printf функций библиотеки не совместима с каноническим форматом, что существенно затрудняет безболезненную замену одного логера другим
  5. Передача nullptr строки в качестве аргумента логеру вызывает segmentation fault/access violation
  6. Наиболее производительные Sink: Binary file, Baical

Инициализация логера


Инициализация или передача параметров достаточно важный пункт т.к. добавляет гибкости логеру и избавляет от необходимости перекомпиляции, если Вы решили изменить уровень логирования к примеру.
Pantheios Ручная (только в коде)
Glog Командная строка, ручная, переменные среды окружения
log4cpp Конфигурационный файл (1), ручная
P7 Командная строка (2), ручная
G3log Ручная (только в коде)
Spdlog Ручная (только в коде)
Easylogging Конфигурационный файл, командная строка, ручная
Boost.Log Конфигурационный файл(3), ручная

  1. Подробная и хорошо организованная настройка параметров логера, пожалуй, самая развернутая из всех.
  2. Во всех остальных логерах для того чтоб параметры командной строки были обработаны – вы должны передать их в логер руками из int main(int argc, char* argv[]) функции. В данном логере эти параметры могут быть перехвачены автоматически из любой части программы/модуля (dll,so).
  3. Библиотека предоставляет только самые базовые примитивы для конфигурации посредством конфигурационного файла. Более полное обсуждение этого вопроса можно найти по ссылке. И как заключение
    It seems to me that it is difficult to find a description of the configuration file format entries because the valid entries are derived from the source code implementing the sinks, filters etc. This implementation may be even user defined so it is impossible to give explicit configuration format description

Настройка фильтрации


Самая распространенная методика фильтрации – по уровням логирования, скажем если фильтр установлен на ERROR уровень — то все что меньше ERROR (TRACE, DEBUG, INFO, WARNING …) в лог попадать не будет. Этот метод очень удобен для отсеивания большого количества ненужной в данный момент информации и сохранения CPU и места на диске.
Pantheios Нет (1)
Glog Командная строка, ручная, переменные среды окружения
log4cpp Конфигурационный файл (4), ручная
P7 Командная строка, удаленно по сети в режиме реального времени (2) (3), ручная
G3log Нет (5)
Spdlog Ручная
Easylogging Ручная (6)
Boost.Log Конфигурационный файл, ручная

  1. Для организации фильтрации нужно разработать свой FrontEnd
  2. Поддерживается только если данные высылаются по сети, в случае записи в локальный файл сервер не имеет доступа к Verbosity level
  3. В дополнение к глобальному уровню можно устанавливать уровни для каждого модуля.
  4. Иерархические логирование и установка уровней индивидуально для каждого логера
  5. По умолнчанию отключена, включается макросом G3_DYNAMIC_LOGGING, после этого можно в ручном режиме задать уровень на все логеры. Существенно снижает производительность.
  6. Поддержка заявлена производителем, но заставить ее работать не удалось, во время использования сложилось впечатление, что функция находится в стадии разработки или заброшена.

Поддержка юникода


Эта часть тестирования была одной из самых грустных, в 2016 году поддержка юникода в таких известных библиотеках все еще находится на уровне «not officially».

Библиотека нужна для того что сохранить важные данные приложения (фамилия пользователя, путь файла, имя домена), а большинство из существующих просто не позволят Вам это сделать если данные не укладываются в тривиальный char.
Pantheios Utf-16 (1)(4), Utf-8
Glog Нет
log4cpp Нет
P7 Windows — UTF-16, *nix — UTF-8, UTF-32
G3log Нет
Spdlog Нет
Easylogging Windows Utf-16 (2), Utf-8 (3)
Boost.Log UTF-8 частично (5)

  1. Почти идеально, за исключением того что итоговый лог файл не имеет маркера юникода и кодировку в программе просмотра нужно будет выбирать самому.
  2. Поддержка заявлена, но не реализована, символы юникода в лог файл не попадают.
  3. Макрос START_EASYLOGGINGPP не поддерживает юникод
  4. В одном сообщении нельзя совместить ANSI строку скажем с UTF-16
  5. Поддержка юникода осуществляется с помощью библиотеки Boost.Locale, предоставляется впечатляюще широкая поддержка разных форматов UTF-8/16/32 и прочие национальные локали, к сожалению, заставить работать Boost.Log удалось только с UTF-8 форматом файла и только с использованием функции инициализации синхронного логера «logging::add_file_log», все остальные попытки провалились и символы юникода не достигли файла, возможно при наличии достаточного времени этот функционал можно заставить работать

Доступ к логеру


В современных библиотеках вопрос «кто владеет логером» остается за кадром, чаще всего можно написать LOG(ERROR) << “My message” и библиотека позаботиться обо всем сама. Такая простота достигается с помощью глобальных переменных. Этичность использования глобальных переменных я оставлю за кадром, все таки это особый случай, но простота использования глобальной переменной в случае простого приложения оборачивается против разработчика сложного приложения состоящего из многих динамических или статических модулей.

Другой вариант получения доступа к логеру это самостоятельно создать объект и контролировать его жизненный цикл.

И последний вариант – гибридный, объекты логера создаются в ручном режиме, а затем используется глобальные переменные (registry) или разделяемая память, общая для всего процесса включая динамические модули.
Pantheios Глобальные переменные, автоматическая инициализация
Glog Глобальные переменные, автоматическая инициализация
log4cpp Глобальные переменные, автоматическая и ручная инициализация
P7 Разделяемая память, ручная инициализация
G3log Глобальные переменные, автоматическая и ручная инициализация
Spdlog Глобальные переменные, ручная инициализация
Easylogging Глобальные переменные, автоматическая инициализация
Boost.Log Глобальные переменные, автоматическая и ручная инициализация

Ротация файлов


Pantheios Нет
Glog Размер
log4cpp Размер (2)
P7 Время, размер (1) (2)
G3log Размер, по умолчанию не доступен (1)
Spdlog Размер, время(дневной) (1)
Easylogging Размер
Boost.Log Время, размер (1) (2)

  1. Каждый файл в названии содержит дату и время
  2. Поддерживается опция «макс. кол-во файлов» позволяющая хранить только N последних файлов

Точность времени


Многие из рассматриваемых в данной статье логеров разрабатывались с прицелом на высокую производительность, с потенциалом в миллионы сообщений в секунду. Но помимо высоких скоростей нужны точные временные метки высокого разрешения, т.к. если у вас в лог файле есть пара десятков или даже сотен сообщений с одинаковой временной меткой – это означает, что часть информации о времени исполнения уже утеряна.
Pantheios Windows: 10ms(1), custom back-end может помочь увеличить точность
Linux: теор. минимальное значение 1ns, зависит от аппаратной части
Glog Windows: 10ms(1)
Linux: теор. минимальное значение 1ns, зависит от аппаратной части
log4cpp Windows: 10ms(1)
Linux: теор. минимальное значение 1ns, зависит от аппаратной части
P7 Windows: 100ns
Linux: теор. минимальное значение 1ns, зависит от аппаратной части
G3log Windows: 1ms
Linux: 1us
Spdlog Windows: 1ms
Linux: теор. минимальное значение 1ns, зависит от аппаратной части
Easylogging Windows: 1ms
Linux: 1us
Boost.Log Windows: 10ms(1)
Linux: теор. минимальное значение 1ns, зависит от аппаратной части

  1. Иногда можно получить гранулярность в 1 миллисекунду, но намного чаще квант равен 10 миллисекундам.

Производительность


Очень многие логеры из приведенного в данной статье списка заявляют, что одним из главных приоритетов для них является производительность.

Я отнесся к этому заявлению более чем серьезно и провел ряд тестов:

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

Для каждого теста делается замер времени и CPU потраченного логером на сохранение 1 миллиона сообщений в файл. Проводятся 3 замера и вычисляются средние показатели.

Так же проводились тесты в debug (оптимизация отключена) и release (оптимизация O2) сборке. Для тестов использовалась следующая конфигурация:

  • Window 7x64 (6.1.7601 Service Pack 1 Build 7601)
  • Visual studio 2015, update 2
  • RAM: 16Gb (DDR3-1600 / PC3-12800)
  • CPU: Intel Core i7-870
  • HDD: Samsung EVO SSD 850 256GB (SATA3)

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

  • Номер сообщения
  • Имя исходного файла
  • Номер строки кода
  • Имя исходной функции
  • Имя логера/модуля
  • Уровень (error, warning, …)
  • Время
  • ID текущего потока (thread Id)
  • Номер ядра CPU
  • Текстовое сообщение

Код который исполнялся для каждого логера (для компиляции требуется поддержка С++11):

Исходный текст
#include <stdio.h>
#include <atomic>
#include <thread>
#include <vector>

//Include specific logger headers
#include "Logger headers ..."
 
 
using namespace std;
using namespace std::chrono;
 
//Use this macro to switch on multi-threading mode
//#define MULTI_THREAD
 
 
int main(int argc, char* argv[])
{
    //Logger initialization
    //..
  
    unsigned int thread_count = 4;
    unsigned int howmany      = 1'000'000;
    vector<thread> threads;
    auto start = system_clock::now();
 
 
#if !defined(MULTI_THREAD)
    for(unsigned int i=0; i < howmany; i++)
    {
        //Has to be customized for every logger
        LOG(INFO) << " Message + all required information, #" << i;
    }
#else
    howmany /= thread_count;
    for (int t = 0; t < thread_count; ++t)
    {
        threads.push_back(std::thread([&]
        {
            for(unsigned int i=0; i < howmany; i++)
            {
                //Has to be customized for every logger
                LOG(INFO) << " Message + all required information, #" << i;
            }
        }));
    }
 
 
    for(auto &t:threads)
    {
        t.join();
    };
 
    howmany *= thread_count;

#endif
 
    auto delta = system_clock::now() - start;
    auto delta_d = duration_cast<duration<double>> (delta).count();
 
    LOG(INFO) << "Time = " << (double)howmany / delta_d << " per second, total time = "  << delta_d;

    //Logger uninitialization if necessary 
 
    return 0;
}


Один поток


Один поток должен сохранить 1 миллион сообщений в файл, фильтрация отключена, ротация файлов отключена. Измеряется время исполнения и среднее использование CPU.
Debug
Time (ms)
Debug
CPU (%)
Release
Time (ms)
Release
CPU (%)
Pantheios 140 300 13% 28 400 13%
Glog 52 500 13% 8 270 13%
log4cpp 130 570 13% 13 806 13%
P7 (1)(2)(6) 520 14% 100 14%
G3log (1) (3) 102 990 38% 3 660 37%
Spdlog (1) (4) 64 250 13% 869 13%
Spdlog (1) (5) 65 660 13% 885 13%
Easylogging 271 060 13% 9 100 13%
Boost.Log(1) (7) 2 310 200 17% 44 300 9%
Boost.Log(1) (8) 649 480 25% 12 680 25%

  1. Асинхронное логирование
  2. Скомпилирован с опцией «/P7.Pool=1024» — общий объем доступной памяти 1 мегабайт.
  3. Замеры можно считать синтетическими, так как большую часть времени логер складывает данные в буфера, а запись происходит по выходу из приложения и эта работа занимает время превышающее время логирования на порядок, 1 секунда логирования и 10 секунд сохранения данных.
  4. Логирование в условиях рекомендованных производителем «spdlog::set_async_mode(1048576)» при этом логер потребляет около 250 мегабайт памяти
  5. Логирование в условиях равного потребления памяти «spdlog::set_async_mode(4096)» — в этом случае у логера выделен буфер из 4k элементов, каждый элемент занимает около 250 байт что в итоге дает потребление памяти около 1 мегабайта.
  6. Запись велась в бинарный файл, текст в формате UTF-16
  7. Логирование в условиях равного потребления памяти (asynchronous_sink + text_file_backend + bounded_ordering_queue + block_on_overflow), длина очереди 1024 элемента, каждый элемент занимает около 1100 байт, что в итоге дает потребление памяти чуть больше 1 мегабайта
  8. Синтетический тест. Логирование в максимально комфортных условиях, конфигурация логера по умолчанию (asynchronous_sink + text_file_backend + unbounded_fifo_queue). Нет ограничений в потреблении памяти, нет сортировки сообщений по порядку поступления, потребление памяти было зафиксировано на уровне 1.8GB

4 потока


4 потока должны суммарно сохранить 1 миллион сообщений в файл, фильтрация отключена, ротация файлов отключена.

Измеряется время исполнения и среднее использование CPU.
Debug
Time (ms)
Debug
CPU (%)
Release
Time (ms)
Release
CPU (%)
Pantheios 10 600 48% 9 500 48%
Glog 30 200 93% 5 900 93%
log4cpp 149 600 18% 16 900 19%
P7 (1)(2)(6) 790 19% 230 19%
G3log (1)(3) 39 700 75% 2 300 75%
Spdlog (1)(4) 11 510 13% 270 25%
Spdlog (1)(5) 73 240 25% 4 653 25%
Easylogging 328 230 19% 8 575 25%
Boost.Log (1)(7) 2 645 120 14% 48 290 14%
Boost.Log (1)(8) 655 470 65% 13 560 65%

  1. Асинхронное логирование
  2. Скомпилирован с опцией «/P7.Pool=1024» — общий объем доступной памяти 1 мегабайт.
  3. Замеры можно считать синтетическими, так как большую часть времени логер складывает данные в буфера, а запись происходит по выходу из приложения и эта работа занимает время превышающее время логирования на порядок, 1 секунда логирования и 10 секунд сохранения данных.
  4. Логирование в условиях рекомендованных производителем «spdlog::set_async_mode(1048576)» при этом логер потребляет около 250 мегабайт памяти
  5. Логирование в условиях равного потребления памяти «spdlog::set_async_mode(4096)» — в этом случае у логера выделен буфер из 4k элементов, каждый элемент занимает около 250 байт что в итоге дает потребление памяти около 1 мегабайта.
  6. Запись велась в бинарный файл, текст в формате UTF-16
  7. Логирование в условиях равного потребления памяти (asynchronous_sink + text_file_backend + bounded_ordering_queue + block_on_overflow), длина очереди 1024 элемента, каждый элемент занимает около 1100 байт, что в итоге дает потребление памяти чуть больше 1 мегабайта
  8. Синтетический тест. Логирование в максимально комфортных условиях, конфигурация логера по умолчанию (asynchronous_sink + text_file_backend + unbounded_fifo_queue). Нет ограничений в потреблении памяти, нет сортировки сообщений по порядку поступления, потребление памяти было зафиксировано на уровне 1.8GB

Фильтрация


логер должен обработать 1 миллион сообщений и отфильтровать их, т.е. в итоговый файл не попадет ни 1 сообщения.

Измеряется время исполнения.
Debug
1 thread, time (ms)
Debug
4 threads, time (ms)
Release
1 thread, time (ms)
Release
4 threads, time (ms)
Pantheios (1) - - - -
Glog 55 520 28 240 6 840 4 790
log4cpp 200 70 80 45
P7 84 102 23 42
G3log 5 530 1950 24 9
Spdlog 269 134 6 32
Easylogging (2) - - - -
Boost.Log 26 407 8 554 699 389

  1. Фильтрация не доступна по умолчанию
  2. Не получилось заставить работать фильтрацию

Обзор производительности


Производительность многих логеров оказалась на очень хорошем уровне.
К сожалению, почти у всех логеров кроме P7 наблюдается колоссальный разрыв производительности между debug и release сборкой, порой коэффициент достигает 74 (Spdlog: 65660 / 885). Это может усложнить отладку проектов в силу возросших задержек при логировании.

Произведенные тесты в определенном смысле можно назвать синтетическими, так как ни 1 разработчик, внедряющий в свое приложение библиотеку логирования, не желает, чтоб та выделяла под свои нужды 250 мегабайт памяти или потребляла 75% CPU или больше.

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

Поэтому я сделал пересчет лучших показателей каждого логера (тесты с равным потреблением памяти) с целью определить, сколько может быть записано лог сообщений, используя только 1% CPU и разумный, но все еще большой объем памяти равный 1mb

Формула расчёта:
((1 000 ms / время теста в ms) * 1 000 000 сообщений) / использование СPU в тесте

Количество сообщений в сек при использовании 1% CPU
P7 714 285 сообщений (1000000 * (1000 / 100 ms) / 14%)
Spdlog 148 148 сообщений (1000000 * (1000 / 270 ms) / 25%)
Glog 9 301 сообщений (1000000 * (1000 / 8270 ms) / 13%)
Easylogging 8 453 сообщений (1000000 * (1000 / 9100 ms) / 13%)
G3log 7 384 сообщений (1000000 * (1000 / 3660 ms) / 37%)
log4cpp 5 571 сообщений (1000000 * (1000 / 13806 ms) / 13%)
Pantheios 2 708 сообщений (1000000 * (1000 / 28400 ms) / 13%)
Boost.Log 2 508 сообщений (1000000 * (1000 / 44300 ms) / 9%)


Выводы


Pantheios


Эта библиотека произвела достаточно смешанные впечатления, с одной стороны к проекту автор подошел основательно и вдумчиво, хорошие обзоры функционала, документация, с другой стороны низкая производительность (хотя автор отмечает обратное (1)), отсутствие ротации файлов и прочих мелочей сильно портят общее впечатление.

Итак, плюсы библиотеки:
  • Type-safe вызовы основанные на шаблонах
  • Хорошая и полная документация
  • Один из немногих логеров, что поддерживает юникод, правда с небольшими ограничениями
  • Низкое потребление памяти
  • Большое количество поддерживаемых Sink
  • Большое количество поддерживаемых компиляторов

Минусы:
  • Разработка судя по всему остановлена
  • Статическое связывание на этапе компиляции логера и sink (file, network, etc.)
  • Невероятно долгая компиляция даже на мощных компьютерах и как опосредованный результат – размер бинарных файлов
  • Один из самых низких показателей производительности из всех рассмотренных логеров
  • Конфигурация логера только в коде (нет поддержки конфигурационных файлов, командной строки …)
  • Нет поддержки ротации файлов
  • Синхронное исполнение, т.е. все задержки Sink (запись в файл например) отразятся на скорости исполнения Вашего кода.
  • Нет возможности комбинировать юникод и ANSI строки внутри одного сообщения
  • Высокий порог вхождения (достаточно сложна в изучении и запутанна)

  1. It's incredibly efficient, and is faster than all other serious C++ diagnostic logging libraries by a huge margin (up to two orders of magnitude) www.pantheios.org/essentials.html

N.B.: Библиотека одна из самых больших и сложных из всех сравниваемых, поэтому высока вероятность, что многие ее возможности не были рассмотрены.

Glog


Несмотря на то, что библиотека достаточно бедна функционалом и не блещет производительностью, зато эта библиотека вдохновила ряд других проектов, которые переросли своего «отца» считай на голову. Только это уже является жирным плюсом в карму авторам.

Плюсы библиотеки:
  • Простая в понимании и обслуживании библиотека
  • Поддержка сбоев процесса
  • Возможность конфигурации через командную строку
  • Низкое потребление памяти

Минусы:
  • Очень слабая документация
  • Слабая поддержка сбоев процесса – ограниченное количество сигналов, только Linux
  • Высокие накладные расходы при фильтрации сообщений
  • Многократный разрыв (до 9 раз) в производительности Debug и Release кода
  • Синхронное исполнение, т.е. все задержки Sink (запись в файл например) отразятся на скорости исполнения Вашего кода.
  • Активная разработка приостановлена
  • Нет поддержки юникода
  • Точность временных меток недостаточна
  • Высочайшее потребление CPU в тестах на много поточность при очень скромном выигрыше в производительности логера по сравнению с 1 потоком


log4cpp


Еще один заслуженный логер, произвел достаточно хорошее впечатление, никаких больших разочарований, никаких громких рекламных акций, никаких невыполненных обещаний.

Плюсы библиотеки:
  • Генерируемая документация
  • Большое количество поддерживаемых Sink
  • Низкое потребление памяти
  • Поддержка конфигурационных файлов
  • Хорошая эффективность фильтрации сообщений

Минусы:
  • Синхронное исполнение, т.е. все задержки Sink (запись в файл например) отразятся на скорости исполнения Вашего кода.
  • Нет поддержки юникода
  • Многократный разрыв (до 10 раз) в производительности Debug и Release кода
  • Низкая производительность (предпоследнее место)
  • Размер строки не должен превышать 1024 символа – иначе ассерт
  • Если путь к лог файлам не существует – генерируется exception


P7


Один из самых необычных логеров среди рассмотренных в данной статье, в комплект поставки входит не только логер, но и сервер для приема сообщений по сети, просмотра лог файлов, фильтрации, экспорта, управлением уровнем verbosity удаленно и многих других функций. Как и в случае со SpdLog прицел был на производительность и возможность использовать на встраиваемых устройствах, так как проект заточен на сеть и точное управление памятью.
Стоит так же отметить, что в случае использования Sink=FileBin или Sink=Baical проект предоставляет бесплатный софт для приема по сети, просмотра, фильтации и тд.:
софт.
image


Плюсы библиотеки:
  • Полная документация
  • Высокая производительность + высокая точность временных меток, большой отрыв от ближайшего конкурента SpdLog
  • Поддержка юникода
  • Полный контроль над потреблением памяти
  • Асинхронный – вызов Log функции независим от записи данных, таким образом нивелируются задержки на вызов функции.
  • Использование Shared memory (вместо «глобальных» переменных) для доступа к логерам и Sink, что позволяет получать доступ к логеру из любых участков кода процесса, даже из динамических модулей написанных на других языка поддерживаемых библиотекой, например C# или С
  • В комплект входят обертки для C, C#, Python
  • Помимо логов библиотека поддерживает запись и мониторинг в реальном времени
    телеметрии
    image
  • Возможность управлять удаленно (по сети) уровнем логирования на каждом устройстве, процессе или модуле, включать/выключать счетчики телеметрии
  • Из всех рассмотренных логеров имеет минимальные требования к процессору в пересчете на производительность
  • Доступен для нескольких языков и если разные части приложения написаны на C#, C++, C все они могут использовать один логер
  • Простота подключения собственного Text Sink
  • Широкий выбор Sink

Минусы:
  • Высоко интенсивное использование логера из нескольких потоков снижает (в 2.3 раза) производительность логера (плата за последовательность лог сообщений и временных меток)
  • Baical сервер имеет версию только под Windows, правда автор заявляет, что портирование под Linux в процессе
  • Разработка собственного Sink для бинарных данных сложна
  • Из-за сериализации лог сообщений нет возможности загрузить равномерно все ядра процессора


G3log


Наследник G2Log который в свою очередь был результатом переосмысления Glog. Автор преследовал в первую очередь производительность и ведет достаточно активную просветительскую работу на этот счет (1)(2). К сожалению, тесты производительности оказались синтетическими, и результат был далек от ожиданий.

Плюсы библиотеки:
  • Хорошая скорость фильтрации
  • Обработка сбоев процесса (генерация stack trace при наличии информации), к сожалению только под Linux
  • В целом удовлетворительная документация
  • Асинхронный – вызов Log функции независим от записи данных, таким образом нивелируются задержки на вызов функции.

Минусы:
  • Нет поддержки юникода
  • Слабая производительность, при этом высокое потребление ресурсов CPU
  • Замеры производительности можно считать синтетическими, так как большую часть времени логер складывает данные в буфера, а запись происходит по выходу из приложения и эта работа занимает время превышающее время логирования на порядок
  • Полная асинхронность – временные метки и сообщения могут быть перемешаны группами или по одному при интенсивном логировании
  • Неконтролируемое выделение памяти достигающие сотен мегабайт иногда гигабайт при интенсивном логировании
  • Конфигурация логера только посредством кода
  • Точность временных меток недостаточна
  • Многократный разрыв (до 30 раз) в производительности Debug и Release кода
  • Высокое потребление CPU
  • Компиляторы с поддержкой С++11 или выше


  1. kjellkod.wordpress.com/2014/08/16/presenting-g3log-the-next-version-of-the-next-generation-of-loggers
  2. kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log


Spdlog


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

Плюсы библиотеки:
  • Скорость, второе место в общем зачете
  • В целом удовлетворительная документация
  • Не требователен к процессору
  • Асинхронный – вызов Log функции независим от записи данных, таким образом нивелируются задержки на вызов функции.
  • Частичный контроль потребления памяти
  • Хороший набор вспомогательных макросов

Минусы:
  • Нет обработки сбоев процесса
  • Формат строка для Printf функций библиотеки не совместима с каноническим форматом, что существенно затрудняет безболезненную замену одного логера другим, по сути это билет в один конец
  • Полная асинхронность – временные метки и сообщения могут быть перемешаны группами или по одному при интенсивном логировании в итоговом файле
  • Для достижения оптимальной производительности потребляет около 250-300 мегабайт оперативной памяти, при уменьшении объема памяти производительность снижается почти в 18 раз.
  • Нет поддержки юникода
  • Только заголовочные файлы, что я отношу к минусу, так как включение этих файлов в каждый файл Вашего проекта существенно замедляет компиляцию, возможно использовать предварительно откомпилированные заголовки
  • Конфигурация логера только в коде
  • Точность временных меток недостаточна, особенно в свете достигаемой производительности (десятки и сотни тысяч сообщений в секунду при разрешении временных меток в 1ms)
  • Компиляторы с поддержкой С++11 или выше
  • Многократный разрыв (до 74 раз) в производительности Debug и Release кода


Easylogging


Логер с прицелом на “light-weight”, единственный заголовочный файл (около 6700 строк кода). Функционал стандартен для многих других логеров.

Плюсы библиотеки:
  • В целом удовлетворительная документация
  • Не требователен к процессору
  • Умеренное потребления памяти
  • Хороший набор вспомогательных макросов
  • Поддержка конфигурационных файлов, командной строки
  • Заявлен большое количество поддерживаемых платформ

Минусы:
  • Синхронное исполнение, т.е. все задержки Sink (запись в файл например) отразятся на скорости исполнения Вашего кода.
  • Низкая производительность
  • Не получилось завести фильтрацию, возможно опция находится в процессе разработки
  • Заявлена поддержка юникода, но по факту символы юникода не достигают файла и теряются по дороге
  • Только заголовочный файл, что я отношу к минусу, так как включение его в каждый файл Вашего проекта существенно замедляет компиляцию, возможно использовать предварительно откомпилированные заголовки
  • Точность временных меток недостаточна
  • Компиляторы с поддержкой С++11 или выше
  • Многократный разрыв (до 40 раз) в производительности Debug и Release кода
  • Необходимо включать потоко-безопасность специальным макросом (ELPP_THREAD_SAFE), по умолчанию отключено


Boost.Log


Логер с прицелом на функциональность, простоту, а так же производительность, такие приоритеты и цели преследует автор: мотивация.
Надо отдать должное, проект развивается почти 9 лет и присутствует в таком большом проекте как Boost.

Плюсы библиотеки:
  • Поддержка синхронного и асинхронного логирования (вызов Log функции независим от записи данных, таким образом нивелируются задержки на вызов функции)
  • Достаточно хорошая документация, покрывающая основные нужды
  • Атрибуты лог записей (номер потока, исходный файл, счетчики и тд) упрощают логирование
  • Поддерживается форматирование на «per sink» основе
  • Возможность использовать предикаты для фильтрации
  • Заявлено большое количество поддерживаемых Sink
  • Поддержка юникода
  • Поддержка конфигурационных файлов

Минусы:
  • Одна из самых сложных и запутанных библиотек логирования из представленных в этой статье. Целиком построена на смеси комплексных макросов и шаблонов. На изучение было потрачено больше всего времени. Еще больше времени потрачено на изучение «под капотом», возможно поэтому одним из главных участников является лишь один человек
  • Самая низкая производительность из всех рассмотренных логеров
  • Segmentation fault/access violation при выполнении участка кода
    char *pStr = nullptr;
    //….
    BOOST_LOG_SEV(lg, trace) << pStr;
    
  • Точность временных меток недостаточна, хотя с такой производительностью не столь критично (до десятка сообщений в один временной отрезок)
  • Многократный разрыв (до 54 раз) в производительности Debug и Release кода, следует отметить что в Debug режиме время исполнения становится совершенно неприемлимым (десятки минут на логирование)
  • Поддержка юникода не производит впечатление хорошо протестированного функционала (не удалось завести для асинхронного логирования в файл с сортировкой и FIFO фиксированного размера)
  • Нет обработки сбоев процесса, в свете того, что сохранение данных буферов важно даже для синхронного логера — представляет существенную опастность потери важных данных
  • Атрибуты лог записей не тривиальны – например имя исходной функции, файла, номера строки. Создание своих атрибутов так же не является тривиальной задачей
  • Запутанная компиляция со своими утилитами, но следует заметить, что поставляются так же скомпилированные бинарники, проблема общая для всего Boost
  • Включение библиотеки в свой проект существенно замедляет компиляцию и линковку (даже при использовании предварительно откомпилированных заголовков)
  • Все участки кода библиотеки покрыты генерацией исключений, правда, можно установить свой перехватчик, что уменьшает неудобства для людей предпочитающих не ловить исключения из логера при каждом обращении


Итог


Внимание!
Субъективное мнение автора.
  • Pantheios — успешный проект с точки зрения рекламы, но настолько тяжелый, неповротливый и перегруженный, что я не стал бы его подключать ни к каким проектам в которых я учавствовал
  • Glog — проект имел очень хороший старт, но к сожалению так и не вышел на марафонскую дистанцию, зато дал заряд идей и бодрости другим проектам. Пожалуй его я бы с легкостью использовал в небольших проектах не требовательых к производительности — утилиты для внутренних нужд, мини проекты и тд.
  • Log4cpp — произвел достаточно хорошее впечатление, но из-за внутренних орграничений (длинна строки, ассерты и тд) для меня ниша его использования ограничена небольшими приложениями для внутренних нужд, ничего критически важного/производительного.
  • P7 — серьезный проект, заточенный в первую очередь на сетевое использование и проекты от маленьких embedded до больших систем. Поддержка несколькиз языков, точный контроль памяти и удаленное управление, превосходная производительность, телеметрия делают его очень предпочтительным кандидатом для целого спектра задач (кроме пожалуй мелких утилит и скромных home проектов). Принцип – пишем все, а разберемся потом. Так же стоит отметить, что размер итогового файла (если Sink сконфигурирован на использование бинарныго файла) меньше файлов записанных другими протестированными логерами в несколько раз (5-7)
  • G3Log, SpdLog — синтетические тесты производительности от их авторов совершенно не позволяют мне их рассматривать серьезно, особенно в свете потребления памяти и процессора, возникает вопрос – где еще меня пытались очаровать рекламными заявлениями которые верны только если читать мелкий шрифт
  • Easylogging — произвел впечатление развивающегося проекта в котором многие функции еще недостаточно отполированы, может в будущем это будет прекрасный логгер, но пока вряд ли я буду где нибудь его использовать
  • Boost.Log — серьезный и большой проект, я испытываю уважение к автору способному поддерживать его напротяжении многих лет и удовлетворения такого большого спектра запросов, но не смотря на это вряд ли буду его использовать из-за его сложности, медлительности, возможности обрушить мое приложение просто передав nullptr как параметр. Для маленьких проектов он слишком сложен, для серьезных проектов он не достаточно стабилен и не вряд ли будет таковым опять же из-за внутренней сложности и ограниченности доступных человеческих ресурсов для полноценного поддержания проекта.


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

Подробнее
Реклама
Комментарии 41
  • +1

    Какие из этих логгеров поддерживают логгирование структурированной информации?

    • +2
      Сайт P7 уронили хабраэффектом :(
      Жалко, было бы интересно посмотреть
      • +1
        А можно ссылочку на конкретные настройки для каждого логгера в ваших тестах?
        • +1
          Как раз стояла цель не заниматся кастомизацией, а взять и использовать, так что кроме теста производительности в условиях равного потребления памяти никаких спец настроек не применялось. Все по умолчанию.
          • 0
            Не, ну это ни о чем. То-то я смотрю по вашим тестам асинхронный g3log наравне с синхронными логгерами. Там flush policy по умолчанию = 1.
            • +3
              Я думаю, в большинстве проектов не занимаются тонкой настройкой логера из-за времени/сроков/лени :-)
        • +1

          У Glog лицензия 3-clause BSD


          https://github.com/google/glog/blob/master/COPYING

        • +1

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

          • +1
            Boost.Log, всё-таки, очень хотелось бы увидеть в сравнении.
            • +1
              Это достаточно хорошая библиотека в плане функционала, постараюсь выкроить время и добавить ее в сравнение
              • 0
                Статья обновлена, Boost добавлен
              • 0
                А почему boost не рассмотрен? Просто любопытно.
                • +1
                  Время, эта статья заняла несколько недель моей жизни, в принципе вижу, что интерес к бусту есть, постараюсь найти время и обновить статью.
                  • 0
                    Статья обновлена, Boost добавлен
                  • 0
                    Спасибо за статью, как раз сейчас думаю какой логгер использовать. А что вы знаете/думаете насчет Boost.Log?
                    • 0
                      Хорошая библиотека в плане функционала, немного тяжеловата в плане скорости, но повторюсь — ее главный плюс функционал.
                      • 0
                        Спасибо, скорость в моем нынешнем проекте не так важна. Буду использовать Boost.
                      • 0
                        Статья обновлена, Boost добавлен
                      • +1
                        Думаю стоит отметить, что spdlog использует для вывода библиотеку fmt, и, на данный момент, не поддерживает стандартные printf последовательности. Не знаю плюс это или минус, но внимание обратить стоит.

                        Пользовался Easylogging и spdlog, удобно настраиваются, но компиляция ~5 секунд на простом hello world очень расстраивает, ох уж эти темплейты.
                        • 0
                          Спасибо за комментарий, статья поправлена. Скорее это минус ибо препятствует безболезненной замене одного логера другим, нужно будет выпиливать все руками и тестировать каждый вызов лог функции.
                        • 0
                          Понравился хороший анализ внутренностей.
                          Но не нравится постанова вопроса: какая библиотека лучше. И вот почему: выбор «логера» зависит еще (а на мой взгляд в первую очередь) от того, что вы хотите достичь.
                          1. Я имею ввиду следующее: лог трассировки это одно — для дебага вполне может подойти те библиотеки о которых вы говорите. Но допустим для аудита (лога безопасности)-нет. Они не ответят быстро на вопрос, а кто имел доступ к определенному объекту (например, запись о клиенте созданная 10 лет назад и редактируемая время от времени), и кто его и почему изменял — для этого лучше, чтобы лог был не в виде «плоского файла» с кучей другого мусора, а в виде структутрированной и нормализированной БД — вряд ли указанные логгеры позволяют делать что-то подобное.
                          2. А еще событие в трассировочном файле (для программиста, например событие с уровнем Warning) может иметь совершенной другой смысл (и уровень) для администратора (от вообще ничего не значащего, до Error). Аналогично относительно самого сообщения. Для программиста нужно выводить стек ошибки, для администратора — вряд ли. Ему бы лучше описание проблемы и решения.
                          3. Еще логгеры навязывают свою классификацию уровня сообщений. Мне часто не хватает уровня Success и Fail(типа Info, но с другим «оттенком» — так по логу проще искать удачные и не удачные операции), не хватает дополнительной категоризации — кроме уровня и категории, например название операции; или уникальный номер операции (номер потока не подходит) — приходится придумывать
                          А вообще это я к чему… На мой взгляд правильнее:
                          * для себя и команды выработать правила как, что и когда логгировать (я имею ввиду трассировку) и как обрабатывать эти логи
                          * отталкиваться от требований (какие еще логи нужно вести — для админов, для безопасников...) в каждом конкретном проекте — от этого может поменяться и подход вообще.
                          * выработать для себя интерфейс с набором удобных методов логгирования
                          * а потом под этой интерфейс реализовывать что угодно и иметь возможность в любой момент поменять библиотеку на любую другую.
                          • +1
                            Вы правы, для каждой задачи свой инструмент и если я правильно понял автора — сравниваются логеры по различным критериям и как раз была попытка уйти от формата советов «я пользуюсь бустом и мне хорошо», а дать более объективную картину, а каждый уж пусть решает для себя сам в зависимости от поставленной задачи.
                          • 0
                            Не знаю как у log4cpp, а вот у его «идейного предка» log4cxx на 30 минут работы написать свои аппендеры, например dailyrolling. Да и ещё через час работы над его словами его можно спокойно заставить миксовать wchar_t* и обычный char* (правда с условием, что первым будет wchar_t логгироваться) в одной строчке. Так же log4cxx не имеет таких минусов log4cpp как ограничение на 1024 символов в строке (хоть мегабайты туда пиши) и на исключение при отсутствие файла с логами (реально такое было лет 9 назад в версии 0.9.х). И ещё плюс — это динамическая конфигурация логгера (не перезапуская программу), он умеет следить за конфигурационный файлом и на лету подхватывать из него новые настройки. Так же странно, что синхронность отнесена к недостаткам. Обычно наоборот в случае падения приложения самые интересные логи они в самом конце. А если они не успеют сброситься на диск, то беда беда.
                            Так же у обоих большой плюс в том что для этого семейства логгеров (включая log4j и log4net) есть сторонние инструменты для работы с файлами логов, не надо придумывать свои велосипеды (например, logMX).
                            • +1
                              на 30 минут работы написать свои аппендеры

                              Задача статьи сравнить логеры с точки зрения их простоты использования. Возможно для Вас это 30 минут работы, для другого инженера не знакомого с логером — это несколько дней плюс тестирование и исправления и все это при наличии желания, особенно если можно взять что-то другое где функция доступна из коробки.

                              Так же странно, что синхронность отнесена к недостаткам

                              Я не смог отнести к достоинствам тот факт, что синхронный логер может приостановить выполнение вашей программы на длительное время только потому, что обращается к файлу, а HDD скажем занят. Для домашних проектов это не ограничение, для серьезных проектов — лучше не иметь таких случайных задержек плюс существенное падение производительности в случае интенсивного логирования.

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

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

                              logMX

                              Утилита стоит денег если используется профессионально.
                              • 0
                                Задача статьи сравнить логеры с точки зрения их простоты использования. Возможно для Вас это 30 минут работы, для другого инженера не знакомого с логером — это несколько дней плюс тестирование и исправления и все это при наличии желания, особенно если можно взять что-то другое где функция доступна из коробки.

                                Смысл моего этого замечания на самом деле был в том, что log4cxx (уверен, что это относится и к log4cpp/log4cplusplus) можно быть сильно, и быстро, кастомизировать под свои кейзы в случае необходимости. Конечно, такое требуется обычно раз в жизни, но обычно в тот самый момент, когда времени переходить на новый логгер точно не будет. Я, кстати, слабо представляю зачем тот же DayliRollingAppender вообще нужен, хоть и делал его. Другими словами, кастомизация логгера — это один из кейсов его использования. И если вы хотели «сравнить логеры с точки зрения их простоты использования», простота расширения log4cxx — это вообще в тему.

                                Я не смог отнести к достоинствам тот факт, что синхронный логер может приостановить выполнение вашей программы на длительное время только потому, что обращается к файлу, а HDD скажем занят. Для домашних проектов это не ограничение, для серьезных проектов — лучше не иметь таких случайных задержек плюс существенное падение производительности в случае интенсивного логирования.

                                Это большой миф, что синхронное логгирование может что-то там замедлить. Объясню на примере. Мы делаем софт, который сильно грузит cpu и диск. И для нас каждый процент cpu, так же как любая задержка работы с файловой системой, очень важен. Вплоть до того, что пришлось Sleep(0.5) реализовывать, так как 1 миллисекунда для нас — это слишком большая задержка. Соответственно, постоянно профилируемся по нагрузке проца, и часто ищем задержки с помощью gpuview и других тулзовин. Так вот, синхронный логгер не даёт ни сколько нибудь значительную нагрузку на cpu, ни сколько нибудь значительных задержек работы с файловой системой. Насколько я помню, несмотрю на то, что пишет он синхронно в файл, то сам файл по дефолту флашится на диск сразу после записи только при ERROR/FATAL. А так собственно есть буферизация в винде, по умолчанию винда скидывает данные на диск большими блоками (аля секторами), что очень эффективно. В синтетических тестах может и есть какая-то разница с асинхронными логгерами, а вот в реальных приложениях — нету.

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

                                Вы зря путаете логгирование и перехват исключений. Одно другое никак не заменяет. А случаев, когда партнёр, находясь в 300 км от ближайшего интернета, по телефону зачитывает логи модуля, который падает, за неимением возможности выслать даже минимальные дампы, в моей практике было не один, не пять, и даже не 10. Кстати, как показала практика — распечатывание стека при падении в самом падающем приложении — тоже не состоятельная идея (применительно к Винде). Там куча проблем, которые надо решать. Например, либо символы в бинари встраивать (C7), увеличивая их размер в разы, а то и в десятки раз, либо всё-таки старая добрая pdb и раскрутка стека на машине разработчика. Или ещё например, что бы записать в файл, когда закончилась память (выжрали 4 гига, и new постоянно кидает bad_alloc), тоже надо постараться, так как даже fopen пытаться выделить некие буффера, и если памяти реально не осталось, то даже это сделать не получится, и том чтобы использовать ofstream разговор вообще уже не идёт (если что я знаю как такую проблему решать, я к тому, что это всё не тривиально).

                                Утилита стоит денег если используется профессионально.

                                Вы здесь опять суть не уловили. Дело не в том, что есть бесплатный logmx для некоммерческого использования, а в том, что для log4j/log4net/log4cxx построена огромная инфрастуктура. Тех же анализаторов логов можно за 10 минут наверное с десяток нагуглить, на любой вкус и кошелёк, всё не закачанчивается на logmx'е. Кстати, обычные логи в log4cxx настраиваются так хорошо, что обычно не требуют специализированных средств для своего анализа, обычно это бывает полезно в очень специфичных случаях.
                                • 0
                                  Кстати, за саму статью и за тот огромный труд, что вы проделали, большое вам спасибо!

                                  Мой комментарий не принимайте близко к себе. Просто меня немного задело «ограничение на 1024 символов в строке и на исключение при отсутствие файла с логами». Решил рассказать людям, о его старом дедушке — рабочей лошадке log4cxx, лишённого этих детских болезней.
                                  • 0
                                    Спасибо, я не принимаю на свой счет, вижу, что у Вас богатый опыт, Вы приводите свои примеры из практики, не сомневаюсь, что для ваших случаев эти утверждения были истины.
                                    Смысл моего этого замечания на самом деле был в том, что log4cxx

                                    Опыт показывает, что при наличии опыта и мотивации можно кастомизировать почти все :) Вопрос чаще всего стоит во времени. Но вы правы эти логеры не так сложны в кастомизации при наличии опыта.
                                    Это большой миф, что синхронное логгирование может что-то там замедлить.

                                    Видимо зависит от области, устройств и тд, так как если вы пишете только ошибки и время от времени распечатываете какие то значения то никакого вреда от синхронного логгера не будет. Если речь идет о тысячах в секунду то уже приходится считать.
                                    На прошлой работе люди писали по 30-40 тысяч сообщений в секунду в нормальном режиме и порядка 2.5M под нагрузкой (разработка железа и софта к нему), синхронный логгер такого просто не вытянет, физически. Если у вас маленькое линукс устройство и нужно логировать каждый чих — тоже нет смысла даже пытаться прикрутить синхронный логер, процессор будет обслуживать только его.
                                    Пересчет одного лог вызова в наносекунды дело бессмысленное если у вас 3 лог строчки в секунду и очень полезное если вы занимаетесь трассированием.
                                    Я что пытаюсь сказать — зависит от области где вы применяете и самое главное как.
                                    Вы зря путаете логгирование и перехват исключений. Одно другое никак не заменяет.

                                    Хотел лишь сказать, что они друг друга очень неплохо дополняют и сброс важных буферов в случае краша дело полнезное, будь то синхронный логер пишущий в свой внутренний буфер и сбрасывающий время от времени в файл или асихнронный.
                                    А уж краш дамп если он есть — даст фору логу и приличную в плане «поймать и убить».
                                    Распечатывание стека при падении в самом падающем приложении

                                    Согласен, под виндой страшный ужас, под Линуксом — достаточно легко.
                                    Тех же анализаторов логов можно за 10 минут наверное с десяток нагуглить

                                    Да, вы правы, софта такого рода много, да и самому его сделать не сложно.

                                    Еще раз спасибо за отзыв и комментарии и поменьше Вам случае зачитывания логов по телефону :) А логеры… зависит от области, знаний, способов применения, наличия сети и кучи других факторов, в любом случае никого не хотел обидеть, а только дать некий срез по десяткам критериев.
                            • 0
                              насколько я помню у log4cpp есть сетевой адаптер, который передаёт данные по не своему протоколу.
                              • 0
                                У меня есть свой логгер, основу которого я заложил ещё в далёком 2004 году. И с тех пор ещё никогда такого не было, чтобы мне удалось использовать его в каком-то проекте без изменений. Каждый раз приходится либо что-то допиливать в самом логгере под конкретный проект, либо отказываться от его использования вообще. Так что я давно на собственном опыте понял, что «законченная библиотека логирования» — это оксюморон. Ну, или что-то типа единорога. Можно очень легко представить его мысленно, поскольку существует обширная мифология на эту тему. Но похоже, что в реальности ничего такого нет.
                                • 0
                                  Статья большая и основательная, автору большое спасибо за проделанную работу!

                                  Но пока читал, прям на фразу «если какой-то очень важный логгер был пропущен (boost к примеру)» очень расстроился. Мне кажется, для многих проектов Boost.Log является стандартом де-факто логгирования, ведь все равно часто boost приходится тащить с собой. Собственно, у себя его и используем, как вы сказали, «полет нормальный».
                                  • +1
                                    Да. Именно потому, что Boost все равно уже прикручен я и решил на нем остановиться. Грустно только, что даже tutorials с официального сайта не работают без приседаний.
                                    • 0
                                      Статья обновлена, Boost добавлен
                                    • 0
                                      в случае Easylogging существенно снижается скорость компиляции.
                                      Как раз таки для таких случаев и был выдуман такой полезный механизм, как предварительно откомпилированные заголовки.
                                      • 0

                                        Вы не знаете, есть какой-то способ единообразно генерировать Precompiled-Headers для clang и gcc?
                                        Что бы в Makefile можно было просто заменить CPP = g++ на CPP = clang++?

                                        • 0
                                          Знаю. Используйте cmake, который вам сгенерирует Makefile для нужного компилятора, и вот эту примочку к нему, которая умеет проставлять cmake'у правильные флаги прекомпилированных заголовков, и для clang, и для gcc, и даже для visual studio.
                                        • 0
                                          Вы правы — это снизит время компиляции.
                                          В статье я постарался рассказать о том какие особенности у каждого логера и какие дополнительные действия нужно предпринять чтоб интеграция и использование принесли желаемую пользу.
                                        • 0
                                          А можно log4cplus добавить?
                                          Хорошая лицензия. Разработка не заморожена.
                                          • +1
                                            Сейчас на очереди Boost.Log, как его закончу постараюсь найти время и посмотреть в сторону предложенного вами проекта.
                                            Изучение каждого логера занимает существенное время, Boost например занял чуть больше недели и еще не закончен.
                                            Если я не ошибаюсь Вы являетесь одним из контрибьюторов этого проекта — если это так и вы готовы помочь мне с ответами на некоторые вопросы связанные с предложенным вами логером (что существенно сократит время на вычитку документации и исходников) — пожалуйста скиньте в личку контактный е-мэйл.
                                            Всего наилучшего!
                                          • 0
                                            По поводу P7:

                                            + Высокая производительность + высокая точность временных меток, большой отрыв от ближайшего конкурента SpdLog

                                            — Высоко интенсивное использование логера из нескольких потоков снижает (в 2.3 раза) производительность логера (плата за последовательность лог сообщений и временных меток)

                                            Если я правильно понял, P7 быстрее всех конкурентов и в многопоточном режиме, а значит это нельзя указывать как недостаток. Правильнее написать в плюсах, что отрыв в многопоточном режиме меньше.
                                            • 0

                                              Запустил ваш тест на нашем рабочем логгере (который на 100% написан мной :)


                                              Kонфигурация:


                                              • Window 7x64 (6.1.7601 Service Pack 1 Build 7601)
                                              • Visual studio 2015, update 3
                                              • RAM: 16Gb (???)
                                              • CPU: Intel Core i7-3770 @ 3.40GHz (~ +40%)
                                              • HDD: SanDisk SDSSDHP256G ATA Device

                                              Строка в коде выглядит так:


                                              LOGF_TRC("Message + all required information, #%u", i);


                                              в файле:


                                              [07968] [2016/12/22 15:56:22.625] [TRC] main(): Message + all required information, #0


                                              Releаse Time (ms):
                                              1 поток в синхронном режиме — 3300
                                              1 поток в асинхронном режиме — 200
                                              4 потока в синхронном режиме — 4800
                                              4 потока в асинхронном режиме — 150

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