Перейти к основному содержимому

Операции и типизированные события

Помимо OTel-формата передачи логгер публикует набор семантических соглашений — стандартные ключи атрибутов для распространённых паттернов. Два из них центральные: операции (стабильный идентификатор для длительной единицы работы) и типизированные события (вариант, проверяемый по схеме, в качестве замены свободному body).

Операции

Любая бизнес-операция, которая длится дольше одного такта (index_repo, query_rag, load_plugin, process_order, embed_batch), несёт следующие атрибуты на каждой записи внутри границ операции:

АтрибутТипЗначение
operation.namestringСтабильный snake_case идентификатор — process_order, index_repo. Не меняется между релизами.
operation.idstringУникальный UUID для экземпляра операции. По сути алиас span_id, но со стабильным именем (UI-поиск без знания о трассировке).
operation.kindenumindexing, query, lifecycle, hook, maintenance, admin. Расширение запланировано через _meta/conventions/operation_kinds.yaml в v1.1; v1.0-биндинги поставляют enum инлайн.
operation.parent.idstring?Родительская операция, когда есть иерархия (query_ragsemantic_searchembed_batch).
operation.statusenum?На событии completed: ok, error, cancelled, timeout.
operation.duration_msint?На событии completed: миллисекунды от старта до конца.

operation.name и operation.id обязательны в каждой записи внутри границ операции. Хелпер биндинга logger.operation(name, kind) — стандартная точка входа: он устанавливает активную операцию в контексте и автоматически добавляет атрибуты во все дочерние записи и span'ы.

:::caution Статус Phase 1 Хелпер logger.operation(...) нормативен в спеке §5.1, но пока не вошёл ни в один v0.1.x-биндинг (dagstack-logger, @dagstack/logger, go.dagstack.dev/logger). Phase 1-вызовы должны устанавливать атрибуты вручную через child(attributes=...) или передавать их в каждый emit. :::

Обходной путь, пока хелпер не приземлился:

import uuid
from dagstack.logger import Logger

logger = Logger.get("order_service")

op_logger = logger.child(attributes={
"operation.name": "process_order",
"operation.id": str(uuid.uuid4()),
"operation.kind": "lifecycle",
})
op_logger.info("started", attributes={"order.id": 1234})
op_logger.info("completed", attributes={
"operation.status": "ok",
"operation.duration_ms": 142,
})

Типизированные события

Вместо свободного body соглашение — отправлять типизированные события с обязательными event.domain и event.name:

attributes: {
event.domain: "rag",
event.name: "chunk_retrieved",
event.schema_version: "1.0",
rag.chunk.id: "abc123",
rag.chunk.score: 0.87,
rag.chunk.repo: "order-service",
}

Схемы будут жить в _meta/events/<domain>.yaml в spec-репозитории (v1.1) и эмитятся в per-language константы (Python Literal, TS as const, Go const); v1.0-биндинги поставляют per-domain списки атрибутов инлайн. Биндинг валидирует обязательные атрибуты на этапе emit; отсутствующие обязательные ключи приводят к ошибке до того, как запись достигнет приёмника.

:::caution Статус Phase 1 Хелпер logger.emit_event(domain, name, attrs) нормативен в спеке §5.2, но пока не вошёл ни в один v0.1.x-биндинг. В Phase 1 вызовы собирают event.domain / event.name и per-domain атрибуты вручную и вызывают logger.info(...) напрямую. :::

Зарезервированные домены

Домены событий разделены на два уровня:

  • Core-резервированные — биндинги отвергают отправки пользовательского кода в этих доменах: dagstack.*, operation.*, progress.*. Их семантика зафиксирована в core-спеке.
  • Резервированные расширениями — действуют только когда соответствующий extension pack загружен. Набор для AI-агентов (см. Наблюдаемость AI-агентов) резервирует gen_ai.*, ai_agent.*, rag.*, agent.*, prompt.*, mcp.*.

Пользовательские домены следуют reverse-DNS-нотации (com.example.myapp.checkout) во избежание коллизий между приложениями.

События прогресса

Отчёт о прогрессе (10 000 проиндексированных файлов, пакет эмбеддингов, эпоха обучения) — это соглашение поверх LogRecord, а не отдельный API. Биндинг отправляет записи с event.domain = "progress" и одним из четырёх имён: tick, started, completed, failed.

АтрибутТипЗначение
progress.currentintЗавершённые единицы.
progress.totalint?Всего единиц, если известно; null для потоков с неизвестным total.
progress.unitstring"files", "chunks", "tokens", "bytes", "items".
progress.phasestring?"discovery", "chunking", "embedding", "upserting".
progress.ratefloat?Единиц/сек (скользящее окно).
progress.eta_msint?Оценка миллисекунд до завершения, когда total известен.
operation.idstringОбязателен — прогресс всегда привязан к операции.

При высокой частоте тиков хелпер биндинга rate-лимитирует тики (по умолчанию min_interval_ms=500); started / completed / failed отправляются всегда без отбрасывания.

См. также