feat: add --log-level CLI flag (#54)
This commit is contained in:
parent
cbd0305a39
commit
55702db014
|
|
@ -32,9 +32,9 @@ Create `CWS_conf.yaml` using:
|
||||||
Environment placeholders (for example `${CWS_DISCORD_WEBHOOK}`) are resolved
|
Environment placeholders (for example `${CWS_DISCORD_WEBHOOK}`) are resolved
|
||||||
from process env vars and from a `.env` file next to your config.
|
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`,
|
Set logging with `--log-level trace|debug|info|warn|error` or env
|
||||||
`debug`, `info`, `warning`, `error`, `fatal`. Minimal example:
|
`CWS_LOG_LEVEL` / `LOG_LEVEL`. Precedence: CLI > env > default `info`.
|
||||||
[.env.example](.env.example).
|
Minimal env example: [.env.example](.env.example).
|
||||||
|
|
||||||
Generate starter config:
|
Generate starter config:
|
||||||
|
|
||||||
|
|
@ -64,6 +64,8 @@ Run continuously:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dart run bin/code_work_spawner.dart
|
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:
|
Override CLI paths if needed:
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,10 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
|
||||||
'gosmee-command',
|
'gosmee-command',
|
||||||
help: 'Path to the gosmee executable.',
|
help: 'Path to the gosmee executable.',
|
||||||
defaultsTo: 'gosmee',
|
defaultsTo: 'gosmee',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'log-level',
|
||||||
|
help: 'Minimum log level: trace, debug, info, warn, error.',
|
||||||
);
|
);
|
||||||
|
|
||||||
addCommand(_InitConfigCommand());
|
addCommand(_InitConfigCommand());
|
||||||
|
|
@ -64,6 +68,14 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> runCommand(ArgResults topLevelResults) async {
|
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) {
|
if (topLevelResults['help'] as bool) {
|
||||||
stdout.writeln(usage);
|
stdout.writeln(usage);
|
||||||
return;
|
return;
|
||||||
|
|
@ -76,7 +88,10 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
|
||||||
|
|
||||||
final configPath = topLevelResults['config'] as String;
|
final configPath = topLevelResults['config'] as String;
|
||||||
AppEnvironment.loadForConfig(configPath);
|
AppEnvironment.loadForConfig(configPath);
|
||||||
_configureLogging();
|
_configureLogging(
|
||||||
|
cliMinimumLevel: cliMinimumLevel,
|
||||||
|
environment: AppEnvironment.variables,
|
||||||
|
);
|
||||||
|
|
||||||
final config = await AppConfig.load(configPath);
|
final config = await AppConfig.load(configPath);
|
||||||
final logger = AppLogger('code_work_spawner.cli');
|
final logger = AppLogger('code_work_spawner.cli');
|
||||||
|
|
@ -124,6 +139,21 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
|
||||||
await app.close();
|
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) {
|
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(
|
AppLogger.configure(
|
||||||
minimumLevel: AppLogger.resolveMinimumLevel(AppEnvironment.variables),
|
minimumLevel:
|
||||||
|
cliMinimumLevel ??
|
||||||
|
AppLogger.resolveMinimumLevel(environment, fallback: AppLogLevel.info),
|
||||||
useColors: stdout.hasTerminal,
|
useColors: stdout.hasTerminal,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue