1
0
mirror of https://github.com/golang/go synced 2024-09-29 18:24:29 -06:00

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>
This commit is contained in:
Ian Lance Taylor 2021-09-09 14:49:41 -07:00
parent 7f36ef0aff
commit 323c6f74d3
2 changed files with 55 additions and 12 deletions

View File

@ -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...))
}

View File

@ -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++ {