...

Text file src/github.com/sirupsen/logrus/README.md

Documentation: github.com/sirupsen/logrus

     1# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus)
     2
     3Logrus is a structured logger for Go (golang), completely API compatible with
     4the standard library logger.
     5
     6**Seeing weird case-sensitive problems?** It's in the past been possible to
     7import Logrus as both upper- and lower-case. Due to the Go package environment,
     8this caused issues in the community and we needed a standard. Some environments
     9experienced problems with the upper-case variant, so the lower-case was decided.
    10Everything using `logrus` will need to use the lower-case:
    11`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
    12
    13To fix Glide, see [these
    14comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
    15For an in-depth explanation of the casing issue, see [this
    16comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
    17
    18**Are you interested in assisting in maintaining Logrus?** Currently I have a
    19lot of obligations, and I am unable to provide Logrus with the maintainership it
    20needs. If you'd like to help, please reach out to me at `simon at author's
    21username dot com`.
    22
    23Nicely color-coded in development (when a TTY is attached, otherwise just
    24plain text):
    25
    26![Colored](http://i.imgur.com/PY7qMwd.png)
    27
    28With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
    29or Splunk:
    30
    31```json
    32{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
    33ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
    34
    35{"level":"warning","msg":"The group's number increased tremendously!",
    36"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
    37
    38{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
    39"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
    40
    41{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
    42"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
    43
    44{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
    45"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
    46```
    47
    48With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
    49attached, the output is compatible with the
    50[logfmt](http://godoc.org/github.com/kr/logfmt) format:
    51
    52```text
    53time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
    54time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
    55time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
    56time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
    57time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
    58time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
    59```
    60To ensure this behaviour even if a TTY is attached, set your formatter as follows:
    61
    62```go
    63	log.SetFormatter(&log.TextFormatter{
    64		DisableColors: true,
    65		FullTimestamp: true,
    66	})
    67```
    68
    69#### Logging Method Name
    70
    71If you wish to add the calling method as a field, instruct the logger via:
    72```go
    73log.SetReportCaller(true)
    74```
    75This adds the caller as 'method' like so:
    76
    77```json
    78{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
    79"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
    80```
    81
    82```text
    83time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
    84```
    85Note that this does add measurable overhead - the cost will depend on the version of Go, but is
    86between 20 and 40% in recent tests with 1.6 and 1.7.  You can validate this in your
    87environment via benchmarks: 
    88```
    89go test -bench=.*CallerTracing
    90```
    91
    92
    93#### Case-sensitivity
    94
    95The organization's name was changed to lower-case--and this will not be changed
    96back. If you are getting import conflicts due to case sensitivity, please use
    97the lower-case import: `github.com/sirupsen/logrus`.
    98
    99#### Example
   100
   101The simplest way to use Logrus is simply the package-level exported logger:
   102
   103```go
   104package main
   105
   106import (
   107  log "github.com/sirupsen/logrus"
   108)
   109
   110func main() {
   111  log.WithFields(log.Fields{
   112    "animal": "walrus",
   113  }).Info("A walrus appears")
   114}
   115```
   116
   117Note that it's completely api-compatible with the stdlib logger, so you can
   118replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
   119and you'll now have the flexibility of Logrus. You can customize it all you
   120want:
   121
   122```go
   123package main
   124
   125import (
   126  "os"
   127  log "github.com/sirupsen/logrus"
   128)
   129
   130func init() {
   131  // Log as JSON instead of the default ASCII formatter.
   132  log.SetFormatter(&log.JSONFormatter{})
   133
   134  // Output to stdout instead of the default stderr
   135  // Can be any io.Writer, see below for File example
   136  log.SetOutput(os.Stdout)
   137
   138  // Only log the warning severity or above.
   139  log.SetLevel(log.WarnLevel)
   140}
   141
   142func main() {
   143  log.WithFields(log.Fields{
   144    "animal": "walrus",
   145    "size":   10,
   146  }).Info("A group of walrus emerges from the ocean")
   147
   148  log.WithFields(log.Fields{
   149    "omg":    true,
   150    "number": 122,
   151  }).Warn("The group's number increased tremendously!")
   152
   153  log.WithFields(log.Fields{
   154    "omg":    true,
   155    "number": 100,
   156  }).Fatal("The ice breaks!")
   157
   158  // A common pattern is to re-use fields between logging statements by re-using
   159  // the logrus.Entry returned from WithFields()
   160  contextLogger := log.WithFields(log.Fields{
   161    "common": "this is a common field",
   162    "other": "I also should be logged always",
   163  })
   164
   165  contextLogger.Info("I'll be logged with common and other field")
   166  contextLogger.Info("Me too")
   167}
   168```
   169
   170For more advanced usage such as logging to multiple locations from the same
   171application, you can also create an instance of the `logrus` Logger:
   172
   173```go
   174package main
   175
   176import (
   177  "os"
   178  "github.com/sirupsen/logrus"
   179)
   180
   181// Create a new instance of the logger. You can have any number of instances.
   182var log = logrus.New()
   183
   184func main() {
   185  // The API for setting attributes is a little different than the package level
   186  // exported logger. See Godoc.
   187  log.Out = os.Stdout
   188
   189  // You could set this to any `io.Writer` such as a file
   190  // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
   191  // if err == nil {
   192  //  log.Out = file
   193  // } else {
   194  //  log.Info("Failed to log to file, using default stderr")
   195  // }
   196
   197  log.WithFields(logrus.Fields{
   198    "animal": "walrus",
   199    "size":   10,
   200  }).Info("A group of walrus emerges from the ocean")
   201}
   202```
   203
   204#### Fields
   205
   206Logrus encourages careful, structured logging through logging fields instead of
   207long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
   208to send event %s to topic %s with key %d")`, you should log the much more
   209discoverable:
   210
   211```go
   212log.WithFields(log.Fields{
   213  "event": event,
   214  "topic": topic,
   215  "key": key,
   216}).Fatal("Failed to send event")
   217```
   218
   219We've found this API forces you to think about logging in a way that produces
   220much more useful logging messages. We've been in countless situations where just
   221a single added field to a log statement that was already there would've saved us
   222hours. The `WithFields` call is optional.
   223
   224In general, with Logrus using any of the `printf`-family functions should be
   225seen as a hint you should add a field, however, you can still use the
   226`printf`-family functions with Logrus.
   227
   228#### Default Fields
   229
   230Often it's helpful to have fields _always_ attached to log statements in an
   231application or parts of one. For example, you may want to always log the
   232`request_id` and `user_ip` in the context of a request. Instead of writing
   233`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
   234every line, you can create a `logrus.Entry` to pass around instead:
   235
   236```go
   237requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
   238requestLogger.Info("something happened on that request") # will log request_id and user_ip
   239requestLogger.Warn("something not great happened")
   240```
   241
   242#### Hooks
   243
   244You can add hooks for logging levels. For example to send errors to an exception
   245tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
   246multiple places simultaneously, e.g. syslog.
   247
   248Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
   249`init`:
   250
   251```go
   252import (
   253  log "github.com/sirupsen/logrus"
   254  "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
   255  logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
   256  "log/syslog"
   257)
   258
   259func init() {
   260
   261  // Use the Airbrake hook to report errors that have Error severity or above to
   262  // an exception tracker. You can create custom hooks, see the Hooks section.
   263  log.AddHook(airbrake.NewHook(123, "xyz", "production"))
   264
   265  hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
   266  if err != nil {
   267    log.Error("Unable to connect to local syslog daemon")
   268  } else {
   269    log.AddHook(hook)
   270  }
   271}
   272```
   273Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
   274
   275A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
   276
   277
   278#### Level logging
   279
   280Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
   281
   282```go
   283log.Trace("Something very low level.")
   284log.Debug("Useful debugging information.")
   285log.Info("Something noteworthy happened!")
   286log.Warn("You should probably take a look at this.")
   287log.Error("Something failed but I'm not quitting.")
   288// Calls os.Exit(1) after logging
   289log.Fatal("Bye.")
   290// Calls panic() after logging
   291log.Panic("I'm bailing.")
   292```
   293
   294You can set the logging level on a `Logger`, then it will only log entries with
   295that severity or anything above it:
   296
   297```go
   298// Will log anything that is info or above (warn, error, fatal, panic). Default.
   299log.SetLevel(log.InfoLevel)
   300```
   301
   302It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
   303environment if your application has that.
   304
   305#### Entries
   306
   307Besides the fields added with `WithField` or `WithFields` some fields are
   308automatically added to all logging events:
   309
   3101. `time`. The timestamp when the entry was created.
   3112. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
   312   the `AddFields` call. E.g. `Failed to send event.`
   3133. `level`. The logging level. E.g. `info`.
   314
   315#### Environments
   316
   317Logrus has no notion of environment.
   318
   319If you wish for hooks and formatters to only be used in specific environments,
   320you should handle that yourself. For example, if your application has a global
   321variable `Environment`, which is a string representation of the environment you
   322could do:
   323
   324```go
   325import (
   326  log "github.com/sirupsen/logrus"
   327)
   328
   329init() {
   330  // do something here to set environment depending on an environment variable
   331  // or command-line flag
   332  if Environment == "production" {
   333    log.SetFormatter(&log.JSONFormatter{})
   334  } else {
   335    // The TextFormatter is default, you don't actually have to do this.
   336    log.SetFormatter(&log.TextFormatter{})
   337  }
   338}
   339```
   340
   341This configuration is how `logrus` was intended to be used, but JSON in
   342production is mostly only useful if you do log aggregation with tools like
   343Splunk or Logstash.
   344
   345#### Formatters
   346
   347The built-in logging formatters are:
   348
   349* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
   350  without colors.
   351  * *Note:* to force colored output when there is no TTY, set the `ForceColors`
   352    field to `true`.  To force no colored output even if there is a TTY  set the
   353    `DisableColors` field to `true`. For Windows, see
   354    [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
   355  * When colors are enabled, levels are truncated to 4 characters by default. To disable
   356    truncation set the `DisableLevelTruncation` field to `true`.
   357  * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
   358* `logrus.JSONFormatter`. Logs fields as JSON.
   359  * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
   360
   361Third party logging formatters:
   362
   363* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
   364* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
   365* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
   366* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
   367* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
   368
   369You can define your formatter by implementing the `Formatter` interface,
   370requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
   371`Fields` type (`map[string]interface{}`) with all your fields as well as the
   372default ones (see Entries section above):
   373
   374```go
   375type MyJSONFormatter struct {
   376}
   377
   378log.SetFormatter(new(MyJSONFormatter))
   379
   380func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
   381  // Note this doesn't include Time, Level and Message which are available on
   382  // the Entry. Consult `godoc` on information about those fields or read the
   383  // source of the official loggers.
   384  serialized, err := json.Marshal(entry.Data)
   385    if err != nil {
   386      return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
   387    }
   388  return append(serialized, '\n'), nil
   389}
   390```
   391
   392#### Logger as an `io.Writer`
   393
   394Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
   395
   396```go
   397w := logger.Writer()
   398defer w.Close()
   399
   400srv := http.Server{
   401    // create a stdlib log.Logger that writes to
   402    // logrus.Logger.
   403    ErrorLog: log.New(w, "", 0),
   404}
   405```
   406
   407Each line written to that writer will be printed the usual way, using formatters
   408and hooks. The level for those entries is `info`.
   409
   410This means that we can override the standard library logger easily:
   411
   412```go
   413logger := logrus.New()
   414logger.Formatter = &logrus.JSONFormatter{}
   415
   416// Use logrus for standard log output
   417// Note that `log` here references stdlib's log
   418// Not logrus imported under the name `log`.
   419log.SetOutput(logger.Writer())
   420```
   421
   422#### Rotation
   423
   424Log rotation is not provided with Logrus. Log rotation should be done by an
   425external program (like `logrotate(8)`) that can compress and delete old log
   426entries. It should not be a feature of the application-level logger.
   427
   428#### Tools
   429
   430| Tool | Description |
   431| ---- | ----------- |
   432|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
   433|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
   434
   435#### Testing
   436
   437Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
   438
   439* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
   440* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
   441
   442```go
   443import(
   444  "github.com/sirupsen/logrus"
   445  "github.com/sirupsen/logrus/hooks/test"
   446  "github.com/stretchr/testify/assert"
   447  "testing"
   448)
   449
   450func TestSomething(t*testing.T){
   451  logger, hook := test.NewNullLogger()
   452  logger.Error("Helloerror")
   453
   454  assert.Equal(t, 1, len(hook.Entries))
   455  assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
   456  assert.Equal(t, "Helloerror", hook.LastEntry().Message)
   457
   458  hook.Reset()
   459  assert.Nil(t, hook.LastEntry())
   460}
   461```
   462
   463#### Fatal handlers
   464
   465Logrus can register one or more functions that will be called when any `fatal`
   466level message is logged. The registered handlers will be executed before
   467logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
   468to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
   469
   470```
   471...
   472handler := func() {
   473  // gracefully shutdown something...
   474}
   475logrus.RegisterExitHandler(handler)
   476...
   477```
   478
   479#### Thread safety
   480
   481By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
   482If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
   483
   484Situation when locking is not needed includes:
   485
   486* You have no hooks registered, or hooks calling is already thread-safe.
   487
   488* Writing to logger.Out is already thread-safe, for example:
   489
   490  1) logger.Out is protected by locks.
   491
   492  2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
   493
   494     (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)

View as plain text