feat: add --log-level CLI flag (#54)

This commit is contained in:
existedinnettw 2026-04-14 15:25:30 +08:00 committed by GitHub
parent cbd0305a39
commit 55702db014
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 141 additions and 6 deletions

View File

@ -32,9 +32,9 @@ Create `CWS_conf.yaml` using:
Environment placeholders (for example `${CWS_DISCORD_WEBHOOK}`) are resolved
from process env vars and from a `.env` file next to your config.
Logging level can be set with `CWS_LOG_LEVEL` (or `LOG_LEVEL`): `trace`,
`debug`, `info`, `warning`, `error`, `fatal`. Minimal example:
[.env.example](.env.example).
Set logging with `--log-level trace|debug|info|warn|error` or env
`CWS_LOG_LEVEL` / `LOG_LEVEL`. Precedence: CLI > env > default `info`.
Minimal env example: [.env.example](.env.example).
Generate starter config:
@ -64,6 +64,8 @@ Run continuously:
```bash
dart run bin/code_work_spawner.dart
# With higher verbosity for troubleshooting:
dart run bin/code_work_spawner.dart --log-level debug
```
Override CLI paths if needed:

View File

@ -57,6 +57,10 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
'gosmee-command',
help: 'Path to the gosmee executable.',
defaultsTo: 'gosmee',
)
..addOption(
'log-level',
help: 'Minimum log level: trace, debug, info, warn, error.',
);
addCommand(_InitConfigCommand());
@ -64,6 +68,14 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
@override
Future<void> runCommand(ArgResults topLevelResults) async {
final cliMinimumLevel = _resolveCliMinimumLevel(
topLevelResults['log-level'] as String?,
);
_configureLogging(
cliMinimumLevel: cliMinimumLevel,
environment: AppEnvironment.variables,
);
if (topLevelResults['help'] as bool) {
stdout.writeln(usage);
return;
@ -76,7 +88,10 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
final configPath = topLevelResults['config'] as String;
AppEnvironment.loadForConfig(configPath);
_configureLogging();
_configureLogging(
cliMinimumLevel: cliMinimumLevel,
environment: AppEnvironment.variables,
);
final config = await AppConfig.load(configPath);
final logger = AppLogger('code_work_spawner.cli');
@ -124,6 +139,21 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
await app.close();
}
}
AppLogLevel? _resolveCliMinimumLevel(String? value) {
if (value == null) {
return null;
}
final parsed = AppLogger.parseLevel(value);
if (parsed == null || parsed == AppLogLevel.fatal) {
throw UsageException(
'Invalid value for --log-level "$value". '
'Allowed values: trace, debug, info, warn, error.',
usage,
);
}
return parsed;
}
}
String _cliEventSourceSettings(IssueEventSourceConfig eventSource) {
@ -210,9 +240,14 @@ class _InitConfigCommand extends Command<void> {
}
}
void _configureLogging() {
void _configureLogging({
required Map<String, String> environment,
AppLogLevel? cliMinimumLevel,
}) {
AppLogger.configure(
minimumLevel: AppLogger.resolveMinimumLevel(AppEnvironment.variables),
minimumLevel:
cliMinimumLevel ??
AppLogger.resolveMinimumLevel(environment, fallback: AppLogLevel.info),
useColors: stdout.hasTerminal,
);
}

View File

@ -0,0 +1,98 @@
import 'package:code_work_spawner/code_work_spawner.dart';
import 'package:test/test.dart';
import '../support/cli_utils.dart';
void main() {
/// ```gherkin
/// Feature: CLI log level option
///
/// As an operator running code_work_spawner
/// I want to configure log verbosity with a CLI flag
/// So that I can easily collect the right level of diagnostics
/// ```
group('code_work_spawner --log-level', () {
setUp(AppEnvironment.resetForTest);
tearDown(() {
AppEnvironment.resetForTest();
AppLogger.configure(minimumLevel: AppLogLevel.info, useColors: false);
});
/// ```gherkin
/// Scenario: Parse a valid log level from the CLI
/// Given the operator passes --log-level WARN
/// When the CLI argument parser initializes logging
/// Then the configured minimum level is warning
/// ```
test('Parse a valid log level from the CLI', () async {
// Given the operator passes --log-level WARN.
final result = await runCli(['--log-level', 'WARN', '--help']);
// When the CLI argument parser initializes logging.
final minimumLevel = AppLogger.minimumLevel;
// Then the configured minimum level is warning.
expect(result.exitCode, 0);
expect(minimumLevel, AppLogLevel.warning);
});
/// ```gherkin
/// Scenario: Reject an invalid CLI log level
/// Given the operator passes --log-level verbose
/// When the CLI validates the value
/// Then the command exits with a non-zero status and prints allowed values
/// ```
test('Reject an invalid CLI log level', () async {
// Given the operator passes --log-level verbose.
final result = await runCli(['--log-level', 'verbose']);
// When the CLI validates the value.
final output = result.stdout;
// Then the command exits with a non-zero status and prints allowed values.
expect(result.exitCode, isNonZero);
expect(output, contains('Invalid value for --log-level "verbose".'));
expect(
output,
contains('Allowed values: trace, debug, info, warn, error.'),
);
});
/// ```gherkin
/// Scenario: CLI log level overrides environment variables
/// Given LOG_LEVEL is set to error in the environment
/// When the operator passes --log-level debug
/// Then the minimum logging level is debug
/// ```
test('CLI log level overrides environment variables', () async {
// Given LOG_LEVEL is set to error in the environment.
AppEnvironment.resetForTest(const <String, String>{'LOG_LEVEL': 'error'});
// When the operator passes --log-level debug.
final result = await runCli(['--log-level', 'debug', '--help']);
// Then the minimum logging level is debug.
expect(result.exitCode, 0);
expect(AppLogger.minimumLevel, AppLogLevel.debug);
});
/// ```gherkin
/// Scenario: Default log level falls back to info
/// Given no CLI or environment log level is configured
/// When the CLI initializes logging
/// Then the minimum logging level is info
/// ```
test('Default log level falls back to info', () async {
// Given no CLI or environment log level is configured.
AppEnvironment.resetForTest(const <String, String>{});
// When the CLI initializes logging.
final result = await runCli(['--help']);
// Then the minimum logging level is info.
expect(result.exitCode, 0);
expect(AppLogger.minimumLevel, AppLogLevel.info);
});
});
}