Commit 31c78b3
feat(core): Add unified observability type system and context propagation (#13058)
## Summary
- Introduces a new observability type system in
`packages/core/src/observability/types/` with interfaces for logging
(`LoggerContext`), metrics (`MetricsContext`, `Counter`, `Gauge`,
`Histogram`), scores (`ScoreInput`, `ExportedScore`), feedback
(`FeedbackInput`, `ExportedFeedback`), and an event bus
(`ObservabilityEventBus`)
- Creates `ObservabilityContext` — a unified interface combining
`tracing`, `loggerVNext`, `metrics`, and `tracingContext` (alias) — that
all internal execution contexts now extend
- Adds `createObservabilityContext()` factory and
`resolveObservabilityContext()` resolver with no-op defaults for
graceful degradation
- Refactors `tracing.ts` to extract a `SpanData` base interface, split
`RecordedSpan`/`RecordedTrace` (immutable storage types) from live
`Span` interfaces, and reorganize core infrastructure types into
`core.ts`
- Migrates 60+ sites across tools, workflows, agents, evals, processors,
LLM, and stream systems to use `ObservabilityContext` (or `Partial<>` at
public boundaries) instead of the former `TracingContext`.
- Establishes a consistent forwarding pattern using `...rest`
destructuring + `resolveObservabilityContext()` so observability fields
flow through without listing them individually
- Tightens internal-only sites from `Partial<ObservabilityContext>` to
full `ObservabilityContext` so future logging/metrics additions won't
require signature changes
- Adds `loggerVNext` and `metrics` getters to the `Mastra` class
## Motivation
This PR lays the groundwork for unified observability in Mastra. Today,
only tracing (`TracingContext`) flows through execution contexts.
Logging is ad-hoc via `IMastraLogger`, and there is no metrics system at
all. Neither logging nor metrics are available in
tool/workflow/processor execute callbacks.
By establishing the type system and context plumbing now (all within
`@mastra/core`), subsequent PRs can add concrete implementations (event
bus, storage adapters, exporters) without touching interface signatures
across the codebase.
## Design decisions
**`loggerVNext` naming**: Uses VNext suffix to avoid collision with the
existing `logger: IMastraLogger` infrastructure logger used throughout
the codebase (`MastraPrimitives.logger`, `LoopOptions.logger`,
`MastraBase.logger`). Follows established codebase pattern:
`MastraLLMVNext`, `streamVNext`, `generateVNext`.
**Partial vs Full boundary**: Public-facing APIs (tool execute, workflow
step execute, processor context) use `Partial<ObservabilityContext>` —
users shouldn't need full observability to call APIs.
Internal/infrastructure code uses full `ObservabilityContext` —
guarantees tracing/logging/metrics are always available.
**Source vs Derived**: `tracingContext` is the source (position in span
tree). `loggerVNext` and `metrics` are derived (rebuilt from current
span for correlation).
**Forwarding pattern**: To avoid listing `tracingContext`, `tracing`,
`loggerVNext`, `metrics` individually at every call site, all forwarding
uses a two-step pattern:
1. Destructure known fields + `...rest` to capture the observability
fields
2. `const observabilityContext = resolveObservabilityContext(rest)` to
fill in defaults
3. Forward via `...observabilityContext` spread at downstream calls
This keeps forwarding sites concise and means adding a new observability
signal requires zero changes at forwarding sites.
**Span-creation boundaries left intact**: Sites that call
`getOrCreateSpan()` or `createChildSpan()` to create a new span, then
derive a fresh `ObservabilityContext` via `createObservabilityContext({
currentSpan })`, are intentionally not changed — they correctly
establish new observability scopes rather than forwarding the parent's.
## What changed
### New files
| File | Description |
| --------------------------------------- |
--------------------------------------------------------------------------------------------------------------
|
| `observability/types/core.ts` | `ObservabilityContext`,
`ObservabilityInstance`, `ObservabilityExporter`, event bus, bridge,
config interfaces |
| `observability/types/logging.ts` | `LoggerContext`, `LogLevel`,
`ExportedLog`, `LogEvent` |
| `observability/types/metrics.ts` | `MetricsContext`, `Counter`,
`Gauge`, `Histogram`, `ExportedMetric`, `MetricEvent`, cardinality
config |
| `observability/types/scores.ts` | `ScoreInput`, `ExportedScore`,
`ScoreEvent` |
| `observability/types/feedback.ts` | `FeedbackInput`,
`ExportedFeedback`, `FeedbackEvent` |
| `observability/context-factory.ts` | `createObservabilityContext()`,
`resolveObservabilityContext()` |
| `observability/context-factory.test.ts` | Unit tests for factory and
resolver |
### Modified files (by area)
**Observability** — Refactored `tracing.ts` (extracted `SpanData` base,
split `RecordedSpan`/`RecordedTrace`), expanded `no-op.ts` with
`noOpLoggerContext`/`noOpMetricsContext`, updated barrel exports
**Agent** — `agent.ts` tool listing/conversion methods and
sub-agent/workflow execution callbacks use `...observabilityFields` rest
+ `resolveObservabilityContext`; `agent-legacy.ts` `__primitive` and
`prepareLLMOptions` use same pattern; `trip-wire.ts`
`getModelOutputForTripwire` forwards full context; workflow steps
(`prepare-memory-step.ts`, `stream-step.ts`, `map-results-step.ts`) use
rest spread
**Workflows** — `step.ts` removed redundant standalone `tracingContext:
TracingContext` from `ExecuteFunctionParams` (already provided by
`Partial<ObservabilityContext>`); `workflow.ts` agent-step and tool-step
execute functions use `...observabilityFields` rest pattern; handlers
(`entry.ts`, `step.ts`, `control-flow.ts`, `sleep.ts`) all use `...rest`
+ `resolveObservabilityContext(rest)` so `observabilityContext` is
available for future logging/metrics; `evented/workflow.ts` creates
context at processor call sites; `default.ts` passes context through
execution engine
**Evals** — `base.ts` scorer workflow uses rest spread; `run/index.ts`
forwards full obs context to `executeWorkflow`, `executeAgent`, and all
`scorer.run()` calls; `hooks.ts` `runScorer` requires full context;
`scoreTracesWorkflow.ts` resolves at boundary
**Processors** — `runner.ts` internal methods require full
`ObservabilityContext`; boundary methods (`runProcessInputStep`,
`runProcessOutputStep`) stay Partial; six processor implementations
(`pii-detector`, `moderation`, `structured-output`, `language-detector`,
`prompt-injection-detector`, `system-prompt-scrubber`) use
`...observabilityFields` rest in public methods and accept full
`ObservabilityContext` in private methods
**Memory processors** — `message-history.ts` and `semantic-recall.ts`
use `& Partial<ObservabilityContext>` on method signatures
**Stream** — `output.ts` bridges Partial options to Full at
`processPart`/`runOutputProcessors` calls via
`resolveObservabilityContext()`
**LLM** — `model.ts` methods destructure obs fields (with `_` prefix
aliases to exclude from AI SDK `...rest`), create `observabilityContext`
for internal use; `model.loop.ts`, `model.loop.types.ts`,
`base.types.ts`, `llm/index.ts` extend context
**Tools** — `types.ts` extends `Partial<ObservabilityContext>`;
`builder.ts` uses `createObservabilityContext()`
**Loop** — `types.ts` extends `Partial<ObservabilityContext>`;
`network/index.ts` resolves at bridge site
**Mastra class** — Added `loggerVNext` and `metrics` getters
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Unified ObservabilityContext with factory/resolver to provide tracing,
structured logging, and metrics.
* Mastra accessors for structured logging and metrics (loggerVNext,
metrics).
* New public types for logging, metrics, scoring, and feedback to
surface observability signals.
* **Chores**
* Propagated ObservabilityContext across APIs and workflows for
consistent telemetry.
* Added no-op fallbacks and tests to ensure graceful degradation when
observability implementations are absent.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Sprite <noreply@sprite.dev>1 parent 09c3b18 commit 31c78b3
70 files changed
Lines changed: 2447 additions & 1221 deletions
File tree
- .changeset
- packages/core/src
- agent
- __tests__
- workflows/prepare-stream
- evals
- run
- scoreTraces
- llm
- model
- loop
- network
- workflows
- agentic-execution
- mastra
- observability
- types
- processors
- memory
- processors
- stream
- aisdk/v4
- base
- tools
- tool-builder
- workflows
- evented
- handlers
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
| 59 | + | |
| 60 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
243 | 243 | | |
244 | 244 | | |
245 | 245 | | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
258 | 266 | | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
| 267 | + | |
266 | 268 | | |
267 | 269 | | |
268 | 270 | | |
| |||
0 commit comments