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

..27-Nov-2021-

internal/H27-Nov-2021-2,2301,767

journald/H27-Nov-2021-12689

.gitignoreH A D27-Nov-2021270 2620

CNAMEH A D27-Nov-202110 11

LICENSEH A D27-Nov-20211 KiB2217

README.mdH A D27-Nov-202121.8 KiB696502

_config.ymlH A D27-Nov-202127 21

array.goH A D27-Nov-20216.2 KiB241168

console.goH A D27-Nov-20219 KiB410345

context.goH A D27-Nov-202113.5 KiB434296

ctx.goH A D27-Nov-20211.2 KiB5229

encoder.goH A D27-Nov-20212.2 KiB5754

encoder_cbor.goH A D27-Nov-2021941 4325

encoder_json.goH A D27-Nov-2021680 4025

event.goH A D27-Nov-202119 KiB774576

fields.goH A D27-Nov-20216.2 KiB278270

globals.goH A D27-Nov-20214.1 KiB13965

go112.goH A D27-Nov-2021161 82

hook.goH A D27-Nov-20211.5 KiB6550

log.goH A D27-Nov-202112.3 KiB458246

not_go112.goH A D27-Nov-202174 62

sampler.goH A D27-Nov-20213.1 KiB13599

syslog.goH A D27-Nov-20211.9 KiB8158

writer.goH A D27-Nov-20213.7 KiB155113

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](#benchmarks)
22* [Low to zero allocation](#benchmarks)
23* [Leveled logging](#leveled-logging)
24* [Sampling](#log-sampling)
25* [Hooks](#hooks)
26* [Contextual fields](#contextual-logging)
27* `context.Context` integration
28* [Integration with `net/http`](#integration-with-nethttp)
29* [JSON and CBOR encoding formats](#binary-encoding)
30* [Pretty logging for development](#pretty-logging)
31* [Error Logging (with optional Stacktrace)](#error-logging)
32
33## Installation
34
35```bash
36go get -u github.com/rs/zerolog/log
37```
38
39## Getting Started
40
41### Simple Logging Example
42
43For simple logging, import the global logger package **github.com/rs/zerolog/log**
44
45```go
46package main
47
48import (
49    "github.com/rs/zerolog"
50    "github.com/rs/zerolog/log"
51)
52
53func main() {
54    // UNIX Time is faster and smaller than most timestamps
55    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
56
57    log.Print("hello world")
58}
59
60// Output: {"time":1516134303,"level":"debug","message":"hello world"}
61```
62> Note: By default log writes to `os.Stderr`
63> Note: The default log level for `log.Print` is *debug*
64
65### Contextual Logging
66
67**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:
68
69```go
70package main
71
72import (
73    "github.com/rs/zerolog"
74    "github.com/rs/zerolog/log"
75)
76
77func main() {
78    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
79
80    log.Debug().
81        Str("Scale", "833 cents").
82        Float64("Interval", 833.09).
83        Msg("Fibonacci is everywhere")
84
85    log.Debug().
86        Str("Name", "Tom").
87        Send()
88}
89
90// Output: {"level":"debug","Scale":"833 cents","Interval":833.09,"time":1562212768,"message":"Fibonacci is everywhere"}
91// Output: {"level":"debug","Name":"Tom","time":1562212768}
92```
93
94> 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)
95
96### Leveled Logging
97
98#### Simple Leveled Logging Example
99
100```go
101package main
102
103import (
104    "github.com/rs/zerolog"
105    "github.com/rs/zerolog/log"
106)
107
108func main() {
109    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
110
111    log.Info().Msg("hello world")
112}
113
114// Output: {"time":1516134303,"level":"info","message":"hello world"}
115```
116
117> 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.
118
119**zerolog** allows for logging at the following levels (from highest to lowest):
120
121* panic (`zerolog.PanicLevel`, 5)
122* fatal (`zerolog.FatalLevel`, 4)
123* error (`zerolog.ErrorLevel`, 3)
124* warn (`zerolog.WarnLevel`, 2)
125* info (`zerolog.InfoLevel`, 1)
126* debug (`zerolog.DebugLevel`, 0)
127* trace (`zerolog.TraceLevel`, -1)
128
129You 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.
130
131#### Setting Global Log Level
132
133This example uses command-line flags to demonstrate various outputs depending on the chosen log level.
134
135```go
136package main
137
138import (
139    "flag"
140
141    "github.com/rs/zerolog"
142    "github.com/rs/zerolog/log"
143)
144
145func main() {
146    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
147    debug := flag.Bool("debug", false, "sets log level to debug")
148
149    flag.Parse()
150
151    // Default level for this example is info, unless debug flag is present
152    zerolog.SetGlobalLevel(zerolog.InfoLevel)
153    if *debug {
154        zerolog.SetGlobalLevel(zerolog.DebugLevel)
155    }
156
157    log.Debug().Msg("This message appears only when log level set to Debug")
158    log.Info().Msg("This message appears when log level set to Debug or Info")
159
160    if e := log.Debug(); e.Enabled() {
161        // Compute log output only if enabled.
162        value := "bar"
163        e.Str("foo", value).Msg("some debug message")
164    }
165}
166```
167
168Info Output (no flag)
169
170```bash
171$ ./logLevelExample
172{"time":1516387492,"level":"info","message":"This message appears when log level set to Debug or Info"}
173```
174
175Debug Output (debug flag set)
176
177```bash
178$ ./logLevelExample -debug
179{"time":1516387573,"level":"debug","message":"This message appears only when log level set to Debug"}
180{"time":1516387573,"level":"info","message":"This message appears when log level set to Debug or Info"}
181{"time":1516387573,"level":"debug","foo":"bar","message":"some debug message"}
182```
183
184#### Logging without Level or Message
185
186You 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.
187
188```go
189package main
190
191import (
192    "github.com/rs/zerolog"
193    "github.com/rs/zerolog/log"
194)
195
196func main() {
197    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
198
199    log.Log().
200        Str("foo", "bar").
201        Msg("")
202}
203
204// Output: {"time":1494567715,"foo":"bar"}
205```
206
207### Error Logging
208
209You can log errors using the `Err` method
210
211```go
212package main
213
214import (
215	"errors"
216
217	"github.com/rs/zerolog"
218	"github.com/rs/zerolog/log"
219)
220
221func main() {
222	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
223
224	err := errors.New("seems we have an error here")
225	log.Error().Err(err).Msg("")
226}
227
228// Output: {"level":"error","error":"seems we have an error here","time":1609085256}
229```
230
231> The default field name for errors is `error`, you can change this by setting `zerolog.ErrorFieldName` to meet your needs.
232
233#### Error Logging with Stacktrace
234
235Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors.
236
237```go
238package main
239
240import (
241	"github.com/pkg/errors"
242	"github.com/rs/zerolog/pkgerrors"
243
244	"github.com/rs/zerolog"
245	"github.com/rs/zerolog/log"
246)
247
248func main() {
249	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
250	zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
251
252	err := outer()
253	log.Error().Stack().Err(err).Msg("")
254}
255
256func inner() error {
257	return errors.New("seems we have an error here")
258}
259
260func middle() error {
261	err := inner()
262	if err != nil {
263		return err
264	}
265	return nil
266}
267
268func outer() error {
269	err := middle()
270	if err != nil {
271		return err
272	}
273	return nil
274}
275
276// Output: {"level":"error","stack":[{"func":"inner","line":"20","source":"errors.go"},{"func":"middle","line":"24","source":"errors.go"},{"func":"outer","line":"32","source":"errors.go"},{"func":"main","line":"15","source":"errors.go"},{"func":"main","line":"204","source":"proc.go"},{"func":"goexit","line":"1374","source":"asm_amd64.s"}],"error":"seems we have an error here","time":1609086683}
277```
278
279> zerolog.ErrorStackMarshaler must be set in order for the stack to output anything.
280
281#### Logging Fatal Messages
282
283```go
284package main
285
286import (
287    "errors"
288
289    "github.com/rs/zerolog"
290    "github.com/rs/zerolog/log"
291)
292
293func main() {
294    err := errors.New("A repo man spends his life getting into tense situations")
295    service := "myservice"
296
297    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
298
299    log.Fatal().
300        Err(err).
301        Str("service", service).
302        Msgf("Cannot start %s", service)
303}
304
305// Output: {"time":1516133263,"level":"fatal","error":"A repo man spends his life getting into tense situations","service":"myservice","message":"Cannot start myservice"}
306//         exit status 1
307```
308
309> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.
310
311
312### Create logger instance to manage different outputs
313
314```go
315logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
316
317logger.Info().Str("foo", "bar").Msg("hello world")
318
319// Output: {"level":"info","time":1494567715,"message":"hello world","foo":"bar"}
320```
321
322### Sub-loggers let you chain loggers with additional context
323
324```go
325sublogger := log.With().
326                 Str("component", "foo").
327                 Logger()
328sublogger.Info().Msg("hello world")
329
330// Output: {"level":"info","time":1494567715,"message":"hello world","component":"foo"}
331```
332
333### Pretty logging
334
335To log a human-friendly, colorized output, use `zerolog.ConsoleWriter`:
336
337```go
338log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
339
340log.Info().Str("foo", "bar").Msg("Hello world")
341
342// Output: 3:04PM INF Hello World foo=bar
343```
344
345To customize the configuration and formatting:
346
347```go
348output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
349output.FormatLevel = func(i interface{}) string {
350    return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
351}
352output.FormatMessage = func(i interface{}) string {
353    return fmt.Sprintf("***%s****", i)
354}
355output.FormatFieldName = func(i interface{}) string {
356    return fmt.Sprintf("%s:", i)
357}
358output.FormatFieldValue = func(i interface{}) string {
359    return strings.ToUpper(fmt.Sprintf("%s", i))
360}
361
362log := zerolog.New(output).With().Timestamp().Logger()
363
364log.Info().Str("foo", "bar").Msg("Hello World")
365
366// Output: 2006-01-02T15:04:05Z07:00 | INFO  | ***Hello World**** foo:BAR
367```
368
369### Sub dictionary
370
371```go
372log.Info().
373    Str("foo", "bar").
374    Dict("dict", zerolog.Dict().
375        Str("bar", "baz").
376        Int("n", 1),
377    ).Msg("hello world")
378
379// Output: {"level":"info","time":1494567715,"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
380```
381
382### Customize automatic field names
383
384```go
385zerolog.TimestampFieldName = "t"
386zerolog.LevelFieldName = "l"
387zerolog.MessageFieldName = "m"
388
389log.Info().Msg("hello world")
390
391// Output: {"l":"info","t":1494567715,"m":"hello world"}
392```
393
394### Add contextual fields to the global logger
395
396```go
397log.Logger = log.With().Str("foo", "bar").Logger()
398```
399
400### Add file and line number to log
401
402```go
403log.Logger = log.With().Caller().Logger()
404log.Info().Msg("hello world")
405
406// Output: {"level": "info", "message": "hello world", "caller": "/go/src/your_project/some_file:21"}
407```
408
409
410### Thread-safe, lock-free, non-blocking writer
411
412If 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:
413
414```go
415wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
416		fmt.Printf("Logger Dropped %d messages", missed)
417	})
418log := zerolog.New(wr)
419log.Print("test")
420```
421
422You will need to install `code.cloudfoundry.org/go-diodes` to use this feature.
423
424### Log Sampling
425
426```go
427sampled := log.Sample(&zerolog.BasicSampler{N: 10})
428sampled.Info().Msg("will be logged every 10 messages")
429
430// Output: {"time":1494567715,"level":"info","message":"will be logged every 10 messages"}
431```
432
433More advanced sampling:
434
435```go
436// Will let 5 debug messages per period of 1 second.
437// Over 5 debug message, 1 every 100 debug messages are logged.
438// Other levels are not sampled.
439sampled := log.Sample(zerolog.LevelSampler{
440    DebugSampler: &zerolog.BurstSampler{
441        Burst: 5,
442        Period: 1*time.Second,
443        NextSampler: &zerolog.BasicSampler{N: 100},
444    },
445})
446sampled.Debug().Msg("hello world")
447
448// Output: {"time":1494567715,"level":"debug","message":"hello world"}
449```
450
451### Hooks
452
453```go
454type SeverityHook struct{}
455
456func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
457    if level != zerolog.NoLevel {
458        e.Str("severity", level.String())
459    }
460}
461
462hooked := log.Hook(SeverityHook{})
463hooked.Warn().Msg("")
464
465// Output: {"level":"warn","severity":"warn"}
466```
467
468### Pass a sub-logger by context
469
470```go
471ctx := log.With().Str("component", "module").Logger().WithContext(ctx)
472
473log.Ctx(ctx).Info().Msg("hello world")
474
475// Output: {"component":"module","level":"info","message":"hello world"}
476```
477
478### Set as standard logger output
479
480```go
481log := zerolog.New(os.Stdout).With().
482    Str("foo", "bar").
483    Logger()
484
485stdlog.SetFlags(0)
486stdlog.SetOutput(log)
487
488stdlog.Print("hello world")
489
490// Output: {"foo":"bar","message":"hello world"}
491```
492
493### Integration with `net/http`
494
495The `github.com/rs/zerolog/hlog` package provides some helpers to integrate zerolog with `http.Handler`.
496
497In this example we use [alice](https://github.com/justinas/alice) to install logger for better readability.
498
499```go
500log := zerolog.New(os.Stdout).With().
501    Timestamp().
502    Str("role", "my-service").
503    Str("host", host).
504    Logger()
505
506c := alice.New()
507
508// Install the logger handler with default output on the console
509c = c.Append(hlog.NewHandler(log))
510
511// Install some provided extra handler to set some request's context fields.
512// Thanks to that handler, all our logs will come with some prepopulated fields.
513c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
514    hlog.FromRequest(r).Info().
515        Str("method", r.Method).
516        Stringer("url", r.URL).
517        Int("status", status).
518        Int("size", size).
519        Dur("duration", duration).
520        Msg("")
521}))
522c = c.Append(hlog.RemoteAddrHandler("ip"))
523c = c.Append(hlog.UserAgentHandler("user_agent"))
524c = c.Append(hlog.RefererHandler("referer"))
525c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
526
527// Here is your final handler
528h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
529    // Get the logger from the request's context. You can safely assume it
530    // will be always there: if the handler is removed, hlog.FromRequest
531    // will return a no-op logger.
532    hlog.FromRequest(r).Info().
533        Str("user", "current user").
534        Str("status", "ok").
535        Msg("Something happened")
536
537    // 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"}
538}))
539http.Handle("/", h)
540
541if err := http.ListenAndServe(":8080", nil); err != nil {
542    log.Fatal().Err(err).Msg("Startup failed")
543}
544```
545
546## Multiple Log Output
547`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs.
548In this example, we send the log message to both `os.Stdout` and the in-built ConsoleWriter.
549```go
550func main() {
551	consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
552
553	multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout)
554
555	logger := zerolog.New(multi).With().Timestamp().Logger()
556
557	logger.Info().Msg("Hello World!")
558}
559
560// Output (Line 1: Console; Line 2: Stdout)
561// 12:36PM INF Hello World!
562// {"level":"info","time":"2019-11-07T12:36:38+03:00","message":"Hello World!"}
563```
564
565## Global Settings
566
567Some settings can be changed and will by applied to all loggers:
568
569* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
570* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode).
571* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
572* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
573* `zerolog.LevelFieldName`: Can be set to customize level field name.
574* `zerolog.MessageFieldName`: Can be set to customize message field name.
575* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
576* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formated as UNIX timestamp.
577* `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).
578* `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`).
579* `zerolog.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.
580
581## Field Types
582
583### Standard Types
584
585* `Str`
586* `Bool`
587* `Int`, `Int8`, `Int16`, `Int32`, `Int64`
588* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
589* `Float32`, `Float64`
590
591### Advanced Fields
592
593* `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name.
594* `Func`: Run a `func` only if the level is enabled.
595* `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`.
596* `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`.
597* `Dur`: Adds a field with `time.Duration`.
598* `Dict`: Adds a sub-key/value as a field of the event.
599* `RawJSON`: Adds a field with an already encoded JSON (`[]byte`)
600* `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`)
601* `Interface`: Uses reflection to marshal the type.
602
603Most fields are also available in the slice format (`Strs` for `[]string`, `Errs` for `[]error` etc.)
604
605## Binary Encoding
606
607In 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:
608
609```bash
610go build -tags binary_log .
611```
612
613To Decode binary encoded log files you can use any CBOR decoder. One has been tested to work
614with zerolog library is [CSD](https://github.com/toravir/csd/).
615
616## Related Projects
617
618* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
619* [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog`
620* [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog`
621
622## Benchmarks
623
624See [logbench](http://hackemist.com/logbench/) for more comprehensive and up-to-date benchmarks.
625
626All operations are allocation free (those numbers *include* JSON encoding):
627
628```text
629BenchmarkLogEmpty-8        100000000    19.1 ns/op     0 B/op       0 allocs/op
630BenchmarkDisabled-8        500000000    4.07 ns/op     0 B/op       0 allocs/op
631BenchmarkInfo-8            30000000     42.5 ns/op     0 B/op       0 allocs/op
632BenchmarkContextFields-8   30000000     44.9 ns/op     0 B/op       0 allocs/op
633BenchmarkLogFields-8       10000000     184 ns/op      0 B/op       0 allocs/op
634```
635
636There are a few Go logging benchmarks and comparisons that include zerolog.
637
638* [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
639* [uber-common/zap](https://github.com/uber-go/zap#performance)
640
641Using Uber's zap comparison benchmark:
642
643Log a message and 10 fields:
644
645| Library | Time | Bytes Allocated | Objects Allocated |
646| :--- | :---: | :---: | :---: |
647| zerolog | 767 ns/op | 552 B/op | 6 allocs/op |
648| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op |
649| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op |
650| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op |
651| lion | 5392 ns/op | 5807 B/op | 63 allocs/op |
652| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op |
653| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op |
654| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op |
655
656Log a message with a logger that already has 10 fields of context:
657
658| Library | Time | Bytes Allocated | Objects Allocated |
659| :--- | :---: | :---: | :---: |
660| zerolog | 52 ns/op | 0 B/op | 0 allocs/op |
661| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op |
662| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
663| lion | 2702 ns/op | 4074 B/op | 38 allocs/op |
664| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op |
665| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op |
666| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op |
667| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op |
668
669Log a static string, without any context or `printf`-style templating:
670
671| Library | Time | Bytes Allocated | Objects Allocated |
672| :--- | :---: | :---: | :---: |
673| zerolog | 50 ns/op | 0 B/op | 0 allocs/op |
674| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op |
675| standard library | 453 ns/op | 80 B/op | 2 allocs/op |
676| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
677| go-kit | 508 ns/op | 656 B/op | 13 allocs/op |
678| lion | 771 ns/op | 1224 B/op | 10 allocs/op |
679| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op |
680| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op |
681| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op |
682
683## Caveats
684
685Note that zerolog does no de-duplication of fields. Using the same key multiple times creates multiple keys in final JSON:
686
687```go
688logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
689logger.Info().
690       Timestamp().
691       Msg("dup")
692// Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
693```
694
695In this case, many consumers will take the last value, but this is not guaranteed; check yours if in doubt.
696