A logr LogSink implementation using Zerolog

Zerologr

Go Reference test Go Report Card

A logr LogSink implementation using Zerolog.

Usage

import (
    "os"

    "github.com/go-logr/logr"
    "github.com/hn8/zerologr"
    "github.com/rs/zerolog"
)

func main() {
    zerologr.NameFieldName = "logger"
    zerologr.NameSeparator = "/"

    zl := zerolog.New(os.Stderr)
    var log logr.Logger = zerologr.New(&zl)

    log.Info("Logr in action!", "the answer", 42)
}

Implementation Details

For the most part, concepts in Zerolog correspond directly with those in logr.

Levels in logr correspond to custom debug levels in Zerolog. Any given level in logr is represents by zerologLevel = 1 - logrLevel.

For example V(2) is equivalent to Zerolog's TraceLevel, while V(1) is equivalent to Zerolog's DebugLevel.

Benchmark

go version go1.16.6 linux/amd64
cpu: Intel(R) Xeon(R) CPU E5-2673 v3 @ 2.40GHz

BenchmarkZerologrInfoOneArg-2        	      2243 ns/op	     176 B/op	       4 allocs/op
BenchmarkZerologrInfoSeveralArgs-2   	      2776 ns/op	     336 B/op	       6 allocs/op
BenchmarkZerologrV0Info-2            	      2833 ns/op	     336 B/op	       6 allocs/op
BenchmarkZerologrV9Info-2            	       124 ns/op	     176 B/op	       2 allocs/op
BenchmarkZerologrError-2             	      2837 ns/op	     336 B/op	       6 allocs/op
BenchmarkZerologrWithValues-2        	       214 ns/op	     192 B/op	       3 allocs/op
BenchmarkZerologrWithName-2          	        58 ns/op	      64 B/op	       1 allocs/op

github.com/go-logr/stdr

BenchmarkStdrInfoOneArg-2            	      1615 ns/op	     232 B/op	       8 allocs/op
BenchmarkStdrInfoSeveralArgs-2       	      2384 ns/op	     552 B/op	      14 allocs/op
BenchmarkStdrV0Info-2                	      2402 ns/op	     552 B/op	      14 allocs/op
BenchmarkStdrV9Info-2                	       121 ns/op	     176 B/op	       2 allocs/op
BenchmarkStdrError-2                 	      2549 ns/op	     600 B/op	      16 allocs/op
BenchmarkStdrWithValues-2            	       230 ns/op	     208 B/op	       3 allocs/op
BenchmarkStdrWithName-2              	        79 ns/op	      80 B/op	       1 allocs/op

github.com/go-logr/glogr

BenchmarkGlogrInfoOneArg-2           	      3188 ns/op	     400 B/op	       9 allocs/op
BenchmarkGlogrInfoSeveralArgs-2      	      4259 ns/op	     664 B/op	      15 allocs/op
BenchmarkGlogrV0Info-2               	      4120 ns/op	     664 B/op	      15 allocs/op
BenchmarkGlogrV9Info-2               	       129 ns/op	     176 B/op	       2 allocs/op
BenchmarkGlogrError-2                	      4699 ns/op	     696 B/op	      17 allocs/op
BenchmarkGlogrWithValues-2           	       215 ns/op	     192 B/op	       3 allocs/op
BenchmarkGlogrWithName-2             	        70 ns/op	      64 B/op	       1 allocs/op

github.com/go-logr/zapr

BenchmarkZaprInfoOneArg-2            	      6894 ns/op	     712 B/op	      15 allocs/op
BenchmarkZaprInfoSeveralArgs-2       	      9028 ns/op	    1192 B/op	      17 allocs/op
BenchmarkZaprV0Info-2                	      8979 ns/op	    1192 B/op	      17 allocs/op
BenchmarkZaprV9Info-2                	       140 ns/op	     176 B/op	       2 allocs/op
BenchmarkZaprError-2                 	      8869 ns/op	    1320 B/op	      18 allocs/op
BenchmarkZaprWithValues-2            	      1199 ns/op	    1560 B/op	      10 allocs/op
BenchmarkZaprWithName-2              	       213 ns/op	     216 B/op	       4 allocs/op

github.com/bombsimon/logrusr/v2

BenchmarkLogrusrInfoOneArg-2         	      5812 ns/op	    1752 B/op	      28 allocs/op
BenchmarkLogrusrInfoSeveralArgs-2    	      8973 ns/op	    2088 B/op	      35 allocs/op
BenchmarkLogrusrV0Info-2             	      8754 ns/op	    2088 B/op	      35 allocs/op
BenchmarkLogrusrV9Info-2             	       125 ns/op	     176 B/op	       2 allocs/op
BenchmarkLogrusrError-2              	     10566 ns/op	    2624 B/op	      41 allocs/op
BenchmarkLogrusrWithValues-2         	       875 ns/op	     960 B/op	       8 allocs/op
BenchmarkLogrusrWithName-2           	       646 ns/op	     592 B/op	       7 allocs/op
Owner
Comments
  • Enabled() returns true when it should not

    Enabled() returns true when it should not

    I tried the trick described in https://github.com/go-logr/logr/issues/149#issuecomment-1231513709 but it doesn't work for zerologr.

    Here is a slightly altered example;

    package main
    
    import (
    	"fmt"
    	"os"
    
    	"github.com/go-logr/logr"
    	"github.com/go-logr/zerologr"
    	"github.com/rs/zerolog"
    )
    
    func main() {
    	zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs
    	zerologr.NameFieldName = "logger"
    	zerologr.NameSeparator = "/"
    
    	zl := zerolog.New(os.Stderr)
    	zl = zl.With().Caller().Timestamp().Logger().Level(zerolog.InfoLevel)
    	var log logr.Logger = zerologr.New(&zl)
    	lvl := 2
    	fmt.Println("Lvl", lvl, "Enabled", log.V(lvl).Enabled())
    	log.Info("Logr in action!", "the answer", 42)
    }
    // output: Lvl 2 Enabled true
    

    https://go.dev/play/p/sR1JXKaxgPs

    When the level is zerolog.InfoLevel log.V(2).Enabled() should return false.

  • NewLogSync mutates global zerolog variables

    NewLogSync mutates global zerolog variables

    When calling NewLogSync, zerolog.LevelField is mutated. This is problematic when an application uses zerolog as its logger and uses zerologr in order to satisfy a library's requirement to pass a logr implementation. This causes a side effect on the zerolog global state and forces the application to restore global variables.

    https://github.com/go-logr/zerologr/blob/master/zerologr.go#L75-L77

  • Fix Enabled detection

    Fix Enabled detection

    Unfortunately, the optimization in the LogSink#Enabled method breaks the intended use of that function, as it is exposed to the user by logr.Logger#Enabled. Currently, it's hardcoded to return true for any value between 0 and 2. However, if I'm bothering to call log.Enabled explicitly, it's for one reason: I want to check if logging is enabled prior to doing an expensive operation solely for the sake of logging, such as doing an http dump of a request, or a stack trace.

    As the comment in the code notes, Info() will do that for me already. So I never need to call Enabled but for one reason: I need to know for sure that the message will be output prior to doing that expensive operation.

    This implementation breaks that, since Enabled doesn't check the actual verbosity level of the logger implementation. If it is to be of any use whatsoever, the result of Enabled must match that of the underlying logsink implementation. The only way to check that is to...well, check it.

    Since there's more than one place that the zerolog.Level is compared to the int level, I added a helper function to convert consistently. I also parameterized it slightly with a constant. Although it doesn't do anything now, that levelDelta value could be configurable in the future.

  • bump zerolog version

    bump zerolog version

    bump zerolog version to the latest v1.27.0 and update dependencies with:

    $ go mod tidy -compat=1.17
    

    this also solves all currently open CVEs, see Snyk Report before: Screenshot 2022-08-19 at 13 30 14

  • feat: remove the correspondence between v-levels and zerolog levels

    feat: remove the correspondence between v-levels and zerolog levels

    We can only use the v-levels of 0, 1, 2, corresponding to InfoLevel, DebugLevel and TraceLevel of Zerolog.

    It limits the finer level of V-level control.

    This pr remove the correspondence between v-levels and zerolog levels, to more v-levels support.

  • Would it be possible to have different options between Info() and Error()

    Would it be possible to have different options between Info() and Error()

    func main() {
    	zl := zerolog.New(os.Stderr)
    	zl = zl.With().
    		Caller().
    		Timestamp().
    		Logger()
    	logger := zerologr.New(&zl)
    
    

    I don't need the caller() option for regular Info() log, I want it only for Error(). Is it possible without creating two different logger ?

  • Give options to choose level key

    Give options to choose level key

    https://github.com/go-logr/zerologr/issues/6#issuecomment-1012533184

    This gives the user an option to retain Zerolog's level key, for example if they have already configured Zerolog elsewhere in their application and want Logr to output in the same format/options, or disable it and use the v level key.

    If options are not set, no changes are made to the current API and both Zerolog's level key and the v level key are printed.

  • Fix Enabled detection

    Fix Enabled detection

    Unfortunately, the optimization in the LogSink#Enabled method breaks the intended use of that function, as it is exposed to the user by logr.Logger#Enabled. Currently, it's hardcoded to return true for any value between 0 and 2. However, if I'm bothering to call log.Enabled explicitly, it's for one reason: I want to check if logging is enabled prior to doing an expensive operation solely for the sake of logging, such as doing an http dump of a request, or a stack trace.

    As the comment in the code notes, Info() will do that for me already. So I never need to call Enabled but for one reason: I need to know for sure that the message will be output prior to doing that expensive operation.

    This implementation breaks that, since Enabled doesn't check the actual verbosity level of the logger implementation. If it is to be of any use whatsoever, the result of Enabled must match that of the underlying logsink implementation. The only way to check that is to...well, check it.

    Since there's more than one place that the zerolog.Level is compared to the int level, I added a helper function to convert consistently. I also parameterized it slightly with a constant. Although it doesn't do anything now, that levelDelta value could be configurable in the future.

  • Cannot log with verbosity > 2

    Cannot log with verbosity > 2

    When logging at a verbosity > 2 no log messages appear, despite configuring zerolog's minimum accepted level.

    I can see logging is short circuited here but I'm not sure why given logr's philosophy seems to imply we choose verbosity based on the applications needs.

    package main
    
    import (
    	"os"
    
    	"github.com/go-logr/zerologr"
    	"github.com/rs/zerolog"
    )
    
    func main() {
    	zLogger := zerolog.New(os.Stderr)
    	zLogger = zLogger.Level(-10)
    	zerolog.SetGlobalLevel(-10)
    
    	zLogger.WithLevel(-1).Msg("zLogger -1") // trace
    	zLogger.WithLevel(-2).Msg("zLogger -2") // less than trace
    
    	logger := zerologr.New(&zLogger)
    	logger.V(2).Info("logger v2")
    	logger.V(3).Info("logger v3")
    }
    

    Go playground.

    Expected output:

    {"level":"trace","message":"zLogger -1"} {"level":"-2","message":"zLogger -2"} {"level":"trace","v":2,"message":"logger v2"} {"level":"-2","v":3,"message":"logger v3"}

    Actual output:

    {"level":"trace","message":"zLogger -1"} {"level":"-2","message":"zLogger -2"} {"level":"trace","v":2,"message":"logger v2"}

A zerolog Logger/Recoverer middleware for chi.
A zerolog Logger/Recoverer middleware for chi.

zchi A zerolog Logger/Recoverer middleware for chi. Install go get -u github.com/Lavalier/zchi Usage package main import ( "net/http" "github.com/

Jan 19, 2022
Go-zla - Zerolog adapter to go-facades logger interface

GO-ZLA GO ZeroLog Adapter (a.k.a. go-zla) is a lightweight Golang module that ad

Feb 16, 2022
Benchmark - Benchmark of logr implementations

Benchmark of logr implementations Implementations a function (can bridge to non-

Nov 6, 2022
Go implementation of systemd Journal's native API for logging

journald Package journald offers Go implementation of systemd Journal's native API for logging. Key features are: based on a connection-less socket wo

Dec 23, 2022
A Statsd implementation written in GO lang

statsgod Statsgod is a metric aggregation service inspired by the statsd project. Written in Golang, it increases performance and can be deployed with

Oct 1, 2022
A reference implementation of blockchain in Go to demonstrate how blockchain works. For education purpose.
A reference implementation of blockchain in Go to demonstrate how blockchain works. For education purpose.

Mini-Blockchain Mini-Blockchain is a reference design for a blockchain system to demostate a full end2end flow in current blockchain technology. There

Nov 18, 2022
An open source Pusher server implementation compatible with Pusher client libraries written in GO

Try browsing the code on Sourcegraph! IPÊ An open source Pusher server implementation compatible with Pusher client libraries written in Go. Why I wro

Jan 3, 2023
An implementation of A* in Golang

General This is an implementation of the a star path finding algoritm written in Golang. State This software is in pre-alpha state. Development starte

Jan 7, 2022
Gale-Shapley algoritm implementation in Go

Stable matching Gale-Shapley algoritm implementation in Go. Inspired by Numberphile video. See the explanation on Wikipedia Inputs N×N table of propos

Feb 12, 2022
A golang implementation of the Open Pixel Control protocol

__ ___ ___ _____ ___ /'_ `\ / __`\ _______ / __`\/\ '__`\ /'___\ /\ \L\ \/\ \L\ \/\______\/\ \L\ \ \ \L\ \/\ \__/ \ \

Jul 3, 2022
The full power of the Go Compiler directly in your browser, including a virtual file system implementation. Deployable as a static website.
The full power of the Go Compiler directly in your browser, including a virtual file system implementation. Deployable as a static website.

Static Go Playground Features Full Go Compiler running on the browser. Supports using custom build tags. Incremental builds (build cache). Supports mu

Jun 16, 2022
Easily listen to events from multiple contracts using go-ethereum bindings!

eth-log-aggregator Easily listen to events from multiple contracts using go-ethereum bindings! Usage The usage of this tool is exactly the same as abi

Dec 2, 2022
An example logging system using Prometheus, Loki, and Grafana.
An example logging system using Prometheus, Loki, and Grafana.

Logging Example Structure Collector Export numerical data for Prometheus and log data for Promtail. Exporter uses port 8080 Log files are saved to ./c

Nov 21, 2022
A simple re-creation of the first level for Space Invaders using Go and SDL-2.
A simple re-creation of the first level for Space Invaders using Go and SDL-2.

Space Invaders Go Written in Go using SDL2 Usage This application is a sample first level for a re-creation of Space Invaders. The structure of the pr

Feb 27, 2022
Gom: DOM building using Go

gom DOM building using Go Usage package main import "github.com/hadihammurabi/gom" func main() { dom := gom.H("html").Children( gom.H("head").Chi

Dec 16, 2021
Print build info from binary, using buildinfo package.

Go Build Info Print build info from binary, using buildinfo package. https://pkg.go.dev/debug/[email protected] Note: This was created to help me

Dec 30, 2021
Go-bqstreamer - Stream data into Google BigQuery concurrently using InsertAll()

Kik and me (@oryband) are no longer maintaining this repository. Thanks for all the contributions. You are welcome to fork and continue development. B

Dec 7, 2022
A Golang library to manage your environment variables using structs.

genv A simple Golang library to manage your environment variables using structs. How to use package main import ( "github.com/vdgonc/genv" ) type E

Aug 15, 2022
Coletor-mpac - Collector of the Public Ministry of Acre (MPAC) using CDP technology

Ministério Público do Acre(MPAC) Esse coletor é baseado na tecnologia Chrome Dev

Jan 28, 2022