Choosing between dart:developer logging and ServiceExtension for runtime‑controlled alerts with minimal production noise
0 reputation · 13 Mar 2025, 01:07 UTC
0 reputation · 13 Mar 2025, 01:07 UTC
Goal: Emit developer alerts in a production Dart application only when needed, while keeping runtime overhead and notification noise to a minimum when alerts are disabled.
Two documented mechanisms fit this need: adjusting Logger.level for dart:developer log calls, which incurs a small constant cost per log invocation even when filtered out, and registering a ServiceExtension that can be toggled via Observatory or an IDE breakpoint, which adds no per‑call cost when the extension is not invoked but requires a connected observatory session to be usable.
Uncertainty remains about whether either approach can achieve true zero‑cost disabling in a standalone VM without an observatory connection, and how the constant cost of filtered log calls scales with high‑frequency alert sites.
Which approach provides truly zero overhead when alerts are disabled? Can a ServiceExtension be used effectively without an active observatory session in a command‑line or server‑side Dart program? Is there a way to combine runtime level filtering with a service‑extension‑style toggle to obtain both low per‑call cost and on‑demand activation?
30275 reputation · 13 Mar 2025, 03:33 UTC
• True zero‑overhead when alerts are disabled can only be achieved by removing the call entirely (compile‑time guard) or by guarding it with a flag that is false by default.
• A ServiceExtension adds no per‑call cost when the extension is not invoked, but it is only useful when an Observatory session is attached; in a plain release binary it is inert.
• The practical mix is to register a ServiceExtension that flips a global boolean, and let your log statements check that boolean before invoking log(). This keeps per‑call cost negligible when the flag is off and gives you on‑demand activation when you want to debug.
dart:developer log isn’t zero‑costEach call to log() incurs a small amount of work: the function creates a StackTrace, formats the message, and checks Logger.level. Even if the message is filtered out, the call still executes these steps, which can add up at high frequencies. The cost cannot be eliminated without a compile‑time guard that removes the call from the bytecode.
ServiceExtensions are only evaluated when the Dart VM’s Observatory is connected—either via the command line, an IDE, or a remote debugging session. If you run a release binary with Observatory disabled, the extension is never called, so it can’t trigger any actions. In that scenario the extension is effectively a no‑op, but the registration itself has a very small startup cost.
--enable-vm-service (or the default in dev builds).log() or perform any expensive diagnostics.In a release build where Observatory is not enabled, you can still get near‑zero overhead by combining a runtime flag with a compile‑time guard:
import 'dart:developer' as dev;
// Compile‑time guard – removed in release if kReleaseMode is true
bool _debugEnabled = !kReleaseMode;
// Runtime flag that can be toggled via a ServiceExtension
bool _serviceToggle = false;
void _maybeLog(String msg) {
if (_debugEnabled && _serviceToggle) {
dev.log(msg, level: 0);
}
}
void main() {
// Register the extension only if Observatory is available
try {
dev.ServiceExtensionHandler.registerExtension('ext/debug', (params) async {
_serviceToggle = params['enabled'] == 'true';
return {'status': 'ok'};
});
} catch (_) {}
// Example high‑frequency call site
for (var i = 0; i < 1_000_000; i++) {
_maybeLog('Iteration $i');
}
}
When _serviceToggle is false, the body of _maybeLog is never executed, so the only cost is the boolean check, which is negligible. In a release build _debugEnabled is false, so the if condition short‑circuits and the call is effectively removed.
Does your production environment run with Observatory enabled (e.g., --enable-vm-service)? If so, a pure ServiceExtension approach may be sufficient; if not, the hybrid guard pattern is recommended.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.