aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-09-09 14:49:41 -0700
committerIan Lance Taylor <iant@golang.org>2021-09-17 19:00:29 +0000
commit323c6f74d35ec29baac2a1aba4270f89b022815a (patch)
tree6724d4c5268ce221753f7e971ce681bbafa0d040
parent7f36ef0aff702f2598390d0349f9c9632942d40b (diff)
downloadgo-323c6f74d35ec29baac2a1aba4270f89b022815a.tar.gz
go-323c6f74d35ec29baac2a1aba4270f89b022815a.zip
log: don't format if writing to io.Discard
Fixes #47164 Change-Id: Ied03842360be4c86f1d9ead816f12c057a1f8dad Reviewed-on: https://go-review.googlesource.com/c/go/+/348741 Trust: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> Reviewed-by: Valentin Deleplace <deleplace@google.com>
-rw-r--r--src/log/log.go53
-rw-r--r--src/log/log_test.go14
2 files changed, 55 insertions, 12 deletions
diff --git a/src/log/log.go b/src/log/log.go
index b77af29032..3172384718 100644
--- a/src/log/log.go
+++ b/src/log/log.go
@@ -20,6 +20,7 @@ import (
"os"
"runtime"
"sync"
+ "sync/atomic"
"time"
)
@@ -50,11 +51,12 @@ const (
// the Writer's Write method. A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
- mu sync.Mutex // ensures atomic writes; protects the following fields
- prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
- flag int // properties
- out io.Writer // destination for output
- buf []byte // for accumulating text to write
+ mu sync.Mutex // ensures atomic writes; protects the following fields
+ prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
+ flag int // properties
+ out io.Writer // destination for output
+ buf []byte // for accumulating text to write
+ isDiscard int32 // atomic boolean: whether out == io.Discard
}
// New creates a new Logger. The out variable sets the
@@ -63,7 +65,11 @@ type Logger struct {
// after the log header if the Lmsgprefix flag is provided.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
- return &Logger{out: out, prefix: prefix, flag: flag}
+ l := &Logger{out: out, prefix: prefix, flag: flag}
+ if out == io.Discard {
+ l.isDiscard = 1
+ }
+ return l
}
// SetOutput sets the output destination for the logger.
@@ -71,6 +77,11 @@ func (l *Logger) SetOutput(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
l.out = w
+ isDiscard := int32(0)
+ if w == io.Discard {
+ isDiscard = 1
+ }
+ atomic.StoreInt32(&l.isDiscard, isDiscard)
}
var std = New(os.Stderr, "", LstdFlags)
@@ -188,16 +199,29 @@ func (l *Logger) Output(calldepth int, s string) error {
// Printf calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Printf(format string, v ...interface{}) {
+ if atomic.LoadInt32(&l.isDiscard) != 0 {
+ return
+ }
l.Output(2, fmt.Sprintf(format, v...))
}
// Print calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Print.
-func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) }
+func (l *Logger) Print(v ...interface{}) {
+ if atomic.LoadInt32(&l.isDiscard) != 0 {
+ return
+ }
+ l.Output(2, fmt.Sprint(v...))
+}
// Println calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Println.
-func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) }
+func (l *Logger) Println(v ...interface{}) {
+ if atomic.LoadInt32(&l.isDiscard) != 0 {
+ return
+ }
+ l.Output(2, fmt.Sprintln(v...))
+}
// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).
func (l *Logger) Fatal(v ...interface{}) {
@@ -277,9 +301,7 @@ func (l *Logger) Writer() io.Writer {
// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.out = w
+ std.SetOutput(w)
}
// Flags returns the output flags for the standard logger.
@@ -314,18 +336,27 @@ func Writer() io.Writer {
// Print calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Print.
func Print(v ...interface{}) {
+ if atomic.LoadInt32(&std.isDiscard) != 0 {
+ return
+ }
std.Output(2, fmt.Sprint(v...))
}
// Printf calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Printf.
func Printf(format string, v ...interface{}) {
+ if atomic.LoadInt32(&std.isDiscard) != 0 {
+ return
+ }
std.Output(2, fmt.Sprintf(format, v...))
}
// Println calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Println.
func Println(v ...interface{}) {
+ if atomic.LoadInt32(&std.isDiscard) != 0 {
+ return
+ }
std.Output(2, fmt.Sprintln(v...))
}
diff --git a/src/log/log_test.go b/src/log/log_test.go
index 5be8e82258..938ed42357 100644
--- a/src/log/log_test.go
+++ b/src/log/log_test.go
@@ -9,6 +9,7 @@ package log
import (
"bytes"
"fmt"
+ "io"
"os"
"regexp"
"strings"
@@ -20,7 +21,7 @@ const (
Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
- Rline = `(60|62):` // must update if the calls to l.Printf / l.Print below move
+ Rline = `(61|63):` // must update if the calls to l.Printf / l.Print below move
Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline
)
@@ -179,6 +180,17 @@ func TestEmptyPrintCreatesLine(t *testing.T) {
}
}
+func TestDiscard(t *testing.T) {
+ l := New(io.Discard, "", 0)
+ s := strings.Repeat("a", 102400)
+ c := testing.AllocsPerRun(100, func() { l.Printf("%s", s) })
+ // One allocation for slice passed to Printf,
+ // but none for formatting of long string.
+ if c > 1 {
+ t.Errorf("got %v allocs, want at most 1", c)
+ }
+}
+
func BenchmarkItoa(b *testing.B) {
dst := make([]byte, 0, 64)
for i := 0; i < b.N; i++ {