Форматы передачи
Внутренняя форма LogRecord идентична во всех биндингах. Различается wire-кодирование — то, как запись сериализуется в поток байт, попадающий в приёмник. Спека определяет три формата передачи; приёмники выбирают один (или несколько) согласно своему объявленному выводу.
Три формата
| Формат | Используется в | Регистр | Timestamp'ы | trace_id / span_id |
|---|---|---|---|---|
| OTLP protobuf | OTLPSink (gRPC, HTTP/protobuf) | (бинарный) | fixed64 | bytes(16) / bytes(8) |
| OTel JSON | OTLPSink (HTTP/JSON), FileSink режим OTLP, ConsoleSink режим JSON | camelCase | строка-десятичные наносекунды | lowercase 32-hex / 16-hex |
| dagstack JSON-lines | FileSink режим по умолчанию, ConsoleSink wire-режим | snake_case | int64 наносекунды | lowercase 32-hex / 16-hex |
OTel-форматы обязательны для cross-vendor совместимости с observability-бэкендами; dagstack JSON-lines — это snake_case-вариант, согласованный с соглашениями config-spec Canonical JSON, используемый приёмниками, которые целятся во внутренние log-конвейеры dagstack.
OTLP protobuf
Нативный wire OTel — те же байты, что OTel SDK говорит OTel-коллектору. Используется запланированным OTLPSink (Phase 2). Имена полей совпадают со схемой proto:
time_unix_nano,observed_time_unix_nano—fixed64.trace_id—bytes(ровно 16 байт).span_id—bytes(ровно 8 байт).severity_number— int в диапазоне1..24.bodyиattributesиспользуют рекурсивный sum-типAnyValueиз OTel.
Сериализация делегируется protobuf-runtime каждого языка (google.protobuf в Python, protobufjs в TS, google.golang.org/protobuf в Go). Формат бинарный и не предназначен для чтения человеком; используй экспортёр logging в OTel-коллекторе или otlp-debug-exporter, чтобы посмотреть его содержимое.
OTel JSON
Рекомендованное OTel JSON-кодирование (по protobuf JSON-спецификации для 64-битных целых). Используется OTLPSink поверх HTTP+JSON, FileSink в режиме OTLP и ConsoleSink в режиме JSON.
Правила кодирования ключей:
- camelCase-ключи:
timeUnixNano,observedTimeUnixNano,severityNumber,severityText,traceId,spanId,traceFlags. - Timestamp'ы как десятичные строки. Значение
fixed64, превышающее2^53, нельзя представить как JSON-число без потери точности, поэтому OTel JSON оборачивает его в строку:"timeUnixNano": "1729800000000000000". trace_id/span_idкак lowercase hex-строки. 32 hex-символа дляtrace_id(с ведущими нулями), 16 дляspan_id.- Body и attributes используют форму OTel JSON
AnyValue—{"stringValue": "..."},{"intValue": 42},{"kvlistValue": {"values": [{"key": ..., "value": ...}, ...]}}.
Бэкенды вроде OpenTelemetry Collector, Honeycomb и Datadog парсят OTel JSON нативно.
dagstack JSON-lines
snake_case-вариант для приёмников, целящихся во внутренние конвейеры и инструменты dagstack. Формат — Canonical JSON (рекурсивно сортированные ключи, UTF-8, LF, без trailing newline) — байт-в-байт идентичен между биндингами, подходит для хеширования и детерминированного сравнения.
Отличия от OTel JSON:
- snake_case-ключи:
time_unix_nano,observed_time_unix_nano,severity_number,severity_text,trace_id,span_id,trace_flags. - Timestamp'ы как целые числа. Canonical JSON допускает int64 как нативное число; потребители должны корректно обрабатывать bigint при чтении формата (чтобы 64-битные nano-значения не теряли точность в JavaScript).
trace_id/span_idкак lowercase hex-строки (как и в OTel JSON).- Body и attributes используют естественное JSON-кодирование для
Value— строки, числа, булы, null, массивы, объекты. Без обёрткиAnyValue.
Пример записи:
{"attributes":{"order.id":1234,"request.id":"req-abc"},"body":"order placed","instrumentation_scope":{"name":"order_service.checkout","version":"1.0.0"},"resource":{"attributes":{"service.name":"order-service"}},"severity_number":9,"severity_text":"INFO","span_id":"00f067aa0ba902b7","time_unix_nano":1729800000000000000,"trace_flags":1,"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736"}
FileSink Python в режиме по умолчанию выдаёт ровно этот формат, по одной записи на строку, без trailing newline в конце файла.
Pretty для человека (только для презентации)
ConsoleSink(mode="pretty") производит цветной текст для интерактивного TTY — timestamp, тег уровня, имя scope, body, summary атрибутов. Это не формат передачи; парсер не должен уметь восстанавливать LogRecord из pretty-вывода. Pretty-режим — только для глаз разработчика; CI-логи, stdout контейнера и файловые приёмники всегда используют формат, поддающийся парсингу.
Владение observed_time_unix_nano
Producer (путь emit биндинга) оставляет observed_time_unix_nano = null. Приёмник заполняет это поле на этапе ingest, если оно пустое, прямо перед сериализацией. Это гарантирует, что файл и OTLP-вывод всегда несут timestamp ingest; в in-memory-конвейере (InMemorySink) оба timestamp'а могут совпадать.
Валидация
Conformance-набор (спека §14.1) тестирует каждый формат передачи через roundtrip emit → parse → re-emit:
- Собрать репрезентативный LogRecord (по одному на каждый уровень, по одному на каждый основной
event.domain, один с применённым маскированием, один с полным контекстом). - Сериализовать в проверяемый формат.
- Распарсить байты обратно в LogRecord.
- Сериализовать заново и сравнить байт-в-байт с первым emit.
Для формата dagstack JSON-lines применяются правила Canonical JSON. Несоответствующий вход (неизвестный severity_number, отсутствующий instrumentation_scope, плохо сформированный trace_id) отвергается на парсинге.
См. также
- Поля LogRecord — полный список полей с типами.
- Приёмники — какие приёмники в каком формате выдают.
- ADR-0001 §1 (полный нормативный текст).