1# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![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* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. 365* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. 366* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. 367 368You can define your formatter by implementing the `Formatter` interface, 369requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a 370`Fields` type (`map[string]interface{}`) with all your fields as well as the 371default ones (see Entries section above): 372 373```go 374type MyJSONFormatter struct { 375} 376 377log.SetFormatter(new(MyJSONFormatter)) 378 379func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { 380 // Note this doesn't include Time, Level and Message which are available on 381 // the Entry. Consult `godoc` on information about those fields or read the 382 // source of the official loggers. 383 serialized, err := json.Marshal(entry.Data) 384 if err != nil { 385 return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 386 } 387 return append(serialized, '\n'), nil 388} 389``` 390 391#### Logger as an `io.Writer` 392 393Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. 394 395```go 396w := logger.Writer() 397defer w.Close() 398 399srv := http.Server{ 400 // create a stdlib log.Logger that writes to 401 // logrus.Logger. 402 ErrorLog: log.New(w, "", 0), 403} 404``` 405 406Each line written to that writer will be printed the usual way, using formatters 407and hooks. The level for those entries is `info`. 408 409This means that we can override the standard library logger easily: 410 411```go 412logger := logrus.New() 413logger.Formatter = &logrus.JSONFormatter{} 414 415// Use logrus for standard log output 416// Note that `log` here references stdlib's log 417// Not logrus imported under the name `log`. 418log.SetOutput(logger.Writer()) 419``` 420 421#### Rotation 422 423Log rotation is not provided with Logrus. Log rotation should be done by an 424external program (like `logrotate(8)`) that can compress and delete old log 425entries. It should not be a feature of the application-level logger. 426 427#### Tools 428 429| Tool | Description | 430| ---- | ----------- | 431|[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.| 432|[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) | 433 434#### Testing 435 436Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: 437 438* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook 439* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): 440 441```go 442import( 443 "github.com/sirupsen/logrus" 444 "github.com/sirupsen/logrus/hooks/test" 445 "github.com/stretchr/testify/assert" 446 "testing" 447) 448 449func TestSomething(t*testing.T){ 450 logger, hook := test.NewNullLogger() 451 logger.Error("Helloerror") 452 453 assert.Equal(t, 1, len(hook.Entries)) 454 assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) 455 assert.Equal(t, "Helloerror", hook.LastEntry().Message) 456 457 hook.Reset() 458 assert.Nil(t, hook.LastEntry()) 459} 460``` 461 462#### Fatal handlers 463 464Logrus can register one or more functions that will be called when any `fatal` 465level message is logged. The registered handlers will be executed before 466logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need 467to 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. 468 469``` 470... 471handler := func() { 472 // gracefully shutdown something... 473} 474logrus.RegisterExitHandler(handler) 475... 476``` 477 478#### Thread safety 479 480By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs. 481If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. 482 483Situation when locking is not needed includes: 484 485* You have no hooks registered, or hooks calling is already thread-safe. 486 487* Writing to logger.Out is already thread-safe, for example: 488 489 1) logger.Out is protected by locks. 490 491 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) 492 493 (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) 494