Skip to content

Commit 08e6e8b

Browse files
Fix stderrthreshold not honored when logtostderr is set
Add two new flags to allow severity filtering with logtostderr while maintaining full backward compatibility: - legacy_stderr_threshold_behavior (default: true): Controls whether stderrthreshold is honored when logtostderr=true - alsologtostderrthreshold (default: INFO): Provides filtering control when alsologtostderr=true This fix allows users to opt-in to severity filtering when logging to stderr, addressing a long-standing issue where all logs would be written to stderr regardless of severity level. The default behavior remains unchanged for backward compatibility. Ref: #212
1 parent a9f20c8 commit 08e6e8b

File tree

4 files changed

+462
-26
lines changed

4 files changed

+462
-26
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Example demonstrating the new stderr threshold behavior
2+
package main
3+
4+
import (
5+
"flag"
6+
7+
"k8s.io/klog/v2"
8+
)
9+
10+
func main() {
11+
klog.InitFlags(nil)
12+
flag.Parse()
13+
14+
klog.Info("This is an INFO message")
15+
klog.Warning("This is a WARNING message")
16+
klog.Error("This is an ERROR message")
17+
18+
klog.Flush()
19+
}
20+
21+
// Run examples:
22+
//
23+
// 1. Legacy behavior (default) - all logs to stderr:
24+
// go run main.go -logtostderr=true -stderrthreshold=ERROR
25+
// Result: All three messages appear
26+
//
27+
// 2. New behavior - filter by severity:
28+
// go run main.go -logtostderr=true -legacy_stderr_threshold_behavior=false -stderrthreshold=ERROR
29+
// Result: Only ERROR message appears
30+
//
31+
// 3. New behavior - show WARNING and above:
32+
// go run main.go -logtostderr=true -legacy_stderr_threshold_behavior=false -stderrthreshold=WARNING
33+
// Result: WARNING and ERROR messages appear
34+
//
35+
// 4. Using alsologtostderrthreshold with file logging:
36+
// go run main.go -logtostderr=false -alsologtostderr=true -alsologtostderrthreshold=ERROR -log_dir=/tmp/logs
37+
// Result: All logs in files, only ERROR to stderr

klog.go

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,27 @@
5858
//
5959
// -logtostderr=true
6060
// Logs are written to standard error instead of to files.
61-
// This shortcuts most of the usual output routing:
62-
// -alsologtostderr, -stderrthreshold and -log_dir have no
63-
// effect and output redirection at runtime with SetOutput is
64-
// ignored.
61+
// By default, all logs are written regardless of severity
62+
// (legacy behavior). To filter logs by severity when
63+
// -logtostderr=true, set -legacy_stderr_threshold_behavior=false
64+
// and use -stderrthreshold.
65+
// When -logtostderr=true, the following flags have no effect:
66+
// -alsologtostderr, -alsologtostderrthreshold, and -log_dir.
67+
// Output redirection at runtime with SetOutput is also ignored.
6568
// -alsologtostderr=false
6669
// Logs are written to standard error as well as to files.
70+
// -alsologtostderrthreshold=INFO
71+
// Log events at or above this severity are logged to standard
72+
// error when -alsologtostderr=true (no effect when -logtostderr=true).
73+
// Default is INFO to maintain backward compatibility.
6774
// -stderrthreshold=ERROR
6875
// Log events at or above this severity are logged to standard
69-
// error as well as to files.
76+
// error as well as to files. When -logtostderr=true, this flag
77+
// has no effect unless -legacy_stderr_threshold_behavior=false.
78+
// -legacy_stderr_threshold_behavior=true
79+
// If true, -stderrthreshold is ignored when -logtostderr=true
80+
// (legacy behavior). If false, -stderrthreshold is honored even
81+
// when -logtostderr=true, allowing severity-based filtering.
7082
// -log_dir=""
7183
// Log files will be written to this directory instead of the
7284
// default temporary directory.
@@ -156,7 +168,7 @@ func (s *severityValue) Set(value string) error {
156168
}
157169
threshold = severity.Severity(v)
158170
}
159-
logging.stderrThreshold.set(threshold)
171+
s.set(threshold)
160172
return nil
161173
}
162174

@@ -416,6 +428,7 @@ func init() {
416428
"If the value is 0, the maximum file size is unlimited.")
417429
commandLine.BoolVar(&logging.toStderr, "logtostderr", true, "log to standard error instead of files")
418430
commandLine.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files (no effect when -logtostderr=true)")
431+
commandLine.BoolVar(&logging.legacyStderrThresholdBehavior, "legacy_stderr_threshold_behavior", true, "If true, stderrthreshold is ignored when logtostderr=true (legacy behavior). If false, stderrthreshold is honored even when logtostderr=true")
419432
logging.setVState(0, nil, false)
420433
commandLine.Var(&logging.verbosity, "v", "number for the log level verbosity")
421434
commandLine.BoolVar(&logging.addDirHeader, "add_dir_header", false, "If true, adds the file directory to the header of the log messages")
@@ -425,7 +438,11 @@ func init() {
425438
logging.stderrThreshold = severityValue{
426439
Severity: severity.ErrorLog, // Default stderrThreshold is ERROR.
427440
}
428-
commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true)")
441+
commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true unless -legacy_stderr_threshold_behavior=false)")
442+
logging.alsologtostderrthreshold = severityValue{
443+
Severity: severity.InfoLog, // Default alsologtostderrthreshold is INFO (to maintain backward compatibility).
444+
}
445+
commandLine.Var(&logging.alsologtostderrthreshold, "alsologtostderrthreshold", "logs at or above this threshold go to stderr when -alsologtostderr=true (no effect when -logtostderr=true)")
429446
commandLine.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
430447
commandLine.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
431448

@@ -470,11 +487,13 @@ type settings struct {
470487
// Boolean flags. Not handled atomically because the flag.Value interface
471488
// does not let us avoid the =true, and that shorthand is necessary for
472489
// compatibility. TODO: does this matter enough to fix? Seems unlikely.
473-
toStderr bool // The -logtostderr flag.
474-
alsoToStderr bool // The -alsologtostderr flag.
490+
toStderr bool // The -logtostderr flag.
491+
alsoToStderr bool // The -alsologtostderr flag.
492+
legacyStderrThresholdBehavior bool // The -legacy_stderr_threshold_behavior flag.
475493

476494
// Level flag. Handled atomically.
477-
stderrThreshold severityValue // The -stderrthreshold flag.
495+
stderrThreshold severityValue // The -stderrthreshold flag.
496+
alsologtostderrthreshold severityValue // The -alsologtostderrthreshold flag.
478497

479498
// Access to all of the following fields must be protected via a mutex.
480499

@@ -890,9 +909,32 @@ func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Bu
890909
}
891910
}
892911
} else if l.toStderr {
893-
os.Stderr.Write(data)
912+
// When logging to stderr only, check if we should filter by severity.
913+
// This is controlled by the legacy_stderr_threshold_behavior flag.
914+
if l.legacyStderrThresholdBehavior {
915+
// Legacy behavior: always write to stderr, ignore stderrthreshold
916+
os.Stderr.Write(data)
917+
} else {
918+
// New behavior: honor stderrthreshold even when logtostderr=true
919+
if s >= l.stderrThreshold.get() {
920+
os.Stderr.Write(data)
921+
}
922+
}
894923
} else {
895-
if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
924+
// Determine if we should also write to stderr
925+
shouldWriteToStderr := alsoToStderr
926+
927+
// If alsologtostderr is set, check alsologtostderrthreshold
928+
if l.alsoToStderr && s >= l.alsologtostderrthreshold.get() {
929+
shouldWriteToStderr = true
930+
}
931+
932+
// Otherwise, check stderrThreshold (when not using alsologtostderr)
933+
if !l.alsoToStderr && s >= l.stderrThreshold.get() {
934+
shouldWriteToStderr = true
935+
}
936+
937+
if shouldWriteToStderr {
896938
os.Stderr.Write(data)
897939
}
898940

klog_test.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,10 @@ func TestCommandLine(t *testing.T) {
944944
If true, adds the file directory to the header of the log messages
945945
-alsologtostderr
946946
log to standard error as well as files (no effect when -logtostderr=true)
947+
-alsologtostderrthreshold value
948+
logs at or above this threshold go to stderr when -alsologtostderr=true (no effect when -logtostderr=true)
949+
-legacy_stderr_threshold_behavior
950+
If true, stderrthreshold is ignored when logtostderr=true (legacy behavior). If false, stderrthreshold is honored even when logtostderr=true (default true)
947951
-log_backtrace_at value
948952
when logging hits line file:N, emit a stack trace
949953
-log_dir string
@@ -961,7 +965,7 @@ func TestCommandLine(t *testing.T) {
961965
-skip_log_headers
962966
If true, avoid headers when opening log files (no effect when -logtostderr=true)
963967
-stderrthreshold value
964-
logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true) (default 2)
968+
logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true unless -legacy_stderr_threshold_behavior=false) (default 2)
965969
-v value
966970
number for the log level verbosity
967971
-vmodule value
@@ -1949,19 +1953,21 @@ func checkLogrEntryCorrectCaller(t *testing.T, wantFile string, wantLine int, en
19491953

19501954
// existedFlag contains all existed flag, without KlogPrefix
19511955
var existedFlag = map[string]struct{}{
1952-
"log_dir": {},
1953-
"add_dir_header": {},
1954-
"alsologtostderr": {},
1955-
"log_backtrace_at": {},
1956-
"log_file": {},
1957-
"log_file_max_size": {},
1958-
"logtostderr": {},
1959-
"one_output": {},
1960-
"skip_headers": {},
1961-
"skip_log_headers": {},
1962-
"stderrthreshold": {},
1963-
"v": {},
1964-
"vmodule": {},
1956+
"log_dir": {},
1957+
"add_dir_header": {},
1958+
"alsologtostderr": {},
1959+
"alsologtostderrthreshold": {},
1960+
"legacy_stderr_threshold_behavior": {},
1961+
"log_backtrace_at": {},
1962+
"log_file": {},
1963+
"log_file_max_size": {},
1964+
"logtostderr": {},
1965+
"one_output": {},
1966+
"skip_headers": {},
1967+
"skip_log_headers": {},
1968+
"stderrthreshold": {},
1969+
"v": {},
1970+
"vmodule": {},
19651971
}
19661972

19671973
// KlogPrefix define new flag prefix

0 commit comments

Comments
 (0)