• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

cmd/lint/H25-Apr-2019-214159

diode/H25-Apr-2019-622373

hlog/H25-Apr-2019-506413

internal/H25-Apr-2019-3,3152,761

journald/H25-Apr-2019-16190

log/H25-Apr-2019-260124

pkgerrors/H25-Apr-2019-10880

.gitignoreH A D25-Apr-2019270 2620

.travis.ymlH A D25-Apr-2019265 1615

CNAMEH A D25-Apr-201910 11

LICENSEH A D25-Apr-20191 KiB2217

README.mdH A D25-Apr-201918.7 KiB591426

_config.ymlH A D25-Apr-201927 21

array.goH A D25-Apr-20195.7 KiB225156

array_test.goH A D25-Apr-2019640 3532

benchmark_test.goH A D25-Apr-20197.9 KiB361347

binary_test.goH A D25-Apr-201910.3 KiB500347

console.goH A D25-Apr-20198.5 KiB392330

console_test.goH A D25-Apr-20199.8 KiB331259

context.goH A D25-Apr-201913 KiB422286

ctx.goH A D25-Apr-20191.1 KiB4826

ctx_test.goH A D25-Apr-20191.4 KiB6453

encoder.goH A D25-Apr-20192.2 KiB5754

encoder_cbor.goH A D25-Apr-2019773 3620

encoder_json.goH A D25-Apr-2019512 3320

event.goH A D25-Apr-201917.2 KiB705528

fields.goH A D25-Apr-20195.4 KiB243237

globals.goH A D25-Apr-20193.1 KiB11154

go.modH A D25-Apr-2019258 129

go112.goH A D25-Apr-2019161 82

hook.goH A D25-Apr-20191.4 KiB6146

hook_test.goH A D25-Apr-20195.5 KiB168163

log.goH A D25-Apr-201911.1 KiB413213

log_example_test.goH A D25-Apr-20199.5 KiB476310

log_test.goH A D25-Apr-201924.2 KiB749677

not_go112.goH A D25-Apr-201974 62

sampler.goH A D25-Apr-20192.9 KiB12791

sampler_test.goH A D25-Apr-20191.4 KiB8578

syslog.goH A D25-Apr-20191.1 KiB5844

syslog_test.goH A D25-Apr-20191.6 KiB6456

writer.goH A D25-Apr-20192.4 KiB10175

writer_test.goH A D25-Apr-2019776 3024

README.md

1# Zero Allocation JSON Logger
2
3[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/zerolog) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/zerolog/master/LICENSE) [![Build Status](https://travis-ci.org/rs/zerolog.svg?branch=master)](https://travis-ci.org/rs/zerolog) [![Coverage](http://gocover.io/_badge/github.com/rs/zerolog)](http://gocover.io/github.com/rs/zerolog)
4
5The zerolog package provides a fast and simple logger dedicated to JSON output.
6
7Zerolog's API is designed to provide both a great developer experience and stunning [performance](#benchmarks). Its unique chaining API allows zerolog to write JSON (or CBOR) log events by avoiding allocations and reflection.
8
9Uber's [zap](https://godoc.org/go.uber.org/zap) library pioneered this approach. Zerolog is taking this concept to the next level with a simpler to use API and even better performance.
10
11To keep the code base and the API simple, zerolog focuses on efficient structured logging only. Pretty logging on the console is made possible using the provided (but inefficient) [`zerolog.ConsoleWriter`](#pretty-logging).
12
13![Pretty Logging Image](pretty.png)
14
15## Who uses zerolog
16
17Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog) and add your company / project to the list.
18
19## Features
20
21* Blazing fast
22* Low to zero allocation
23* Level logging
24* Sampling
25* Hooks
26* Contextual fields
27* `context.Context` integration
28* `net/http` helpers
29* JSON and CBOR encoding formats
30* Pretty logging for development
31
32## Installation
33
34```go
35go get -u github.com/rs/zerolog/log
36```
37
38## Getting Started
39
40### Simple Logging Example
41
42For simple logging, import the global logger package **github.com/rs/zerolog/log**
43
44```go
45package main
46
47import (
48    "github.com/rs/zerolog"
49    "github.com/rs/zerolog/log"
50)
51
52func main() {
53    // UNIX Time is faster and smaller than most timestamps
54    // If you set zerolog.TimeFieldFormat to an empty string,
55    // logs will write with UNIX time
56    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
57
58    log.Print("hello world")
59}
60
61// Output: {"time":1516134303,"level":"debug","message":"hello world"}
62```
63> Note: By default log writes to `os.Stderr`
64> Note: The default log level for `log.Print` is *debug*
65
66### Contextual Logging
67
68**zerolog** allows data to be added to log messages in the form of key:value pairs. The data added to the message adds "context" about the log event that can be critical for debugging as well as myriad other purposes. An example of this is below:
69
70```go
71package main
72
73import (
74    "github.com/rs/zerolog"
75    "github.com/rs/zerolog/log"
76)
77
78func main() {
79    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
80
81    log.Debug().
82        Str("Scale", "833 cents").
83        Float64("Interval", 833.09).
84        Msg("Fibonacci is everywhere")
85}
86
87// Output: {"time":1524104936,"level":"debug","Scale":"833 cents","Interval":833.09,"message":"Fibonacci is everywhere"}
88```
89
90> You'll note in the above example that when adding contextual fields, the fields are strongly typed. You can find the full list of supported fields [here](#standard-types)
91
92### Leveled Logging
93
94#### Simple Leveled Logging Example
95
96```go
97package main
98
99import (
100    "github.com/rs/zerolog"
101    "github.com/rs/zerolog/log"
102)
103
104func main() {
105    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
106
107    log.Info().Msg("hello world")
108}
109
110// Output: {"time":1516134303,"level":"info","message":"hello world"}
111```
112
113> It is very important to note that when using the **zerolog** chaining API, as shown above (`log.Info().Msg("hello world"`), the chain must have either the `Msg` or `Msgf` method call. If you forget to add either of these, the log will not occur and there is no compile time error to alert you of this.
114
115**zerolog** allows for logging at the following levels (from highest to lowest):
116
117* panic (`zerolog.PanicLevel`, 5)
118* fatal (`zerolog.FatalLevel`, 4)
119* error (`zerolog.ErrorLevel`, 3)
120* warn (`zerolog.WarnLevel`, 2)
121* info (`zerolog.InfoLevel`, 1)
122* debug (`zerolog.DebugLevel`, 0)
123
124You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level.  Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.
125
126#### Setting Global Log Level
127
128This example uses command-line flags to demonstrate various outputs depending on the chosen log level.
129
130```go
131package main
132
133import (
134    "flag"
135
136    "github.com/rs/zerolog"
137    "github.com/rs/zerolog/log"
138)
139
140func main() {
141    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
142    debug := flag.Bool("debug", false, "sets log level to debug")
143
144    flag.Parse()
145
146    // Default level for this example is info, unless debug flag is present
147    zerolog.SetGlobalLevel(zerolog.InfoLevel)
148    if *debug {
149        zerolog.SetGlobalLevel(zerolog.DebugLevel)
150    }
151
152    log.Debug().Msg("This message appears only when log level set to Debug")
153    log.Info().Msg("This message appears when log level set to Debug or Info")
154
155    if e := log.Debug(); e.Enabled() {
156        // Compute log output only if enabled.
157        value := "bar"
158        e.Str("foo", value).Msg("some debug message")
159    }
160}
161```
162
163Info Output (no flag)
164
165```bash
166$ ./logLevelExample
167{"time":1516387492,"level":"info","message":"This message appears when log level set to Debug or Info"}
168```
169
170Debug Output (debug flag set)
171
172```bash
173$ ./logLevelExample -debug
174{"time":1516387573,"level":"debug","message":"This message appears only when log level set to Debug"}
175{"time":1516387573,"level":"info","message":"This message appears when log level set to Debug or Info"}
176{"time":1516387573,"level":"debug","foo":"bar","message":"some debug message"}
177```
178
179#### Logging without Level or Message
180
181You may choose to log without a specific level by using the `Log` method. You may also write without a message by setting an empty string in the `msg string` parameter of the `Msg` method. Both are demonstrated in the example below.
182
183```go
184package main
185
186import (
187    "github.com/rs/zerolog"
188    "github.com/rs/zerolog/log"
189)
190
191func main() {
192    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
193
194    log.Log().
195        Str("foo", "bar").
196        Msg("")
197}
198
199// Output: {"time":1494567715,"foo":"bar"}
200```
201
202#### Logging Fatal Messages
203
204```go
205package main
206
207import (
208    "errors"
209
210    "github.com/rs/zerolog"
211    "github.com/rs/zerolog/log"
212)
213
214func main() {
215    err := errors.New("A repo man spends his life getting into tense situations")
216    service := "myservice"
217
218    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
219
220    log.Fatal().
221        Err(err).
222        Str("service", service).
223        Msgf("Cannot start %s", service)
224}
225
226// Output: {"time":1516133263,"level":"fatal","error":"A repo man spends his life getting into tense situations","service":"myservice","message":"Cannot start myservice"}
227//         exit status 1
228```
229
230> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.
231
232### Create logger instance to manage different outputs
233
234```go
235logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
236
237logger.Info().Str("foo", "bar").Msg("hello world")
238
239// Output: {"level":"info","time":1494567715,"message":"hello world","foo":"bar"}
240```
241
242### Sub-loggers let you chain loggers with additional context
243
244```go
245sublogger := log.With().
246                 Str("component", "foo").
247                 Logger()
248sublogger.Info().Msg("hello world")
249
250// Output: {"level":"info","time":1494567715,"message":"hello world","component":"foo"}
251```
252
253### Pretty logging
254
255To log a human-friendly, colorized output, use `zerolog.ConsoleWriter`:
256
257```go
258log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
259
260log.Info().Str("foo", "bar").Msg("Hello world")
261
262// Output: 3:04PM INF Hello World foo=bar
263```
264
265To customize the configuration and formatting:
266
267```go
268output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
269output.FormatLevel = func(i interface{}) string {
270    return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
271}
272output.FormatMessage = func(i interface{}) string {
273    return fmt.Sprintf("***%s****", i)
274}
275output.FormatFieldName = func(i interface{}) string {
276    return fmt.Sprintf("%s:", i)
277}
278output.FormatFieldValue = func(i interface{}) string {
279    return strings.ToUpper(fmt.Sprintf("%s", i))
280}
281
282log := zerolog.New(output).With().Timestamp().Logger()
283
284log.Info().Str("foo", "bar").Msg("Hello World")
285
286// Output: 2006-01-02T15:04:05Z07:00 | INFO  | ***Hello World**** foo:BAR
287```
288
289### Sub dictionary
290
291```go
292log.Info().
293    Str("foo", "bar").
294    Dict("dict", zerolog.Dict().
295        Str("bar", "baz").
296        Int("n", 1),
297    ).Msg("hello world")
298
299// Output: {"level":"info","time":1494567715,"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
300```
301
302### Customize automatic field names
303
304```go
305zerolog.TimestampFieldName = "t"
306zerolog.LevelFieldName = "l"
307zerolog.MessageFieldName = "m"
308
309log.Info().Msg("hello world")
310
311// Output: {"l":"info","t":1494567715,"m":"hello world"}
312```
313
314### Add contextual fields to the global logger
315
316```go
317log.Logger = log.With().Str("foo", "bar").Logger()
318```
319
320### Add file and line number to log
321
322```go
323log.Logger = log.With().Caller().Logger()
324log.Info().Msg("hello world")
325
326// Output: {"level": "info", "message": "hello world", "caller": "/go/src/your_project/some_file:21"}
327```
328
329
330### Thread-safe, lock-free, non-blocking writer
331
332If your writer might be slow or not thread-safe and you need your log producers to never get slowed down by a slow writer, you can use a `diode.Writer` as follow:
333
334```go
335wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
336		fmt.Printf("Logger Dropped %d messages", missed)
337	})
338log := zerolog.New(w)
339log.Print("test")
340```
341
342You will need to install `code.cloudfoundry.org/go-diodes` to use this feature.
343
344### Log Sampling
345
346```go
347sampled := log.Sample(&zerolog.BasicSampler{N: 10})
348sampled.Info().Msg("will be logged every 10 messages")
349
350// Output: {"time":1494567715,"level":"info","message":"will be logged every 10 messages"}
351```
352
353More advanced sampling:
354
355```go
356// Will let 5 debug messages per period of 1 second.
357// Over 5 debug message, 1 every 100 debug messages are logged.
358// Other levels are not sampled.
359sampled := log.Sample(zerolog.LevelSampler{
360    DebugSampler: &zerolog.BurstSampler{
361        Burst: 5,
362        Period: 1*time.Second,
363        NextSampler: &zerolog.BasicSampler{N: 100},
364    },
365})
366sampled.Debug().Msg("hello world")
367
368// Output: {"time":1494567715,"level":"debug","message":"hello world"}
369```
370
371### Hooks
372
373```go
374type SeverityHook struct{}
375
376func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
377    if level != zerolog.NoLevel {
378        e.Str("severity", level.String())
379    }
380}
381
382hooked := log.Hook(SeverityHook{})
383hooked.Warn().Msg("")
384
385// Output: {"level":"warn","severity":"warn"}
386```
387
388### Pass a sub-logger by context
389
390```go
391ctx := log.With().Str("component", "module").Logger().WithContext(ctx)
392
393log.Ctx(ctx).Info().Msg("hello world")
394
395// Output: {"component":"module","level":"info","message":"hello world"}
396```
397
398### Set as standard logger output
399
400```go
401log := zerolog.New(os.Stdout).With().
402    Str("foo", "bar").
403    Logger()
404
405stdlog.SetFlags(0)
406stdlog.SetOutput(log)
407
408stdlog.Print("hello world")
409
410// Output: {"foo":"bar","message":"hello world"}
411```
412
413### Integration with `net/http`
414
415The `github.com/rs/zerolog/hlog` package provides some helpers to integrate zerolog with `http.Handler`.
416
417In this example we use [alice](https://github.com/justinas/alice) to install logger for better readability.
418
419```go
420log := zerolog.New(os.Stdout).With().
421    Timestamp().
422    Str("role", "my-service").
423    Str("host", host).
424    Logger()
425
426c := alice.New()
427
428// Install the logger handler with default output on the console
429c = c.Append(hlog.NewHandler(log))
430
431// Install some provided extra handler to set some request's context fields.
432// Thanks to those handler, all our logs will come with some pre-populated fields.
433c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
434    hlog.FromRequest(r).Info().
435        Str("method", r.Method).
436        Str("url", r.URL.String()).
437        Int("status", status).
438        Int("size", size).
439        Dur("duration", duration).
440        Msg("")
441}))
442c = c.Append(hlog.RemoteAddrHandler("ip"))
443c = c.Append(hlog.UserAgentHandler("user_agent"))
444c = c.Append(hlog.RefererHandler("referer"))
445c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
446
447// Here is your final handler
448h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
449    // Get the logger from the request's context. You can safely assume it
450    // will be always there: if the handler is removed, hlog.FromRequest
451    // will return a no-op logger.
452    hlog.FromRequest(r).Info().
453        Str("user", "current user").
454        Str("status", "ok").
455        Msg("Something happened")
456
457    // Output: {"level":"info","time":"2001-02-03T04:05:06Z","role":"my-service","host":"local-hostname","req_id":"b4g0l5t6tfid6dtrapu0","user":"current user","status":"ok","message":"Something happened"}
458}))
459http.Handle("/", h)
460
461if err := http.ListenAndServe(":8080", nil); err != nil {
462    log.Fatal().Err(err).Msg("Startup failed")
463}
464```
465
466## Global Settings
467
468Some settings can be changed and will by applied to all loggers:
469
470* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
471* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Set this to `zerolog.Disabled` to disable logging altogether (quiet mode).
472* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
473* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
474* `zerolog.LevelFieldName`: Can be set to customize level field name.
475* `zerolog.MessageFieldName`: Can be set to customize message field name.
476* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
477* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix` or `zerolog.TimeFormatUnixMs`, times are formated as UNIX timestamp.
478* DurationFieldUnit defines the unit for time.Duration type fields added using the Dur method.
479* `DurationFieldUnit`: Sets the unit of the fields added by `Dur` (default: `time.Millisecond`).
480* `DurationFieldInteger`: If set to true, `Dur` fields are formatted as integers instead of floats.
481* `ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
482
483## Field Types
484
485### Standard Types
486
487* `Str`
488* `Bool`
489* `Int`, `Int8`, `Int16`, `Int32`, `Int64`
490* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
491* `Float32`, `Float64`
492
493### Advanced Fields
494
495* `Err`: Takes an `error` and render it as a string using the `zerolog.ErrorFieldName` field name.
496* `Timestamp`: Insert a timestamp field with `zerolog.TimestampFieldName` field name and formatted using `zerolog.TimeFieldFormat`.
497* `Time`: Adds a field with the time formated with the `zerolog.TimeFieldFormat`.
498* `Dur`: Adds a field with a `time.Duration`.
499* `Dict`: Adds a sub-key/value as a field of the event.
500* `Interface`: Uses reflection to marshal the type.
501
502## Binary Encoding
503
504In addition to the default JSON encoding, `zerolog` can produce binary logs using [CBOR](http://cbor.io) encoding. The choice of encoding can be decided at compile time using the build tag `binary_log` as follows:
505
506```bash
507go build -tags binary_log .
508```
509
510To Decode binary encoded log files you can use any CBOR decoder. One has been tested to work
511with zerolog library is [CSD](https://github.com/toravir/csd/).
512
513## Related Projects
514
515* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
516
517## Benchmarks
518
519See [logbench](http://hackemist.com/logbench/) for more comprehensive and up-to-date benchmarks.
520
521All operations are allocation free (those numbers *include* JSON encoding):
522
523```text
524BenchmarkLogEmpty-8        100000000    19.1 ns/op     0 B/op       0 allocs/op
525BenchmarkDisabled-8        500000000    4.07 ns/op     0 B/op       0 allocs/op
526BenchmarkInfo-8            30000000     42.5 ns/op     0 B/op       0 allocs/op
527BenchmarkContextFields-8   30000000     44.9 ns/op     0 B/op       0 allocs/op
528BenchmarkLogFields-8       10000000     184 ns/op      0 B/op       0 allocs/op
529```
530
531There are a few Go logging benchmarks and comparisons that include zerolog.
532
533* [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
534* [uber-common/zap](https://github.com/uber-go/zap#performance)
535
536Using Uber's zap comparison benchmark:
537
538Log a message and 10 fields:
539
540| Library | Time | Bytes Allocated | Objects Allocated |
541| :--- | :---: | :---: | :---: |
542| zerolog | 767 ns/op | 552 B/op | 6 allocs/op |
543| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op |
544| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op |
545| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op |
546| lion | 5392 ns/op | 5807 B/op | 63 allocs/op |
547| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op |
548| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op |
549| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op |
550
551Log a message with a logger that already has 10 fields of context:
552
553| Library | Time | Bytes Allocated | Objects Allocated |
554| :--- | :---: | :---: | :---: |
555| zerolog | 52 ns/op | 0 B/op | 0 allocs/op |
556| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op |
557| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
558| lion | 2702 ns/op | 4074 B/op | 38 allocs/op |
559| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op |
560| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op |
561| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op |
562| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op |
563
564Log a static string, without any context or `printf`-style templating:
565
566| Library | Time | Bytes Allocated | Objects Allocated |
567| :--- | :---: | :---: | :---: |
568| zerolog | 50 ns/op | 0 B/op | 0 allocs/op |
569| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op |
570| standard library | 453 ns/op | 80 B/op | 2 allocs/op |
571| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
572| go-kit | 508 ns/op | 656 B/op | 13 allocs/op |
573| lion | 771 ns/op | 1224 B/op | 10 allocs/op |
574| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op |
575| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op |
576| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op |
577
578## Caveats
579
580Note that zerolog does de-duplication fields. Using the same key multiple times creates multiple keys in final JSON:
581
582```go
583logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
584logger.Info().
585       Timestamp().
586       Msg("dup")
587// Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
588```
589
590However, it’s not a big deal as JSON accepts dup keys; the last one prevails.
591