Last active
April 4, 2020 16:05
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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