Каждый запуск даёт 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_start | span_start — type (литерал), runId, spanId (уникальная строка в запуске), name, spanType, необязательный input (объект JSON), необязательный metadata (объект, Node.js), timestamp (ISO-8601). |
span_end | span_end — type, runId, spanId (как при старте), name, spanType, success (логическое), duration (целые миллисекунды), timestamp, необязательный output (JSON-совместимый), необязательный error (строка), если success равно false. |
log | log — type, runId, level (например INFO), message (строка), необязательный metadata, timestamp. Если level нет, на сервере подставляется INFO. |
Как сохраняется spanType
Оркестратор сопоставляет строку spanType с сохраняемым типом шага. activity → activity; generation, llm, ai → generation (такие шаги всегда остаются в трассе, даже если очень короткие); tool → tool; retrieval, database, db → activity; остальные известные имена (orchestrator, wait, timer и т.д.) маппятся напрямую. Неизвестные имена дают обобщённый шаг function.
SDK: пользовательские спаны и логи
Оборачивайте осмысленные участки кода именованными спанами, чтобы таймлайн отражал структуру приложения. SDK уже внутри образа рантайма — не добавляйте его в package.json, requirements.txt или Go-модули.
| Рантайм | Импорт |
|---|---|
| Node.js 22 | global.observe (внедряется автоматически) |
| Python 3.12 | from observability import observe |
| Go 1.22 | import 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 | Общая работа: валидация, маппинг, внутренние вспомогательные функции |
activity | I/O: HTTP-клиенты, БД, очереди, чтение файлов |
generation | Вызовы LLM, эмбеддинги, шаги с тяжёлым токенайзером |
Node.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
# 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
// 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, чтобы не отфильтровываться по минимальной длительности.
// 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 дней по умолчанию.