ruk·si

🐹 Go
Logging

Updated at 2018-12-31 05:16

fmt has the most important debug functionality, printing.

package main

import "fmt"

func main() {
    fmt.Print("Hello World!\n")
    fmt.Println(fmt.Sprintf("Hi %s %s.", "Ruksi", "Laine")) // Hi Ruksi Laine.

    // %s => string representation
    fmt.Printf("Hi %s!\n", "Ruksi") // Hi Ruksi!

    // %.2f => float point to certain precision
    fmt.Printf("Hi %.2f!\n", 123.4567) // 123.45

    // %v => default value representation
    fmt.Printf("Hi %v!\n", "Ruksi") // Hi Ruksi!

    // %#v => Go syntax like representation
    fmt.Printf("Hi %#v!\n", "Ruksi") // Hi "Ruksi"!

    // %T => Go syntax like representation
    fmt.Printf("Hi %T!\n", "Ruksi") // Hi string!

    // Writing to io.Writer interface.
    //fmt.Fprintln(w, "Hi,", name)
}

log package is worth a look. Especially nice as messages are returned with timestamps.

package main

import "log"

func main() {
    log.Fatalln("main: log entry")  // 2009/11/10 23:00:00 main: log entry
    log.Panicln("main: log entry")  // not printed, would print same and stack trace
    log.Println("main: log entry")  // not printed
}

Loggers are dependencies! Loggers should be dependencies, like any other library. Testing the log output of components that use the fixed global logger is annoying.

// Bad
func (f *foo) process() {
    fmt.Fprintf(f.Output, "start\n")
    result := f.Bar.compute()
    log.Printf("bar: %v", result) // Whoops!
}

// Good
func (f *foo) process() {
    fmt.Fprintf(f.Output, "start\n")
    result := f.Bar.compute()
    f.Logger.Printf("bar: %v", result) // Better.
}

// Still, add sensible default
func newFoo(..., cfg fooConfig) *foo {
    if cfg.Logger == nil {
        cfg.Logger = log.NewLogger(ioutil.Discard, ...)
    }
}

Use structured logging. Pretty easy to read by humans, but also searchable by machines.

import log "github.com/Sirupsen/logrus"

func main() {
  log.SetFormatter(&log.JSONFormatter{})
  log.WithFields(log.Fields{
    "userId": 1,
    "server": "www.google.com",
  }).Info("Redirecting user")
}

Don't log too much. Log only actionable information, which will be read by a human or a machine. Avoid too fine-grained log levels — info and debug are probably enough. Consider using structured logging: https://github.com/go-kit/kit/tree/master/log

Add monitoring. At least record what percent of requests get an error response and what is the 99th percentile response time.

Sources