From 52eb6b2fe675fd6d3462833579b01ca4ca0c0bc2 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 6 Mar 2014 21:20:13 -0500 Subject: [PATCH] readme: update --- README.md | 171 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 114 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 1449967..be34190 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,132 @@ # Logrus -Logrus is a simple, opinionated logging package for Go. Features include: +Logrus is a simple, opinionated logging package for Go which is completely API +compatible with the standard library logger. It has six logging levels: Debug, +Info, Warn, Error, Fatal and Panic. It supports custom logging formatters, and +ships with JSON and nicely formatted text by default. It encourages the use of +logging key value pairs for discoverability. Logrus allows you to add hooks to +logging events at different levels, for instance to notify an external error +tracker. -* **Level logging**. Logrus has the levels: Debug, Info, Warning and Fatal. -* **Exceptions**. Warnings will log as an exception along with logging it to - out, without quitting. Fatal will do the same, but call `os.Exit(1)` after - emitting the exception. -* **JSON**. Logrus currently logs as JSON by default. +#### Fields -The API is completely compatible with the Go standard lib logger, with only the -features above added. - -## Motivation - -The motivation for this library came out of a pattern seen in Go applications me -and others have been writing with functions such as: +Logrus encourages careful, informative logging. It encourages the use of logging +fields, instead of long, unparseable error messages. For example, instead of: +`log.Fatalf("Failed to send event %s to topic %s with key %d")`, you should log +the much more discoverable: ```go -func reportFatalError(err error) { - airbrake.Notify(err) - log.Fatal(err) -} +log = logrus.New() +log.WithFields(&logrus.Fields{ + "event": event, + "topic": topic, + "key": key +}).Fatal("Failed to send event") +``` -func reportWarning(err error) { - airbrake.Notify(err) +We've found this API forces you to think about logging in a way that produces +much more useful logging messages. The `WithFields` call is optional. + +#### Hooks + +You can add hooks for logging levels. For example to send errors, to an +exception tracking service: + +```go +log.AddHook("error", func(entry logrus.Entry) { + err := airbrake.Notify(errors.New(entry.String())) + if err != nil { + log.WithFields(logrus.Fields{ + "source": "airbrake", + "endpoint": airbrake.Endpoint, + }).Info("Failed to send error to Airbrake") + } +}) +``` + +#### Errors + +You can also use Logrus to return errors with fields. For instance: + +```go +err := record.Destroy() +if err != nil { + return log.WithFields(&logrus.Fields{ + "id": record.Id, + "method": "destroy" + }).AsError("Failed to destroy record") } ``` -JSON logging is excellent for parsing logs for analysis and troubleshooting. -It's supported natively by log aggregators such as logstash and Splunk. Logging -JSON with logrus with the `WithFields` and `WithField` API in logrus forces you -to think about what context to log, to provide valuable troubleshoot information -later. +Will return a `logrus.Error` object. Passing it to +`log.{Info,Warn,Error,Fatal,Panic}` will log it according to the formatter set +for the environment. -## Example +#### Level logging + +Logrus has six levels: Debug, Info, Warning, Error, Fatal and Panic. ```go -import ( - "github.com/Sirupsen/logrus" -) +log.Info("Something noteworthy happened!") +log.Warn("You should probably take a look at this.") +log.Error("Something failed but I'm not quitting.") +log.Fatal("Bye.") +log.Panic("I'm bailing.") +``` -var logger logrus.New() -func main() { - logger.WithFields(Fields{ - "animal": "walrus", - "location": "New York Aquarium", - "weather": "rain", - "name": "Wally", - "event": "escape", - }).Info("Walrus has escaped the aquarium! Action required!") - // { - // "level": "info", - // "animal": "walrus", - // "location": "New York Aquarium", - // "weather":"rain", - // "name": "Wally", - // "event":"escape", - // "msg": "Walrus has escaped the aquarium! Action required!") - // "time": "2014-02-23 19:57:35.862271048 -0500 EST" - // } +#### Entries - logger.WithField("source", "kafka").Infof("Connection to Kafka failed with %s", "some error") - // { - // "level": "info", - // "source": "kafka", - // "msg": "Connection to Kafka failed with some error", - // "time": "2014-02-23 19:57:35.862271048 -0500 EST" - // } +Besides the fields added with `WithField` or `WithFields` some fields are +automatically added to all logging events: + +1. `time`. The timestamp when the entry was created. +2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after + the `AddFields` call. E.g. `Failed to send event.` +3. `level`. The logging level. E.g. `info`. +4. `file`. The file (and line) where the logging entry was created. E.g., + `main.go:82`. + +#### Environments + +Logrus has no notion of environment. If you wish for hooks and formatters to +only be used in specific environments, you should handle that yourself. For +example, if your application has a global variable `Environment`, which is a +string representation of the environment you could do: + +```go +init() { + // do something here to set environment depending on an environment variable + // or command-line flag + + if Environment == "production" { + log.SetFormatter(logrus.JSONFormatter) + } else { + // The TextFormatter is default, you don't actually have to do this. + log.SetFormatter(logrus.TextFormatter) + } } ``` -Using `Warning` and `Fatal` to log to `airbrake` requires setting -`airbrake.Endpoint` and `airbrake.ApiKey`. See -[tobi/airbrake-go](https://github.com/tobi/airbrake-go). +#### Formats + +The built in logging formatters are: + +* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise + without colors. Default for the development environment. +* `logrus.JSONFormatter`. Default for the production environment. + +You can define your formatter taking an entry. `entry.Data` is a `Fields` type +which is a `map[string]interface{}` with all your fields as well as the default +ones (see Entries above): + +```go +log.SetFormatter(func(entry *logrus.Entry) { + serialized, err = json.Marshal(entry.Data) + if err != nil { + return nil, log.WithFields(&logrus.Fields{ + "source": "log formatter", + "entry": entry.Data + }).AsError("Failed to serialize log entry to JSON") + } +}) +```