Skip to content

Instantly share code, notes, and snippets.

@stephen-fox
Last active April 4, 2020 16:05
Show Gist options
  • Save stephen-fox/05b1e282ff052b41ef39ca08f26503c3 to your computer and use it in GitHub Desktop.
Save stephen-fox/05b1e282ff052b41ef39ca08f26503c3 to your computer and use it in GitHub Desktop.
Adding a prefix to Go's log.Logger after the log flags
// This example code demonstrates how to add a prefix to messages
// generated by Go's standard library logger *after* the log flags.
//
// For example, this source file's main() produces the
// following output:
// 2020/04/04 11:45:24 main.go:13: [my cool prefix] some work from anonymous go routine
// 2020/04/04 11:45:25 main.go:18: [my cool prefix] here is a panic 1
// panic: here is a panic 1
//
// goroutine 1 [running]:
// log.(*Logger).Panicf(0xc00009c0f0, 0x10d4d5b, 0x7, 0xc00007af40, 0x1, 0x1)
// /opt/pkg/go113/src/log/log.go:219 +0xc1
// main.main()
// main.go:18 +0x15c
//
// It is not perfect (see the ':' above), but I like how it
// preserves the log.Logger, and does not require you to reimplement
// the log.Logger API. Assuming you are already creating custom
// log.Loggers, this change will generate a small diff, and will
// be transparent to callers.
//
// Note: I have not benchmarked this implementation. I am sure it
// incurrs some kind of performance penalty.
package main
import (
"log"
"strings"
"time"
)
func main() {
lg := newLogger(log.New(log.Writer(), "[my cool prefix] ", log.Flags()|log.Llongfile))
go func() {
lg.Println("some work from anonymous go routine")
}()
time.Sleep(1 * time.Second)
lg.Panicf("here is a panic %d", 1)
}
// newLogger returns a *log.Logger containing a io.Writer that intercepts
// log messages and adds a prefix after the log flags (e.g., the timestamp).
//
// The input to this doesn't need to be a log.Logger - I just thought it was
// a nice way to convey log settings. The log.Logger has methods for retrieving
// settings, so it beats making our own config struct.
//
// Kinda wasteful though... We're making 3 Loggers, lol... Cutting down on one
// would save some memory - input log.Logger is 72 bytes.
func newLogger(settings *log.Logger) *log.Logger {
return log.New(&logInterceptor{
prefix: settings.Prefix(),
out: log.New(settings.Writer(), "", settings.Flags()),
}, "", 0)
}
// logInterceptor is an io.Writer that intercepts and appends
// a prefix to log messages.
type logInterceptor struct {
prefix string
out *log.Logger
}
func (o *logInterceptor) Write(p []byte) (n int, err error) {
sb := strings.Builder{}
sb.WriteString(o.prefix)
sb.Write(p)
// calldepth argument specifies how many function calls back did
// the original call occur.
err = o.out.Output(4, sb.String())
return len(p), err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment