Порівняння бібліотек логування



У мережі величезна кількість майданчиків формату 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. Для третьої і самої педантичною групи я сподіваюся, ця стаття збереже, як мінімум, одну з 2х тижнів вишукувань, в масштабах всієї індустрії це може бути значна кількість людино-годин
N. B. Стаття досить об'ємна, тому якщо зважитеся на читання, будь ласка, запасіться терпінням!

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

Але навіть порівняння цих 7 кандидатів зайняло більше 2х тижнів викурювання доків, issues, читання форумів, написання тестів та збору результатів.

Так чи інакше, якщо якийсь дуже важливий логгер був пропущений (boost до прикладу) – статтю можна оновити. В огляд потрапили:

  1. Pantheios
  2. Glog
  3. log4cpp
  4. P7
  5. G3log
  6. Spdlog
  7. Easylogging
Загальні характеристики вибраних логгерів
Осіб. Мови Обновок. Платф. Комп.
Pantheios BSD C++ 2010 Windows, *nix, OS X VC++, GCC, Intel, Borland, Comeau,Digital Mars,Metrowerks
Glog Невідома С++ 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
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, RaspberryPi VC++, GCC, Clang, Intel
Документація і залежності
Важко заперечувати важливість документації для складних проектів, сучасні gps логери простими проектами назвати можна з великою натяжкою і наявність хорошої документації часом істотно прискорює впровадження і виправлення помилок:
Документація Залежно
Pantheios Повна (API + використання) STLSoft
Glog Рудиментарна, майже відсутній Google gflags, слабка залежність (1)
log4cpp Генерується (Doxygen) (API тільки) Boost, слабка залежність (1)
P7 Повна (API + використання) Немає
G3log Базова (загальні методи використання) Немає
Spdlog Базова (загальні методи використання) — Ні, тільки заголовковий файл (2)
Easylogging Базова (загальні методи використання) — Ні, тільки заголовковий файл (2)
  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)
  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 мегабайт тільки на бібліотеку логування.
Обробка збоїв процесу
Правильна обробка збоїв процесу (crash handling) в першу чергу важлива для асинхронних логгерів, оскільки частина даних зберігається в буферах і якщо їх вчасно не зберегти, то можливо найцінніші дані прямо перед збоєм будуть втрачені.

Поширені 3 підходи в перехопленні падінь:

  • Автоматичний, бібліотека сама налаштовує всі вектори і зробить все сама, великим мінусом такого рішення є мізерність перехоплених сигналів, а так само перешкоди для програми, яку можливо хотіло б сама обробляти падіння в цілях збереження crash dump файлу або скидання буфера.
  • Ручний – бібліотека надає примітиви для перехоплення падінь і скидання буферів, а програма вже сама вирішує, коли і як встановити перехоплювач і що робити у випадку падіння.
  • Нехай падає, справа житейська
Pantheios Немає
Glog автоматичний, тільки під Linux (1)
log4cpp Немає
P7 ручний (2)
G3log автоматичний, тільки під Linux (3)
Spdlog Немає (4)
Easylogging автоматичний, тільки під Linux (5)
  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
Стиль логування та висновок (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 Printf File, network (собств. протокол і сервер(2)), null
G3log Printf, Log() << Message File (3)
Spdlog Printf File, syslog, console
Easylogging Printf (1), Log() << Message File, syslog, console
  1. У разі використання «Printf» генерував exception, ймовірно це тимчасова проблема.
  2. Свій сервер, використовується для досягнення максимальної продуктивності. Сервер безкоштовний, але на жаль підтримує тільки Windows, судячи з усього заснований на Qt, запит про підтримку Linux був спрямований автору. Швидкість відправки логів по мережі в тестовій конфігурації була близько 3,5 мільйонів у секунду при завантаженні CPU – 13%. Докладно тести продуктивності обговорюються в наступних розділах.
  3. офіційну поставку не входить Sink з підтримкою ротації файлів і консоль, але ці додатки можна завантажити github.com/KjellKod/g3sinks
Ініціалізація логер
Ініціалізація або передача параметрів досить важливий пункт т. к. додає гнучкості логгеру і усуває необхідність перекомпіляції, якщо Ви вирішили змінити рівень логування наприклад.
Pantheios Ручна (тільки в коді)
Glog Командний рядок, ручна, змінні середовища оточення
log4cpp Конфігураційний файл (1), ручна
P7 Командний рядок (2), ручна
G3log Ручна (тільки в коді)
Spdlog Ручна (тільки в коді)
Easylogging Конфігураційний файл, командний рядок, ручна
  1. Докладна і добре організована настройка параметрів логер, мабуть, найбільш розгорнута з усіх.

  2. У всіх інших логгерах для того щоб параметри командного рядка були оброблені – ви повинні передати їх в логгер руками з int main(int argc, char* argv[]) функції. В даному логгере ці параметри можуть бути перехоплені автоматично з будь-якої частини програми/модулі (dll,so).
Налаштування фільтрації
Найпоширеніша методика фільтрації – за рівнями логування, скажімо, якщо фільтр встановлений на ERROR рівень — то все що менше ERROR (TRACE, DEBUG, INFO, WARNING ...) в лог потрапляти не буде. Цей метод дуже зручний для відсіювання великої кількості непотрібних в даний момент інформації і збереження CPU і місця на диску.
Pantheios Немає (1)
Glog Командний рядок, ручна, змінні середовища оточення
log4cpp Конфігураційний файл (4), ручна
P7 Командний рядок, віддалено по мережі в режимі реального часу (2) (3), ручна
G3log Немає (5)
Spdlog Ручна
Easylogging Ручна (6)
  1. Для організації фільтрації потрібно розробити свій FrontEnd
  2. Підтримується тільки якщо дані висилаються мережі, у разі запису в локальний файл сервер не має доступу до Багатослівність level
  3. На додаток до глобального рівня можна встановлювати рівні для кожного модуля.
  4. Ієрархічні логування та встановлення рівнів індивідуально для кожного логер
  5. За умолнчанию відключена, включається макросом G3_DYNAMIC_LOGGING, після цього можна в ручному режимі встановити рівень на всі логгер. Істотно знижує продуктивність.
  6. Підтримка заявлена виробником, але змусити її працювати не вдалося, під час використання склалося враження, що функція знаходиться в стадії розробки або закинута.
Підтримка unicode
Ця частина тестування була однією з найсумніших, в 2016 році підтримка юнікоду в таких відомих бібліотеках все ще перебуває на рівні «not officially».

Бібліотека потрібна для того, що зберегти важливі дані програми (прізвище користувача, шлях до файлу, ім'я домену), а більшість просто не дозволять Вам це зробити якщо дані не вкладаються в тривіальний char.
Pantheios Utf-16 (1)(4), Utf-8
Glog Немає
log4cpp Немає
P7 Windows — Utf-16, *nix — Utf-8
G3log Немає
Spdlog Немає
Easylogging Windows Utf-16 (2), Utf-8 (3)
  1. Майже ідеально, за винятком того, що підсумковий лог файл не має маркера юнікоду та кодування в програмі перегляду потрібно буде вибирати самому.
  2. Підтримка заявлена, але не реалізована, символи юнікоду в лог файл не потрапляють.
  3. Макрос START_EASYLOGGINGPP не підтримує юнікод
  4. В одному повідомленні неможливо поєднати ANSI рядок скажімо з UTF-16
Доступ до логгеру
У сучасних бібліотеках питання «хто володіє логером» залишається за кадром, найчастіше можна написати LOG(ERROR) << «My message» і бібліотека подбати про все сама. Така простота досягається з допомогою глобальних змінних. Етичність використання глобальних змінних я залишу за кадром, все таки це особливий випадок, але простота використання глобальної змінної у разі простого додатка обертається проти розробника складного додатка складається з багатьох динамічних або статичних модулів.

Інший варіант отримання доступу до логгеру це самостійно створити об'єкт і контролювати його життєвий цикл.

І останній варіант – гібридний, об'єкти логер створюються в ручному режимі, а потім використовується глобальні змінні (registry) або колективна пам'ять, спільна для всього процесу включаючи динамічні модулі.
Pantheios Глобальні змінні, автоматична ініціалізація
Glog Глобальні змінні, автоматична ініціалізація
log4cpp Глобальні змінні, автоматична і ручна ініціалізація
P7 Загальна пам'ять, ручна ініціалізація
G3log Глобальні змінні, автоматична і ручна ініціалізація
Spdlog Глобальні змінні, ручна ініціалізація
Easylogging Глобальні змінні, автоматична ініціалізація
Ротація файлів
Pantheios Немає
Glog Розмір
log4cpp Розмір (2)
P7 Час, розмір (1) (2)
G3log Розмір, за замовчуванням не доступний (1)
Spdlog Розмір, час(денної) (1)
Easylogging Розмір
  1. Кожен файл у назві містить дату і час
  2. Підтримується опція «макс. кількість файлів» дозволяє зберігати тільки N останніх файлів


Точність часу
Багато з розглянутих у цій статті логгерів розроблялися з прицілом на високу продуктивність, з потенціалом в мільйони повідомлень в секунду. Але крім високих швидкостей потрібні точні часові мітки високого дозволу, оскільки якщо у вас в лог-файлі є пара десятків або навіть сотень повідомлень з однаковою тимчасовою міткою – це означає, що частина інформації про час виконання вже втрачена.
Pantheios Windows: 10ms, custom back-end може допомогти збільшити точність
Linux: теор. мінімальне значення 1ns, залежить від апаратної частини
Glog Windows: 10ms
Linux: теор. мінімальне значення 1ns, залежить від апаратної частини
log4cpp Windows: 10ms
Linux: теор. мінімальне значення 1ns, залежить від апаратної частини
P7 Windows: 100ns
Linux: теор. мінімальне значення 1ns, залежить від апаратної частини
G3log Windows: 1ms
Linux: 1us
Spdlog Windows: 1ms
Linux: теор. мінімальне значення 1ns, залежить від апаратної частини
Easylogging Windows: 1ms
Linux: 1us
Продуктивність
Дуже багато логгер з наведеного в даній статті списку заявляють, що одним з головних пріоритетів для них є продуктивність.

Я поставився до цього заявою більш ніж серйозно і провів ряд тестів:

  • у конфігурації за замовчуванням
  • в умовах рівного використання пам'яті
  • в режимі одного потоку
  • в режимі багато-потоковості
Для кожного тесту робиться вимір часу і 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) 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%
  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 мегабайта.
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) 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%
  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 мегабайта.
Фільтрація
логгер повинен обробити 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) - - - -
  1. Фільтрація не доступна за замовчуванням
  2. Не вийшло змусити працювати фільтрацію
Огляд продуктивності
Продуктивність багатьох логгерів опинилася на дуже хорошому рівні.
На жаль, майже у всіх логгерів крім P7 спостерігається колосальний розрив у продуктивності між debug і release складанням, деколи коефіцієнт досягає 74 (Spdlog: 65660 / 885). Це може ускладнити налагодження проектів у силу зростання затримок при логировании.

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

Зазвичай розробник-інтегратор бажає, щоб бібліотека була непомітна і робила свою справу з мінімальними вимогами до устаткування, особливо якщо мова йде про маленьких вбудованих системах.

Тому я зробив перерахунок кращих показників кожного логер (тести з рівним споживанням пам'яті) з метою визначити, скільки може бути записано лог повідомлень, використовуючи тільки 1% CPU і розумний, але все ще великий обсяг пам'яті дорівнює 1 mb

Формула розрахунку:
((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%)
Висновки
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
Один з найбільш незвичайних логгерів серед розглянутих у цій статті, в комплект поставки входить не тільки логгер, але і сервер для прийому повідомлень по мережі, перегляду лог файлів, фільтрації та експорту. Як і у випадку з SpdLog приціл був на продуктивність і можливість використовувати на вбудованих пристроях, так як проект заточений на мережу і точне управління пам'яттю.
Варто зазначити, що проект використовує в якості лог бінарний формат файлів і для перегляду/конвертації потрібно безкоштовний софт.

Такийimage
Плюси бібліотеки:

  • Повна документація
  • Висока продуктивність + висока точність часових міток, великий відрив від найближчого конкурента SpdLog
  • Підтримка unicode
  • Повний контроль над споживанням пам'яті
  • Асинхронний виклик Log функції незалежний від запису даних, таким чином нівелюються затримки на виклик функції.
  • Використання Shared memory (замість «глобальних» змінних) для доступу до логгерам і Sink
  • У комплект входять обгортки для C, C#, Python
  • Крім логів бібліотека підтримує запис і моніторинг в реальному часі телеметрії.

    Такimage
  • Можливість управляти дистанційно (по мережі) рівнем логування на кожному пристрої, процесі або модулі, вмикати/вимикати лічильники телеметрії
  • Не вимогливий до процесора
  • Доступний для декількох мов і якщо різні частини програми написані на C#, C++, C всі вони можуть використовувати один логгер
Мінуси:

  • Складність в написанні custom Sink
  • Високо інтенсивне використання логер з декількох потоків знижує (в 2.3 рази) продуктивність логер (плата за послідовність лог повідомлень і тимчасових міток)
  • Лог файл має бінарний формат, потрібен спеціальний софт для перегляду або конвертації в текстову форму (формат файлу відкритий)
  • Немає підтримки SysLog, на заміну йому пропонується інший мережевий протокол з можливістю зворотного зв'язку
  • Немає автоматичної обробки збоїв процесу – залишено на інтегратора, є функція по скиданню буферів


G3log
Спадкоємець G2Log який в свою чергу було результатом переосмислення Glog. Автор переслідував в першу чергу продуктивність і веде досить активну просвітницьку роботу на цей рахунок (1)(2).

Плюси бібліотеки:

  • Хороша швидкість фільтрації
  • Обробка збоїв процесу (генерація stack trace при наявності інформації)
  • В цілому задовільна документація
  • Асинхронний виклик 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 функції незалежний від запису даних, таким чином нівелюються затримки на виклик функції.
  • Частковий контроль споживання пам'яті
  • Хороший набір допоміжних макросів
Мінуси:

  • Немає обробки збоїв процесу
  • Повна асинхронність – тимчасові мітки і повідомлення можуть бути перемішані групами або по одному при інтенсивному логировании в отриманому файлі
  • Для досягнення оптимальної продуктивності споживає близько 250-300 мегабайт оперативної пам'яті, при зменшенні об'єму пам'яті продуктивність знижується майже в 18 разів.
  • Немає підтримки юнікоду
  • Тільки відмінності файли, що я відношу до мінуса, так як включення цих файлів в кожен файл Вашого проекту істотно уповільнює компіляцію
  • Конфігурація логер тільки в коді
  • Точність часових міток недостатня, особливо в світлі досягається продуктивності (3.7 мільйона повідомлень в секунду при дозволі тимчасових міток в 1ms)
  • Компілятори з підтримкою С++11 або вище
  • Багаторазовий розрив (до 74 разів) в продуктивності Debug і Release коду
Easylogging
Логгер з прицілом на «light-weight», єдиний заголовковий файл (близько 6700 рядків коду). Стандартний функціонал для багатьох інших логгерів.

Плюси бібліотеки:

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

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

Суб'єктивна думка автора.
  • Якщо Вам потрібен заслужений логгер без підвищених вимог до продуктивності, але гнучкий, з ієрархічною системою фільтрації – вибирайте «log4cpp» — це робоча конячка багатьох проектів
  • Якщо Вам потрібна екстремальна продуктивність, точний контроль пам'яті, віддалене управління – вибирайте P7, у Вас буде потужний інструмент моніторингу вашого софта навіть на дуже скромному залозі, за принципом – пишемо все, а потім розберемося

  • Синтетичні тести виробників G3Log, SpdLog абсолютно не дозволяють мені їх рекомендувати, особливо в світлі споживання пам'яті і процесора, виникає питання – де ще мене намагалися зачарувати рекламними заявами які вірні тільки якщо читати дрібний шрифт
  • Easylogging справив враження розвивається проекту в якому багато функцій ще недостатньо відполіровані, може в майбутньому це буде прекрасний логгер
  • Glog — проект мав дуже хороший старт, але на жаль так і не вийшов на марафонську дистанцію, зате дав заряд ідей і бадьорості іншим проектам
  • Pantheios — добротний логгер, правда з деякими недоліками в функціоналі. З-за дуже низької продуктивності обмежене застосування

Спасибі велике за увагу та Ваш час, сподіваюся ця стаття допомогла Вам у складанні загального уявлення про реалії сучасних логгерів.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.