Что такое структурированное логирование и каковы его преимущества и недостатки?

Ответ

Структурированное логирование — это подход к записи логов не в виде простого текста, а в формате с четкой структурой (чаще всего JSON или logfmt), где каждое сообщение представляет собой набор пар «ключ-значение».

Плюсы:

  • Машиночитаемость: Структурированные логи легко парсятся и индексируются автоматизированными системами (ELK, Grafana Loki, Splunk, Datadog). Это упрощает поиск, фильтрацию и агрегацию данных.
  • Контекстуальность: Позволяет добавлять к сообщению произвольные контекстные поля (user_id, request_id, duration_ms), что делает логи гораздо более информативными для отладки и анализа.
  • Надежный поиск: Вместо поиска по подстроке в текстовых логах можно выполнять точные запросы по полям, например, level=error AND user_id=123.

Минусы:

  • Повышенный объем: JSON и другие форматы обычно занимают больше места, чем простой текст, что увеличивает затраты на хранение.
  • Сложнее для быстрого чтения человеком: Необработанные JSON-логи могут быть менее читаемыми в консоли по сравнению с простым текстом. Однако многие инструменты предоставляют "красивый" вывод для разработки.
  • Накладные расходы на производительность: Сериализация данных в JSON или другой формат требует дополнительных ресурсов CPU по сравнению с простой записью строки.

Примеры в Go:

Начиная с Go 1.21, в стандартной библиотеке появился пакет slog для структурированного логирования, который является предпочтительным выбором.

Пример с slog (стандартная библиотека):

import (
    "log/slog"
    "os"
)

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

    logger.Info("Пользователь вошел в систему",
        slog.String("username", "admin"),
        slog.String("ip_address", "127.0.0.1"),
    )
}

Вывод slog:

{"time":"2023-10-26T15:00:00.123Z","level":"INFO","msg":"Пользователь вошел в систему","username":"admin","ip_address":"127.0.0.1"}

Пример с logrus (популярная библиотека):
Примечание: logrus находится в режиме поддержки, новым проектам рекомендуется использовать slog или zerolog.

import "github.com/sirupsen/logrus"

func main() {
    log := logrus.New()
    log.SetFormatter(&logrus.JSONFormatter{})

    log.WithFields(logrus.Fields{
        "user": "admin",
        "ip":   "127.0.0.1",
    }).Info("User logged in") 
}