lager

package module
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: May 18, 2022 License: Unlicense Imports: 17 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.

Logging code like:

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.

Forks

If you use a fork of this repository and want to have changes you make accepted upstream, you should use the fork by adding (to go.mod in modules where you use the fork) a line like:

replace github.com/TyeMcQueen/go-lager => github.com/your/lager v1.2.3

And then use "github.com/TyeMcQueen/go-lager" in 'import' statements and in a 'require' directive in the go.mod. (See "replace directive" in https://go.dev/ref/mod.)

Documentation

Overview

Lager makes logs that are easy for computers to parse, easy for people to read, and easy for programmers to generate. It also encourages logging data over logging messages, which tends to make logs more useful as well as easier for programmers to generate.

You don't need to pass around a logging object. You can decorate a Go context.Context with additional data to be added to each log line written when that context applies.

The logs are written in JSON format but the items in JSON are written in a controlled order, preserving the order used in the program code. This makes the logs pretty easy for humans to scan even with no processing or tooling.

Typical logging code like:

lager.Fail(ctx).MMap("Can't merge", "dest", dest, "err", err)
// MMap() takes a message followed by a map of key/value pairs.

could output:

["2019-12-31 23:59:59.1234Z", "FAIL", "Can't merge",
	{"dest":"localhost", "err":"refused"}]

(but as a single line). If you declare that the code is running inside Google Cloud Platform (GCP), it could instead output:

{"time":"2019-12-31T23:59:59.1234Z", "severity":"500",
	"message":"Can't merge", "dest":"localhost", "err":"refused"}

(as a single line) which GCP understands well but note that it is still easy for a human to read with the consistent order used.

You don't even need to take the time to compose and type labels for data items, if it doesn't seem worth it in some cases:

lager.Fail(ctx).MList("Can't merge", dest, err)
// MList() takes a message followed by arbitrary data items.

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

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

Panic and Exit cannot be disabled. Fail, Warn, Note, and Acc are enabled by default.

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).MMap(msg, pairs...)

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

Most log archiving systems expect JSON log lines to be a hash (map/object) not a list (array). To get that you just declare what labels to use for: timestamp, level, message, list data, context, and module.

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

Support for GCP CloudTrace is integrated.

Index

Constants

View Source
const GcpSpanKey = "logging.googleapis.com/spanId"
View Source
const GcpTraceKey = "logging.googleapis.com/trace"
View Source
const InlinePairs = inlinePairs("")

InlinePairs can be used as a "label" to indicate that the following value that contains label-subvalue pairs (a value of type AMap or RawMap) should be treated as if the pairs had been passed in at that higher level.

func Assert(pairs ...interface{}) {
    lager.Fail().MMap("Assertion failed", lager.InlinePairs, pairs)
}
View Source
const SkipThisPair = skipThisPair("")

SkipThisPair can be used as a "label" to indicate that the following value should just be ignored. You would usually call lager.Unless() rather than use this directly.

Variables

View Source
var LevelNotation = func(lev string) string { return lev }

LevelNotation takes a log level name (like "DEBUG") and returns how that level should be shown in the log. This defaults to not changing the level name. If the environment variable LAGER_GCP is non-empty, then it instead defaults to using GcpLevelName().

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 GcpFakeResponse added in v1.1.5

func GcpFakeResponse(status int, size int64, desc string) *http.Response

GcpFakeResponse() creates an http.Response suitable for passing to GcpHttp() [or similar] when you just have a status code (and/or a response size) and not a http.Response.

Pass 'size' as -1 to omit this information. Passing 'status' as 0 will cause an explicit 0 to be logged. Pass 'status' as -1 to omit it. Pass 'desc' as "" to have it set based on 'status'.

func GcpFinishSpan added in v1.1.5

func GcpFinishSpan(span spans.Factory, resp *http.Response) time.Duration

GcpFinishSpan() updates a span with the status information from a http.Response and Finish()es the span (which registers it with GCP).

func GcpHttpF added in v1.1.5

func GcpHttpF(
	req *http.Request, resp *http.Response, start *time.Time,
) func() interface{}

GcpHttpF() can be used for logging just like GcpHttp(), it just returns a function so that the work is only performed if the logging level is enabled.

If you are including GcpHttp() information in a lot of log lines [which can be quite useful], then you can get even more efficiency by adding the pair ' "httpRequest", GcpHttp(req, nil, nil) ' to your Context [which you then pass to 'lager.Warn(ctx)', for example] so the data is only calculated once. You can add this to an *http.Request's Context by calling GcpRequestAddTrace().

For this to work best, you should specify "" as the key name for context information; which is automatically done if LAGER_GCP is non-empty in the environment and LAGER_KEYS is not set.

You'll likely include 'GcpHttp(req, resp, &start)' in one log line [to record the response information and latency, not just the request]. If you added "httpRequest" to your context, then that logging is best done via:

lager.Acc(
    lager.AddPairs(ctx, "httpRequest", GcpHttp(req, resp, &start)),
).List("Response sent")

so that the request-only information is not also output. Doing this via GcpLogAccess() is easier.

func GcpLevelName added in v1.1.2

func GcpLevelName(lev string) string

GcpLevelName takes a Lager level name (only the first letter matters and it must be upper case) and returns the corresponding value GCP uses in structured logging to represent the severity of such logs. Levels are mapped as:

Not used: Alert ("700") and Emergency ("800")
Panic, Exit - Critical ("600")
Fail - Error ("500")
Warn - Warning ("400")
Note - Notice ("300")
Access, Info - Info ("200")
Trace, Debug, Obj, Guts - Debug ("100")
If an invalid level name is passed: Default ("0")

If the environment variable LAGER_GCP is not empty, then lager.LevelNotation will be initalized to lager.GcpLevelName.

func GcpProjectID added in v1.1.5

func GcpProjectID(ctx Ctx) (string, error)

GcpProjectID() returns the current GCP project ID [which is not the project number]. Once the lookup succeeds, that value is saved and returned for subsequent calls. The lookup times out after 0.1s.

Set GCP_PROJECT_ID in your environment to avoid the more complex lookup.

func GcpReceivedRequest added in v1.1.5

func GcpReceivedRequest(pReq **http.Request) spans.Factory

GcpReceivedRequest() gets the Context from '*pReq' and uses it to call GcpContextReceivedRequest(). Then it replaces '*pReq' with a version of the request with the new Context attached. Then it returns the Factory.

It is usually called in a manner similar to:

defer spans.FinishSpan(lager.GcpReceivedRequest(&req))

or

var resp *http.Response
defer lager.GcpSendingResponse(
    lager.GcpReceivedRequest(&req), req, resp)

Using GcpContextReceivedRequest() can be slightly more efficient if you either start with a Context different from the one attached to the Request or will not attach the new Context to the Request (or will adjust it further before attaching it) since each time WithContext() is called on a Request, the Request must be copied.

The spans package is from "github.com/TyeMcQueen/tools-gcp/trace/spans".

func GcpReceivedResponse added in v1.1.5

func GcpReceivedResponse(
	span spans.Factory,
	req *http.Request,
	resp *http.Response,
	pairs ...interface{},
)

GcpReceivedResponse() combines GcpLogAccess() and GcpFinishSpan(). The access log line written will use the message "Received response" and will include the passed-in 'pairs' which should be zero or more pairs of a string key followed by an arbitrary value. However, logging every response received from a dependent service may be excessive.

func GcpSendingNewRequest added in v1.1.5

func GcpSendingNewRequest(
	ctx Ctx, method, url string, body io.Reader,
) (*http.Request, spans.Factory, error)

GcpSendingNewRequest() does several things that are useful when a server is about to send a request to a dependent service, by calling GcpContextSendingRequest(). It takes the same arguments as http.NewRequestWithContext() but returns an extra value.

It is usually called in a manner similar to:

req, span, err := lager.GcpSendingNewRequest(ctx, "GET", url, nil)
if nil != err { ... }
defer span.FinishSpan(span)

func GcpSendingRequest added in v1.1.5

func GcpSendingRequest(pReq **http.Request) spans.Factory

GcpSendingRequest() does several things that are useful when a server is about to send a request to a dependent service, by calling GcpContextSendingRequest(). It uses the Context from '*pReq' and then replaces '*pReq' with a copy of the original Request but with the new Context attached.

It is usually called in a manner similar to:

defer spans.FinishSpan(lager.GcpSendingRequest(&req))

func GcpSendingResponse added in v1.1.5

func GcpSendingResponse(
	span spans.Factory,
	req *http.Request,
	resp *http.Response,
	pairs ...interface{},
)

GcpSendingResponse() does several things that are useful when a server is about to send a response to a request it received. It combines GcpLogAccess() and GcpFinishSpan(). The access log line written will use the message "Sending response" and will include the passed-in 'pairs' which should be zero or more pairs of a string key followed by an arbitrary value.

'resp' will often be constructed via GcpFakeResponse().

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 "FWNAITDOG" 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, Note, Acc, Info, Trace, Debug, Obj, or Guts). Levels Panic and Exit are always enabled. Init("") acts like Init("FWNA"), the default setting. To disable all optional logs, you can use Init("-") as any characters not from "FWNAITDOG" are silently ignored. So you can also call Init("Fail Warn Note 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(). 'msg' is also used for the first argument to MMap() and MList(). 'args' is used for the arguments to List() when 'msg' is not. 'mod' is used for the module name (if any).

'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. In this case, care should be taken to avoid using the same key name both in a context pair and in a pair being passed to, for example, MMap(). If you do that, both pairs will be output but anything parsing the log line will only record one of the pairs.

If the environment variable LAGER_KEYS is set it must contain 6 key names separated by commas and those become the default keys to use. Otherwise, if the environment variable LAGER_GCP is not empty, then it is as if you had the following set (among other changes):

LAGER_KEYS="time,severity,message,data,,module"

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

func RunningInGcp added in v1.1.5

func RunningInGcp()

RunningInGcp() tells Lager to log messages in a format that works best when running inside of the Google Cloud Platform (GCP). You can call it from your main() func so you don't have to set LAGER_GCP=1 in your environment. Do not call it from an init() function as that can cause a race condition.

In particular, it runs:

if "" == os.Getenv("LAGER_KEYS") {
    lager.Keys("time", "severity", "message", "data", "", "module")
}
lager.LevelNotation = lager.GcpLevelName

It also arranges for an extra element to be added to the JSON if nothing but a message is logged so that jsonPayload.message does not get transformed into textPayload when the log is ingested into Cloud Logging.

RunningInGcp() is called automatically if the "LAGER_GCP" environment variable contains a non-empty value when the process is started.

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.

func Unless added in v1.1.2

func Unless(cond bool, label string) interface{}

Unless() is used to pass an optional label+value pair to Map(). Use Unless() to specify the label and, if the value is unsafe or expensive to compute, then wrap it in a deferring function:

lager.Debug().Map(
    "Ran", stage,
    // Don't include `"Error": null,` in the log:
    lager.Unless(nil == err, "Error"), err,
    // Don't call config.Proxy() if config is nil:
    lager.Unless(nil == config, "Proxy"),
        func() interface{} { return config.Proxy() },
)

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.

func GcpContextAddTrace added in v1.1.5

func GcpContextAddTrace(ctx Ctx, span spans.Factory) Ctx

GcpContextAddTrace() takes a Context and returns one that has the span added as 2 pairs that will be logged and recognized by GCP when that Context is passed to lager.Warn() or similar methods. If 'span' is 'nil' or an empty Factory, then the original 'ctx' is just returned.

'ctx' is the Context from which the new Context is created. 'span' contains the GCP CloudTrace span to be added.

See also GcpContextReceivedRequest() and/or GcpContextSendingRequest() which call this and do several other useful things.

func GcpContextReceivedRequest added in v1.1.5

func GcpContextReceivedRequest(
	ctx Ctx, req *http.Request,
) (Ctx, spans.Factory)

GcpContextReceivedRequest() does several things that are useful when a server receives a new request. 'ctx' is the Context passed to the request handler and 'req' is the received request.

An "httpRequest" key/value pair is added to the Context so that the request details will be included in any subsequent log lines [when the returned Context is passed to lager.Warn() or similar methods].

If the request headers include GCP trace information, then that is extracted [see spans.Factory.ImportFromHeaders()].

If 'ctx' contains a spans.Factory, then that is fetched and used to create either a new sub-span or (if there is no CloudTrace context in the headers) a new trace (and span). If the Factory is able to create a new span, then it is marked as a "SERVER" span and it is stored in the context via spans.ContextStoreSpan().

If a span was imported or created, then the span information is added to the Context as pairs to be logged [see GcpContextAddTrace()] and a span will be contained in the returned Factory.

The updated Context is returned (Contexts are immutable).

It is usually called in a manner similar to:

ctx, span := lager.GcpContextReceivedRequest(ctx, req)
defer spans.FinishSpan(span)

or

ctx, span := lager.GcpContextReceivedRequest(ctx, req)
var resp *http.Response
defer lager.GcpSendingResponse(span, req, resp)

See also GcpReceivedRequest().

The order of arguments is 'ctx' then 'req' as information moves only in the direction ctx <- req (if we consider 'ctx' to represent both the argument and the returned value) and information always moves right-to- left in Go (in assignment statements and when using channels).

func GcpContextSendingRequest added in v1.1.5

func GcpContextSendingRequest(req *http.Request, ctx Ctx) (Ctx, spans.Factory)

GcpContextSendingRequest() does several things that are useful when a server is about to send a request to a dependent service. 'req' is the Request that is about to be sent. 'ctx' is the server's current Context.

The current span is fetched from 'ctx' [such as the one placed there by GcpReceivedRequest() when the original request was received]. A new sub-span is created, if possible. If so, then it is marked as a "CLIENT" span, it is stored in the Context via spans.ContextStoreSpan(), the returned Factory will contain the new span, and the updated Context will contain 2 pairs (to be logged) from the new span. Note that the original Context is not (cannot be) modified, so the trace/span pair logged after the request-sending function returns will revert to the prior span.

If a span was found or created, then its CloudContext is added to the headers for 'req' so that the dependent service can log it and add its own spans to the trace (unless 'req' is 'nil').

The updated Context is returned (Contexts are immutable).

The order of arguments is 'req' then 'ctx' as information moves only in the direction req <- ctx and information always moves right-to-left in Go (in assignment statements and when using channels).

It is usually called in a manner similar to:

ctx, span := lager.GcpContextSendingRequest(req, ctx)
defer span.FinishSpan(span)

See also GcpSendingRequest().

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{})

	// If Keys() have not been set, then acts similar to List().
	//
	// If Keys() have been set, then acts similar to:
	//      Map("msg", message, "args", lager.List(args...))
	// except the "msg" and "data" keys are taken from the Keys() config.
	//
	// Prefer MList() or List() with a single argument over List() with
	// multiple arguments if using log parsers that expect to always have
	// a "message" string (such as GCP logging).  But try to avoid
	// interpretting data into your message string as this can make
	// structured logs less useful.
	MList(message string, args ...interface{})

	// Same as '.WithCaller(0,-1).MList(...)'.
	CMList(message string, 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{})

	// The same as Map("message", message, pairs...) except instead of using
	// the hard-coded "message" key, it uses the corresponding key configured
	// via Keys().
	//
	// Prefer MMap() over Map() if using log parsers that expect to always
	// have a "message" string (such as GCP logging).
	MMap(message string, pairs ...interface{})

	// Same as '.WithCaller(0,-1).MMap(...)'.
	CMMap(message string, 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 Println() method is provided for minimal compatibility with
	// log.Logger, as this method is the one most used by other modules.
	// It is just an alias for the List() method.
	Println(...interface{})
}

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 GcpLogAccess added in v1.1.5

func GcpLogAccess(
	req *http.Request, resp *http.Response, pStart *time.Time,
) Lager

GcpLogAccess() creates a standard "access log" entry. It is just a handy shortcut for:

lager.Acc(
    lager.AddPairs(req.Context(),
        "httpRequest", GcpHttp(req, resp, pStart)))

You would use it like, for example:

lager.GcpLogAccess(req, resp, &start).MMap(
    "Response sent", "User", userID)

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 minor milestones that are part of normal flow.

func Level

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

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

func Note added in v1.1.2

func Note(cs ...Ctx) Lager

Returns a Lager object. If Note log level has been disabled, 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 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 minor 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 "FWNAITDOG" 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, Note, 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 "FWNAITDOG" are silently ignored. So you can also call Init("Fail Warn Note 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) Note added in v1.1.2

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

Returns a Lager object. If Note log level has been disabled, 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) 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 GcpHttp added in v1.1.2

func GcpHttp(req *http.Request, resp *http.Response, start *time.Time) RawMap

GcpHtttp() returns a value for logging that GCP will recognize as details about an HTTP(S) request (and perhaps its response), if placed under the key "httpRequest".

'req' must not be 'nil' but 'resp' and 'start' can be. None of the arguments passed will be modified; 'start' is of type '*time.Time' only to make it simple to omit latency calculations by passing in 'nil'. If 'start' points to a 'time.Time' that .IsZero(), then it is ignored.

When using tracing, this allows GCP logging to display log lines for the same request (if each includes this block) together. So this can be a good thing to add to a context.Context used with your logging. For this to work, you must log a final message that includes all three arguments (as well as using GCP-compatible tracing).

The following items will be logged (in order in the original JSON, but GCP does not preserve order of JSON keys, understandably), except that some can be omitted depending on what you pass in.

"requestMethod"     E.g. "GET"
"requestUrl"        E.g. "https://cool.me/api/v1"
"protocol"          E.g. "HTTP/1.1"
"status"            E.g. 403
"requestSize"       Omitted if the request body size is not yet known.
"responseSize"      Omitted if 'resp' is 'nil' or body size not known.
"latency"           E.g. "0.1270s".  Omitted if 'start' is 'nil'.
"remoteIp"          E.g. "127.0.0.1"
"serverIp"          Not currently ever included.
"referer"           Omitted if there is no Referer[sic] header.
"userAgent"         Omitted if there is no User-Agent header.

Note that "status" is logged as "0" in the special case where 'resp' is 'nil' but 'start' is not 'nil'. This allows you to make an "access log" entry for cases where you got an error that prevents you from either making or getting an http.Response.

See also GcpHttpF() and GcpRequestAddTrace().

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))

type Stringer added in v1.1.2

type Stringer interface {
	String() string
}

A Stringer just has a String() method that returns its stringification.

Jump to

Keyboard shortcuts

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