lager

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 24, 2020 License: Unlicense Imports: 11 Imported by: 10

README

Lager

Logs (in golang) that are easy for computers to parse, easy for people to read, and easy for programmers to generate. The logs are written in JSON format but the order of the data in each line is controlled to make the logs quite nice for humans to read even with no processing or tooling.

This is most similar to https://github.com/uber-go/zap and is about as efficient in CPU usage and lack of allocations per log line. But this efficiency was not allowed to make it inconvenient to write code that uses this library.

Another departure from most logging systems is that Lager encourages you to log data, not messages. This difference can be a little subtle, but it can make it faster to add logging to code while also making it more likely that you'll be able to parse out the data you care about from the log rather than having to do string matching.

It also provides more granularity than is typical when controlling which log lines to write and which to suppress. It offers 9 log levels, most of which can be enabled individually (enabling "Debug" does not force you to also enable "Info"). You can also easily allow separate log levels for specific packages or any other logical division you care to use.

Typical logging code:

lager.Fail(ctx).Map("Err", err, "for", obj)

might produce a log line like:

["2018-12-31 23:59:59.870Z", "FAIL", {"Err":"bad cost", "for":{"cost":-1}}]

Note that the key/value pairs are always logged in the same order you listed them in your code.

But you can even avoid having to come up with key/value pairs, which can be convenient for quickly adding logging:

lager.Debug(ctx).List("Connecting to", host, port, path)

In order to be efficient and to keep control over the order of output, Lager mostly avoids using the "encoding/json" library and also does not use reflection. But you can pass in any arbitrary data. If it isn't one of the many data types natively supported by Lager, then it will use "encoding/json" to marshal it.

Documentation

Overview

Logs that are easy for computers to parse, easy for people to read, and easy for programmers to generate. The logs are written in JSON format but the order of the data in each line is controlled to make the logs quite nice for humans to read even with no processing or tooling.

lager.Fail(ctx).List("Can't reach", dest, err)

could output:

["2018-12-31 23:59:59.860Z", "FAIL", "Can't reach", "localhost", "refused"]

unless the Fail log level had been disabled. While:

lager.Info().Map("Err", err, "for", obj)

logs nothing by default. But if Info log level is enabled, it might log:

["2018-12-31 23:59:59.870Z", "INFO", {"Err":"bad cost", "for":{"cost":-1}}]

There are 9 log levels and 7 can be separately enabled or diabled. You usually use them via code similar to:

lager.Panic().List(args...) // Calls panic(), always enabled.
lager.Exit().Map(pairs...)  // To Stderr, calls os.Exit(1), always enabled.
lager.Fail().List(args...)  // For non-normal failures (default on).
lager.Warn().Map(pairs...)  // For warnings (default on).
lager.Acc().Map(pairs...)   // For access logs (default on).
lager.Info().List(args...)  // For normal events (default off).
laber.Trace().List(args...) // For tracing code flow (default off).
laber.Debug().Map(pairs...) // For debugging details (default off).
laber.Obj().List(args....)  // For object dumps (default off).
lager.Guts().Map(pairs...)  // For volumious data dumps (default off).

The Panic and Exit levels cannot be disabled. Of the others, only Fail, Warn, and Acc levels are enabled by default. When the Lager module is initialized, the following code is called:

lager.Init(os.Getenv("LAGER_LEVELS"))

and lager.Init("") is the same as lager.Init("Fail Warn Acc") [which is the same as lager.Init("FWA")]. So you can set the LAGER_LEVELS environment variable to change which log levels are enabled. Application code can also choose to support other ways to override those defaults.

If you want to decorate each log line with additional key/value pairs, then you can accumulate those in a context.Context value that gets passed around and then pass that Context in when logging:

// In a method dispatcher, for example:
ctx = lager.AddPairs(ctx, "srcIP", ip, "user", username)

// In a method or code that a method calls, for example:
lager.Info(ctx).Map(pairs...)

// Or, if you have multiple log lines at the same level:
debug := lager.Debug(ctx)
debug.Map(pairs...)
debug.List(args...)

Most log archiving systems expect JSON log lines to be a hash not a list. This is globally enabled by specifying the keys to use for the items that can be included in a log line: timestamp, level, single item, list data, context, and module.

// Example choice of logging keys:
lager.Keys("t", "l", "msg", "a", "", "mod")

// Sample usage of List():
lager.Fail(ctx).List("Conn", dest, err)
// Example log line from above code:
{"t":"2018-12-31 23:59:59.860Z", "l":"FAIL", "a":["Conn", "mx", "reset"]}

// Sample usage of Map():
lager.Warn().Map("Err", err, "for", obj)
// Example log line from above code:
{"t":"2018-12-31 23:59:59.870Z", "l":"WARN", "Err":"<0", "for":{"cost":-1}}

Index

Constants

This section is empty.

Variables

View Source
var OutputDest io.Writer

Set OutputDest to a non-nil io.Writer to not write logs to os.Stdout and os.Stderr.

View Source
var PathParts = 0

'pathParts' to use when -1 is passed to WithCaller() or WithStack().

Functions

func GetModuleLevels

func GetModuleLevels(name string) string

En-/disables log levels for the named module. If no module by that name exists yet, then "n/a" is returned. Otherwise returns the enabled levels.

func GetModules

func GetModules() map[string]string

Returns a map[string]string where the keys are all of the module names and the values are their enabled levels.

func Init

func Init(levels string)

En-/disables log levels. Pass in a string of letters from "FWAITDOG" to indicate which log levels should be the only ones that produce output. Each letter is the first letter of a log level (Fail, Warn, Acc, Info, Trace, Debug, Obj, or Guts). Levels Panic and Exit are always enabled. Init("") acts like Init("FWA"), the default setting. To disable all optional logs, you can use Init("-") as any characters not from "FWAITDOG" are silently ignored. So you can also call Init("Fail Warn Acc Info").

func Keys

func Keys(when, lev, msg, args, ctx, mod string)

Output each future log line as a JSON hash ("object") using the given keys. 'when' is used for the timestamp. 'lev' is used for the log level name. 'msg' is either "" or will be used when a single argument is passed to List(). 'args' is used for the arguments to List() when 'msg' is not. 'ctx' is used for the hash context values (if any). Specify "" for 'ctx' to have any context key/value pairs included in-line in the top-level JSON hash (care should be taken to avoid duplicate key names). 'mod' is used for the module name (if any).

Pass in 6 empty strings to revert to logging a JSON list (array).

func S

func S(arg interface{}) string

Converts an arbitrary value to a string. Very similar to fmt.Sprintf("%v",arg) but treats []byte values the same as strings rather then dumping them as a list of byte values in base 10.

func SetModuleLevels

func SetModuleLevels(name, levels string) bool

En-/disables log levels for the named module. If no module by that name exists yet, then false is returned.

Types

type AList

type AList = []interface{}

A list type that we efficiently convert to JSON.

func List

func List(args ...interface{}) AList

Returns a slice (lager.AList) that can be passed as an argument to another List() or Map() call to construct nested data that can be quickly serialized to JSON.

lager.Info().Map("User", user, "not in", lager.List(one, two, three))

type AMap

type AMap = *KVPairs

A processed list of key/value pairs we can efficiently convert to JSON.

func ContextPairs

func ContextPairs(ctx Ctx) AMap

Fetches the lager key/value pairs stored in a context.Context.

func Pairs

func Pairs(pairs ...interface{}) AMap

Returns a (processed) list of key/value pairs (lager.AMap) that can be added to a context.Context to specify additional data to append to each log line.

func (AMap) AddPairs

func (p AMap) AddPairs(pairs ...interface{}) AMap

Return an AMap with the passed-in key/value pairs added to and/or replacing the keys/values from the method receiver.

func (AMap) InContext

func (p AMap) InContext(ctx Ctx) Ctx

Get a new context with this map stored in it.

func (AMap) Merge

func (a AMap) Merge(b AMap) AMap

Return an AMap with the keys/values from the passed-in AMap added to and/or replacing the keys/values from the method receiver.

type Ctx

type Ctx = context.Context

Just an alias for context.Context that takes up less space in function signatures. You never need to use lager.Ctx in your code.

func AddPairs

func AddPairs(ctx Ctx, pairs ...interface{}) Ctx

Add/update Lager key/value pairs to/in a context.Context.

type KVPairs

type KVPairs struct {
	// contains filtered or unexported fields
}

Storage for an ordered list of key/value pairs (without duplicate keys).

type Lager

type Lager interface {
	// Writes a single log line to Stdout in JSON format encoding a UTC time-
	// stamp followed by the log level and the passed-in values.  For example:
	//   lager.Warn().List("Timeout", dest, secs)
	// might output:
	//   ["2018-12-31 23:59:59.086Z", "WARN", ["Timeout", "dbhost", 5]]
	// Or logs nothing if the corresponding log level is not enabled.
	List(args ...interface{})

	// Same as '.WithCaller(0,-1).List(...)'.  Good to use when your data does
	// not contain a fairly unique string to search for in the code.
	CList(args ...interface{})

	// Writes a single log line to Stdout in JSON format encoding a list of
	// a UTC timestamp (string), the log level (string), and a map composed
	// of the key/value pairs passed in.  For example:
	//   lager.Warn().Map("Err", err, "for", obj)
	// might output:
	//   ["2018-12-31 23:59:59.086Z", "WARN", {"Err":"no cost", "for":{"c":-1}}]
	// Or logs nothing if the corresponding log level is not enabled.
	Map(pairs ...interface{})

	// Same as '.WithCaller(0,-1).Map(...)'.  Good to use when your data does
	// not contain a fairly unique string to search for in the code.
	CMap(pairs ...interface{})

	// Gets a new Lager that adds to each log line the key/value pairs from
	// zero or more context.Context values.
	With(ctxs ...context.Context) Lager

	// Does this Lager log anything?
	Enabled() bool

	// Adds a "_stack" key/value pair to the logged context.  The value
	// is a list of strings where each string is a line number (base 10)
	// followed by a space and then the code file name (shortened to the last
	// 'pathParts' components).
	//
	// If 'stackLen' is 0 (or negative), then the full stack trace will be
	// included.  Otherwise, the list will contain at most 'stackLen' strings.
	// The first string will always be for depth 'minDepth'.
	//
	// See WithCaller() for details on usage of 'minDepth' and 'pathParts'.
	WithStack(minDepth, stackLen, pathParts int) Lager

	// Adds "_file" and "_line" key/value pairs to the logged context.
	// 'depth' of 0 means the line where WithCaller() was called.  'depth'
	// of 1 would be the line of the caller of the caller of WithCaller().
	//
	// 'pathParts' indicates how many directories to include in the code file
	// name.  A 0 'pathParts' includes the full path.  A 1 would only include
	// the file name.  A 2 would include the file name and the deepest sub-
	// directory name.  A -1 uses the value of lager.PathParts.
	WithCaller(depth, pathParts int) Lager
}

The interface returned from lager.Warn() and the other log-level selectors.

func Acc

func Acc(cs ...Ctx) Lager

Returns a Lager object. If Acc log level has been disabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to write access logs. The level is recorded as "ACCESS".

func Debug

func Debug(cs ...Ctx) Lager

Returns a Lager object. If Debug log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to log important details that may help in debugging.

func Exit

func Exit(cs ...Ctx) Lager

Returns a Lager object that writes to os.Stderr then calls os.Exit(1). This log level is often called "Fatal" but loggers are inconsistent as to whether logging at the Fatal level causes the process to exit. By naming this level Exit, that ambiguity is removed.

func Fail

func Fail(cs ...Ctx) Lager

Returns a Lager object. If Fail log level has been disabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to report errors that are not part of the normal flow.

func Guts

func Guts(cs ...Ctx) Lager

Returns a Lager object. If Guts log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level for debugging data that is too voluminous to always include when debugging.

func Info

func Info(cs ...Ctx) Lager

Returns a Lager object. If Info log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to report major milestones that are part of normal flow.

func Level

func Level(lev byte, cs ...Ctx) Lager

Pass in one character from "PEFWAITDOG" to get a Lager object that either logs or doesn't, depending on whether the specified log level is enabled.

func Obj

func Obj(cs ...Ctx) Lager

Returns a Lager object. If Obj log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to log the details of internal data structures.

func Panic

func Panic(cs ...Ctx) Lager

Returns a Lager object that calls panic(). The JSON log line is first output to os.Stderr and then

panic("lager.Panic() logged (see above)")

is called.

func Trace

func Trace(cs ...Ctx) Lager

Returns a Lager object. If Trace log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to trace how execution is flowing through the code.

func Warn

func Warn(cs ...Ctx) Lager

Returns a Lager object. If Warn log level has been disabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to report unusual conditions that may be signs of problems.

type Module

type Module struct {
	// contains filtered or unexported fields
}

A named module that allows separate log levels to be en-/disabled.

func NewModule

func NewModule(name string, defaultLevels ...string) *Module

Create a new Module with the given name. Default log levels can also be passed in as an optional second argument. The initial log levels enabled are taken from the last item in the list that is not "":

The current globally enabled levels.
The (optional) passed-in defaultLevels.
The value of the LAGER_{module_name}_LEVELS environment variable.

If you wish to ignore the LAGER_{module_name}_LEVELS environment varible, then write code similar to:

mod := lager.NewModule("mymod").Init("FW")

func (*Module) Acc

func (m *Module) Acc(cs ...Ctx) Lager

Returns a Lager object. If Acc log level has been disabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to write access logs.

func (*Module) Debug

func (m *Module) Debug(cs ...Ctx) Lager

Returns a Lager object. If Debug log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to log important details that may help in debugging.

func (*Module) Exit

func (m *Module) Exit(cs ...Ctx) Lager

Returns a Lager object that writes to os.Stderr then calls os.Exit(1). This log level is often called "Fatal" but loggers are inconsistent as to whether logging at the Fatal level causes the process to exit. By naming this level Exit, that ambiguity is removed.

func (*Module) Fail

func (m *Module) Fail(cs ...Ctx) Lager

Returns a Lager object. If Fail log level has been disabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to report errors that are not part of the normal flow.

func (*Module) Guts

func (m *Module) Guts(cs ...Ctx) Lager

Returns a Lager object. If Guts log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level for debugging data that is too voluminous to always include when debugging.

func (*Module) Info

func (m *Module) Info(cs ...Ctx) Lager

Returns a Lager object. If Info log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to report major milestones that are part of normal flow.

func (*Module) Init

func (m *Module) Init(levels string) *Module

En-/disables log levels. Pass in a string of letters from "FWAITDOG" to indicate which log levels should be the only ones that produce output. Each letter is the first letter of a log level (Fail, Warn, Acc, Info, Trace, Debug, Obj, or Guts). Levels Panic and Exit are always enabled. Init("") copies the current globally enabled levels. To disable all optional logs, you can use Init("-") as any characters not from "FWAITDOG" are silently ignored. So you can also call Init("Fail Warn Acc Info").

func (*Module) Level

func (m *Module) Level(lev byte, cs ...Ctx) Lager

Pass in one character from "PEFWITDOG" to get a Lager object that either logs or doesn't, depending on whether the specified log level is enabled.

func (*Module) Obj

func (m *Module) Obj(cs ...Ctx) Lager

Returns a Lager object. If Obj log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to log the details of internal data structures.

func (*Module) Panic

func (m *Module) Panic(cs ...Ctx) Lager

Returns a Lager object that calls panic(). The JSON log line is first output to os.Stderr and then

panic("lager.Panic() logged (see above)")

is called.

func (*Module) Trace

func (m *Module) Trace(cs ...Ctx) Lager

Returns a Lager object. If Trace log level is not enabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to trace how execution is flowing through the code.

func (*Module) Warn

func (m *Module) Warn(cs ...Ctx) Lager

Returns a Lager object. If Warn log level has been disabled, then the returned Lager will be one that does nothing (produces no output). Use this log level to report unusual conditions that may be signs of problems.

type RawMap

type RawMap []interface{}

A raw list of key/value pairs we can efficiently convert to JSON as a map.

func Map

func Map(pairs ...interface{}) RawMap

Returns a raw list of key/value pairs (lager.RawMap) that can be passed as an argument to another List() or Map() call to construct nested data that can be quickly serialized to JSON.

Dupliate keys all get output. If you need to squash duplicate keys, then call lager.Pairs() instead.

lager.Info(ctx).List("Using", lager.Map("name", name, "age", age))

Jump to

Keyboard shortcuts

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