logger

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 29, 2019 License: MIT Imports: 7 Imported by: 0

README

Logger

This package contains an abstraction for logging so different log implementations can be used for different purposes.

Why?

Depending on your use case you might want to use a different log implementation. Logrus looks nice on a console output, but is a rather slow JSON logger.

Another reason might be you want to use multiple parametrized loggers. This package allows you to wrap multiple loggers around a single interface. Each logger can have its own io.Writer as Output and its own log level set. This way, you can send only Error+Above levels to one of your log servers and have a second log server which gets log levels of all type, plus a third logger which prints errors to stdout.

Implementation

It's relatively easy to build a wrapper around the standard interface. There are currently three implementations: logrus, zerolog and gelf.

  • Logrus can be used as a console logger while running your application locally
  • Zerolog is a fast JSON logger that prints everything as a JSON message
  • GELF is a wrapper around zerolog that prints everything in GELF format.

Additionally, a Multilogger exists. You can pass as many loggers as you want to your Multilogger and the Multilogger behaves as a single logger that calls the same functions on all Loggers.

Usage

Simple

Create a logger with new and select a predefined implementation.

package main

import (
	"os"

	"github.com/juju/errors"
	"github.com/leononame/logger"
)

func main() {
	// Options are: LogrusBackend, ZerologBackend, GelfBackend
	l := logger.New(os.Stdout, logger.InfoLevel, logger.LogrusBackend)
	err := errors.New("test")
	l.Info().AddStr("key", "value").AddErr(err).Flush("some message")
}

Output:

INFO[0000] some message                                  err=test err_stack="/Users/leo/Documents/code/vgo/test/main.go:13: test" fields.time="2019-03-29 15:53:48.947011 -0500 -05 m=+0.000544729" key=value
Multiple loggers

Use multiple loggers with different levels as if it was only a single one.

package main

import (
	"os"

	"github.com/juju/errors"
	"github.com/leononame/logger"
)

func main() {
	l1 := logger.New(os.Stdout, logger.InfoLevel, logger.LogrusBackend)
	l2 := logger.New(os.Stderr, logger.ErrorLevel, logger.ZeroLogBackend)
	l := logger.NewMulti(l1, l2)
	// This gets printed to stdout with logrus
	l.Info().AddStr("key", "value").Flush("message")
	err := errors.New("err1")
	err = errors.Annotate(err, "additional trace")
	// This gets printed to stdout with logrus and stderr with zerolog
	l.Error().AddErr(err).Flush("additional message")
}

Output:

INFO[0000] message                                       fields.time="2019-03-29 15:57:51.307562 -0500 -05 m=+0.000549599" key=value
ERRO[0000] additional message                            err="additional trace: err1" err_stack="/Users/leo/Documents/code/vgo/test/main.go:16: err1\n/Users/leo/Documents/code/vgo/test/main.go:17: additional trace" fields.time="2019-03-29 15:57:51.307811 -0500 -05 m=+0.000799046"
{"level":"error","err":"additional trace: err1","err_stack":"/Users/leo/Documents/code/vgo/test/main.go:16: err1\n/Users/leo/Documents/code/vgo/test/main.go:17: additional trace","time":1553893071,"message":"additional message"}
Customized logger

Customize your logrus parameters

package main

import (
	"time"

	"github.com/leononame/logger"
	"github.com/sirupsen/logrus"
)

func main() {
	ll := logrus.New()
	ll.WithField("source", "cli_client")
	ll.SetLevel(logrus.WarnLevel)
	// Alternatively: logger.FromZerolog instantiates a Logger with zerolog implementation
	l := logger.FromLogrus(ll)
	l.Info().AddStr("key", "value").Flush("message")
	l.Warn().AddInt("iteration", 1000).Flush("finished calculation")
	l.Error().AddDur("duration", time.Minute).Flush("duration calculated")
}

Output:

WARN[0000] finished calculation                          fields.time="2019-03-29 16:02:58.70876 -0500 -05 m=+0.000626253" iteration=1000
ERRO[0000] duration calculated                           duration=1m0s fields.time="2019-03-29 16:02:58.708996 -0500 -05 m=+0.000862119"

Logger API

Each logger implements the interface below. Calling WithField returns a logger that always logs the specified field. All other calls return a log entry (not written yet) that will log at the specified level.

// Logger is an standard interface for logging so that different log implementations can be wrapped around.
// The API is heavily influenced by the zerolog API for structured JSON logging
type Logger interface {
	// WithField returns a new Logger that always logs the specified field
	WithField(key, value string) Logger
	// Level creates a new Entry with the specified Level
	Level(Level) Entry
	// Debug creates a new Entry with level Debug
	Debug() Entry
	// Info creates a new Entry with level Info
	Info() Entry
	// Warn creates a new Entry with level Warn
	Warn() Entry
	// Error creates a new Entry with level Error
	Error() Entry
	// Fatal creates a new Entry with level Fatal. Executing a log at fatal level exits the application with exit code 1.
	Fatal() Entry
	// Panic creates a new Entry with level Panic. Executing a log at panic level will call panic().
	Panic() Entry
}

Below you will find the interface of a log entry. There are different functinos to add custom fields for structured logging. The Flush function dumps the log (only if the level of the entry is at least the logger's level). If Flush is not called, nothing will be logged

// Entry is an interface for a log entry. A single entry always has defined a log level. Custom fields can be
// added. An entry will never be written to the log unless Flush is called.
type Entry interface {
	// Flush writes the entry as a single log statement. Optionally, a message can be added which will
	// be included in the final log entry
	Flush(string)

	// Add a range of fields to the log statement
	AddFields(map[string]interface{}) Entry
	// Add an error to the log statement. The error will have the key "err". An error stack will be included
	// under the key "err_stack"
	AddErr(err error) Entry
	// Add an error to the log statement. An error stack will be included under the key "${key}_stack"
	AddError(key string, val error) Entry
	// Add a bool value to the log statement.
	AddBool(key string, val bool) Entry
	// Add an integer value to the log statement.
	AddInt(key string, val int) Entry
	// Add a string value to the log statement.
	AddStr(key string, val string) Entry
	// Add a time value to the log statement.
	AddTime(key string, val time.Time) Entry
	// Add a duration value to the log statement.
	AddDur(key string, val time.Duration) Entry
	// Add any value to the log statement.
	AddAny(key string, val interface{}) Entry
}

Documentation

Overview

Package logger contains an interface for abstracting any log implementation in case the implementation needs to be switched.

Logging can be a performance bottleneck due to slow JSON marshalling or bad concurrent implementation. Hence, an abstraction is needed. Currently this package implements two different log backends, zerolog for fast JSON logging and logrus for pretty logging. The implementation can be chosen on creation.

Index

Constants

View Source
const (
	// DebugLevel defines debug log level.
	DebugLevel Level = 7
	// InfoLevel defines info log level.
	InfoLevel = 6
	// WarnLevel defines warn log level.
	WarnLevel = 4
	// ErrorLevel defines error log level.
	ErrorLevel = 3
	// FatalLevel defines fatal log level.
	FatalLevel = 2
	// PanicLevel defines panic log level.
	PanicLevel = 1
)

Levels have the same value as syslog, hence 5 is skipped

Variables

This section is empty.

Functions

This section is empty.

Types

type Entry

type Entry interface {
	// Flush writes the entry as a single log statement. Optionally, a message can be added which will
	// be included in the final log entry
	Flush(string)

	// AddFields adds a range of fields to the log statement
	AddFields(map[string]interface{}) Entry
	// AddErr adds an error to the log statement. The error will have the key "err". An error stack will be included
	// under the key "err_stack"
	AddErr(err error) Entry
	// AddError adds an error to the log statement. An error stack will be included under the key "${key}_stack"
	AddError(key string, val error) Entry
	// AddBool adds a bool value to the log statement.
	AddBool(key string, val bool) Entry
	// AddInt adds an integer value to the log statement.
	AddInt(key string, val int) Entry
	// AddStr adds a string value to the log statement.
	AddStr(key string, val string) Entry
	// AddTime adds a time value to the log statement.
	AddTime(key string, val time.Time) Entry
	// AddDur adds a duration value to the log statement.
	AddDur(key string, val time.Duration) Entry
	// AddAny adds any value to the log statement.
	AddAny(key string, val interface{}) Entry
}

Entry is an interface for a log entry. A single entry always has defined a log level. Custom fields can be added. An entry will never be written to the log unless Flush is called.

type Implementation

type Implementation int
const (
	// ZeroLogBackend defines zerolog as the actual log implementation
	ZeroLogBackend Implementation = iota
	// LogrusBackend defines logrus as the actual log implementation
	LogrusBackend
	// GelfBackend initializes a new logger with zerolog, but logs in GELF format
	GelfBackend
)

type Level

type Level int

type Logger

type Logger interface {
	// WithField returns a new Logger that always logs the specified field
	WithField(key, value string) Logger
	// Level creates a new Entry with the specified Level
	Level(Level) Entry
	// Debug creates a new Entry with level Debug
	Debug() Entry
	// Info creates a new Entry with level Info
	Info() Entry
	// Warn creates a new Entry with level Warn
	Warn() Entry
	// Error creates a new Entry with level Error
	Error() Entry
	// Fatal creates a new Entry with level Fatal. Executing a log at fatal level exits the application with exit code 1.
	Fatal() Entry
	// Panic creates a new Entry with level Panic. Executing a log at panic level will call panic().
	Panic() Entry
}

Logger is an standard interface for logging so that different log implementations can be wrapped around. The API is heavily influenced by the zerolog API for structured JSON logging

func FromLogrus

func FromLogrus(l logrus.FieldLogger) Logger

FromLogrus creates a logger instance from an existing logrus logger

func FromZerolog

func FromZerolog(l *zerolog.Logger) Logger

FromZerolog creates a logger instance from an existing zerolog logger

func New

func New(w io.Writer, lvl Level, impl Implementation) Logger

New returns a logger. The logger will write to the writer specified and will use the log backend specified

func NewMulti

func NewMulti(ls ...Logger) Logger

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL