feat(log): live verbosity toggle — dock Level chip + clide log level (T-433)
The boot-time toggle (CLIDE_LOG / app.log.level) existed; this makes it adjustable at runtime, from the two surfaces that fit — D-6 parity — and NOT a Claude-composer slash command (log verbosity isn't a Claude-session concept). - Output dock Level chip: was a view-only filter; now also drives the kernel Logger.minLevel and persists app.log.level, so the choice is real (changes what's captured) and survives restart. Initialized from the logger's current level so it reflects a CLI change. A null callback keeps it a pure view filter (tests / no kernel). - `clide log level [<level>]` (lib/src/daemon/log_commands.dart): no arg reports the level + vocabulary; a valid level sets the live logger and persists; unknown → bad_arg (code 64), logger untouched. Wired in buildDispatcher with the kernel logger + settings (captured post-boot). Tested: the command (get/set/case-insensitive/bad-arg, live + persist) and the controller (initial level, callback fires, same-level no-op, filter-only when unwired). Coverage gate 95.05%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/// Registers `log.level` — the live dev/prod verbosity toggle (T-433).
|
||||
///
|
||||
/// With no arg it reports the running [Logger]'s minimum level; with
|
||||
/// `level=<name>` it sets the Logger AND persists `app.log.level` so the
|
||||
/// choice survives a restart (resolved at boot by `resolveLogLevel`). The UI
|
||||
/// half is the output dock's Level chip — D-6 parity. Flutter-free + trivially
|
||||
/// testable: the handler takes the Logger and a persist callback.
|
||||
library;
|
||||
|
||||
import 'package:clide/kernel/src/log.dart';
|
||||
|
||||
import '../ipc/envelope.dart';
|
||||
import 'dispatcher.dart';
|
||||
|
||||
/// Persists the chosen level name (the owner wires this to
|
||||
/// `settings.set('app.log.level', name)`).
|
||||
typedef LogLevelPersist = Future<void> Function(String levelName);
|
||||
|
||||
void registerLogCommands(DaemonDispatcher d, Logger log, LogLevelPersist persist) {
|
||||
d.register('log.level', (req) async {
|
||||
final raw = req.args['level'];
|
||||
if (raw == null) {
|
||||
// Read: report the current level + the vocabulary.
|
||||
return IpcResponse.ok(id: req.id, data: {'level': log.minLevel.name, 'levels': LogLevel.values.map((l) => l.name).toList()});
|
||||
}
|
||||
final level = parseLogLevel(raw is String ? raw : raw.toString());
|
||||
if (level == null) {
|
||||
return IpcResponse.err(
|
||||
id: req.id,
|
||||
error: IpcError(code: 64, kind: 'bad_arg', message: 'unknown log level: $raw', hint: 'one of: ${LogLevel.values.map((l) => l.name).join(', ')}'),
|
||||
);
|
||||
}
|
||||
log.minLevel = level;
|
||||
await persist(level.name);
|
||||
return IpcResponse.ok(id: req.id, data: {'level': level.name});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user