Site icon Заметки разработчика

Настраиваем уровень логирования подсистем Logme

подсистемы логирования С++

В многокомпонентном приложении один канал логирования часто используется сразу несколькими компонентами. Logme позволяет задать отдельный уровень логирования подсистемы, не меняя уровень всего канала. Благодаря этому можно включить DEBUG только для проблемного компонента или, наоборот, подавить слишком частые INFO-сообщения одной подсистемы.

Представим сервер, в котором подсистемы HTTP, TLS, AUTH и POLICY пишут в один журнал:

2026-07-28 14:37:05:201   #HTTP Accept(): Accepted connection from 192.168.1.25
2026-07-28 14:37:05:204   #AUTH Authenticate(): User john authenticated
2026-07-28 14:37:05:208   #POLICY Apply(): Policy 17 applied
2026-07-28 14:37:05:212   #TLS Handshake(): TLS connection established

Уровень канала установлен в INFO. Для штатной работы этого достаточно: в журнал попадают основные события, но низкоуровневая диагностика не создаёт лишний шум.

Проблема начинается, когда подробные сообщения нужны только от одной подсистемы.

Почему уровень канала не всегда подходит для диагностики

Допустим, некоторые клиенты периодически не могут установить TLS-соединение. На уровне INFO в журнале может остаться только итоговая ошибка:

2026-07-28 14:41:52:018 E #TLS Error: Failed to establish a secure connection

По такой строке нельзя определить, на каком этапе возникла проблема:

Для расследования нужны сообщения уровня DEBUG.

Можно временно переключить весь канал с INFO на DEBUG, но тогда подробные сообщения начнут писать все подсистемы:

2026-07-28 14:42:31:101 D #HTTP ParseHeader(): Parsed header Host
2026-07-28 14:42:31:102 D #HTTP ParseHeader(): Parsed header User-Agent
2026-07-28 14:42:31:103 D #POLICY Evaluate(): Evaluating condition 31
2026-07-28 14:42:31:104 D #POLICY Evaluate(): Evaluating condition 32
2026-07-28 14:42:31:105 D #AUTH FindToken(): Looking up token in cache
2026-07-28 14:42:31:106 D #TLS Handshake(): Starting TLS handshake
2026-07-28 14:42:31:107 D #HTTP ParseBody(): Received 512 bytes
2026-07-28 14:42:31:108 D #POLICY Evaluate(): Condition 31 returned true

Нужная информация в журнале присутствует, но быстро теряется среди посторонних сообщений.

На нагруженной системе глобальный DEBUG создаёт и другие проблемы:

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

Как задать отдельный уровень логирования подсистемы

Вместо изменения уровня всего канала можно задать override только для TLS:

Уровень канала:        INFO
Уровень подсистемы TLS: DEBUG

Уровень канала остаётся прежним для HTTP, AUTH, POLICY и остальных компонентов. Только сообщения с SID TLS начинают проходить начиная с уровня DEBUG.

Журнал становится заметно полезнее:

2026-07-28 14:45:17:201   #HTTP Accept(): Accepted connection from 192.168.1.25
2026-07-28 14:45:17:203 D #TLS Handshake(): Starting TLS handshake
2026-07-28 14:45:17:204 D #TLS Handshake(): Received ClientHello
2026-07-28 14:45:17:205 D #TLS Handshake(): Client requested TLS 1.3
2026-07-28 14:45:17:206 D #TLS Handshake(): Selected cipher TLS_AES_256_GCM_SHA384
2026-07-28 14:45:17:207 D #TLS Verify(): Verifying certificate chain
2026-07-28 14:45:17:209 D #TLS Verify(): Certificate expires at 2026-07-20 00:00:00
2026-07-28 14:45:17:211 E #TLS Error: Certificate verification failed: certificate has expired
2026-07-28 14:45:17:214   #POLICY Apply(): Policy 17 applied

Теперь видна вся последовательность TLS-handshake:

Starting TLS handshake
Received ClientHello
Client requested TLS 1.3
Selected cipher TLS_AES_256_GCM_SHA384
Verifying certificate chain
Certificate verification failed: certificate has expired

При этом в журнал не попадают отладочные сообщения парсера HTTP, кеша аутентификации и интерпретатора политик.

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

Почему для этого не обязательно создавать отдельный канал

На первый взгляд можно создать отдельный канал для каждой подсистемы:

http
tls
auth
policy

Но канал обычно отвечает не только за уровень фильтрации. С ним могут быть связаны:

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

Subsystem level override решает задачу без изменения архитектуры каналов. Сообщения продолжают попадать в те же backend-ы и файлы, но порог фильтрации выбирается с учётом SID.

Это особенно удобно, когда один канал намеренно объединяет события нескольких логически связанных компонентов.

Override может сделать журнал не только подробнее, но и тише

Отдельный уровень логирования подсистемы полезен и в обратной ситуации.

Допустим, канал работает на INFO, но подсистема HTTP регистрирует каждый запрос:

2026-07-28 15:01:17:441   #HTTP Accept(): Accepted connection from 192.168.1.31
2026-07-28 15:01:17:443   #HTTP Parse(): Request GET /api/status
2026-07-28 15:01:17:447   #HTTP Send(): Response 200, 842 bytes
2026-07-28 15:01:17:451   #HTTP Accept(): Accepted connection from 192.168.1.32
2026-07-28 15:01:17:453   #HTTP Parse(): Request GET /api/status
2026-07-28 15:01:17:456   #HTTP Send(): Response 200, 842 bytes

Если повысить уровень всего канала до WARN, одновременно исчезнут полезные события остальных подсистем:

#AUTH User john authenticated
#POLICY Policy 17 applied
#TLS TLS connection established

Вместо этого можно оставить канал на INFO, а для HTTP задать WARN:

Уровень канала:         INFO
Уровень подсистемы HTTP: WARN

После этого обычные HTTP-запросы перестают попадать в журнал, но предупреждения и ошибки сохраняются:

2026-07-28 15:02:03:101   #AUTH Authenticate(): User john authenticated
2026-07-28 15:02:03:108   #POLICY Apply(): Policy 17 applied
2026-07-28 15:02:08:512 W #HTTP Send(): Client response is delayed by 5240 ms
2026-07-28 15:02:11:737 E #HTTP Error: Failed to accept connection: too many open files

Рутинный поток запросов больше не занимает место в журнале, но действительно важные события HTTP не потеряны.

Как выбирается итоговый уровень сообщения

Уровень канала остаётся правилом по умолчанию.

Например:

Канал: INFO

Без overrides все подсистемы используют этот уровень:

HTTP    → INFO
TLS     → INFO
AUTH    → INFO
POLICY  → INFO

После настройки индивидуальных уровней:

TLS  → DEBUG
HTTP → WARN

получается следующая схема:

HTTP    → WARN
TLS     → DEBUG
AUTH    → INFO
POLICY  → INFO

Override именно заменяет уровень канала для соответствующей подсистемы.

Это не дополнительный фильтр, который может быть только строже основного уровня. Override работает в обе стороны:

INFO → DEBUG

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

INFO → WARN

позволяет подавить рутинные сообщения.

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

Несколько уровней подсистем одновременно

В реальном приложении часто требуется настроить сразу несколько компонентов.

Например:

Уровень канала: INFO

TLS:    DEBUG
HTTP:   WARN
POLICY: ERROR

Такая конфигурация означает:

Журнал при этом может одновременно стать и подробнее, и компактнее:

2026-07-28 16:18:40:100   #AUTH Authenticate(): User john authenticated
2026-07-28 16:18:40:103 D #TLS Handshake(): Starting TLS handshake
2026-07-28 16:18:40:104 D #TLS Handshake(): Received ClientHello
2026-07-28 16:18:40:106 D #TLS Verify(): Verifying certificate chain
2026-07-28 16:18:40:109 E #TLS Error: Certificate verification failed
2026-07-28 16:18:42:320 W #HTTP Send(): Client response is delayed by 5240 ms

В нём отсутствуют:

При этом подробная последовательность TLS остаётся доступной.

Почему точечная настройка особенно полезна для редких ошибок

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

При глобальном DEBUG за это время журнал может:

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

Это даёт практические преимущества:

Временная диагностика работающей системы

Subsystem level override хорошо подходит для расследования проблем в рабочем окружении.

Типичный процесс выглядит так:

  1. Приложение продолжает работать со штатным уровнем канала.
  2. Для проблемной подсистемы временно устанавливается DEBUG.
  3. Инженер дожидается повторения ошибки.
  4. Из журнала извлекается нужный временной интервал.
  5. Override удаляется.
  6. Подсистема снова использует уровень канала.

Например, до расследования:

Канал: INFO
TLS:   уровень канала

Во время расследования:

Канал: INFO
TLS:   DEBUG

После удаления override:

Канал: INFO
TLS:   уровень канала

Не требуется запоминать и восстанавливать отдельное прежнее значение для TLS. После удаления индивидуальной настройки подсистема автоматически возвращается к общему уровню своего канала.

Что происходит с сообщениями без SID

Override применяется к сообщениям, для которых Logme определил соответствующий SID.

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

Например:

Уровень канала: INFO
Уровень TLS:    DEBUG

Сообщение с SID TLS может пройти на уровне DEBUG, а сообщение без SID по-прежнему будет отфильтровано по INFO.

Поэтому механизм наиболее эффективен, когда сообщения компонентов последовательно помечаются соответствующими SID. Если часть TLS-кода пишет без SID, индивидуальный уровень TLS на такие строки не повлияет.

Когда subsystem override особенно полезен

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

Диагностика одного проблемного компонента

Нужно включить DEBUG для TLS, базы данных, сетевого транспорта или интерпретатора правил, не затрагивая остальное приложение.

Подавление шумного компонента

Одна подсистема пишет слишком много INFO, но полностью отключать информационные сообщения всего канала нельзя.

Сбор данных о редкой ошибке

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

Работа с общим каналом

Разные подсистемы намеренно используют один файл и один набор backend-ов, поэтому создание дополнительных каналов нежелательно.

Настройка журнала под конкретный инцидент

Для одной подсистемы нужен DEBUG, для другой достаточно WARN, а третью требуется ограничить уровнем ERROR.

Что даёт уровень логирования подсистемы на практике

Без subsystem overrides обычно приходится выбирать между двумя крайностями:

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

Он помогает:

Уровень канала остаётся общим правилом, а subsystem overrides позволяют точечно изменить его там, где это действительно необходимо.

Exit mobile version