# Logging

Plugin Kit logs through Dart's standard [`package:logging`](https://pub.dev/packages/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

| 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

```dart
extractRegion(loggingDart, 'logging-logger-listen')
```

If your app already uses [`hyper_logger`](https://github.com/SaadArdati/hyper_logger), Plugin Kit records flow through its root subscription automatically. No extra setup.

## 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

Plugin Kit does not silently swallow errors.

When a plugin throws during a lifecycle phase, the runtime:

1. Logs the failure at `severe`, including the stack trace.
2. Continues processing the remaining plugins in that phase.
3. After the phase finishes, throws `PluginLifecycleException` with every collected failure.

```dart
extractRegion(loggingDart, 'logging-try-catch-plugin-init')
```

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.
**The bus does not aggregate:** `PluginLifecycleException` is about plugin lifecycle (register, attach, detach, onPluginSettingsChanged). It is not how the event bus reports errors. If a handler throws during `emit` or `request`, the caller sees the exception directly. The bus never swallows handler exceptions. See [Event Bus](https://plugin-kit.saad-ardati.dev/concepts/event-bus/).

## 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.**

```dart
extractRegion(loggingDart, 'logging-fine-level')
```

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.

## Related reading

[Runtime](https://plugin-kit.saad-ardati.dev/concepts/runtime/)
  [Testing](https://plugin-kit.saad-ardati.dev/guides/testing/)
  [Event Bus](https://plugin-kit.saad-ardati.dev/concepts/event-bus/)