blocpod_arch_logger
Bridge adapter between blocpod_arch event records and blocpod_logger sinks.
This package owns:
BlocpodEventLogFormatterEventLogRecordFormatterPrettyEventLogRecordFormattereventLogPhaseLabelBlocpodEventLogger
blocpod_arch_logger is the only package in this workspace that should depend
on both blocpod_arch and blocpod_logger.
Usage
Install the bridge by overriding eventLoggerProvider at the application
boundary:
import 'package:blocpod_arch/blocpod_arch.dart';
import 'package:blocpod_arch_logger/blocpod_arch_logger.dart';
import 'package:blocpod_logger/blocpod_logger.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
ProviderScope(
overrides: [
eventLoggerProvider.overrideWithValue(
BlocpodEventLogger(DebugPrintLogSink()),
),
],
child: const Placeholder(),
);
BlocpodEventLogger converts EventLogRecord values into BlocpodLogEntry
values and isolates sink failures from application flow. The formatter includes
the observer phase, dedicated sequence/trace fields, event name, transition
index, state kinds, optional state labels, nested eventMetadata /
stateMetadata, duration, errors, and stack traces.
EventLogRecord.metadata does not flatten into top-level logger fields anymore.
Use the dedicated BlocpodLogEntry.sequence, traceId, spanId, and
parentSpanId fields, and read remaining payload from attributes.
Formatter styles
The default EventLogRecordFormatter is compact and structured. It is best for
log sinks that index metadata:
eventLoggerProvider.overrideWithValue(
BlocpodEventLogger(DebugPrintLogSink()),
);
Compact output uses log-friendly phase labels such as state.established,
event.started, state.transition, and event.completed. Use
eventLogPhaseLabel when custom formatters need the same phase labels.
For local debugging, pair PrettyEventLogRecordFormatter with
PrettyLogEncoder:
eventLoggerProvider.overrideWithValue(
BlocpodEventLogger(
DebugPrintLogSink(
encoder: const PrettyLogEncoder(
detail: PrettyLogDetail.verbose,
),
),
formatter: const PrettyEventLogRecordFormatter(),
),
);
Build/dispatch records follow controllerCreated → initialStateEstablished → eventStarted → transition* → eventCompleted | eventFailed.
state.established is emitted once for the first terminal, non-loading build
state; canceled builds and intermediate retry loading states are ignored. It has
no event or previous state; its sanitized state summaries come from stateLabel
and stateMetadata, with the final state supplied as both previous and
next. A synchronous or asynchronous terminal error carries its error and
stackTrace.
controllerDisposed records the first Riverpod ref disposal signal registered
by the controller. Provider invalidation may emit it before a rebuild on the
same notifier, so it is not proof that the notifier instance was destroyed.
Blocpod does not emit a separate BLoC-style onChange phase. transition is
the canonical event-attributed state-assignment record, so direct assignments
outside dispatch remain intentionally unobserved. Pretty output renders the
same transition record in a human-readable form instead of duplicating the core
record stream.
Example pretty entry:
12:00:00.000Z INFO ✅ CounterController · IncrementEvent completed data(count:0) → data(count:1) in 12ms
sequence=42 trace=trace-1/span-2 parent=span-1
attributes={phase: event.completed, controllerName: CounterController, eventName: IncrementEvent, durationMicros: 12000, previousStateKind: data, nextStateKind: data, eventMetadata: {feature: counter}, stateMetadata: {changedBy: 1}}
Migration details:
recordSequencenow maps toBlocpodLogEntry.sequence.- Trace IDs no longer travel inside metadata; they map to dedicated trace fields.
- Event payload lives under
attributes['eventMetadata']. - State payload lives under
attributes['stateMetadata']. - Sensitive-data policy belongs to the application or sink adapter that forwards logs beyond Blocpod.
Libraries
- blocpod_arch_logger
- Bridge adapter between Blocpod architecture events and Blocpod log sinks.