Skip to content

Instantly share code, notes, and snippets.

@com30n
Last active October 27, 2021 09:54
Show Gist options
  • Save com30n/f7c607000fd523437981a497ebb41568 to your computer and use it in GitHub Desktop.
Save com30n/f7c607000fd523437981a497ebb41568 to your computer and use it in GitHub Desktop.

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

  • timestamp: Время в UTC

    • Время в UTC для синхронизации всех записей в логах
  • app_name: Имя приложения

    • Нужен для идентификакции и возможности фильтрации по имени приложения
  • app_version: Версия приложения

    • Нужен для определения версии приложения
  • hostname: Имя хоста на котором запущен сервис

    • Имя хоста/ноды, на котором работает приложение, нужен для быстрой возможности определения общих проблем из-за инфраструктуры
  • request_id: ID запроса от пользователя, либо для запросов генерируемых сервисом,

    • Уникальный, и генерируется один раз на входе первым же приложением/балансировщиком, в который прилетел запрос без заголовка X-Request-Id

    • Request ID в UUID hex, с дефисами в формате 8-4-4-4-12

    • Request ID не должен изменяться сервисом, даже если пришел не в правильном формате

    • Нужен для трасировки запроса по всему стеку приложений

  • level: Log level: trace, debug, info, warn, error, critical

    • Нужно синхронизировать понятия уровней логирования для каждого из сервисов, для того чтобы иметь возможность быстро обогащать логи дополнительной, требуемой, информацией для исследования проблем
  • uid: User UID: UUID с дефисами в формате 8-4-4-4-12

    • UID не должен изменяться сервисом, даже если пришел не в правильном формате

    • Нужен для поиска проблем у конкретного пользователя, а так же для подсчета скопа пользователей которые могли быть затронуты исследуемым инцидентом

  • user_agent: User Agent

    • передается в заголовке User-Agent

    • Каждый сервис проставляет свой собственный, уникальный для каждого отдельного приложения user agent

    • Каждый сервер должен кастомизировать свой user-agent заголовок, чтобы сервисы в которые приходят запросы от него могли четко идентифицировать потребителя

  • client_ip: Client IP Address

    • Обычно проставляется балансировщиком в заголовок x-forwarded-for

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

  • message: Тело сообщения

  • http_method: Request HTTP Method

    • Тип запроса
  • http_status: HTTP статус ответа

  • requested_path: Request Path (relative)

    • uri запроса не включающий в себя схему и хост

Формат записи тех же логов, но в json (для быстрого парсинга в системах записи логов):

В общем случае, логи у сервиса делятся на 2 типа:

  • Логи отображающие внешние события, влияющие не только на этот сервис (запросы, ответы на запросы)

  • Логи отображающие внутренние события, влияющие только на этот сервис

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

Для этого нужно обогатить логи следующими параметрами:

  • Пришел запрос в сервис: "type": ->

  • Отдаем ответ: "type": <-

  • Создаем запрос в зависимость: "type": =>>

  • Получаем ответ от зависимости: "type": <<=

Формат записи обычного лога, не относящегося к внутренним событиям:

{"timestamp":TIMESTAMP, "level":LEVEL, "message":MESSAGE, app_name":app_name, "app_version":app_version, "hostname":hostname, "extras": EXTRAS}
  • где EXTRAS, это строка с дополнительными данными, записанная в формате:

    • "extras": "<key>=<value>,<key>=<value> …"
  • так же в эти логи могут быть добавлены добавлены дополнительные поля, если лог относится к ходу обработки внешнего события:

    • "request_id":request_id, "uid":uid, "user_agent":user_agent, "client_ip":client_ip, "path":path

Формат записи лога относящегося к внешним событиям:

{"timestamp":TIMESTAMP, "level":LEVEL, "type":"->", "message":MESSAGE, app_name":app_name, "app_version":app_version, "hostname":hostname, "request_id":request_id, "uid":uid, "user_agent":user_agent, "client_ip":client_ip, "path":path, "extras": EXTRAS}
{"timestamp":TIMESTAMP, "level":LEVEL, "type":"=>>", "http_method":<method>, "requested_path":<requested_path>, "message":MESSAGE, app_name":app_name, "app_version":app_version, "hostname":hostname, "request_id":request_id, "uid":uid, "user_agent":user_agent, "client_ip":client_ip, "path":path, "extras": EXTRAS}
{"timestamp":TIMESTAMP, "level":LEVEL, "type":"<<=", "http_status":<http status>, "http_method":<method>, "requested_path":<requested_path>, "message":MESSAGE, app_name":app_name, "app_version":app_version, "hostname":hostname, "request_id":request_id, "uid":uid, "user_agent":user_agent, "client_ip":client_ip, "path":path, "extras": EXTRAS}
{"timestamp":TIMESTAMP, "level":LEVEL, "type":"<-", "http_status":<http status>, "http_method":<method>, "requested_path":<requested_path>, "message":MESSAGE, app_name":app_name, "app_version":app_version, "hostname":hostname, "request_id":request_id, "uid":uid, "user_agent":user_agent, "client_ip":client_ip, "path":path, "extras": EXTRAS}
  • в данном случае поля "type","message","app_name","app_version","hostname","request_id","uid","user_agent","client_ip","path" являются обязательными

  • где EXTRAS, это строка с дополнительными данными, записанная в формате:

    • "extras": "<key>=<value>,<key>=<value> …"

В случае, если json записывается в систему логирования не методом сбора логов из stdout:

Если логи пушатся в ELK не путем сбора логов из stdout, а например прямой записью через gelf, или из файла логирования, то в stdout было бы здорово писать логи в более человеко читаемом формате.

Формат записи логов для внешних событий:

  • Пришел запрос в сервис: -> Start handle request

    • Нужен для быстрого визуального определения типа записи в лог, а так же для различных систем фильтрации
  • Посылаем ответ: <- Response <SUCCESS|PARTIAL|ERROR>

    • Нужно для того чтобы быстро понять/отфильтровать был ли ответ сформированный сервисом успешным
  • Делаем запрос: =>> Making request to

    • Определение запроса создаваемый самим сервисом в его зависимости
  • Получаем ответ: <<= Got response for

    • Определение успешности ответа от зависимостей сервиса на заданный им запрос
       

Важное пояснение, относительно этого пункта:
сочетания символов:

" > ", " < ", " =>> ", " <<= "

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

Формат записи обычного лога, не относящегося к внешним, в stdout:

TIMESTAMP LEVEL MESSAGE APP_NAME APP_VERSION HOSTNAME EXTRAS
  • где EXTRAS: <key>=<value> <key>=<value> ...

    • Все поля кроме message и extras должны быть одной длины
  • так же в эти логи могут быть добавлены добавлены дополнительные поля, если лог относится к ходу обработки внешнего события:

    • REQUEST_ID UID USER_AGENT CLIENT_IP PATH

Формат записи сообщения на событие из вне в stdout:
 

TIMESTAMP LEVEL -> Start handle request {MESSAGE} APP_NAME REQUEST_ID APP_VERSION HOSTNAME UID USER_AGENT CLIENT_IP PATH EXTRAS
TIMESTAMP LEVEL =>> Making request to <method> <url> {MESSAGE} APP_NAME REQUEST_ID APP_VERSION HOSTNAME UID USER_AGENT CLIENT_IP PATH EXTRAS
TIMESTAMP LEVEL <<= Got response <http status> for <method> <url> {MESSAGE} APP_NAME REQUEST_ID APP_VERSION HOSTNAME UID USER_AGENT CLIENT_IP PATH EXTRAS
TIMESTAMP LEVEL <- Response <http status> <SUCCESS|PARTIAL|ERROR> {MESSAGE} APP_NAME REQUEST_ID APP_VERSION HOSTNAME UID USER_AGENT CLIENT_IP PATH EXTRAS
  • где перемменные объявленные в {} могут не существовать;
    EXTRAS: <key>=<value> <key>=<value> ...

  • Все поля кроме message и extras должны быть одной длины

  • в данном случае поля REQUEST_ID UID USER_AGENT CLIENT_IP PATH являются обязательными

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment