diff --git a/clickhouse_options.go b/clickhouse_options.go index a18101a594..256b220ea9 100644 --- a/clickhouse_options.go +++ b/clickhouse_options.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "net/url" + "os" "strconv" "strings" "time" @@ -438,14 +439,22 @@ func (o Options) setDefaults() *Options { // logger returns the appropriate logger based on the Options configuration. // Priority order: // 1. If Debug=true and Debugf is set, use legacy Debugf (backward compatibility) -// 2. If Logger is set, use the provided logger -// 3. Otherwise, use a noop logger (no logging) +// 2. If Debug=true but no Debugf provided, use default stdout logger +// 3. If Logger is set, use the provided logger +// 4. Otherwise, use a noop logger (no logging) func (o *Options) logger() *slog.Logger { // Backward compatibility: if legacy Debug/Debugf is set, use it if o.Debug && o.Debugf != nil { return newDebugfLogger(o.Debugf) } + // If Debug=true but no Debugf provided, use default stdout logger + if o.Debug { + return slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelDebug, + })) + } + // If user provided a custom logger, use it if o.Logger != nil { return o.Logger diff --git a/clickhouse_options_test.go b/clickhouse_options_test.go index b3abd5d31c..5b95eeba50 100644 --- a/clickhouse_options_test.go +++ b/clickhouse_options_test.go @@ -2,7 +2,9 @@ package clickhouse import ( "crypto/tls" + "log/slog" "net/url" + "os" "testing" "time" @@ -584,3 +586,47 @@ func parseURL(t *testing.T, v string) *url.URL { require.NoError(t, err) return u } + +func TestLogger(t *testing.T) { + t.Run("debug=1 via DSN produces non-noop logger", func(t *testing.T) { + opts, err := ParseDSN("clickhouse://127.0.0.1/test?debug=1") + require.NoError(t, err) + require.True(t, opts.Debug) + + logger := opts.logger() + require.NotNil(t, logger) + // Verify it's not a noop logger by checking it has a handler + // The handler should not be *noopHandler + _, isNoop := logger.Handler().(*noopHandler) + assert.False(t, isNoop, "expected non-noop logger when debug=1") + }) + + t.Run("debug=false produces noop logger", func(t *testing.T) { + opts, err := ParseDSN("clickhouse://127.0.0.1/test") + require.NoError(t, err) + require.False(t, opts.Debug) + + logger := opts.logger() + require.NotNil(t, logger) + _, isNoop := logger.Handler().(*noopHandler) + assert.True(t, isNoop, "expected noop logger when debug is not set") + }) + + t.Run("debug=true with Debugf uses legacy handler", func(t *testing.T) { + var logged string + opts := &Options{Debug: true, Debugf: func(format string, v ...any) { + logged = format + }} + logger := opts.logger() + require.NotNil(t, logger) + logger.Debug("test message") + assert.Equal(t, "%s", logged, "legacy handler uses %s format") + }) + + t.Run("custom Logger is used when provided", func(t *testing.T) { + customLogger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelWarn})) + opts := &Options{Logger: customLogger} + logger := opts.logger() + assert.Equal(t, customLogger, logger) + }) +}