Recorders

This page documents recorder-related public API.

A recorder is the component that receives metric events, owns registered metric instances, processes accepted events, and exposes metric snapshots.

The recorder API has two levels:

  • MetricsRecorderProto — the small contract used by production components;

  • AsyncioMetricsRecorder — the default asynchronous recorder implementation.

Public API

class mvx.common.metrics.MetricsRecorderProto(*args, **kwargs)

Protocol for metrics recorders.

This protocol is the narrow integration contract used by runtime components that publish metric events. Components depend on this protocol instead of a concrete recorder implementation.

A recorder owns registered metrics, accepts MetricEvent objects, dispatches events to metrics, and exposes metric snapshots for diagnostics, tests, or monitoring integrations.

register_metric(metric)

Register a metric in the recorder.

Registered metrics receive subsequently dispatched metric events. Metric names are expected to be stable within one recorder instance.

Parameters:

metric (Metric) – metric instance to register.

Return type:

None

Returns:

None.

register_event(event)

Register a metric event in the recorder.

The recorder accepts the event for asynchronous or synchronous dispatch, depending on the concrete implementation.

Parameters:

event (MetricEvent) – metric event to register.

Return type:

None

Returns:

None.

get_metric_snapshots()

Return snapshots of registered metrics.

The outer mapping is keyed by metric name. Each inner mapping is the current snapshot returned by the corresponding metric.

Return type:

Mapping[str, Mapping[str, Any]]

Returns:

mapping of metric names to metric snapshots.

iter_metrics()

Iterate over registered metrics.

Return type:

Iterable[Metric]

Returns:

iterable of registered metric instances.

enum mvx.common.metrics.AsyncioMetricsRecorderQueueOverflowPolicy(value)

Queue overflow policy for AsyncioMetricsRecorder.

The policy is applied when the recorder reaches its configured pending-event limit.

Member Type:

str

Valid values are as follows:

DROP = <AsyncioMetricsRecorderQueueOverflowPolicy.DROP: 'DROP'>

Drop an event when the pending-event limit is reached.

RAISE_ERROR = <AsyncioMetricsRecorderQueueOverflowPolicy.RAISE_ERROR: 'RAISE_ERROR'>

Raise AsyncioMetricsRecorderQueueOverflowError when the pending-event limit is reached.

enum mvx.common.metrics.AsyncioMetricsRecorderOp(value)

Lifecycle operation name reported by AsyncioMetricsRecorderOpResult.

Member Type:

str

Valid values are as follows:

START = <AsyncioMetricsRecorderOp.START: 'START'>

Start operation.

STOP = <AsyncioMetricsRecorderOp.STOP: 'STOP'>

Stop operation.

class mvx.common.metrics.AsyncioMetricsRecorderOpResult(op_name, success, error=None)

Result of an AsyncioMetricsRecorder lifecycle operation.

Returned by AsyncioMetricsRecorderWaitHandle.wait() and by awaiting a wait handle.

Parameters:
class mvx.common.metrics.AsyncioMetricsRecorderWaitHandle(operation)

Wait handle returned by AsyncioMetricsRecorder.start() and AsyncioMetricsRecorder.stop().

The handle represents a scheduled lifecycle operation. It can be awaited from async code or waited synchronously through wait(). Both forms return AsyncioMetricsRecorderOpResult.

Parameters:

operation (AsyncioMetricsRecorderOp)

wait()

Wait synchronously until the lifecycle operation completes.

Return type:

AsyncioMetricsRecorderOpResult

Returns:

lifecycle operation result.

enum mvx.common.metrics.AsyncioMetricsRecorderState(value)

Lifecycle states of an AsyncioMetricsRecorder instance.

The state describes whether the recorder has not been started yet, is starting, is running, is stopping, has stopped normally, or has reached a terminal abnormal state.

Member Type:

str

Valid values are as follows:

VIRGIN = <AsyncioMetricsRecorderState.VIRGIN: 'VIRGIN'>

The recorder has been created but has not started yet.

STARTING = <AsyncioMetricsRecorderState.STARTING: 'STARTING'>

Startup is in progress.

RUNNING = <AsyncioMetricsRecorderState.RUNNING: 'RUNNING'>

The recorder is running and can accept events.

STOPPING = <AsyncioMetricsRecorderState.STOPPING: 'STOPPING'>

Shutdown is in progress.

STOPPED = <AsyncioMetricsRecorderState.STOPPED: 'STOPPED'>

The recorder has stopped normally.

FAILURE = <AsyncioMetricsRecorderState.FAILURE: 'FAILURE'>

The recorder has entered a terminal failure state.

CANCELLED = <AsyncioMetricsRecorderState.CANCELLED: 'CANCELLED'>

The dispatcher task was cancelled unexpectedly.

class mvx.common.metrics.AsyncioMetricsRecorder(entity_id, *, namespace=None, queue_max_size=None, queue_overflow_policy=None, log_context=None)

Asynchronous in-memory metrics recorder.

The recorder owns registered metric instances, accepts metric events, places accepted events into its internal processing buffer, dispatches events to registered metrics on its owning asyncio event loop, and exposes metric snapshots.

The recorder is bound to the running asyncio event loop at construction time. For regular application code, recorders are usually created through MetricsRuntime, which provides the required event-loop environment.

Parameters:
  • entity_id (str) – measured entity or recorder scope identifier.

  • namespace (str | None) – optional namespace used for recorder task names and logging.

  • queue_max_size (int | None) – maximum number of accepted pending events.

  • queue_overflow_policy (AsyncioMetricsRecorderQueueOverflowPolicy | None) – policy applied when the pending-event limit is reached.

  • log_context (LogContext | None) – optional log context used for recorder diagnostics.

Raises:
  • ValueError – if entity_id is None or queue_max_size is not positive.

  • TypeError – if an argument has an invalid type.

  • AsyncioMetricsRecorderLoopUnavailableError – if no running asyncio event loop is available.

get_log_context()

Return the log context associated with this recorder.

Used by log_invocation decorators and internal recorder diagnostics.

Return type:

LogContextProto | None

Returns:

log context, or None when recorder logging is disabled.

property entity_id: str

Return the measured entity identifier associated with this recorder.

Returns:

recorder entity id.

get_status()

Return the current recorder lifecycle state.

Return type:

AsyncioMetricsRecorderState

Returns:

current recorder state.

start()

Start recorder processing.

The method schedules startup on the recorder’s owning asyncio event loop and returns immediately with a wait handle. If startup is already in progress, the returned handle is attached to the same startup operation.

Startup runs the _on_starting() hook and starts the dispatcher task.

Return type:

AsyncioMetricsRecorderWaitHandle

Returns:

wait handle for the start operation.

stop()

Stop recorder processing.

The method schedules shutdown on the recorder’s owning asyncio event loop and returns immediately with a wait handle. Stop is valid only when the recorder is running.

Shutdown performs a best-effort flush of accepted events, stops the dispatcher, and runs the _on_stopped() hook.

Return type:

AsyncioMetricsRecorderWaitHandle

Returns:

wait handle for the stop operation.

register_metric(metric)

Register a metric instance in the recorder.

The metric is stored by its metric_name and will receive future metric events during recorder dispatching.

Parameters:

metric (Metric) – metric instance to register.

Raises:
Return type:

None

Returns:

None.

register_event(event)

Register a metric event for recorder-side processing.

The event is accepted into the recorder processing path and scheduled for dispatch to registered metrics. If the recorder is still in the VIRGIN state, event registration schedules recorder startup.

Parameters:

event (MetricEvent) – metric event to register.

Raises:
Return type:

None

Returns:

None.

get_metric_snapshots()

Return snapshots of metrics registered in this recorder.

If called from the recorder’s owning event loop, snapshots are collected directly. Otherwise, snapshot collection is scheduled on the owning loop and the caller waits for the result.

Return type:

Mapping[str, Mapping[str, Any]]

Returns:

mapping from metric name to metric snapshot.

iter_metrics()

Return registered metric instances.

If called from the recorder’s owning event loop, metrics are collected directly. Otherwise, metric collection is scheduled on the owning loop and the caller waits for the result.

Return type:

Iterable[Metric]

Returns:

iterable of registered metric instances.

Recorder contract

MetricsRecorderProto is the narrow interface expected by production code.

A component that emits metric events should depend on this contract, not on a concrete recorder implementation.

Typical component constructor:

class DocumentStorage:
    def __init__(
            self,
            *,
            metrics_recorder: MetricsRecorderProto | None = None,
    ) -> None:
        self._metrics_recorder = metrics_recorder

This keeps metrics optional and keeps the component independent from recorder implementation details.

Default recorder implementation

AsyncioMetricsRecorder is the default recorder implementation.

It is bound to a running asyncio event loop at construction time.

For normal application wiring, recorders are usually created through MetricsRuntime, which provides the required thread and event loop.

Direct AsyncioMetricsRecorder construction is useful for advanced cases where the application already owns the recorder execution environment.

Lifecycle operations

start() and stop() return AsyncioMetricsRecorderWaitHandle.

The handle can be awaited:

result = await recorder.start()

or waited synchronously:

result = recorder.start().wait()

Both forms return AsyncioMetricsRecorderOpResult.

Check the result when managing recorder lifecycle directly:

result = await recorder.start()
if not result.success:
    raise result.error

Runtime-created recorders are started and stopped by MetricsRuntime.

Queue overflow policy

AsyncioMetricsRecorderQueueOverflowPolicy controls what happens when the recorder reaches its configured pending-event limit.

RAISE_ERROR makes overflow visible by raising AsyncioMetricsRecorderQueueOverflowError.

DROP drops the new event.

The policy is configured when creating the recorder directly or when creating recorders through MetricsRuntime.

Metric registration

Metrics are registered with:

recorder.register_metric(metric=DocumentSaveAttemptsMetric())

The recorder stores metrics by metric_name.

When events are processed, registered metrics receive those events through their handle_event() method.

Event registration

Metric events are submitted with:

recorder.register_event(event=event)

This is the production handoff point.

The recorder accepts the event, places it into its processing path, and dispatches it to registered metrics on the metrics side.

If the recorder is still in VIRGIN state, event registration schedules recorder startup.

Snapshots and inspection

Recorder snapshots are read with:

snapshots = recorder.get_metric_snapshots()

The result is a mapping from metric name to metric snapshot.

Registered metrics can also be inspected with:

metrics = recorder.iter_metrics()

These methods are inspection APIs. They do not submit new metric events.

Extension hooks

AsyncioMetricsRecorder provides protected hooks for subclasses:

  • _on_starting();

  • _on_stopped();

  • _on_metric_changed(metric=..., event=...).

These hooks are documented here because they are part of the extension surface of the default recorder.

Use them when implementing custom recorder behavior.

For example, _on_metric_changed() is called only after a metric accepts an event and updates its state.

Logging

AsyncioMetricsRecorder supports MVX Logger integration through an optional log_context.

Selected public recorder methods are instrumented with log_invocation.

register_event() is intentionally not wrapped with log_invocation, because it is the hot-path metric event handoff.

Ready-to-use recorder logging policies are documented separately in the logging policies API page.