feat: add log output destinations (--log-outputs) and file rotation - #166
Merged
JoshVanL merged 11 commits intoSep 3, 2026
Merged
Conversation
javier-aliaga
previously approved these changes
Sep 1, 2026
JoshVanL
requested changes
Sep 1, 2026
Contributor
|
@nelson-parente please resolve conflicts |
When --log-file is set the logger currently replaces the console writer, so logs stop appearing in kubectl logs. --log-file-tee keeps both destinations via io.MultiWriter. The console writer is listed first because io.MultiWriter stops at the first failed writer: this way console output survives file write failures such as a full disk. Default is false, so existing behaviour is unchanged. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
--log-file opens the file in append mode and never rolls it, so long-lived components grow it without bound. This adds rotation via lumberjack behind four new flags: --log-file-max-size megabytes before rotation --log-file-max-backups rotated files to keep --log-file-max-age days to retain rotated files --log-file-compress gzip rotated files When none of them is set the writer stays a plain append-mode *os.File, so existing behaviour is byte-for-byte unchanged. The three numeric options are string-typed and parsed at apply time so that AttachCmdFlags keeps its (stringVar, boolVar) signature. That keeps the kit bump non-breaking and surfaces the flags on every binary with no caller changes. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
t.Cleanup runs LIFO. The tee tests registered their cleanup before calling t.TempDir, so TempDir's RemoveAll ran first — while the log file was still open — and Windows cannot delete an open file: TempDir RemoveAll cleanup: unlinkat ...\dapr.log: The process cannot access the file because it is being used by another process. Calling t.TempDir first registers its cleanup first, so it runs last, after the cleanup that closes the file. The rotation test already had this ordering, which is why only the two tee tests failed. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
The existing rotation tests asserted only that the lumberjack struct was populated correctly. That would still pass if the rotating writer were never installed as the log output, or if MaxSize were interpreted in the wrong unit, so it verified configuration rather than behaviour. Adds three gaps: - TestFileRotationActuallyRotates drives ~3MB through the configured logger with max-size=1MB and asserts on disk that an archive appeared and the active file was rolled. Only size-based rotation is asserted: lumberjack rotates synchronously on the write that exceeds MaxSize, whereas compression and MaxBackups pruning run on a background goroutine and would make the assertion timing-dependent. - TestOptions/registers_the_exact_set_of_log_flags asserts the full registered flag set rather than spot-checking names. Flag names become D3E chart annotations, so a rename is a breaking change for anyone who has already set them; this makes that fail deliberately rather than silently. - TestTeeWithRotation covers file output that both rotates and tees, which is the combination actually configured in the field. Nothing previously exercised the MultiWriter-wrapping-lumberjack composition. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
…rotation Two behaviours surfaced by review: 1. Setting --log-file-tee or any rotation flag without --log-file was a silent no-op. Now it logs a warning through a dapr.kit.logger logger. Warn rather than fail: an error here would turn a harmless misconfiguration into a startup failure for every binary that attaches these flags. The logger is fetched at the top of ApplyOptionsToLoggers, before the registry snapshot, so it always follows the configured format, level and output for that apply. 2. lumberjack creates missing log files as 0600 (and preserves the mode of existing ones), where the non-rotating path creates 0644. Enabling rotation would therefore silently change new log file permissions and break log shippers tailing the file as a non-owner user. newFileWriter now pre-creates the file with the same flags and mode as the plain path, so permissions are identical whether or not rotation is enabled; rotated archives and post-rotation files inherit that mode. The permission test compares the plain and rotating paths against each other rather than asserting an absolute mode, so it is immune to umask and Windows permission semantics. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
Review feedback: the rotation values are semantically unsigned, so parse them with strconv.ParseUint (bit size 31 keeps the int conversion safe on 32-bit platforms) instead of Atoi plus a sign check. The struct fields stay string-typed because they bind through AttachCmdFlags(stringVar, boolVar); widening that signature would break every existing caller. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
nelson-parente
force-pushed
the
feat/log-file-tee-and-rotation
branch
from
September 2, 2026 13:40
1193833 to
aa64af5
Compare
Rebase onto main after dapr#167 merged: the exact-flag-set test failed as designed on the new flag; register it in the expected set. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
nelson-parente
force-pushed
the
feat/log-file-tee-and-rotation
branch
from
September 2, 2026 14:00
aa64af5 to
23ea94d
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #166 +/- ##
==========================================
+ Coverage 72.37% 72.80% +0.42%
==========================================
Files 92 92
Lines 6537 6666 +129
==========================================
+ Hits 4731 4853 +122
- Misses 1617 1622 +5
- Partials 189 191 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ession enum Review feedback: the optional fields are now properly typed and unexported — *uint rotation limits (nil = not provided) and a none|gzip compression enum — with the CLI flags attached to separate unexported string receivers that validate() parses at apply time, mirroring the pattern in dapr/dapr cmd/daprd/options. AttachCmdFlags keeps its (stringVar, boolVar) signature, so no caller changes anywhere. --log-file-compress (bool) becomes --log-file-compression=none|gzip, which leaves room for other codecs without a flag break. Invalid values fail validation before any logger is mutated. Behaviour is unchanged: an explicit 0 still disables the corresponding limit, and with no rotation engaged the writer stays the plain append-mode file. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
nelson-parente
force-pushed
the
feat/log-file-tee-and-rotation
branch
from
September 2, 2026 14:17
b532821 to
662b755
Compare
Review feedback: rather than a tee bool, log destinations are a list. --log-outputs takes a comma-separated list of "stdout", "stderr", or file paths; --log-file merges into that list, so both flags compose and existing behaviour is unchanged. The previous tee semantics are expressed as --log-outputs=stdout,/path/to/file. Destinations are deduplicated (two writers on one path would double every line and corrupt rotation) and console destinations order before files, so io.MultiWriter keeps console output alive when file writes start failing. Rotation and compression options apply to every file destination. The inert-option warning now keys on "no file destination configured" rather than --log-file specifically. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
…e open-failure unwind Pre-merge review findings: - The three rotation usage strings still said "No effect without --log-file", but the actual gating (and the warning) treat a file entry in --log-outputs as an equally valid destination. All four file-option usage strings now say "a file destination". - The mid-list open-failure unwind in setLogOutput (close already-opened files, leave every logger on its previous output) had no test. Added one that applies destinations [stdout, <directory>] and asserts the apply errors while loggers keep writing to their previous output. - File paths are normalized with filepath.Clean before deduplication, so the same file spelled differently (./x.log vs x.log) resolves to a single writer instead of two writers corrupting rotation. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
File paths in the destination list pass through filepath.Clean, whose separator differs by OS. The ordering/dedupe test hardcoded the Unix form and failed on Windows (\var\log\a.log vs /var/log/a.log); compare against the cleaned form so the assertion is platform-correct. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
JoshVanL
approved these changes
Sep 3, 2026
nelson-parente
added a commit
to nelson-parente/kit
that referenced
this pull request
Sep 3, 2026
…apr#166) * feat: add --log-file-tee to write logs to both file and console When --log-file is set the logger currently replaces the console writer, so logs stop appearing in kubectl logs. --log-file-tee keeps both destinations via io.MultiWriter. The console writer is listed first because io.MultiWriter stops at the first failed writer: this way console output survives file write failures such as a full disk. Default is false, so existing behaviour is unchanged. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * feat: add size/age-based rotation for --log-file output --log-file opens the file in append mode and never rolls it, so long-lived components grow it without bound. This adds rotation via lumberjack behind four new flags: --log-file-max-size megabytes before rotation --log-file-max-backups rotated files to keep --log-file-max-age days to retain rotated files --log-file-compress gzip rotated files When none of them is set the writer stays a plain append-mode *os.File, so existing behaviour is byte-for-byte unchanged. The three numeric options are string-typed and parsed at apply time so that AttachCmdFlags keeps its (stringVar, boolVar) signature. That keeps the kit bump non-breaking and surfaces the flags on every binary with no caller changes. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * fix(test): call t.TempDir before registering the tee cleanup t.Cleanup runs LIFO. The tee tests registered their cleanup before calling t.TempDir, so TempDir's RemoveAll ran first — while the log file was still open — and Windows cannot delete an open file: TempDir RemoveAll cleanup: unlinkat ...\dapr.log: The process cannot access the file because it is being used by another process. Calling t.TempDir first registers its cleanup first, so it runs last, after the cleanup that closes the file. The rotation test already had this ordering, which is why only the two tee tests failed. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * test: cover rotation behaviour, flag registration and tee+rotation The existing rotation tests asserted only that the lumberjack struct was populated correctly. That would still pass if the rotating writer were never installed as the log output, or if MaxSize were interpreted in the wrong unit, so it verified configuration rather than behaviour. Adds three gaps: - TestFileRotationActuallyRotates drives ~3MB through the configured logger with max-size=1MB and asserts on disk that an archive appeared and the active file was rolled. Only size-based rotation is asserted: lumberjack rotates synchronously on the write that exceeds MaxSize, whereas compression and MaxBackups pruning run on a background goroutine and would make the assertion timing-dependent. - TestOptions/registers_the_exact_set_of_log_flags asserts the full registered flag set rather than spot-checking names. Flag names become D3E chart annotations, so a rename is a breaking change for anyone who has already set them; this makes that fail deliberately rather than silently. - TestTeeWithRotation covers file output that both rotates and tees, which is the combination actually configured in the field. Nothing previously exercised the MultiWriter-wrapping-lumberjack composition. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * feat: warn on inert file options; keep file permissions stable under rotation Two behaviours surfaced by review: 1. Setting --log-file-tee or any rotation flag without --log-file was a silent no-op. Now it logs a warning through a dapr.kit.logger logger. Warn rather than fail: an error here would turn a harmless misconfiguration into a startup failure for every binary that attaches these flags. The logger is fetched at the top of ApplyOptionsToLoggers, before the registry snapshot, so it always follows the configured format, level and output for that apply. 2. lumberjack creates missing log files as 0600 (and preserves the mode of existing ones), where the non-rotating path creates 0644. Enabling rotation would therefore silently change new log file permissions and break log shippers tailing the file as a non-owner user. newFileWriter now pre-creates the file with the same flags and mode as the plain path, so permissions are identical whether or not rotation is enabled; rotated archives and post-rotation files inherit that mode. The permission test compares the plain and rotating paths against each other rather than asserting an absolute mode, so it is immune to umask and Windows permission semantics. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * refactor: parse rotation values with ParseUint Review feedback: the rotation values are semantically unsigned, so parse them with strconv.ParseUint (bit size 31 keeps the int conversion safe on 32-bit platforms) instead of Atoi plus a sign check. The struct fields stay string-typed because they bind through AttachCmdFlags(stringVar, boolVar); widening that signature would break every existing caller. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * test: include log-timestamp-format in the exact flag set Rebase onto main after dapr#167 merged: the exact-flag-set test failed as designed on the new flag; register it in the expected set. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * refactor: typed optional fields behind separate flag receivers; compression enum Review feedback: the optional fields are now properly typed and unexported — *uint rotation limits (nil = not provided) and a none|gzip compression enum — with the CLI flags attached to separate unexported string receivers that validate() parses at apply time, mirroring the pattern in dapr/dapr cmd/daprd/options. AttachCmdFlags keeps its (stringVar, boolVar) signature, so no caller changes anywhere. --log-file-compress (bool) becomes --log-file-compression=none|gzip, which leaves room for other codecs without a flag break. Invalid values fail validation before any logger is mutated. Behaviour is unchanged: an explicit 0 still disables the corresponding limit, and with no rotation engaged the writer stays the plain append-mode file. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * feat: replace --log-file-tee with a --log-outputs destination list Review feedback: rather than a tee bool, log destinations are a list. --log-outputs takes a comma-separated list of "stdout", "stderr", or file paths; --log-file merges into that list, so both flags compose and existing behaviour is unchanged. The previous tee semantics are expressed as --log-outputs=stdout,/path/to/file. Destinations are deduplicated (two writers on one path would double every line and corrupt rotation) and console destinations order before files, so io.MultiWriter keeps console output alive when file writes start failing. Rotation and compression options apply to every file destination. The inert-option warning now keys on "no file destination configured" rather than --log-file specifically. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * fix: align rotation usage strings with the destination model; test the open-failure unwind Pre-merge review findings: - The three rotation usage strings still said "No effect without --log-file", but the actual gating (and the warning) treat a file entry in --log-outputs as an equally valid destination. All four file-option usage strings now say "a file destination". - The mid-list open-failure unwind in setLogOutput (close already-opened files, leave every logger on its previous output) had no test. Added one that applies destinations [stdout, <directory>] and asserts the apply errors while loggers keep writing to their previous output. - File paths are normalized with filepath.Clean before deduplication, so the same file spelled differently (./x.log vs x.log) resolves to a single writer instead of two writers corrupting rotation. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * fix(test): compare destinations against the filepath.Clean form File paths in the destination list pass through filepath.Clean, whose separator differs by OS. The ordering/dedupe test hardcoded the Unix form and failed on Windows (\var\log\a.log vs /var/log/a.log); compare against the cleaned form so the assertion is platform-correct. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> --------- Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
nelson-parente
added a commit
to nelson-parente/kit
that referenced
this pull request
Sep 3, 2026
…apr#166) * feat: add --log-file-tee to write logs to both file and console When --log-file is set the logger currently replaces the console writer, so logs stop appearing in kubectl logs. --log-file-tee keeps both destinations via io.MultiWriter. The console writer is listed first because io.MultiWriter stops at the first failed writer: this way console output survives file write failures such as a full disk. Default is false, so existing behaviour is unchanged. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * feat: add size/age-based rotation for --log-file output --log-file opens the file in append mode and never rolls it, so long-lived components grow it without bound. This adds rotation via lumberjack behind four new flags: --log-file-max-size megabytes before rotation --log-file-max-backups rotated files to keep --log-file-max-age days to retain rotated files --log-file-compress gzip rotated files When none of them is set the writer stays a plain append-mode *os.File, so existing behaviour is byte-for-byte unchanged. The three numeric options are string-typed and parsed at apply time so that AttachCmdFlags keeps its (stringVar, boolVar) signature. That keeps the kit bump non-breaking and surfaces the flags on every binary with no caller changes. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * fix(test): call t.TempDir before registering the tee cleanup t.Cleanup runs LIFO. The tee tests registered their cleanup before calling t.TempDir, so TempDir's RemoveAll ran first — while the log file was still open — and Windows cannot delete an open file: TempDir RemoveAll cleanup: unlinkat ...\dapr.log: The process cannot access the file because it is being used by another process. Calling t.TempDir first registers its cleanup first, so it runs last, after the cleanup that closes the file. The rotation test already had this ordering, which is why only the two tee tests failed. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * test: cover rotation behaviour, flag registration and tee+rotation The existing rotation tests asserted only that the lumberjack struct was populated correctly. That would still pass if the rotating writer were never installed as the log output, or if MaxSize were interpreted in the wrong unit, so it verified configuration rather than behaviour. Adds three gaps: - TestFileRotationActuallyRotates drives ~3MB through the configured logger with max-size=1MB and asserts on disk that an archive appeared and the active file was rolled. Only size-based rotation is asserted: lumberjack rotates synchronously on the write that exceeds MaxSize, whereas compression and MaxBackups pruning run on a background goroutine and would make the assertion timing-dependent. - TestOptions/registers_the_exact_set_of_log_flags asserts the full registered flag set rather than spot-checking names. Flag names become D3E chart annotations, so a rename is a breaking change for anyone who has already set them; this makes that fail deliberately rather than silently. - TestTeeWithRotation covers file output that both rotates and tees, which is the combination actually configured in the field. Nothing previously exercised the MultiWriter-wrapping-lumberjack composition. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * feat: warn on inert file options; keep file permissions stable under rotation Two behaviours surfaced by review: 1. Setting --log-file-tee or any rotation flag without --log-file was a silent no-op. Now it logs a warning through a dapr.kit.logger logger. Warn rather than fail: an error here would turn a harmless misconfiguration into a startup failure for every binary that attaches these flags. The logger is fetched at the top of ApplyOptionsToLoggers, before the registry snapshot, so it always follows the configured format, level and output for that apply. 2. lumberjack creates missing log files as 0600 (and preserves the mode of existing ones), where the non-rotating path creates 0644. Enabling rotation would therefore silently change new log file permissions and break log shippers tailing the file as a non-owner user. newFileWriter now pre-creates the file with the same flags and mode as the plain path, so permissions are identical whether or not rotation is enabled; rotated archives and post-rotation files inherit that mode. The permission test compares the plain and rotating paths against each other rather than asserting an absolute mode, so it is immune to umask and Windows permission semantics. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * refactor: parse rotation values with ParseUint Review feedback: the rotation values are semantically unsigned, so parse them with strconv.ParseUint (bit size 31 keeps the int conversion safe on 32-bit platforms) instead of Atoi plus a sign check. The struct fields stay string-typed because they bind through AttachCmdFlags(stringVar, boolVar); widening that signature would break every existing caller. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * test: include log-timestamp-format in the exact flag set Rebase onto main after dapr#167 merged: the exact-flag-set test failed as designed on the new flag; register it in the expected set. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * refactor: typed optional fields behind separate flag receivers; compression enum Review feedback: the optional fields are now properly typed and unexported — *uint rotation limits (nil = not provided) and a none|gzip compression enum — with the CLI flags attached to separate unexported string receivers that validate() parses at apply time, mirroring the pattern in dapr/dapr cmd/daprd/options. AttachCmdFlags keeps its (stringVar, boolVar) signature, so no caller changes anywhere. --log-file-compress (bool) becomes --log-file-compression=none|gzip, which leaves room for other codecs without a flag break. Invalid values fail validation before any logger is mutated. Behaviour is unchanged: an explicit 0 still disables the corresponding limit, and with no rotation engaged the writer stays the plain append-mode file. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * feat: replace --log-file-tee with a --log-outputs destination list Review feedback: rather than a tee bool, log destinations are a list. --log-outputs takes a comma-separated list of "stdout", "stderr", or file paths; --log-file merges into that list, so both flags compose and existing behaviour is unchanged. The previous tee semantics are expressed as --log-outputs=stdout,/path/to/file. Destinations are deduplicated (two writers on one path would double every line and corrupt rotation) and console destinations order before files, so io.MultiWriter keeps console output alive when file writes start failing. Rotation and compression options apply to every file destination. The inert-option warning now keys on "no file destination configured" rather than --log-file specifically. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * fix: align rotation usage strings with the destination model; test the open-failure unwind Pre-merge review findings: - The three rotation usage strings still said "No effect without --log-file", but the actual gating (and the warning) treat a file entry in --log-outputs as an equally valid destination. All four file-option usage strings now say "a file destination". - The mid-list open-failure unwind in setLogOutput (close already-opened files, leave every logger on its previous output) had no test. Added one that applies destinations [stdout, <directory>] and asserts the apply errors while loggers keep writing to their previous output. - File paths are normalized with filepath.Clean before deduplication, so the same file spelled differently (./x.log vs x.log) resolves to a single writer instead of two writers corrupting rotation. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> * fix(test): compare destinations against the filepath.Clean form File paths in the destination list pass through filepath.Clean, whose separator differs by OS. The ordering/dedupe test hardcoded the Unix form and failed on Windows (\var\log\a.log vs /var/log/a.log); compare against the cleaned form so the assertion is platform-correct. Signed-off-by: Nelson Parente <nelson_parente@live.com.pt> --------- Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds two independent, opt-in capabilities to log output. All new flags default to off/empty, so behaviour with no new flags is unchanged.
1.
--log-outputs— a list of log destinationsPer review, destinations are a list rather than a tee bool:
--log-outputstakes a comma-separated list ofstdout,stderr, or file paths.--log-filemerges into that list, so the two flags compose and existing behaviour is unchanged; the tee use case is--log-outputs=stdout,/path/to/file.Destinations are deduplicated (two writers on one path would double every line and corrupt rotation), and console destinations order before files —
io.MultiWriterstops at the first failing writer, so this keeps console output alive when file writes start failing (a full disk, for example). Rotation and compression apply to every file destination.2.
--log-file-max-*/--log-file-compression— rotation--log-fileopens the fileO_APPENDand never rolls it, so long-lived components grow it without bound. Rotation is delegated tolumberjack:--log-file-max-size--log-file-max-backups--log-file-max-age--log-file-compressionnone(default) orgzipfor rotated filesWhen none is set,
newFileWriterreturns a plain append-mode*os.File— the existing code path, unchanged.File permissions are kept identical with rotation on or off
lumberjack creates missing files as
0600(and preserves the mode of existing ones), where the plain path creates0644. Left alone, enabling rotation would silently change new log-file permissions and break log shippers tailing the file as a non-owner user.newFileWritertherefore pre-creates the file with the same flags and mode as the plain path; rotated archives and post-rotation files inherit that mode. A test asserts mode parity between the two paths (comparing them to each other, so it is immune to umask and Windows permission semantics).Misconfiguration warns instead of silently no-opping
Rotation or compression flags with no file destination configured (neither
--log-filenor a file entry in--log-outputs) now log a warning through adapr.kit.loggerlogger. Warn rather than error: an error here would turn a harmless misconfiguration into a startup failure for every binary that attaches these flags. The logger is fetched at the top ofApplyOptionsToLoggers, before the registry snapshot, so the warning follows the configured format, level and output of that apply.Typed fields behind separate flag receivers
Per review: the optional fields are properly typed and unexported —
*uintrotation limits (nil = flag not provided) and anone|gzipcompression enum — with the CLI flags attached to separate unexported string receivers thatvalidate()parses at apply time, mirroring the pattern in dapr/daprcmd/daprd/options.AttachCmdFlagskeeps its(stringVar, boolVar)signature, so no caller changes anywhere. Invalid values fail validation with a clear error naming the flag, before any logger is mutated.On lumberjack
gopkg.in/natefinch/lumberjack.v2is MIT-licensed and the de-facto Go rotation library (it is what the zap ecosystem uses). It is low-activity because it is feature-complete rather than unmaintained. Behaviours worth knowing, documented alongside the flags:--log-file-max-ageor--log-file-compressionis set, lumberjack applies its own 100MB defaultMaxSizeNotes
setLogOutputnow takes*Optionsrather than a bare path, since it needs more than the path.logOutputFile *os.FilebecomeslogOutputCloser io.Closerso it can close either writer type. The redirect-before-close ordering is preserved.consoleWriteris introduced as a package-levelio.Writer(defaulting toos.Stdout) purely so tests can capture console output.Loggerinterface is untouched.Test plan
go test -race ./logger/...passes, including-count=2for order-independence; logger package coverage ~90%max-size=1MBand asserts on disk that an archive appeared and the active file rolled (deliberately size-based only — compression andMaxBackupspruning run on a background goroutine and would be timing-flaky in CI)golangci-lint run ./logger/...adds zero new findings versusmain(the 9 remaininggoconsthits are pre-existing)concurrencyandevents/ratelimitingfail to build onmaintoday, unrelated to this change