Inquir Compute
Разделы

Наблюдаемость и трассировка

Каждый запуск даёт logs, execution history, structured traces, разбивку latency и при необходимости spans из SDK — отладка трафика шлюза, cron, пайплайнов и async jobs в одном месте.

Каждый вызов (ручной запуск, запрос через API Gateway, задание, шаг пайплайна и т. д.) создаёт трассировку: упорядоченный таймлайн работы оркестратора, при необходимости — ваши спаны из кода, структурированные логи и длительности шагов, чтобы отладить один прогон от начала до конца.

Трассировка привязана к одному идентификатору запуска. Она дополняет метрики на странице Functions: видно, что произошло внутри конкретного выполнения, а не только факт успеха или ошибки.

Где смотреть трассировки

Выберите удобный экран в зависимости от задачи:

  • Выполнения — в списке выберите запуск воркспейса и откройте карточку: полный таймлайн, статус, длительность, полезная нагрузка или детали ошибки (если платформа их сохранила).
  • Редактор функции — в правой тестовой панели отображаются недавние запуски этой функции; по записи можно перейти к тому же детальному виду выполнения.
  • В одной трассировке объединены встроенные шаги оркестратора ниже и, при использовании SDK, ваши спаны observe.span и строки observe.log на общей шкале времени.

Что показывает таймлайн

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

При ошибке сохраняются спаны до точки сбоя; у проблемного шага фиксируется сообщение об ошибке, если оно доступно. При успехе отображаются выходные данные, если рантайм их записал (например captureOutput в Node.js или set_output в Python).

Автоматические шаги оркестратора

Перед запуском хендлера платформа записывает инфраструктурные шаги для каждого вызова:

  • Build Environment — сборка переменных окружения и конфигурации
  • Setup Layers — разрешение и монтирование слоёв
  • Create Container — создание Docker-контейнера (только холодный старт)
  • Acquire Container — выбор прогретого контейнера (горячий старт)
  • Execute Function — непосредственное выполнение функции

Вывод в консоль

Когда наблюдаемость активна для запуска, console.log, console.warn и console.error также попадают в трассировку как строки лога (в дополнение к stdout процесса). Для стабильного уровня и объекта metadata удобнее observe.log.

Протокол передачи (__OBSERVABILITY__)

Пользовательские спаны и структурированные логи выводятся из контейнера в stderr: по одной строке UTF-8 на событие. Строка должна начинаться с префикса __OBSERVABILITY__: (с двоеточием), сразу за ним — один JSON-объект. Оркестратор разбирает поток stderr Docker и извлекает эти строки; ответ функции уходит в stdout, поэтому события observe нужно писать только в stderr.

Имена полей — в стиле camelCase, как в таблице ниже. Плоскость сопоставляет span_end с открытым шагом по паре runId + spanId; дублирующие spanId в одном запуске испортят таймлайн.

Вложенность: каждый span_start открывает шаг. Следующий span_start до соответствующего span_end считается дочерним в интерфейсе. На каждый открытый спан нужен span_end — встроенные SDK отправляют его и при успехе, и при ошибке.

Снимок входа шага: при приёме, если в span_start задан input, он сохраняется; иначе, когда input пуст, используется metadata (Node.js). Оба поля можно опустить.

В собственном рантайме можно формировать те же JSON-строки. Некорректный JSON после префикса отбрасывается; неполные строки нужно дописать в следующем выводе, чтобы сохранить правило «один объект на строку».

Формы JSON-событий (пользовательские)

Поле <code>type</code>Обязательные и необязательные поля
span_startspan_starttype (литерал), runId, spanId (уникальная строка в запуске), name, spanType, необязательный input (объект JSON), необязательный metadata (объект, Node.js), timestamp (ISO-8601).
span_endspan_endtype, runId, spanId (как при старте), name, spanType, success (логическое), duration (целые миллисекунды), timestamp, необязательный output (JSON-совместимый), необязательный error (строка), если success равно false.
loglogtype, runId, level (например INFO), message (строка), необязательный metadata, timestamp. Если level нет, на сервере подставляется INFO.

Как сохраняется spanType

Оркестратор сопоставляет строку spanType с сохраняемым типом шага. activityactivity; generation, llm, aigeneration (такие шаги всегда остаются в трассе, даже если очень короткие); tooltool; retrieval, database, dbactivity; остальные известные имена (orchestrator, wait, timer и т.д.) маппятся напрямую. Неизвестные имена дают обобщённый шаг function.

SDK: пользовательские спаны и логи

Оборачивайте осмысленные участки кода именованными спанами, чтобы таймлайн отражал структуру приложения. SDK уже внутри образа рантайма — не добавляйте его в package.json, requirements.txt или Go-модули.

РантаймИмпорт
Node.js 22global.observe (внедряется автоматически)
Python 3.12from observability import observe
Go 1.22import obs "lambda/runtime/go122/observability"

В Node.js observe.span(name, fn, options) вызывает fn без аргументов. Чтобы передать JSON-совместимый результат в событие окончания спана, верните значение из fn и укажите captureOutput: true в options. Для ручного управления (несколько выходов, частичный результат) используйте observe.startSpan(name, options) и затем .end({ output, error }).

В Python используйте with observe.span(...) как контекстный менеджер. Объект из yield поддерживает set_output(...) для необязательного структурированного вывода при закрытии спана.

В Go пакет даёт низкоуровневые StartSpan / End и Log; при необходимости оборачивайте их в свои хелперы для вложенных спанов, как в Node или Python.

Типы спанов (семантика)

Задайте type (Node) или span_type (Python), чтобы интерфейсы и будущие инструменты могли классифицировать спаны. Все типы проходят через один и тот же конвейер записи.

ТипНазначение
spanОбщая работа: валидация, маппинг, внутренние вспомогательные функции
activityI/O: HTTP-клиенты, БД, очереди, чтение файлов
generationВызовы LLM, эмбеддинги, шаги с тяжёлым токенайзером

Node.js

index.js
// Node.js — global observe object injected by the runtime
// observe.span(name, fn, options) calls fn with no arguments; use captureOutput or startSpan for outputs.
exports.handler = async (event, context) => {
  return await observe.span(
    'Fetch data',
    async () => {
      const data = await fetchSomething();

      const result = await observe.span(
        'Call LLM',
        async () => callLLM(data),
        { type: 'generation', input: { model: 'gpt-4o' } },
      );

      observe.log('INFO', 'Pipeline complete', { records: data.length });
      return { records: data.length, result };
    },
    { type: 'activity', captureOutput: true },
  );
};

Python

handler.py
# Python — import from the runtime SDK
from observability import observe

async def handler(event, context):
    with observe.span('Fetch data', span_type='activity') as s:
        data = await fetch_something()
        s.set_output({'records': len(data)})

        with observe.span('Call LLM', span_type='generation'):
            result = await call_llm(data)

    observe.log('INFO', 'Pipeline complete', {'records': len(data)})
    return result

Go

main.go
// Go — import the runtime observability package
import obs "lambda/runtime/go122/observability"

func Handler(event, ctx map[string]interface{}) (interface{}, error) {
    span := obs.StartSpan("Fetch data")
    data, err := fetchSomething()
    if err != nil {
        span.End(nil, err.Error())
        return nil, err
    }
    span.End(map[string]interface{}{"records": len(data)}, "")

    obs.Log("INFO", "Pipeline complete", map[string]interface{}{"records": len(data)})
    return data, nil
}

Распределённая трассировка (<code>traceparent</code>)

Если клиент передаёт заголовок W3C traceparent при синхронном вызове, стриминговом вызове или запросе через API Gateway, платформа может связать корневой спан запуска с внешней трассой (если для развёртывания включена распределённая трассировка). Сам заголовок в хранилище не пишется; в метаданных запуска сохраняются upstreamTraceId и upstreamParentSpanId для связи в интерфейсе.

Чтобы связать с вашим сервисом, пробрасывайте тот же traceparent, что используете дальше по цепочке. Для асинхронных заданий можно задать context.__traceparent или context.traceparent в теле invoke-async; HTTP-заголовок копируется в context.__traceparent, если в теле ещё не указана связь. Некорректные или отсутствующие значения игнорируются.

Поля LLM в трассах (control plane)

Интеграции, вызывающие ObservabilityService на хосте, могут добавлять к шагам метаданные generation: модель, провайдер, число токенов, оценка стоимости и необязательные параметры. Тип шага generation для LLM-работы всегда остаётся в трассе, даже если шаг очень короткий. Любой шаг может задать retainInTrace: true, чтобы не отфильтровываться по минимальной длительности.

observability.service.ts (host)
// Control plane (TypeScript) — ObservabilityService
const step = observability.addStep(runId, {
  name: 'OpenAI chat',
  type: 'generation',
  input: { messages },
  generation: { model: 'gpt-4o', provider: 'openai' },
});

observability.completeStep(runId, step.id, completion, undefined, durationMs, {
  generation: {
    inputTokens: usage.prompt_tokens,
    outputTokens: usage.completion_tokens,
    totalTokens: usage.total_tokens,
    costUsd: 0.0012,
  },
});

// Keep a very fast step in the trace (optional):
observability.addStep(runId, {
  name: 'Fast cache hit',
  type: 'activity',
  retainInTrace: true,
});

При завершении шага передайте шестой аргумент в completeStep{ generation: { … } } — чтобы дописать использование токенов и стоимость после ответа модели. Значения проходят через тот же конвейер редактирования секретов, что и остальные полезные нагрузки трасс.

Карточка выполнения

На вкладке трассировки запуска:

  • Compare — вход и выход выбранного шага в двух колонках JSON.
  • Generation — при наличии метаданных generation показывается строка с моделью, провайдером, токенами и стоимостью.
  • Поиск — фильтрует дерево по имени шага и по полям generation.
  • Upstream trace — при принятом traceparent отображается чип с идентификатором внешней трассы.

Структурированные логи

observe.log(level, message, metadata) пишет структурированную запись в трассировку (не только в stdout). Уровень — строка без учёта регистра, например INFO, WARN, ERROR, DEBUG.

Трассировки хранятся 30 дней по умолчанию.