Andy Pan has uploaded this change for review.
log/slog: add SetDefaultWithLevel to enable setting level on the default logger in log
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
M src/log/slog/logger.go
M src/log/slog/logger_test.go
3 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/api/next/62418.txt b/api/next/62418.txt
new file mode 100644
index 0000000..7ad977d
--- /dev/null
+++ b/api/next/62418.txt
@@ -0,0 +1 @@
+pkg log/slog, func SetDefaultWithLevel(*Logger, Level) #62418
diff --git a/src/log/slog/logger.go b/src/log/slog/logger.go
index a42b0a4..3f74e7b 100644
--- a/src/log/slog/logger.go
+++ b/src/log/slog/logger.go
@@ -27,6 +27,16 @@
// After this call, output from the log package's default Logger
// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler.
func SetDefault(l *Logger) {
+ setDefault(l, LevelInfo)
+}
+
+// SetDefaultWithLevel is like SetDefault, but uses the given level
+// instead of LevelInfo for log package.
+func SetDefaultWithLevel(l *Logger, lvl Level) {
+ setDefault(l, lvl)
+}
+
+func setDefault(l *Logger, lvl Level) {
defaultLogger.Store(l)
// If the default's handler is a defaultHandler, then don't use a handleWriter,
// or we'll deadlock as they both try to acquire the log default mutex.
@@ -36,7 +46,7 @@
// See TestSetDefault.
if _, ok := l.Handler().(*defaultHandler); !ok {
capturePC := log.Flags()&(log.Lshortfile|log.Llongfile) != 0
- log.SetOutput(&handlerWriter{l.Handler(), LevelInfo, capturePC})
+ log.SetOutput(&handlerWriter{l.Handler(), lvl, capturePC})
log.SetFlags(0) // we want just the log message, no time or location
}
}
diff --git a/src/log/slog/logger_test.go b/src/log/slog/logger_test.go
index 7d0243e..65a1bcf 100644
--- a/src/log/slog/logger_test.go
+++ b/src/log/slog/logger_test.go
@@ -357,6 +357,55 @@
}
}
+func TestSetDefaultWithLevel(t *testing.T) {
+ removeTime := func(_ []string, a Attr) Attr {
+ if a.Key == TimeKey {
+ return Attr{}
+ }
+ return a
+ }
+
+ var logbuf bytes.Buffer
+ // Revert any changes to the default logger. This is important because other
+ // tests might change the default logger using SetDefault. Also ensure we
+ // restore the default logger at the end of the test.
+ currentLogger := Default()
+ t.Cleanup(func() {
+ SetDefault(currentLogger)
+ })
+
+ for _, test := range []struct {
+ logLevel Level
+ slogLevel Level
+ msg string
+ want string
+ }{
+ {LevelDebug, LevelDebug, "a", "level=DEBUG msg=a"},
+ {LevelDebug, LevelInfo, "a", ""},
+ {LevelDebug, LevelWarn, "a", ""},
+ {LevelDebug, LevelError, "a", ""},
+ {LevelInfo, LevelInfo, "a", "level=INFO msg=a"},
+ {LevelInfo, LevelDebug, "a", "level=INFO msg=a"},
+ {LevelInfo, LevelWarn, "a", ""},
+ {LevelInfo, LevelError, "a", ""},
+ {LevelWarn, LevelWarn, "a", "level=WARN msg=a"},
+ {LevelWarn, LevelDebug, "a", "level=WARN msg=a"},
+ {LevelWarn, LevelInfo, "a", "level=WARN msg=a"},
+ {LevelWarn, LevelError, "a", ""},
+ {LevelError, LevelError, "a", "level=ERROR msg=a"},
+ {LevelError, LevelDebug, "a", "level=ERROR msg=a"},
+ {LevelError, LevelInfo, "a", "level=ERROR msg=a"},
+ {LevelError, LevelWarn, "a", "level=ERROR msg=a"},
+ } {
+ SetDefaultWithLevel(New(NewTextHandler(&logbuf, &HandlerOptions{
+ ReplaceAttr: removeTime,
+ Level: test.slogLevel})), test.logLevel)
+ log.Print(test.msg)
+ checkLogOutput(t, logbuf.String(), test.want)
+ logbuf.Reset()
+ }
+}
+
func TestLoggerError(t *testing.T) {
var buf bytes.Buffer
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Jonathan Amsterdam.
Patch set 1:Run-TryBot +1
Attention is currently required from: Andy Pan, Jonathan Amsterdam.
Patch set 1:Hold +1
1 comment:
Patchset:
Thanks. On hold for proposal.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan, Jonathan Amsterdam.
Andy Pan uploaded patch set #2 to this change.
The following approvals got outdated and were removed: Run-TryBot+1 by Andy Pan, TryBot-Result+1 by Gopher Robot
log/slog: add SetDefaultWithLevel to enable setting level on the default logger in log
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 191 insertions(+), 6 deletions(-)
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
Andy Pan uploaded patch set #3 to this change.
log/slog: add LogLoggerLevel to enable setting level on the default logger
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 191 insertions(+), 6 deletions(-)
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan.
1 comment:
File src/log/slog/logger.go:
Patch Set #3, Line 62: if !w.h.Enabled(context.Background(), w.level.Level()) {
Store w.level.Level() in a local variable here, to avoid a TOCTOU race with the second call on line 78.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
Patch set 3:Run-TryBot +1
1 comment:
File src/log/slog/logger.go:
Patch Set #3, Line 62: if !w.h.Enabled(context.Background(), w.level.Level()) {
Store w.level. […]
I did intend to do that, but I'm not sure which makes more sense, retaining a snapshot of the initial level for the current record or updating it to the latest level.
You prefer the former?
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
11 comments:
File src/log/slog/example_log_level_test.go:
Patch Set #3, Line 14: // Case 1 from https://go.dev/issues/62418#issuecomment-1709243228
Explain rather than refer to the issue.
Unlike in the test, write the explanation at the user level rather than the implementation level.
You can use my text in the referred comment as a start.
slog.Warn("warn") // WARN warn
slog.Error("error") // ERROR error
I don't think we need these two here or below.
Patch Set #3, Line 50: // Case 2 from https://go.dev/issues/62418#issuecomment-1709243228
ditto
Patch Set #3, Line 62: log.Print("error") // level=ERROR msg=error
Line 54 and this line are the only relevant ones. (And of course the defers.)
File src/log/slog/logger.go:
// LogLoggerLevel is the level at which the log package's default Logger,
// calling LogLoggerLevel.Set to modify the logging level will reflect on
// the subsequent output of the log package's default Logger immediately.
//
// Note that even though this initialized variable is safe for use by
// multiple goroutines, but itself must not be replaced with a new one,
// otherwise data race will occur.
See the doc comment I wrote in the issue.
I'd change your second paragraph to
Call Set on this variable, but do not assign to it.
File src/log/slog/logger_test.go:
Patch Set #3, Line 361: // Test case 1 from https://go.dev/issues/62418#issuecomment-1709243228
Just describe the case without referencing the issue:
// Test defaultHandler minimum level.
Patch Set #3, Line 363: const timeRegex = `\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}`
I think if you set flags to 0, you won't get the time so you won't need this.
Since the message is always "a", you can remove it from the struct and hard-code it below.
{LevelWarn, Debug, "a", ""},
{LevelWarn, Info, "a", ""},
{LevelWarn, Warn, "a", timeRegex + " WARN a"},
{LevelWarn, Error, "a", timeRegex + " ERROR a"},
{LevelError, Debug, "a", ""},
{LevelError, Info, "a", ""},
{LevelError, Warn, "a", ""},
{LevelError, Error, "a", timeRegex + " ERROR a"}
I don't think these are necessary. In fact all you need is a debug and and info call at Debug level and the same two at Info level.
Patch Set #3, Line 409: // Test case 2 from https://go.dev/issues/62418#issuecomment-1709243228
ditto
logLevel Level
slogLevel Level
msg string
want string
}{
{LevelDebug, LevelDebug, "a", "level=DEBUG msg=a"},
{LevelDebug, LevelInfo, "a", ""},
{LevelDebug, LevelWarn, "a", ""},
{LevelDebug, LevelError, "a", ""},
{LevelInfo, LevelInfo, "a", "level=INFO msg=a"},
{LevelInfo, LevelDebug, "a", "level=INFO msg=a"},
{LevelInfo, LevelWarn, "a", ""},
{LevelInfo, LevelError, "a", ""},
{LevelWarn, LevelWarn, "a", "level=WARN msg=a"},
{LevelWarn, LevelDebug, "a", "level=WARN msg=a"},
{LevelWarn, LevelInfo, "a", "level=WARN msg=a"},
{LevelWarn, LevelError, "a", ""},
{LevelError, LevelError, "a", "level=ERROR msg=a"},
{LevelError, LevelDebug, "a", "level=ERROR msg=a"},
{LevelError, LevelInfo, "a", "level=ERROR msg=a"},
{LevelError, LevelWarn, "a", "level=ERROR msg=a"},
} {
LogLoggerLevel.Set(test.logLevel)
SetDefault(New(NewTextHandler(&logBuf, &HandlerOptions{
ReplaceAttr: removeTime,
Level: test.slogLevel})))
log.Print(test.msg)
checkLogOutput(t, logBuf.String(), test.want)
logBuf.Reset()
}
}
I think all we need here is:
set LogLoggerLevel to something other than info
call log.Print
check the level is what we set
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
File src/log/slog/logger.go:
Patch Set #3, Line 62: if !w.h.Enabled(context.Background(), w.level.Level()) {
I did intend to do that, but I'm not sure which makes more sense, retaining a snapshot of the initia […]
Yes, for consistency.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan.
Andy Pan uploaded patch set #4 to this change.
The following approvals got outdated and were removed: Run-TryBot+1 by Andy Pan, TryBot-Result+1 by Gopher Robot
log/slog: add LogLoggerLevel to enable setting level on the default logger
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 148 insertions(+), 6 deletions(-)
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
12 comments:
File src/log/slog/example_log_level_test.go:
Explain rather than refer to the issue. […]
Done
slog.Warn("warn") // WARN warn
slog.Error("error") // ERROR error
I don't think we need these two here or below.
Done
Patch Set #3, Line 50: // Case 2 from https://go.dev/issues/62418#issuecomment-1709243228
ditto
Done
Patch Set #3, Line 62: log.Print("error") // level=ERROR msg=error
Line 54 and this line are the only relevant ones. (And of course the defers. […]
Acutally we need to call slog.SetDefault for case 2.
File src/log/slog/logger.go:
// LogLoggerLevel is the level at which the log package's default Logger,
// calling LogLoggerLevel.Set to modify the logging level will reflect on
// the subsequent output of the log package's default Logger immediately.
//
// Note that even though this initialized variable is safe for use by
// multiple goroutines, but itself must not be replaced with a new one,
// otherwise data race will occur.
See the doc comment I wrote in the issue. […]
Done
Patch Set #3, Line 62: if !w.h.Enabled(context.Background(), w.level.Level()) {
Yes, for consistency.
Done
File src/log/slog/logger_test.go:
Patch Set #3, Line 361: // Test case 1 from https://go.dev/issues/62418#issuecomment-1709243228
Just describe the case without referencing the issue: […]
Done
Patch Set #3, Line 363: const timeRegex = `\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}`
I think if you set flags to 0, you won't get the time so you won't need this.
Done
Since the message is always "a", you can remove it from the struct and hard-code it below.
Done
{LevelWarn, Debug, "a", ""},
{LevelWarn, Info, "a", ""},
{LevelWarn, Warn, "a", timeRegex + " WARN a"},
{LevelWarn, Error, "a", timeRegex + " ERROR a"},
{LevelError, Debug, "a", ""},
{LevelError, Info, "a", ""},
{LevelError, Warn, "a", ""},
{LevelError, Error, "a", timeRegex + " ERROR a"}
I don't think these are necessary. […]
Done
Patch Set #3, Line 409: // Test case 2 from https://go.dev/issues/62418#issuecomment-1709243228
ditto
Done
I think all we need here is: […]
Actually we also need to call `slog.SetDefault` to test case 2.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
Andy Pan uploaded patch set #5 to this change.
log/slog: add LogLoggerLevel to enable setting level on the default logger
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 148 insertions(+), 6 deletions(-)
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Patchset:
The proposal is now accepted. If you'd like to continue this work, please change to match the accepted API (SetLogLoggerLevel function).
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
1 comment:
Patchset:
The proposal is now accepted. […]
Working on it, I'll upload a new patchset later.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
Andy Pan uploaded patch set #6 to this change.
log/slog: add LogLoggerLevel to enable setting level on the default logger
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 158 insertions(+), 6 deletions(-)
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
Patch set 6:Commit-Queue +1
Attention is currently required from: Jonathan Amsterdam.
1 comment:
Patchset:
Any new comments?
Also, I think we should remove the "No-Holds" label from this CL since the corresponding proposal has already been accepted.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan.
4 comments:
File src/log/slog/logger.go:
Patch Set #6, Line 28: // slog.SetLogLoggerLevel(slog.LevelDebug)
Indent this line so it is formatted as a code block.
Patch Set #6, Line 35: // slog.SetLogLoggerLevel(slog.LevelDebug)
ditto
Patch Set #6, Line 36: a structured log
"output"
Patch Set #6, Line 54: logLoggerLevel
needs to change.
Maybe:
"... will be logged using l's Handler, at a level controlled by [SetLogLoggerLevel]."
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan.
Andy Pan uploaded patch set #7 to this change.
The following approvals got outdated and were removed: LUCI-TryBot-Result+1 by Go LUCI
log/slog: add LogLoggerLevel to enable setting level on the default logger
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 163 insertions(+), 6 deletions(-)
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonathan Amsterdam.
Patch set 8:Commit-Queue +1
4 comments:
File src/log/slog/logger.go:
Patch Set #6, Line 28: // slog.SetLogLoggerLevel(slog.LevelDebug)
Indent this line so it is formatted as a code block.
Done
Patch Set #6, Line 35: // slog.SetLogLoggerLevel(slog.LevelDebug)
ditto
Done
Patch Set #6, Line 36: a structured log
"output"
Done
Patch Set #6, Line 54: logLoggerLevel
needs to change. […]
Done
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan.
Patch set 8:Code-Review +2
1 comment:
Patchset:
Thanks, great work!
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan, Ian Lance Taylor.
Andy Pan removed a vote from this change.
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Andy Pan, Ian Lance Taylor.
Patch set 8:Code-Review +1
Jonathan Amsterdam submitted this change.
log/slog: add LogLoggerLevel to enable setting level on the default logger
Fixes #62418
Change-Id: I889a53d00c8a463b4d7ddb41893c000d7cd0e7b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/525096
LUCI-TryBot-Result: Go LUCI <golang...@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Heschi Kreinick <hes...@google.com>
Reviewed-by: Jonathan Amsterdam <j...@google.com>
---
A api/next/62418.txt
A src/log/slog/example_log_level_test.go
M src/log/slog/handler.go
M src/log/slog/logger.go
M src/log/slog/logger_test.go
5 files changed, 163 insertions(+), 6 deletions(-)
diff --git a/api/next/62418.txt b/api/next/62418.txt
new file mode 100644
index 0000000..fd482f4
--- /dev/null
+++ b/api/next/62418.txt
@@ -0,0 +1 @@
+pkg log/slog, func SetLogLoggerLevel(Level) Level #62418
diff --git a/src/log/slog/example_log_level_test.go b/src/log/slog/example_log_level_test.go
new file mode 100644
index 0000000..ca8db416
--- /dev/null
+++ b/src/log/slog/example_log_level_test.go
@@ -0,0 +1,58 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package slog_test
+
+import (
+ "log"
+ "log/slog"
+ "log/slog/internal/slogtest"
+ "os"
+)
+
+// This example shows how to use slog.SetLogLoggerLevel to change the minimal level
+// of the internal default handler for slog package before calling slog.SetDefault.
+func ExampleSetLogLoggerLevel_log() {
+ defer log.SetFlags(log.Flags()) // revert changes after the example
+ log.SetFlags(0)
+ defer log.SetOutput(log.Writer()) // revert changes after the example
+ log.SetOutput(os.Stdout)
+
+ // Default logging level is slog.LevelInfo.
+ log.Print("log debug") // log debug
+ slog.Debug("debug") // no output
+ slog.Info("info") // INFO info
+
+ // Set the default logging level to slog.LevelDebug.
+ currentLogLevel := slog.SetLogLoggerLevel(slog.LevelDebug)
+ defer slog.SetLogLoggerLevel(currentLogLevel) // revert changes after the example
+
+ log.Print("log debug") // log debug
+ slog.Debug("debug") // DEBUG debug
+ slog.Info("info") // INFO info
+
+ // Output:
+ // log debug
+ // INFO info
+ // log debug
+ // DEBUG debug
+ // INFO info
+}
+
+// This example shows how to use slog.SetLogLoggerLevel to change the minimal level
+// of the internal writer that uses the custom handler for log package after
+// calling slog.SetDefault.
+func ExampleSetLogLoggerLevel_slog() {
+ // Set the default logging level to slog.LevelError.
+ currentLogLevel := slog.SetLogLoggerLevel(slog.LevelError)
+ defer slog.SetLogLoggerLevel(currentLogLevel) // revert changes after the example
+
+ defer slog.SetDefault(slog.Default()) // revert changes after the example
+ slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime})))
+
+ log.Print("error") // level=ERROR msg=error
+
+ // Output:
+ // level=ERROR msg=error
+}
diff --git a/src/log/slog/handler.go b/src/log/slog/handler.go
index a6c643c..9f6d88b 100644
--- a/src/log/slog/handler.go
+++ b/src/log/slog/handler.go
@@ -100,7 +100,7 @@
}
func (*defaultHandler) Enabled(_ context.Context, l Level) bool {
- return l >= LevelInfo
+ return l >= logLoggerLevel.Level()
}
// Collect the level, attributes and message in a string and
diff --git a/src/log/slog/logger.go b/src/log/slog/logger.go
index fceafe0..f03aeec 100644
--- a/src/log/slog/logger.go
+++ b/src/log/slog/logger.go
@@ -16,6 +16,36 @@
var defaultLogger atomic.Pointer[Logger]
+var logLoggerLevel LevelVar
+
+// SetLogLoggerLevel controls the level for the bridge to the [log] package.
+//
+// Before [SetDefault] is called, slog top-level logging functions call the default [log.Logger].
+// In that mode, SetLogLoggerLevel sets the minimum level for those calls.
+// By default, the minimum level is Info, so calls to [Debug]
+// (as well as top-level logging calls at lower levels)
+// will not be passed to the log.Logger. After calling
+//
+// slog.SetLogLoggerLevel(slog.LevelDebug)
+//
+// calls to [Debug] will be passed to the log.Logger.
+//
+// After [SetDefault] is called, calls to the default [log.Logger] are passed to the
+// slog default handler. In that mode,
+// SetLogLoggerLevel sets the level at which those calls are logged.
+// That is, after calling
+//
+// slog.SetLogLoggerLevel(slog.LevelDebug)
+//
+// A call to [log.Printf] will result in output at level [LevelDebug].
+//
+// SetLogLoggerLevel returns the previous value.
+func SetLogLoggerLevel(level Level) (oldLevel Level) {
+ oldLevel = logLoggerLevel.Level()
+ logLoggerLevel.Set(level)
+ return
+}
+
func init() {
defaultLogger.Store(New(newDefaultHandler(loginternal.DefaultOutput)))
}
@@ -25,7 +55,8 @@
// SetDefault makes l the default [Logger].
// After this call, output from the log package's default Logger
-// (as with [log.Print], etc.) will be logged at [LevelInfo] using l's Handler.
+// (as with [log.Print], etc.) will be logged using l's Handler,
+// at a level controlled by [SetLogLoggerLevel].
func SetDefault(l *Logger) {
defaultLogger.Store(l)
// If the default's handler is a defaultHandler, then don't use a handleWriter,
@@ -36,7 +67,7 @@
// See TestSetDefault.
if _, ok := l.Handler().(*defaultHandler); !ok {
capturePC := log.Flags()&(log.Lshortfile|log.Llongfile) != 0
- log.SetOutput(&handlerWriter{l.Handler(), LevelInfo, capturePC})
+ log.SetOutput(&handlerWriter{l.Handler(), &logLoggerLevel, capturePC})
log.SetFlags(0) // we want just the log message, no time or location
}
}
@@ -45,12 +76,13 @@
// It is used to link the default log.Logger to the default slog.Logger.
type handlerWriter struct {
h Handler
- level Level
+ level Leveler
capturePC bool
}
func (w *handlerWriter) Write(buf []byte) (int, error) {
- if !w.h.Enabled(context.Background(), w.level) {
+ level := w.level.Level()
+ if !w.h.Enabled(context.Background(), level) {
return 0, nil
}
var pc uintptr
@@ -66,7 +98,7 @@
if len(buf) > 0 && buf[len(buf)-1] == '\n' {
buf = buf[:len(buf)-1]
}
- r := NewRecord(time.Now(), w.level, string(buf), pc)
+ r := NewRecord(time.Now(), level, string(buf), pc)
return origLen, w.h.Handle(context.Background(), r)
}
diff --git a/src/log/slog/logger_test.go b/src/log/slog/logger_test.go
index 88aa38e..bb1c8a1 100644
--- a/src/log/slog/logger_test.go
+++ b/src/log/slog/logger_test.go
@@ -191,6 +191,7 @@
}
}
+ defer SetDefault(Default()) // restore
logger := New(h)
SetDefault(logger)
@@ -363,6 +364,71 @@
}
}
+// Test defaultHandler minimum level without calling slog.SetDefault.
+func TestLogLoggerLevelForDefaultHandler(t *testing.T) {
+ // Revert any changes to the default logger, flags, and level of log and slog.
+ currentLogLoggerLevel := logLoggerLevel.Level()
+ currentLogWriter := log.Writer()
+ currentLogFlags := log.Flags()
+ t.Cleanup(func() {
+ logLoggerLevel.Set(currentLogLoggerLevel)
+ log.SetOutput(currentLogWriter)
+ log.SetFlags(currentLogFlags)
+ })
+
+ var logBuf bytes.Buffer
+ log.SetOutput(&logBuf)
+ log.SetFlags(0)
+
+ for _, test := range []struct {
+ logLevel Level
+ logFn func(string, ...any)
+ want string
+ }{
+ {LevelDebug, Debug, "DEBUG a"},
+ {LevelDebug, Info, "INFO a"},
+ {LevelInfo, Debug, ""},
+ {LevelInfo, Info, "INFO a"},
+ } {
+ SetLogLoggerLevel(test.logLevel)
+ test.logFn("a")
+ checkLogOutput(t, logBuf.String(), test.want)
+ logBuf.Reset()
+ }
+}
+
+// Test handlerWriter minimum level by calling slog.SetDefault.
+func TestLogLoggerLevelForHandlerWriter(t *testing.T) {
+ removeTime := func(_ []string, a Attr) Attr {
+ if a.Key == TimeKey {
+ return Attr{}
+ }
+ return a
+ }
+
+ // Revert any changes to the default logger. This is important because other
+ // tests might change the default logger using SetDefault. Also ensure we
+ // restore the default logger at the end of the test.
+ currentLogger := Default()
+ currentLogLoggerLevel := logLoggerLevel.Level()
+ currentLogWriter := log.Writer()
+ currentFlags := log.Flags()
+ t.Cleanup(func() {
+ SetDefault(currentLogger)
+ logLoggerLevel.Set(currentLogLoggerLevel)
+ log.SetOutput(currentLogWriter)
+ log.SetFlags(currentFlags)
+ })
+
+ var logBuf bytes.Buffer
+ log.SetOutput(&logBuf)
+ log.SetFlags(0)
+ SetLogLoggerLevel(LevelError)
+ SetDefault(New(NewTextHandler(&logBuf, &HandlerOptions{ReplaceAttr: removeTime})))
+ log.Print("error")
+ checkLogOutput(t, logBuf.String(), `level=ERROR msg=error`)
+}
+
func TestLoggerError(t *testing.T) {
var buf bytes.Buffer
To view, visit change 525096. To unsubscribe, or for help writing mail filters, visit settings.