From a61a546e7f87b30bf078a07285f5ce477222b774 Mon Sep 17 00:00:00 2001 From: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:44:39 +0100 Subject: [PATCH 1/4] feat: add OutputFile option for log destination in logger Signed-off-by: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> --- logger/options.go | 9 +++++++++ logger/options_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/logger/options.go b/logger/options.go index 25a15ddb..f033ae9a 100644 --- a/logger/options.go +++ b/logger/options.go @@ -33,6 +33,9 @@ type Options struct { // OutputLevel is the level of logging OutputLevel string + + // OutputFile is the destination file path for logs. + OutputFile string } // SetOutputLevel sets the log output level. @@ -62,6 +65,11 @@ func (o *Options) AttachCmdFlags( "log-level", defaultOutputLevel, "Options are debug, info, warn, error, or fatal (default info)") + stringVar( + &o.OutputFile, + "log-file", + "", + "Path to a file where logs will be written") } if boolVar != nil { @@ -79,6 +87,7 @@ func DefaultOptions() Options { JSONFormatEnabled: defaultJSONOutput, appID: undefinedAppID, OutputLevel: defaultOutputLevel, + OutputFile: "", } } diff --git a/logger/options_test.go b/logger/options_test.go index e16ff89f..24c37b9e 100644 --- a/logger/options_test.go +++ b/logger/options_test.go @@ -14,6 +14,9 @@ limitations under the License. package logger import ( + "os" + "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -26,6 +29,7 @@ func TestOptions(t *testing.T) { assert.Equal(t, defaultJSONOutput, o.JSONFormatEnabled) assert.Equal(t, undefinedAppID, o.appID) assert.Equal(t, defaultOutputLevel, o.OutputLevel) + assert.Equal(t, "", o.OutputFile) }) t.Run("set dapr ID", func(t *testing.T) { @@ -40,10 +44,14 @@ func TestOptions(t *testing.T) { o := DefaultOptions() logLevelAsserted := false + logFileAsserted := false testStringVarFn := func(p *string, name string, value string, usage string) { if name == "log-level" && value == defaultOutputLevel { logLevelAsserted = true } + if name == "log-file" && value == "" { + logFileAsserted = true + } } logAsJSONAsserted := false @@ -57,6 +65,7 @@ func TestOptions(t *testing.T) { // assert assert.True(t, logLevelAsserted) + assert.True(t, logFileAsserted) assert.True(t, logAsJSONAsserted) }) } @@ -92,3 +101,28 @@ func TestApplyOptionsToLoggers(t *testing.T) { (l.(*daprLogger)).logger.Logger.GetLevel()) } } + +func TestApplyOptionsToLoggersFileOutput(t *testing.T) { + logPath := filepath.Join(t.TempDir(), "dapr.log") + + testOptions := Options{ + OutputLevel: "debug", + OutputFile: logPath, + } + + l := NewLogger("testLoggerFileOutput") + require.NoError(t, ApplyOptionsToLoggers(&testOptions)) + + dl, ok := l.(*daprLogger) + require.True(t, ok) + fileOut, ok := dl.logger.Logger.Out.(*os.File) + require.True(t, ok) + assert.Equal(t, logPath, fileOut.Name()) + + msg := "log-file-test-message" + l.Info(msg) + + b, err := os.ReadFile(logPath) + require.NoError(t, err) + assert.True(t, strings.Contains(string(b), msg)) +} From ef1d4d27e8eb90b7f5cb9aab8dc28dbf2a8a2d12 Mon Sep 17 00:00:00 2001 From: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:11:54 +0200 Subject: [PATCH 2/4] feat: add file output support for logging Signed-off-by: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> --- logger/options.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/logger/options.go b/logger/options.go index f033ae9a..60c2a200 100644 --- a/logger/options.go +++ b/logger/options.go @@ -15,6 +15,7 @@ package logger import ( "fmt" + "os" ) const ( @@ -113,5 +114,14 @@ func ApplyOptionsToLoggers(options *Options) error { v.SetOutputLevel(daprLogLevel) } + if options.OutputFile != "" { + file, err := os.OpenFile(options.OutputFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600) + if err != nil { + return fmt.Errorf("failed to open log file %q: %w", options.OutputFile, err) + } + for _, v := range internalLoggers { + v.SetOutput(file) + } + } return nil } From 74ae6dff7b3ce05d98170d85f069045a4f28ce3c Mon Sep 17 00:00:00 2001 From: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:18:21 +0200 Subject: [PATCH 3/4] lint Signed-off-by: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> --- logger/options.go | 2 ++ logger/options_test.go | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/logger/options.go b/logger/options.go index 60c2a200..9ac3acb8 100644 --- a/logger/options.go +++ b/logger/options.go @@ -119,9 +119,11 @@ func ApplyOptionsToLoggers(options *Options) error { if err != nil { return fmt.Errorf("failed to open log file %q: %w", options.OutputFile, err) } + for _, v := range internalLoggers { v.SetOutput(file) } } + return nil } diff --git a/logger/options_test.go b/logger/options_test.go index 24c37b9e..d9082365 100644 --- a/logger/options_test.go +++ b/logger/options_test.go @@ -16,7 +16,6 @@ package logger import ( "os" "path/filepath" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -29,7 +28,7 @@ func TestOptions(t *testing.T) { assert.Equal(t, defaultJSONOutput, o.JSONFormatEnabled) assert.Equal(t, undefinedAppID, o.appID) assert.Equal(t, defaultOutputLevel, o.OutputLevel) - assert.Equal(t, "", o.OutputFile) + assert.Empty(t, o.OutputFile) }) t.Run("set dapr ID", func(t *testing.T) { @@ -49,6 +48,7 @@ func TestOptions(t *testing.T) { if name == "log-level" && value == defaultOutputLevel { logLevelAsserted = true } + if name == "log-file" && value == "" { logFileAsserted = true } @@ -111,6 +111,7 @@ func TestApplyOptionsToLoggersFileOutput(t *testing.T) { } l := NewLogger("testLoggerFileOutput") + require.NoError(t, ApplyOptionsToLoggers(&testOptions)) dl, ok := l.(*daprLogger) @@ -124,5 +125,5 @@ func TestApplyOptionsToLoggersFileOutput(t *testing.T) { b, err := os.ReadFile(logPath) require.NoError(t, err) - assert.True(t, strings.Contains(string(b), msg)) + assert.Contains(t, string(b), msg) } From efa95122f050ae082ae356734e3d6dbf353af88a Mon Sep 17 00:00:00 2001 From: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:23:20 +0200 Subject: [PATCH 4/4] add cleanup for file output logger in options tests Signed-off-by: MyMirelHub <15373565+MyMirelHub@users.noreply.github.com> --- logger/options_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/logger/options_test.go b/logger/options_test.go index d9082365..1c329329 100644 --- a/logger/options_test.go +++ b/logger/options_test.go @@ -119,6 +119,13 @@ func TestApplyOptionsToLoggersFileOutput(t *testing.T) { fileOut, ok := dl.logger.Logger.Out.(*os.File) require.True(t, ok) assert.Equal(t, logPath, fileOut.Name()) + t.Cleanup(func() { + for _, logger := range getLoggers() { + logger.SetOutput(os.Stdout) + } + + require.NoError(t, fileOut.Close()) + }) msg := "log-file-test-message" l.Info(msg)