Skip to content

observability_core

Core traits and foundational components for structured logging and observability. This crate provides zero-cost abstractions for telemetry, metrics, and structured logging that can be conditionally compiled based on feature flags.

  • observability: Enables all observability features
  • otel-2025: OpenTelemetry 2025 integration
  • structured-logging: JSON structured logging support
  • prometheus-federation: Prometheus metrics federation
  • auto-instrumentation: Automatic instrumentation capabilities
const VERSION: &str = ;

Version information for the observability core

const DEFAULT_BATCH_SIZE: usize = ;

Default batch size for telemetry data

const DEFAULT_FLUSH_INTERVAL_SECS: u64 = ;

Default flush interval in seconds

type ObservabilityResult = Result<T, ObservabilityError>;

Result type for observability operations

Trait for collecting and managing metrics

Required / Provided Methods

fn register_counter(&mut self, name: &str, description: &str, labels: &[&str]) -> ObservabilityResult<()>

Register a new counter

fn register_histogram(&mut self, name: &str, description: &str, buckets: &[f64], labels: &[&str]) -> ObservabilityResult<()>

Register a new histogram

fn register_gauge(&mut self, name: &str, description: &str, labels: &[&str]) -> ObservabilityResult<()>

Register a new gauge

fn record_counter(&self, name: &str, value: f64, labels: &HashMap<String, String>) -> ObservabilityResult<()>

Record a counter increment

fn record_histogram(&self, name: &str, value: f64, labels: &HashMap<String, String>) -> ObservabilityResult<()>

Record a histogram observation

fn set_gauge(&self, name: &str, value: f64, labels: &HashMap<String, String>) -> ObservabilityResult<()>

Set a gauge value

fn get_metrics(&self) -> HashMap<String, f64>

Get current metric values (for testing/debugging)

fn clear(&mut self)

Clear all metrics

Core observability plugin trait

Required / Provided Methods

fn start_span(&self, name: &str, attributes: &[(&str, &str)]) -> SpanGuard

Start a new span and return a guard

fn end_span(&self, span_id: &str)

End a span by ID

fn add_span_attribute(&self, span_id: &str, key: &str, value: &str)

Add an attribute to an existing span

fn set_span_status(&self, span_id: &str, status: SpanStatus)

Set the status of a span

fn record_metric(&self, name: &str, value: f64, labels: &[(&str, &str)])

Record a metric with labels

fn increment_counter(&self, name: &str, labels: &[(&str, &str)])

Increment a counter metric

fn record_histogram(&self, name: &str, value: f64, labels: &[(&str, &str)])

Record a histogram value

fn log_structured(&self, level: LogLevel, message: &str, fields: &JsonValue)

Log a structured message with fields

fn log(&self, level: LogLevel, message: &str)

Log a simple message

fn write_log(&self, message: &str)

Write log output (implementation-specific)

fn flush(&self) -> ObservabilityResult<()>

Flush any pending telemetry data

fn is_enabled(&self) -> bool

Check if the plugin is enabled

fn plugin_type(&self) -> &''static str

Get the plugin name/type

Trait for structured logging

Required / Provided Methods

fn log_with_trace(&self, level: LogLevel, message: &str, fields: &JsonValue, trace_id: Option<&str>, span_id: Option<&str>)

Log with trace context correlation

fn log_performance(&self, operation: &str, duration: Duration, success: bool, additional_fields: &JsonValue)

Log performance metrics

fn log_error(&self, error: &dyn Error, context: &JsonValue)

Log errors with context

fn set_level(&mut self, level: LogLevel)

Set the minimum log level

fn is_level_enabled(&self, level: LogLevel) -> bool

Check if a level is enabled

Port for batching log entries

This allows different batching strategies:

  • TimeBasedBatcher (flush every N seconds)
  • SizeBasedBatcher (flush when buffer reaches N entries)
  • HybridBatcher (combination of time and size)

Required / Provided Methods

fn add_to_batch(&self, entry: LogEntry) -> ObservabilityResult<()>

Add an entry to the batch

fn flush_batch(&self) -> ObservabilityResult<()>

Force flush the current batch

fn batch_size(&self) -> usize

Get current batch size

Port for accessing context information

This allows the logging system to enrich entries with context:

  • Agent ID, request ID, trace ID
  • User-defined context fields
  • Thread/task local context

Required / Provided Methods

fn get_context(&self) -> HashMap<String, serde_json::Value>

Get current context fields

fn add_context(&self, key: String, value: serde_json::Value)

Add a context field

fn remove_context(&self, key: &str)

Remove a context field

fn clear_context(&self)

Clear all context

Port for formatting log entries

Different formatters can be plugged in:

  • JsonFormatter (structured JSON output)
  • PlainTextFormatter (human-readable text)
  • CompactFormatter (single-line JSON)

Required / Provided Methods

fn format(&self, entry: &LogEntry) -> ObservabilityResult<String>

Format a log entry into a string

Port for metrics collection (basic interface for correlation).

Required / Provided Methods

fn emit_counter_simple(&self, name: &str, value: f64) -> ObservabilityResult<()>

Emit a simple counter metric

fn emit_histogram_simple(&self, name: &str, value: f64) -> ObservabilityResult<()>

Emit a simple histogram/timing metric

fn emit_gauge_simple(&self, name: &str, value: f64) -> ObservabilityResult<()>

Emit a simple gauge metric

fn is_enabled(&self) -> bool

Check if metrics collection is enabled

fn emit_metrics_batch(&self, entries: &[MetricsEntry]) -> ObservabilityResult<()>

Batch emit multiple metrics (optional, has default implementation)

Port for standard logging integration

This port allows us to hook into standard Rust logging infrastructure:

  • log::Log implementation
  • tracing::Subscriber implementation

Required / Provided Methods

fn initialize(&self) -> ObservabilityResult<()>

Initialize the logging system (called once during agent startup)

fn process_standard_log(&self, entry: LogEntry) -> ObservabilityResult<()>

Process a log entry from standard logging macros

fn enabled(&self, level: &LogLevel) -> bool

Check if logging is enabled for this level

Port for transporting log entries to external systems

This is the interface that different adapters implement:

  • WasmStdoutAdapter (writes to WASM stdout)
  • HttpTransportAdapter (sends via HTTP)
  • BatchingTransportAdapter (batches entries)

Required / Provided Methods

fn transport(&self, entry: &LogEntry) -> ObservabilityResult<()>

Transport a single log entry

fn transport_batch(&self, entries: &[LogEntry]) -> ObservabilityResult<()>

Transport multiple log entries (for batching)

Configuration for batching behavior

Fields

FieldTypeDescription
max_batch_sizeusizeMaximum number of items in a batch
flush_intervalweb_time::DurationMaximum time to wait before flushing a batch
max_memory_bytesusizeMaximum memory usage in bytes (approximate)
drop_on_overflowboolWhether to drop items when buffer is full
min_batch_sizeusizeMinimum batch size to trigger early flush

Batching manager for telemetry data

Methods

fn new(config: BatchingConfig) -> Self
fn set_flush_callback<F>(&mut self, callback: F)
where
F: Fn + Send + Sync + ?

Set the flush callback function

fn add_span(&self, span: SpanData) -> ObservabilityResult<()>

Add a span to the batch

fn add_metric(&self, metric: MetricData) -> ObservabilityResult<()>

Add a metric to the batch

fn add_log(&self, log: LogData) -> ObservabilityResult<()>

Add a log to the batch

fn flush_all(&self) -> ObservabilityResult<()>

Force flush all buffers

fn get_stats(&self) -> BatchingStats

Get buffer statistics

Future wrapper that restores trace context across async poll boundaries.

Header extractor for W3C trace context propagation

Methods

fn extract(&self) -> ObservabilityResult<Option<W3CTraceContext>>

Header injector for W3C trace context propagation

Methods

fn inject(&mut self, context: &W3CTraceContext)

Simplified trace context for internal use

Fields

FieldTypeDescription
trace_idString
span_idString
parent_span_idOption&lt;String&gt;
sampledbool

Methods

fn new_root() -> Self

Create a new root trace context

fn new_child(&self) -> Self

Create a child span

fn to_w3c(&self) -> W3CTraceContext

Convert to W3C trace context

fn from_w3c(w3c: &W3CTraceContext) -> Self

Create from W3C trace context

W3C Trace Context implementation for distributed tracing

Fields

FieldTypeDescription
trace_idStringW3C trace ID (32 hex characters)
parent_idStringW3C span ID (16 hex characters)
trace_flagsStringTrace flags (2 hex characters)
trace_stateOption&lt;String&gt;Additional trace state

Methods

fn new_root() -> Self

Create a new root trace context

fn new_child(&self) -> Self

Create a child span context

fn from_traceparent(header: &str) -> ObservabilityResult<Self>

Create from W3C traceparent header

fn from_headers(headers: &HashMap<String, String>) -> ObservabilityResult<Option<Self>>

Create from W3C headers

fn to_traceparent(&self) -> String

Generate W3C traceparent header value

fn to_headers(&self) -> HashMap<String, String>

Generate W3C headers for propagation

fn is_sampled(&self) -> bool

Check if the trace is sampled

fn set_sampled(&mut self, sampled: bool)

Set sampling flag

fn add_trace_state(&mut self, key: &str, value: &str)

Add or update trace state

fn get_trace_state(&self, key: &str) -> Option<String>

Get value from trace state

No-op observability plugin that does nothing

This implementation provides zero runtime cost when observability is disabled. All methods are inlined and compile to nothing in release builds.

Methods

fn new() -> Self

Create a new no-op plugin

fn shared() -> Arc<Self>

Create an Arc’d no-op plugin for sharing

Span guard that automatically ends spans when dropped

Methods

fn new(span_id: String, plugin: Arc<dyn ObservabilityPlugin>) -> Self
fn no_op() -> Self
fn span_id(&self) -> &str
fn duration(&self) -> Duration
fn add_attribute(&self, key: &str, value: &str)
fn set_status(&self, status: SpanStatus)

Compact JSON formatter (single line)

JSON formatter for structured logging output

Parsed RUST_LOG-style directives for per-target log filtering.

Supports: "info", "info,agent_sdk=debug,a2a_protocol_core=trace". Unknown tokens are silently ignored.

Methods

fn from_level(level: LogLevel) -> Self
fn parse(s: &str) -> Self

Parse a RUST_LOG-style directive string.

Examples: "info", "info,agent_sdk=debug", "warn,a2a_protocol_core=trace,llm_client=debug".

fn global_level(&self) -> LogLevel
fn max_level(&self) -> LogLevel
fn enabled(&self, level: LogLevel, target: &str) -> bool

Check whether a log record at level from target should be emitted.

Standard logging adapter that hooks into Rust’s log crate

Methods

fn new(processor_chain: ProcessorChain, transport: Arc<dyn TransportPort>, directives: LogDirectives) -> Self

Extended WASM adapter that handles both logs and metrics with correlation

Methods

fn new() -> Self
fn transport_metric(&self, entry: &MetricsEntry) -> ObservabilityResult<()>

Process metrics entry through same formatting as logs for correlation

WASM stdout transport adapter

Methods

fn new(formatter: Box<dyn FormatterPort>) -> Self
fn with_json_formatter() -> Self
fn with_compact_formatter() -> Self
fn with_plain_text_formatter() -> Self

Basic WASM stdout adapter for metrics (correlation-focused)

Methods

fn new() -> Self
fn disabled() -> Self

Builder for creating tracing-integrated logging setup

Methods

fn new() -> Self
fn with_processor_chain(self, chain: ProcessorChain) -> Self
fn with_transport(self, transport: Arc<dyn TransportPort>) -> Self
fn with_level_filter(self, level: LogLevel) -> Self
fn build(self) -> ObservabilityResult<TracingSubscriberAdapter>

Tracing subscriber adapter for structured logging integration

This adapter implements tracing::Subscriber to capture tracing events and convert them to LogEntry for processing through the hexagonal architecture

Methods

fn new(processor_chain: ProcessorChain, transport: Arc<dyn TransportPort>, level_filter: LogLevel) -> Self

Enhanced processor that combines context enrichment with kv extraction

Methods

fn new() -> Self
fn with_field<impl Into<String>, impl Into<serde_json::Value>>(self, key: impl Into, value: impl Into) -> Self
fn with_kv_extraction(self, extract_kv: bool) -> Self

Core log entry - pure data structure

Fields

FieldTypeDescription
timestampchrono::DateTime&lt;chrono::Utc&gt;
levelLogLevel
messageString
fieldsserde_json::Value
trace_contextOption&lt;TraceContext&gt;
sourceLogSource

Processor that extracts structured fields from log::kv

This processor supports the log::info!(“msg”; “key” => value) syntax by extracting key-value pairs from log::Record and adding them to LogEntry fields

Methods

fn new() -> Self
fn extract_kv_from_record(record: &log::Record<'_>) -> serde_json::Value

Extract key-value pairs from log::Record

This function would be called by StandardLogAdapter when processing log::Record but we’ll implement the interface here for the processor chain

Basic metric entry for correlation with logs

Fields

FieldTypeDescription
nameString
valuef64
metric_typeBasicMetricType
timestampchrono::DateTime&lt;chrono::Utc&gt;
trace_contextOption&lt;TraceContext&gt;
sourceMetricsSource

Methods

fn new<impl Into<String>>(name: impl Into, value: f64, metric_type: BasicMetricType) -> Self

Create a new metrics entry

fn with_trace_context(self, trace_context: TraceContext) -> Self

Add trace context for correlation

fn with_source(self, module: Option<String>, component: Option<String>, operation: Option<String>) -> Self

Add source information

fn to_json(&self) -> serde_json::Value

Convert to JSON for transport

Source of the metric

Fields

FieldTypeDescription
moduleOption&lt;String&gt;
componentOption&lt;String&gt;
operationOption&lt;String&gt;

Processor chain pattern (inspired by structlog)

Methods

fn new() -> Self

Create a new empty processor chain

fn add_processor(self, processor: Box<dyn LogProcessor>) -> Self

Add a processor to the chain

fn process(&self, entry: LogEntry) -> ObservabilityResult<LogEntry>

Process an entry through the entire chain

fn len(&self) -> usize

Get number of processors in the chain

fn is_empty(&self) -> bool

Check if chain is empty

Serializable trace identifiers for log/metric correlation.

Distinct from [crate::context::TraceContext] which is the runtime tracing context with sampling decisions and W3C conversion methods.

Fields

FieldTypeDescription
trace_idString
span_idString
parent_span_idOption&lt;String&gt;

Singleton global logger that initializes once and is shared across all extension instances

Methods

fn get_or_init(config: ObservabilityConfig) -> ObservabilityResult<&''static GlobalLoggerSingleton>

Get or create the singleton instance

This is thread-safe and will initialize exactly once

fn adapter(&self) -> &Arc<StandardLogAdapter>

Get the logger adapter

fn config(&self) -> &ObservabilityConfig

Get the configuration used

Configuration for observability

Fields

FieldTypeDescription
levelStringMinimum log level to process
formatStringOutput format: “json”, “compact”, “plain”
structuredboolEnable structured logging features
context_enrichmentboolEnable context enrichment
default_contextHashMap&lt;String, serde_json::Value&gt;Additional context fields to always include

Methods

fn parse_level(&self) -> ObservabilityResult<LogLevel>

Parse log level from string (global level only, ignores per-crate directives).

fn parse_directives(&self) -> LogDirectives

Parse RUST_LOG-style directives from the level string.

Accepts both simple levels ("info") and per-crate directives ("info,agent_sdk=debug,a2a_protocol_core=trace").

fn create_transport(&self) -> Arc<dyn TransportPort>

Create transport based on format configuration

fn validate(&self) -> ObservabilityResult<()>

Validate configuration

Core observability manager

This provides structured logging integration with standard Rust logging macros (log::info!, log::debug!, etc.) without external extension system dependencies

Methods

fn new(config: ObservabilityConfig) -> ObservabilityResult<Self>

Create a new observability manager with configuration

fn initialize(&mut self) -> ObservabilityResult<()>

Initialize global logging (convenience method)

fn config(&self) -> &ObservabilityConfig

Get current configuration

fn is_enabled(&self, level: LogLevel) -> bool

Check if logging is enabled for a level

fn global_logger() -> Option<Arc<StandardLogAdapter>>

Get the global logger instance (always available with singleton pattern)

fn capabilities(&self) -> Vec<String>

Get capabilities as strings

Comprehensive error types for observability operations

Variants

VariantDescription
Configuration { ... }Configuration errors
Serialization { ... }Serialization/deserialization errors
Transport { ... }Network/transport errors
TraceContext { ... }Trace context propagation errors
Metric { ... }Metric collection errors
Logging { ... }Logging errors
Batching { ... }Batching system errors
Buffer { ... }Buffer overflow or memory errors
FeatureNotEnabled { ... }Feature not enabled
Generic { ... }Generic errors for compatibility

Methods

fn configuration<impl Into<String>>(message: impl Into) -> Self

Create a configuration error

fn serialization<impl Into<String>>(message: impl Into) -> Self

Create a serialization error

fn transport<impl Into<String>>(message: impl Into) -> Self

Create a transport error

fn trace_context<impl Into<String>>(message: impl Into) -> Self

Create a trace context error

fn metric<impl Into<String>>(message: impl Into) -> Self

Create a metric error

fn logging<impl Into<String>>(message: impl Into) -> Self

Create a logging error

fn batching<impl Into<String>>(message: impl Into) -> Self

Create a batching error

fn buffer<impl Into<String>>(message: impl Into) -> Self

Create a buffer error

fn feature_not_enabled<impl Into<String>>(feature: impl Into) -> Self

Create a feature not enabled error

fn generic<impl Into<String>>(message: impl Into) -> Self

Create a generic error

Log levels for structured logging

Variants

VariantDescription
Error
Warn
Info
Debug
Trace

Methods

fn as_str(&self) -> &''static str

Status of a span

Variants

VariantDescription
Ok
Error
Cancelled

Simple metric types supported by core

Variants

VariantDescription
CounterCounter metric (monotonically increasing)
HistogramHistogram/timing metric (distribution of values)
GaugeGauge metric (current value)
fn clear_current_context()

Clear the current trace context

fn get_current_context() -> Option<TraceContext>

Get the current trace context for this thread

fn set_current_context(context: TraceContext)

Set the current trace context for this thread

fn with_context<F, R>(context: TraceContext, f: F) -> R
where
F: FnOnce

Execute a function with a specific trace context

fn with_context_future<F>(context: TraceContext, future: F) -> ContextFuture<F>
where
F: Future

Execute a future with a specific trace context re-applied on every poll.

fn create_counter_metric<impl Into<String>>(name: impl Into, value: f64) -> MetricsEntry

Convenience function to create a counter metric

fn create_gauge_metric<impl Into<String>>(name: impl Into, value: f64) -> MetricsEntry

Convenience function to create a gauge metric

fn create_histogram_metric<impl Into<String>>(name: impl Into, value: f64) -> MetricsEntry

Convenience function to create a histogram metric

fn create_observability_manager(config: Option<serde_json::Value>) -> ObservabilityResult<ObservabilityManager>

Factory function for creating observability manager from JSON configuration

Macro to conditionally create observability plugin based on features

Macro to conditionally create shared observability plugin based on features

Conditional span creation macro

Conditional metric recording macro

Conditional logging macro