Skip to content

Instantly share code, notes, and snippets.

@diyan
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diyan/cc031a92e55d1eb6e5c8 to your computer and use it in GitHub Desktop.
Save diyan/cc031a92e55d1eb6e5c8 to your computer and use it in GitHub Desktop.
I've used strucutred logging approach in Python with structlog library. Now looking for similar options in Golang.

I've used strucutred logging approach in Python (with structlog library).

Now looking for same options but in Golang.

Candidates are logxi, log15 and logrus.

IMO log15 has most idiomatic API while logxi is most convenient (due to stack context info)

To build and run I'm using following:

$ go build && LOGXI=* ./go_logging

All libraries are uing interfaces, so rendering could be customized

  • logxi has Formatter interface
  • log15 has Format interface
  • logrus has Formatter interface

logxi with default settings show no context for INF, short context for WARN and full context for ERR.

Other tow libraries has more standard/boring defaults.

18:55:57.586685 INF ~ logxi info key1: value1 key2: 2
18:55:57.586937 WRN ~ logxi warn key1: value1 key2: 2
   in: sampleBar(go_logs.go:36)
18:55:57.588004 ERR ~ logxi error key1: value1 key2: 2
   in sampleBar(go_logs.go:37)
     35:  logxi.Info("logxi info", "key1", "value1", "key2", two)
     36:  logxi.Warn("logxi warn", "key1", "value1", "key2", two)
     37:  logxi.Error("logxi error", "key1", "value1", "key2", two)
     38:
     39:  logger := log15.New("key1", "value1", "key2", two)
   in sampleFoo(go_logs.go:29)
     27:
     28:  func sampleFoo() {
     29:      sampleBar()
     30:  }
     31:
   in main(go_logs.go:25)
     23:  func main() {
     24:      //logger := logxi.NewLogger(os.Stdout, "logxi logger")
     25:      sampleFoo()
     26:  }
     27:
INFO[03-20|19:12:17] log15 info                               context1=context1 context2=2 key3=value3 key4=four
WARN[03-20|19:12:17] log15 warn                               context1=context1 context2=2 key3=value3 key4=four
EROR[03-20|19:12:17] log15 err                                context1=context1 context2=2 key3=value3 key4=four
INFO[03-20|19:12:17] log15 info                               context1=context1 context2=2 context3=true context4=0.010 key3=value3 key4=four
INFO[0000] logrus info                                   key1=value1 key2=2
WARN[0000] logrus warn                                   key1=value1 key2=2
ERRO[0000] logrus error                                  key1=value1 key2=2

Here is how JSON formatter looks like:

logxi

{
  "_t": "18:21:38.001590",
  "_l": "INF",
  "_n": "logxi logger",
  "_m": "logxi info",
  "key1": "value1",
  "key2": 2
}

log15

{
  "context1": "context1",
  "context2": 2,
  "context3": "true",
  "context4": 0.01,
  "key3": "value3",
  "key4": "four",
  "lvl": 3,
  "msg": "log15 info",
  "t": "2015-03-20T19:13:05.675154775+02:00"
}

logrus

{
  "key1": "value1",
  "key2": 2,
  "level": "info",
  "msg": "logrus info",
  "time": "2015-03-20T18:42:52+02:00"
}

Source code

package main

import (
    //"os"
    logrus "github.com/Sirupsen/logrus"
    log15 "gopkg.in/inconshreveable/log15.v2"
    // NOTE logxi works with go1.4 but failed with go1.2.1
    logxi "github.com/mgutz/logxi/v1"
)

func main() {
    sampleFoo()
}

func sampleFoo() {
    sampleBar()
}

func sampleBar() {
    two := 2

    // Use env variables: LOGXI=* LOGXI_FORMAT=JSON
    // Pass logger name into constructor
    logger1 := logxi.New("logxi logger")

    logger1.Info("logxi info", "key1", "value1", "key2", two)
    logger1.Warn("logxi warn", "key1", "value1", "key2", two)
    logger1.Error("logxi error", "key1", "value1", "key2", two)

    // Pass some context variables into "root" logger
    rootLogger := log15.New("context1", "context1", "context2", two)
    //rootLogger.SetHandler(log15.StreamHandler(os.Stdout, log15.JsonFormat()))
    rootLogger.Info("log15 info", "key3", "value3", "key4", "four")
    rootLogger.Warn("log15 warn", "key3", "value3", "key4", "four")
    rootLogger.Error("log15 err", "key3", "value3", "key4", "four")
    // Add more context variables into sub logger
    subLogger := rootLogger.New("context3", true, "context4", 0.01)
    subLogger.Info("log15 info", "key3", "value3", "key4", "four")

    // Does constructor useless here?
    logger3 := logrus.New()
    //logger3.SetFormatter(&logrus.JSONFormatter{})
    logger3.WithFields(logrus.Fields{"key1": "value1", "key2": two}).Info("logrus info")
    logger3.WithFields(logrus.Fields{"key1": "value1", "key2": two}).Warn("logrus warn")
    logger3.WithFields(logrus.Fields{"key1": "value1", "key2": two}).Error("logrus error")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment