DigiaLogger class

The SDK's one logging style, across core and every CEP plugin.

One instance per module, held as a const field so a call site is a method call and nothing more. The tag becomes the console prefix and is the whole retrieval story: a developer filters their console for DIGIA and pastes. Never reword the prefix; grepping for it is the only way a support ticket carries our logs.

<badge> [<TAG>] [<LEVEL>]: [<campaignKey>] <Message> (key=value, key=value)

πŸ”΄ [DIGIA] [ERROR]: [canvas_new_nudge] Payload parse failed
🟑 [DIGIA] [WARN]: [canvas_new_nudge] Dropped β€” Campaigns are still loading; trigger dropped
πŸ”΅ [DIGIA-ME] [INFO]: [canvas_new_nudge] Received (cepId=6aabbf01362b7cb5)
βšͺ [DIGIA] [DEBUG]: [summer_sale] Dropped β€” frequency capped (policy=1/day)

The campaign slot is what makes same-campaign lines group vertically like a thread, and [<key>] an unambiguous filter β€” grepping the bare key also hits lines that merely mention it (by=summer_sale). It is a parameter, never hand-written brackets, so the format cannot rot; it is omitted entirely when there is no campaign (init, fetch, session, queue), never rendered empty; and it is always campaignKey, the human-meaningful name the author typed into the dashboard. cepCampaignId and presentationId stay in the trailing data.

There are exactly six tags, and the set is closed on purpose β€” nine were tried first and read as noise, because the SDK is one thing to the reader and sub-module detail belongs in the message when it matters:

Tag Written as Covers
DIGIA DigiaLogger() everything in core that is not one of the below
DIGIA-ANALYTICS DigiaLogger('analytics') the first-party analytics pipeline
DIGIA-LIVETEST DigiaLogger('liveTest') live campaign testing
DIGIA-CT / -WE / -ME in the plugin packages the CEP boundary β€” the #1 support question

Message style, so a reader can scan the left edge: outcome first, sentence case (Campaign received, not received campaign); never repeat the tag's module in the message; data trails in one parenthesised key=value group.

One emit, many sinks. Every call builds exactly one TimelineRecord and walks the sink registry; ConsoleSink renders the line above, and ScreenSink records the on-device campaign timeline. Adding a destination never touches a call site.

The two gates are independent, and that is the point:

Sink Accepts when Who owns the knob
ConsoleSink severity is at or above the configured DigiaLogLevel the host developer
ScreenSink the record carries a TimelineStage us, per call site
HealthSink the record's DiagnosticReason is on a central allowlist us, in one list

Each gate reads a different field with a different owner, which is what makes escalation fail closed: forgetting one can only narrow the audience, never widen it. There is deliberately no sendTo: parameter β€” per-call-site routing drifts, and one mistyped flag would leak internals to a campaign creator or to a backend.

So a debug gating drop that a release console suppresses still reaches the campaign creator's screen. Promoting a call is a deliberate act: pass a TimelineStage and a DiagnosticReason from a closed enum, and free-text developer logging can never leak onto a non-developer's screen. The promoted arguments do not change the console line β€” what a developer reads is message and nothing else.

Three rules the SDK's release chain makes non-negotiable, since a bad line ships inside a customer app for months:

  • A log call never throws. It runs on render paths. Every sink call is caught here, so a sink cannot break the app that hosts us.
  • A disabled level costs nothing. Gate an expensive unstaged message with isEnabled rather than building it and discarding it inside.
  • A staged call is never wrapped in isEnabled. That guard is for hot unstaged chatter only; around a staged call it silently blinds the timeline in exactly the release build someone opened it to debug.

Constructors

DigiaLogger([String tag = ''])
Creates a logger for one module.
const

Properties

hashCode β†’ int
The hash code for this object.
no setterinherited
runtimeType β†’ Type
A representation of the runtime type of the object.
no setterinherited
tag β†’ String
The module suffix in this logger's console prefix, or empty for none.
final

Methods

d(String message, {String? campaign, TimelineStage? stage, DiagnosticReason? reason, String? presentationId, Map<String, String>? extras}) β†’ void
Per-node, per-frame, per-request detail.
e(String message, {String? campaign, Object? error, StackTrace? stackTrace, TimelineStage? stage, DiagnosticReason? reason, String? presentationId, Map<String, String>? extras}) β†’ void
The SDK could not do the thing. Visible at every level but DigiaLogLevel.none.
i(String message, {String? campaign, TimelineStage? stage, DiagnosticReason? reason, String? presentationId, Map<String, String>? extras}) β†’ void
A lifecycle milestone.
isEnabled(DigiaLogSeverity severity) β†’ bool
Whether a severity line would be emitted right now.
noSuchMethod(Invocation invocation) β†’ dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() β†’ String
A string representation of this object.
inherited
w(String message, {String? campaign, Object? error, TimelineStage? stage, DiagnosticReason? reason, String? presentationId, Map<String, String>? extras}) β†’ void
Degraded, but recovered.

Operators

operator ==(Object other) β†’ bool
The equality operator.
inherited

Static Properties

level β†’ DigiaLogLevel
The threshold currently in force.
no setter
screenNameSource ↔ String? Function()?
Supplies the screen the app is currently on, so staged records can be stamped with it without every call site passing one.
getter/setter pair

Static Methods

configure(DigiaLogLevel level) β†’ void
Applies the host app's configured verbosity. Called once from Digia.initialize(); until then DigiaLogLevel.auto is in force, so logs emitted during startup are not silently lost.
isSeverityEnabled(DigiaLogSeverity severity) β†’ bool
Whether the configured level admits severity. The console's gate, and nothing else's.
registerSink(DiagnosticSink sink) β†’ void
Adds a sink to the registry. Idempotent, so a repeated init cannot end up emitting a record twice into the same destination.
unregisterSink(DiagnosticSink sink) β†’ void
Removes a sink. A no-op if it was never registered.
withCampaign<T>(String? campaign, T body()) β†’ T
Runs body with campaign stamped on each record it logs that names none. A parse runs deep below the campaign that owns it.