Logging
Plugin Kit logs through Dart’s standard package:logging. By default, nothing is printed. You decide where the output goes.
This page covers what the runtime logs, at what level, and how to listen.
This is an operations guide. Once plugins have lifecycle, settings, and runtime reconciliation, logs become the fastest way to answer “what did the runtime actually do?”
Loggers
Section titled “Loggers”| Logger name | Covers |
|---|---|
plugin_kit.PluginRuntime | runtime init/dispose, global plugin enable/disable, session creation, settings reconciliation, wildcard override resolution |
plugin_kit.PluginSession | session init and dispose |
Both sit under plugin_kit, so you can subscribe to one specifically or to the whole library with a single listener.
Listen to everything
Section titled “Listen to everything”/// Wires the root `plugin_kit` logger to print every record.void listenToPluginKitLogger() { Logger('plugin_kit').onRecord.listen((record) { print('${record.level.name}: ${record.loggerName}: ${record.message}'); });}If your app already uses hyper_logger, Plugin Kit records flow through its root subscription automatically. No extra setup.
Log levels
Section titled “Log levels”| Level | What you will see |
|---|---|
info | Lifecycle events: runtime initialized, session created, plugin enabled or disabled, settings updated |
fine | Diagnostics: which plugins are enabled, wildcard override resolutions |
warning | Partial failures: a lifecycle phase completed but some plugins threw |
severe | Individual plugin errors during attach, detach, or settings update, always with error and stack trace |
Most production apps want info and above in normal operation. Turn on fine when debugging enablement or override behavior. severe is never optional signal.
Error handling
Section titled “Error handling”Plugin Kit does not silently swallow errors.
When a plugin throws during a lifecycle phase, the runtime:
- Logs the failure at
severe, including the stack trace. - Continues processing the remaining plugins in that phase.
- After the phase finishes, throws
PluginLifecycleExceptionwith every collected failure.
Future<void> safeInit() async { final runtime = PluginRuntime();
try { runtime.init(); } on PluginLifecycleException catch (e) { print('attach failed: ${e.phase}'); // Inspect e.failures for per-plugin error details. rethrow; }}The exception carries:
phase: a string like'attachGlobal','detachSession','updateSessionSettings'failures: a list of(pluginId, error, stackTrace)tuples
The aggregation is deliberate. Fail-fast on the first exception would hide real problems: if plugin B and plugin C are both broken, you find out about B, fix it, and then discover C. Plugin Kit surfaces the whole batch at once.
Making the logs useful
Section titled “Making the logs useful”A few things to set up once, then forget about.
Forward to your existing pipeline. Do not write a custom subscription if your app already has a logging setup. Pipe Logger.root.onRecord (or Logger('plugin_kit').onRecord) into whatever that pipeline is.
Fine-grained control during debugging.
/// Raises specific plugin_kit loggers to FINE for diagnostic output.void setFineLogging() { Logger('plugin_kit.PluginRuntime').level = Level.FINE; Logger('plugin_kit.PluginSession').level = Level.FINE;}Raise a specific logger without turning on every library in your app.
Always handle PluginLifecycleException. Uncaught, it aborts your init or session creation. In production, catch it at the runtime boundary and decide whether to continue with a partial runtime or fail the whole process. Both are defensible. Silently ignoring is not.