How to disable a log.Logger

26,768

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 }
Share:
26,768
Matt Joiner
Author by

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, 2020

Comments

  • Matt Joiner
    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
    Asherah almost 12 years
    Could it possibly be the case that some users of the Writer will attempt to continue writing if the int return value is 0? (i.e. maybe should we return len(b) when the argument is b []byte?)
  • Mostafa
    Mostafa almost 12 years
    Generally you're right, but log package just ignores int return value, so we better not waste time in such a frequent function in production code.
  • Matt Joiner
    Matt Joiner almost 12 years
    Is 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
    Mostafa almost 12 years
    Have to go for now, but have you seen this?
  • Mostafa
    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 and errLogger, 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
    Mostafa almost 12 years
    Great find! I looked for such a thing in the standard libraries, but didn't check io/ioutil.
  • rog
    rog almost 12 years
    this 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
    Kiril almost 10 years
    Will this disable the logger only for the package, or the whole application?
  • Joril
    Joril over 9 years
    Since 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
    Aaron over 7 years
    This 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
    Larytet over 5 years
    The 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
    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
    Larytet over 5 years
    Avinash 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
    cristoper over 3 years
    Today, anyway, SetOutput is also a method of the Logger type: golang.org/pkg/log/#Logger.SetOutput
  • Deleplace
    Deleplace over 2 years
    As 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
    Deleplace over 2 years
    A proposal to improve perf when using io.Discard: github.com/golang/go/issues/47164
  • Deleplace
    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
    Deleplace over 2 years
    The stdlib already provides such a device: io.Discard
  • Dmitry M
    Dmitry M over 2 years
    This link appears to be broken.
  • Kenny Grant
    Kenny Grant over 2 years
    I think the pkg has been abandoned now unfortunately. Latest copy here : github.com/alecthomas/log4go