From 4c25a81b036ddb5996052b0850f1d2d78e5f0b54 Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Wed, 20 May 2026 10:06:51 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20logger=20config=20presets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presets.mbt | 5 ++- src/presets_test.mbt | 77 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/presets.mbt b/src/presets.mbt index 585abf9..026285e 100644 --- a/src/presets.mbt +++ b/src/presets.mbt @@ -47,7 +47,10 @@ pub fn file( auto_flush~ : Bool = true, rotation~ : FileRotation? = None, text_formatter~ : TextFormatterConfig = default_text_formatter_config(), -) -> LoggerConfig { +) -> LoggerConfig raise ConfigError { + if path == "" { + raise ConfigError::InvalidConfig("File sink requires non-empty path") + } LoggerConfig::new( min_level=min_level, target=target, diff --git a/src/presets_test.mbt b/src/presets_test.mbt index a9f5192..189de4a 100644 --- a/src/presets_test.mbt +++ b/src/presets_test.mbt @@ -41,14 +41,22 @@ test "file preset uses file sink defaults" { inspect(config.queue is None, content="true") } -test "file preset preserves empty path as configured" { - let config = file("") - inspect(match config.sink.kind { - SinkKind::File => "File" - _ => "other" - }, content="File") - inspect(config.sink.path, content="") - inspect(config.sink.rotation is None, content="true") +test "file preset rejects empty path like parser file sink config" { + let preset_error = (fn() -> String raise ConfigError { + ignore(file("")) + "no error" + })() catch { + ConfigError::InvalidConfig(message) => message + } + inspect(preset_error, content="File sink requires non-empty path") + + let parser_error = (fn() -> String raise ConfigError { + ignore(parse_logger_config_text("{\"sink\":{\"kind\":\"file\",\"path\":\"\"}}")) + "no error" + })() catch { + ConfigError::InvalidConfig(message) => message + } + inspect(parser_error, content="File sink requires non-empty path") } test "preset helpers compose queue and file rotation without losing config" { @@ -94,6 +102,33 @@ test "preset helpers compose queue and file rotation without losing config" { } } +test "queue helper preserves file rotation when applied after rotation" { + let config = with_queue( + with_file_rotation(file("service.log", append=false), 256, max_backups=2), + max_pending=8, + overflow=QueueOverflowPolicy::DropNewest, + ) + inspect(config.sink.path, content="service.log") + inspect(config.sink.append, content="false") + match config.queue { + Some(queue) => { + inspect(queue.max_pending, content="8") + inspect(match queue.overflow { + QueueOverflowPolicy::DropNewest => "DropNewest" + QueueOverflowPolicy::DropOldest => "DropOldest" + }, content="DropNewest") + } + None => inspect(false, content="true") + } + match config.sink.rotation { + Some(rotation) => { + inspect(rotation.max_bytes, content="256") + inspect(rotation.max_backups, content="2") + } + None => inspect(false, content="true") + } +} + test "file rotation helper leaves non-file presets unchanged" { let console_config = with_file_rotation(console(target="console"), 64, max_backups=2) inspect(match console_config.sink.kind { @@ -116,3 +151,29 @@ test "file rotation helper leaves non-file presets unchanged" { inspect(text_config.sink.text_formatter.separator, content=" :: ") inspect(text_config.sink.rotation is None, content="true") } + +test "file rotation helper keeps queued non-file presets unchanged" { + let config = with_queue( + json_console(min_level=Level::Error, target="json"), + max_pending=4, + overflow=QueueOverflowPolicy::DropOldest, + ) + let rotated = with_file_rotation(config, 512, max_backups=5) + inspect(rotated.min_level.label(), content="ERROR") + inspect(rotated.target, content="json") + inspect(match rotated.sink.kind { + SinkKind::JsonConsole => "JsonConsole" + _ => "other" + }, content="JsonConsole") + inspect(rotated.sink.rotation is None, content="true") + match rotated.queue { + Some(queue) => { + inspect(queue.max_pending, content="4") + inspect(match queue.overflow { + QueueOverflowPolicy::DropNewest => "DropNewest" + QueueOverflowPolicy::DropOldest => "DropOldest" + }, content="DropOldest") + } + None => inspect(false, content="true") + } +}