How to disable a log.Logger
Solution 1
For completely disabling logs, it's actually better to call log.SetFlags(0)
Joril and set the output to a no-op io.Writer
(i.e., log.SetOutput(ioutil.Discard)
)
But even after this, the operations will idle around 500-600 ns/op1
This can still be cut short (to around 100 ns/op) by using a custom Logger
implementation, and implementing all the functions to be no-op -- as demonstrated here (only overriding Println
for bervity).
The alternative to all these is to use a custom logging framework with levels and set it to complete OFF.
Note though, one of the commonly used library for logging (logrus) has performance implications -- the same can be found in the benchmarks where it perform with 3K+ ns/op, regardless.
Biased opinion: from the benchmarks, the library go-logging performs in par with the custom Logger
implementation when setting the Level
to -1
, regardless of the backend and formatting
(the benchmark source can be found here)
the output of the benchmark is as follows:
testing: warning: no tests to run
PASS
BenchmarkGoLogging-4 1000000 2068 ns/op
BenchmarkGoLoggingNullBackend-4 5000000 308 ns/op
BenchmarkGoLoggingNullBackendWithFancyFormatter-4 3000000 435 ns/op
BenchmarkGoLoggingOffLevel-4 20000000 109 ns/op
BenchmarkGoLoggingNullBackendAndOffLevel-4 20000000 108 ns/op
BenchmarkGoLoggingNullBackendWithFancyFormatterAndOffLevel-4 20000000 109 ns/op
BenchmarkLog15-4 200000 7359 ns/op
BenchmarkLog15WithDiscardHandler-4 2000000 922 ns/op
BenchmarkLog15WithDiscardHandlerAndOffLevel-4 2000000 926 ns/op
BenchmarkLog15WithNopLogger-4 20000000 108 ns/op
BenchmarkLog15WithNopLoggerDiscardHandlerA-4 20000000 112 ns/op
BenchmarkLog15WithNopLoggerAndDiscardHandlerAndOffLevel-4 20000000 112 ns/op
BenchmarkLog-4 1000000 1217 ns/op
BenchmarkLogIoDiscardWriter-4 2000000 724 ns/op
BenchmarkLogIoDiscardWriterWithoutFlags-4 3000000 543 ns/op
BenchmarkLogCustomNullWriter-4 2000000 731 ns/op
BenchmarkLogCustomNullWriterWithoutFlags-4 3000000 549 ns/op
BenchmarkNopLogger-4 20000000 113 ns/op
BenchmarkNopLoggerWithoutFlags-4 20000000 112 ns/op
BenchmarkLogrus-4 300000 3832 ns/op
BenchmarkLogrusWithDiscardWriter-4 500000 3032 ns/op
BenchmarkLogrusWithNullFormatter-4 500000 3814 ns/op
BenchmarkLogrusWithPanicLevel-4 500000 3872 ns/op
BenchmarkLogrusWithDiscardWriterAndPanicLevel-4 500000 3085 ns/op
BenchmarkLogrusWithDiscardWriterAndNullFormatterAndPanicLevel-4 500000 3064 ns/op
ok log-benchmarks 51.378s
go test -bench . 62.17s user 3.90s system 126% cpu 52.065 total
#1: YMMV, tested on i7-4500U CPU @ 1.80GHz
Solution 2
No reason to create your own type for a common io.Writer when one exists in the io/ioutil package.
import (
"log"
"io/ioutil"
)
func init() {
log.SetOutput(ioutil.Discard)
}
Solution 3
type NullWriter int
func (NullWriter) Write([]byte) (int, error) { return 0, nil }
// ...
log.SetOutput(new(NullWriter))
Solution 4
This approach allows you to turn logging on and off at runtime:
type LogWriter struct{
enabled bool
}
func (l *LogWriter) Enable() {
l.enabled = true
}
func (l *LogWriter) Disable() {
l.enabled = false
}
func (l *LogWriter) Write([]byte) (int, error) {
if l.enabled {
//...
}
return 0, nil
}
And this approach enables or disables logging for the entire runtime:
type LogWriter struct{}
func (l *LogWriter) Write([]byte) (int, error) {
if some.Constant {
//...
}
return 0, nil
}
Where some.Constant
would be either a constant that you set before compiling (producing a "production" binary) or a variable that is set only once when running the program via command-line flags (something like myprogram --enable-logging=true
)
With both approaches you can leave your current code almost entirely untouched.
Solution 5
Since SetOutput()
is only defined for the global logger, a custom writer is still handy for other loggers. A short way of writing one is like this:
type LogWriter struct{ io.Writer }
func (w *LogWriter) Enable() { w.Writer = os.Stdout }
func (w *LogWriter) Disable() { w.Writer = ioutil.Discard }
Matt Joiner
About Me I like parsimonious code, with simple interfaces and excellent documentation. I'm not interested in enterprise, boiler-plate, or cookie-cutter nonsense. I oppose cruft and obfuscation. My favourite languages are Go, Python and C. I wish I was better at Haskell. Google+ GitHub Bitbucket Google code My favourite posts http://stackoverflow.com/questions/3609469/what-are-the-thread-limitations-when-working-on-linux-compared-to-processes-for/3705919#3705919 http://stackoverflow.com/questions/4352425/what-should-i-learn-first-before-heading-to-c/4352469#4352469 http://stackoverflow.com/questions/6167809/how-much-bad-can-be-done-using-register-variables-in-c/6168852#6168852 http://stackoverflow.com/questions/4141307/c-and-c-source-code-profiling-tools/4141345#4141345 http://stackoverflow.com/questions/3463207/how-big-can-a-malloc-be-in-c/3486163#3486163 http://stackoverflow.com/questions/4095637/memory-use-of-stl-data-structures-windows-vs-linux/4183178#4183178
Updated on August 23, 2020Comments
-
Matt Joiner over 3 years
I have some heavily instrumented code that makes use of the
log
package. Now it's come time to turn off the logging, and I can't determine how to turn off the standard logger.Have I missed something? Should I be checking a flag before making log calls, or commenting them out in production?
-
Asherah almost 12 yearsCould it possibly be the case that some users of the Writer will attempt to continue writing if the
int
return value is0
? (i.e. maybe should we returnlen(b)
when the argument isb []byte
?) -
Mostafa almost 12 yearsGenerally you're right, but
log
package just ignoresint
return value, so we better not waste time in such a frequent function in production code. -
Matt Joiner almost 12 yearsIs there any best practises on use of
log
in Go? Are most logging calls eventually removed (or commented out) if they're for tracing purposes? -
Mostafa almost 12 yearsHave to go for now, but have you seen this?
-
Mostafa almost 12 years@MattJoiner I don't keep logging calls that are for testing. Remove them as soon as I don't need them. But you can have multiple loggers in Go. I usually have two:
infoLogger
anderrLogger
, and set them to appropriate outputs at development and production (using command-line flags). You can have another,debugLogger
or something like that, and disable it in production. -
Mostafa almost 12 yearsGreat find! I looked for such a thing in the standard libraries, but didn't check
io/ioutil
. -
rog almost 12 yearsthis is not a good way to do it as it does all the work of formatting the output, only to destroy it. if you want logging calls that you can turn off, it's better to stem the flow at source, by avoiding the call to log.Printf.
-
Kiril almost 10 yearsWill this disable the logger only for the package, or the whole application?
-
Joril over 9 yearsSince this skips writing to disk but still does all the formatting, a call to
log.SetFlags(0)
should ease it a bit, I think -
Aaron over 7 yearsThis is really nice to have when you need to suppress logging for tests/benchmarks. Was about to mock out my own logger before I came across this.
-
Larytet over 5 yearsThe answer is somewhat out of date. CPUs are different, Golang is different. You can do better than 15 ns/operation in Golang with a custom log. This is a simple POC of a binary log github.com/larytet/binlog When outputting only integral types it hits 30-40 ns/op range
-
Avinash R over 5 years@Larytet please feel free to edit the answer with new information (or add a new answer). The above benchmark is meant give a fair idea on the performance of each w.r.t each other; not w.r.t the CPU and Golang version. There may be newer libraries more efficient (as it should be), I cannot keep up each of them. Since the source of the above benchmark is given, I expect the reader to make that comparison for themselves (since almost always YMMV).
-
Larytet over 5 yearsAvinash R, the stated number 500ns/op is not representative of the current state of art. See for example, github.com/PlatformLab/NanoLog - this is a sub 10ns log.
-
cristoper over 3 yearsToday, anyway, SetOutput is also a method of the Logger type: golang.org/pkg/log/#Logger.SetOutput
-
Deleplace over 2 yearsAs of Go 1.16, this helper has moved to the package io : see io.Discard at pkg.go.dev/io#Writer . The ioutil one still works.
-
Deleplace over 2 yearsA proposal to improve perf when using io.Discard: github.com/golang/go/issues/47164
-
Deleplace over 2 years@Joril indeed I confirm with this benchmark that a non-zero flag does incur a performance penalty (in Go 1.17): gist.github.com/Deleplace/ad33f8c6a96e9938e83e4f9cbcc608e0
-
Deleplace over 2 yearsThe stdlib already provides such a device: io.Discard
-
Dmitry M over 2 yearsThis link appears to be broken.
-
Kenny Grant over 2 yearsI think the pkg has been abandoned now unfortunately. Latest copy here : github.com/alecthomas/log4go