├── .gitignore
├── .go
├── LICENSE
├── Makefile
├── README.md
├── cmd
└── benchjson
│ └── main.go
├── docs
├── CNAME
├── _config.yml
├── _layouts
│ └── default.html
└── index.html
├── go.mod
├── go.sum
├── go.version
├── logrus_test.go
├── main_test.go
├── results
├── slog_test.go
├── zap_test.go
├── zapsugar_test.go
└── zerolog_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | docs/_site/
2 | .cache/
3 | .goroot/
4 |
--------------------------------------------------------------------------------
/.go:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | ROOT=$(dirname "$0")
6 |
7 | if [ -z "$GO_VERSION" ]; then
8 | source "$ROOT/go.version"
9 | fi
10 |
11 | if [ -z "$GO_VERSION" ]; then
12 | echo "Missing \$GO_VERSION environment" >&2
13 | exit 1
14 | fi
15 |
16 | GOROOT="$ROOT"/.goroot/$GO_VERSION
17 | GOROOT_CURRENT="$ROOT"/.goroot/current
18 | CACHE_DIR="$ROOT"/.cache
19 |
20 | if [[ "$("$GOROOT"/bin/go version 2>&1 | cut -f3 -d\ )" != "go$GO_VERSION" ]]; then
21 | # Make sure only one instance is executed at the same time
22 | [ "$FLOCKER" != "$0" ] && exec env FLOCKER="$0" flock -e $0 $0 "$@"
23 |
24 | rm -rf "$GOROOT"
25 |
26 | case "$(uname -m)" in
27 | "x86_64")
28 | ARCH=amd64
29 | ;;
30 | "arm64")
31 | ARCH=arm64
32 | ;;
33 | "aarch64")
34 | ARCH=arm64
35 | ;;
36 | *)
37 | echo "Unsupported arch: $(uname -m)"
38 | exit 1
39 | esac
40 |
41 | case "$(uname -s)" in
42 | "Darwin")
43 | OS=darwin
44 | ;;
45 | "Linux")
46 | OS=linux
47 | ;;
48 | "FreeBSD")
49 | OS=freebsd
50 | ;;
51 | *)
52 | echo "Unsupported OS: $(uname -s)"
53 | exit 1
54 | esac
55 |
56 | GO_URL="https://dl.google.com/go/go$GO_VERSION.$OS-$ARCH.tar.gz"
57 |
58 | # Download and install Go.
59 | rm -rf "$GOROOT"
60 | mkdir -p "$GOROOT"
61 | mkdir -p "$CACHE_DIR"
62 | cache_file="$CACHE_DIR"/${GO_URL##*/}
63 | if [ ! -f "$cache_file" ]; then
64 | echo "Installing $GO_URL" >&2
65 | curl -sS -o "$cache_file" "$GO_URL"
66 | else
67 | echo "Installing $GO_URL (from cache)" >&2
68 | fi
69 |
70 | tar --strip-components 1 -C "$GOROOT" -zxf "$cache_file"
71 |
72 | # Reset quilt state.
73 | rm -rf "$ROOT"/.pc
74 |
75 | # Update current symlink so quilt find the files.
76 | rm -rf "$GOROOT_CURRENT"
77 | ln -sf -T "$GO_VERSION" "$GOROOT_CURRENT"
78 | else
79 | # Update current symlink so quilt find the files, may be rolling back to previous version.
80 | ln -sf -T "$GO_VERSION" "$GOROOT_CURRENT"
81 | fi
82 |
83 | "$GOROOT"/bin/go "$@"
84 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Olivier Poitrey
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | ./.go test -timeout 5h -count 5 -bench . -benchmem | tee results
3 | (echo "---\nlayout: default\n---"; go run cmd/benchjson/main.go < results) > docs/index.html
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Logbench
2 |
3 | Logbench is a comparison of benchmarks of various Golang logging libraries.
4 |
5 | See [results here](http://bench.zerolog.io).
6 |
--------------------------------------------------------------------------------
/cmd/benchjson/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "os"
9 | "sort"
10 | "strings"
11 |
12 | "golang.org/x/perf/benchstat"
13 | )
14 |
15 | type Chart struct {
16 | Labels []string `json:"labels"`
17 | Datasets []Dataset `json:"datasets"`
18 | }
19 |
20 | type Dataset struct {
21 | Label string `json:"label"`
22 | Data []*float64 `json:"data"`
23 | }
24 |
25 | // group -> test -> lib
26 | type Report map[string]map[string]*benchstat.Metrics
27 |
28 | func main() {
29 | libs := map[string]struct{}{}
30 | reports := map[string]Report{}
31 |
32 | c := &benchstat.Collection{}
33 | data, err := ioutil.ReadAll(os.Stdin)
34 | if err != nil {
35 | log.Fatal(err)
36 | }
37 | c.AddConfig("-", data)
38 | c.Tables() // trigger stats update
39 | for key, metrics := range c.Metrics {
40 | report := reports[key.Unit]
41 | if report == nil {
42 | report = Report{}
43 | reports[key.Unit] = report
44 | }
45 | idx := strings.IndexByte(key.Benchmark[1:], '/')
46 | if idx == -1 {
47 | log.Fatalf("invalid benchmark name: %s", key.Benchmark)
48 | }
49 | lib := key.Benchmark[1 : idx+1]
50 | libs[lib] = struct{}{}
51 | name := key.Benchmark[idx+2:]
52 | n := report[name]
53 | if n == nil {
54 | n = map[string]*benchstat.Metrics{}
55 | report[name] = n
56 | }
57 | n[lib] = metrics
58 | }
59 |
60 | slibs := []string{}
61 | for lib := range libs {
62 | slibs = append(slibs, lib)
63 | }
64 | sort.Sort(sort.Reverse(sort.StringSlice(slibs)))
65 |
66 | charts := map[string]Chart{}
67 | for unit, report := range reports {
68 | charts[unit] = buildChart(report, slibs)
69 | }
70 | b, err := json.Marshal(charts)
71 | if err != nil {
72 | panic(err)
73 | }
74 | fmt.Println(string(b))
75 | }
76 |
77 | func buildChart(report Report, libs []string) Chart {
78 | chart := Chart{}
79 | for name := range report {
80 | chart.Labels = append(chart.Labels, name)
81 | }
82 | sort.Sort(sort.Reverse(sort.StringSlice(chart.Labels)))
83 | for _, lib := range libs {
84 | chart.Datasets = append(chart.Datasets, Dataset{
85 | Label: lib,
86 | Data: make([]*float64, len(chart.Labels)),
87 | })
88 | }
89 | for i, name := range chart.Labels {
90 | results := report[name]
91 | for j, ds := range chart.Datasets {
92 | if m, found := results[ds.Label]; found {
93 | chart.Datasets[j].Data[i] = &m.Mean
94 | }
95 | }
96 | }
97 | return chart
98 | }
99 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | bench.zerolog.io
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rs/logbench/34a41c891b51bab2ee468adf56d200885e73893e/docs/_config.yml
--------------------------------------------------------------------------------
/docs/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
34 |
35 |
36 |
37 |
39 | Logbench
40 |
41 | Logbench is comparing benchmark results of popular Golang logging libraries.
42 |
43 |
44 |
45 |
46 |
47 |
52 |
53 |
54 |
55 |
157 |
158 |
159 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 | {"B/op":{"labels":["Enabled/WithContext/MsgComplex-24","Enabled/WithContext/Msg-24","Enabled/WithContext/Formatting-24","Enabled/WithContext/Fields/Time-24","Enabled/WithContext/Fields/Strings-24","Enabled/WithContext/Fields/String-24","Enabled/WithContext/Fields/Object-24","Enabled/WithContext/Fields/Ints-24","Enabled/WithContext/Fields/Int-24","Enabled/WithContext/Fields/Floats64-24","Enabled/WithContext/Fields/Floats32-24","Enabled/WithContext/Fields/Float64-24","Enabled/WithContext/Fields/Float32-24","Enabled/WithContext/Fields/Errors-24","Enabled/WithContext/Fields/Error-24","Enabled/WithContext/Fields/Bools-24","Enabled/WithContext/Fields/Bool-24","Enabled/NoContext/MsgComplex-24","Enabled/NoContext/Msg-24","Enabled/NoContext/Formatting-24","Enabled/NoContext/Fields/Time-24","Enabled/NoContext/Fields/Strings-24","Enabled/NoContext/Fields/String-24","Enabled/NoContext/Fields/Object-24","Enabled/NoContext/Fields/Ints-24","Enabled/NoContext/Fields/Int-24","Enabled/NoContext/Fields/Floats64-24","Enabled/NoContext/Fields/Floats32-24","Enabled/NoContext/Fields/Float64-24","Enabled/NoContext/Fields/Float32-24","Enabled/NoContext/Fields/Errors-24","Enabled/NoContext/Fields/Error-24","Enabled/NoContext/Fields/Bools-24","Enabled/NoContext/Fields/Bool-24","Disabled/WithContext/MsgComplex-24","Disabled/WithContext/Msg-24","Disabled/WithContext/Formatting-24","Disabled/WithContext/Fields/Time-24","Disabled/WithContext/Fields/Strings-24","Disabled/WithContext/Fields/String-24","Disabled/WithContext/Fields/Object-24","Disabled/WithContext/Fields/Ints-24","Disabled/WithContext/Fields/Int-24","Disabled/WithContext/Fields/Floats64-24","Disabled/WithContext/Fields/Floats32-24","Disabled/WithContext/Fields/Float64-24","Disabled/WithContext/Fields/Float32-24","Disabled/WithContext/Fields/Errors-24","Disabled/WithContext/Fields/Error-24","Disabled/WithContext/Fields/Bools-24","Disabled/WithContext/Fields/Bool-24","Disabled/NoContext/MsgComplex-24","Disabled/NoContext/Msg-24","Disabled/NoContext/Formatting-24","Disabled/NoContext/Fields/Time-24","Disabled/NoContext/Fields/Strings-24","Disabled/NoContext/Fields/String-24","Disabled/NoContext/Fields/Object-24","Disabled/NoContext/Fields/Ints-24","Disabled/NoContext/Fields/Int-24","Disabled/NoContext/Fields/Floats64-24","Disabled/NoContext/Fields/Floats32-24","Disabled/NoContext/Fields/Float64-24","Disabled/NoContext/Fields/Float32-24","Disabled/NoContext/Fields/Errors-24","Disabled/NoContext/Fields/Error-24","Disabled/NoContext/Fields/Bools-24","Disabled/NoContext/Fields/Bool-24"],"datasets":[{"label":"slog","data":[16,16,97,276,626,48,146,170,32,170,170,162,162,553,32,170,32,16,16,97,276,626,48,146,170,32,170,170,162,162,553,32,170,32,0,0,80,40,40,32,16,40,16,40,40,24,20,40,16,40,16,0,0,80,40,40,32,16,40,16,40,40,24,20,40,16,40,16]},{"label":"Zerolog","data":[0,0,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"label":"ZapSugar","data":[16,16,81,194,194,162,146,195,146,194,194.6,154,150,null,146,195,146,16,16,81,194,194,162,146,195,146,194,195,154,150,null,146,194.6,146,16,16,0,40,40,32,16,40,16,40,40,24,20,null,16,40,16,16,16,0,40,40,32,16,40,16,40,40,24,20,null,16,40,16]},{"label":"Zap","data":[0,0,null,89,88,64,64,89,65,89,89,64,64,null,64,89,65,0,0,null,89,88,64,64,89,65,89,89,64,64,null,64,89,65,0,0,null,88,88,64,64,88,64,88,88,64,64,null,64,88,64,0,0,null,88,88,64,64,88,64,88,88,64,64,null,64,88,64]},{"label":"Logrus","data":[1675.4,1641,1740,null,2834,2819,2802.4,2827.4,2799,2827,2827,2808,2804,null,null,2827,2800,1030,997,1094,null,2192,2174.6,2158,2181.6,2158,2182,2181.6,2166,2161,null,null,2183,2158,32,32,0,null,824,816,800,824,800,824,824,808,804,null,null,824,800,32,32,0,null,873,865,849,873,849,873,873,857,853,null,null,873,849]}]},"allocs/op":{"labels":["Enabled/WithContext/MsgComplex-24","Enabled/WithContext/Msg-24","Enabled/WithContext/Formatting-24","Enabled/WithContext/Fields/Time-24","Enabled/WithContext/Fields/Strings-24","Enabled/WithContext/Fields/String-24","Enabled/WithContext/Fields/Object-24","Enabled/WithContext/Fields/Ints-24","Enabled/WithContext/Fields/Int-24","Enabled/WithContext/Fields/Floats64-24","Enabled/WithContext/Fields/Floats32-24","Enabled/WithContext/Fields/Float64-24","Enabled/WithContext/Fields/Float32-24","Enabled/WithContext/Fields/Errors-24","Enabled/WithContext/Fields/Error-24","Enabled/WithContext/Fields/Bools-24","Enabled/WithContext/Fields/Bool-24","Enabled/NoContext/MsgComplex-24","Enabled/NoContext/Msg-24","Enabled/NoContext/Formatting-24","Enabled/NoContext/Fields/Time-24","Enabled/NoContext/Fields/Strings-24","Enabled/NoContext/Fields/String-24","Enabled/NoContext/Fields/Object-24","Enabled/NoContext/Fields/Ints-24","Enabled/NoContext/Fields/Int-24","Enabled/NoContext/Fields/Floats64-24","Enabled/NoContext/Fields/Floats32-24","Enabled/NoContext/Fields/Float64-24","Enabled/NoContext/Fields/Float32-24","Enabled/NoContext/Fields/Errors-24","Enabled/NoContext/Fields/Error-24","Enabled/NoContext/Fields/Bools-24","Enabled/NoContext/Fields/Bool-24","Disabled/WithContext/MsgComplex-24","Disabled/WithContext/Msg-24","Disabled/WithContext/Formatting-24","Disabled/WithContext/Fields/Time-24","Disabled/WithContext/Fields/Strings-24","Disabled/WithContext/Fields/String-24","Disabled/WithContext/Fields/Object-24","Disabled/WithContext/Fields/Ints-24","Disabled/WithContext/Fields/Int-24","Disabled/WithContext/Fields/Floats64-24","Disabled/WithContext/Fields/Floats32-24","Disabled/WithContext/Fields/Float64-24","Disabled/WithContext/Fields/Float32-24","Disabled/WithContext/Fields/Errors-24","Disabled/WithContext/Fields/Error-24","Disabled/WithContext/Fields/Bools-24","Disabled/WithContext/Fields/Bool-24","Disabled/NoContext/MsgComplex-24","Disabled/NoContext/Msg-24","Disabled/NoContext/Formatting-24","Disabled/NoContext/Fields/Time-24","Disabled/NoContext/Fields/Strings-24","Disabled/NoContext/Fields/String-24","Disabled/NoContext/Fields/Object-24","Disabled/NoContext/Fields/Ints-24","Disabled/NoContext/Fields/Int-24","Disabled/NoContext/Fields/Floats64-24","Disabled/NoContext/Fields/Floats32-24","Disabled/NoContext/Fields/Float64-24","Disabled/NoContext/Fields/Float32-24","Disabled/NoContext/Fields/Errors-24","Disabled/NoContext/Fields/Error-24","Disabled/NoContext/Fields/Bools-24","Disabled/NoContext/Fields/Bool-24"],"datasets":[{"label":"slog","data":[3,3,4,9,7,5,6,7,4,7,7,8,8,9,4,7,4,3,3,4,9,7,5,6,7,4,7,7,8,8,9,4,7,4,0,0,1,2,2,2,1,2,1,2,2,2,2,2,1,2,1,0,0,1,2,2,2,1,2,1,2,2,2,2,2,1,2,1]},{"label":"Zerolog","data":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"label":"ZapSugar","data":[1,1,1,4,4,3,2,4,2,4,4,3,3,null,2,4,2,1,1,1,4,4,3,2,4,2,4,4,3,3,null,2,4,2,1,1,0,2,2,2,1,2,1,2,2,2,2,null,1,2,1,1,1,0,2,2,2,1,2,1,2,2,2,2,null,1,2,1]},{"label":"Zap","data":[0,0,null,2,2,1,1,2,1,2,2,1,1,null,1,2,1,0,0,null,2,2,1,1,2,1,2,2,1,1,null,1,2,1,0,0,null,2,2,1,1,2,1,2,2,1,1,null,1,2,1,0,0,null,2,2,1,1,2,1,2,2,1,1,null,1,2,1]},{"label":"Logrus","data":[29,29,29,null,36,36,35,36,35,36,36,36,36,null,null,36,35,20,20,20,null,28,28,27,28,27,28,28,28,28,null,null,28,27,2,2,0,null,7,7,6,7,6,7,7,7,7,null,null,7,6,2,2,0,null,8,8,7,8,7,8,8,8,8,null,null,8,7]}]},"ns/op":{"labels":["Enabled/WithContext/MsgComplex-24","Enabled/WithContext/Msg-24","Enabled/WithContext/Formatting-24","Enabled/WithContext/Fields/Time-24","Enabled/WithContext/Fields/Strings-24","Enabled/WithContext/Fields/String-24","Enabled/WithContext/Fields/Object-24","Enabled/WithContext/Fields/Ints-24","Enabled/WithContext/Fields/Int-24","Enabled/WithContext/Fields/Floats64-24","Enabled/WithContext/Fields/Floats32-24","Enabled/WithContext/Fields/Float64-24","Enabled/WithContext/Fields/Float32-24","Enabled/WithContext/Fields/Errors-24","Enabled/WithContext/Fields/Error-24","Enabled/WithContext/Fields/Bools-24","Enabled/WithContext/Fields/Bool-24","Enabled/NoContext/MsgComplex-24","Enabled/NoContext/Msg-24","Enabled/NoContext/Formatting-24","Enabled/NoContext/Fields/Time-24","Enabled/NoContext/Fields/Strings-24","Enabled/NoContext/Fields/String-24","Enabled/NoContext/Fields/Object-24","Enabled/NoContext/Fields/Ints-24","Enabled/NoContext/Fields/Int-24","Enabled/NoContext/Fields/Floats64-24","Enabled/NoContext/Fields/Floats32-24","Enabled/NoContext/Fields/Float64-24","Enabled/NoContext/Fields/Float32-24","Enabled/NoContext/Fields/Errors-24","Enabled/NoContext/Fields/Error-24","Enabled/NoContext/Fields/Bools-24","Enabled/NoContext/Fields/Bool-24","Disabled/WithContext/MsgComplex-24","Disabled/WithContext/Msg-24","Disabled/WithContext/Formatting-24","Disabled/WithContext/Fields/Time-24","Disabled/WithContext/Fields/Strings-24","Disabled/WithContext/Fields/String-24","Disabled/WithContext/Fields/Object-24","Disabled/WithContext/Fields/Ints-24","Disabled/WithContext/Fields/Int-24","Disabled/WithContext/Fields/Floats64-24","Disabled/WithContext/Fields/Floats32-24","Disabled/WithContext/Fields/Float64-24","Disabled/WithContext/Fields/Float32-24","Disabled/WithContext/Fields/Errors-24","Disabled/WithContext/Fields/Error-24","Disabled/WithContext/Fields/Bools-24","Disabled/WithContext/Fields/Bool-24","Disabled/NoContext/MsgComplex-24","Disabled/NoContext/Msg-24","Disabled/NoContext/Formatting-24","Disabled/NoContext/Fields/Time-24","Disabled/NoContext/Fields/Strings-24","Disabled/NoContext/Fields/String-24","Disabled/NoContext/Fields/Object-24","Disabled/NoContext/Fields/Ints-24","Disabled/NoContext/Fields/Int-24","Disabled/NoContext/Fields/Floats64-24","Disabled/NoContext/Fields/Floats32-24","Disabled/NoContext/Fields/Float64-24","Disabled/NoContext/Fields/Float32-24","Disabled/NoContext/Fields/Errors-24","Disabled/NoContext/Fields/Error-24","Disabled/NoContext/Fields/Bools-24","Disabled/NoContext/Fields/Bool-24"],"datasets":[{"label":"slog","data":[211.14,210.18,247.32,350.04,585.76,234.44,305.06000000000006,271.14,224.48000000000002,332.9200000000001,312.9,307.14,306.21999999999997,557.2,228.74,268.075,228.84,208.46,207.16,245.29999999999998,346.35999999999996,580.04,233.48000000000002,304.74,270.02,223.74,331.14,310.34000000000003,305.53999999999996,304.22,549.4,228.2,265.03999999999996,225.24,0.5284800000000001,0.5314599999999999,56.45,20.544,20.662,17.703999999999997,9.293000000000001,20.708000000000002,9.4532,20.792,20.6725,13.314,11.656,20.614,9.3232,20.694,9.2706,0.53172,0.53142,56.006,20.486,20.598,17.636,9.277600000000001,20.498,9.4526,20.688000000000002,20.616,13.228,11.758,20.655,9.284,20.582,9.158800000000001]},{"label":"Zerolog","data":[16.556,10.559600000000001,72.248,15.709999999999997,31.066000000000003,11.1775,15.532,17.424000000000003,12.68,65.604,46.716,19.954,16.366,53.924,17.79,11.99,12.4578,13.578,8.0004,70.404,14.868,29.428,11.488,13.868,14.138,10.0912,63.97800000000001,44.682500000000005,17.107999999999997,14.388,50.726,11.582,12.2,10.156400000000001,0.85132,0.85102,0.98736,1.1016,1.102,1.0274,1.0494,1.1004,1.1118000000000001,1.1092,1.2144,1.1454,1.0824,1.1006,1.0628,1.1016,1.116,0.85382,0.85226,0.99318,1.101,1.101,1.0258,1.0496,1.102,1.1016,1.1114000000000002,1.2231999999999998,1.1366,1.0898,1.0984,1.0694,1.1004,1.1152000000000002]},{"label":"ZapSugar","data":[56.248,49.446000000000005,105.74,209.14,269.82,139.22,129.38,162.07999999999998,121.1,216.34,202.3,138.34,136.57999999999998,null,129.1,158.84,121.1,53.294,44.916000000000004,102.72,206.07999999999998,267.03999999999996,138,126.58,161.18,118.74,213.04,199.18,136.2,134.34,null,126.58,157.58,119.26,9.309000000000001,9.2578,0.49858,20.983999999999998,20.904,17.96,9.4698,20.716,9.6718,20.86,20.909999999999997,13.540000000000001,12.011999999999999,null,9.5128,20.906,9.4406,9.240400000000001,9.23125,0.50066,20.906000000000002,20.862000000000002,17.932000000000002,9.509599999999999,20.726000000000003,9.5384,20.776,20.782,13.636,11.918,null,9.636999999999999,20.932,9.540799999999999]},{"label":"Zap","data":[38.019999999999996,32.224,null,144.92,220.62,82.12400000000001,80.592,95.282,69.36200000000001,152.82,136.96,78.456,75.958,null,79.748,91.106,69.52,36.748,29.476,null,141.42,219.62,79.526,79.616,92.54,67.57400000000001,152.72,133.82,76.338,73.826,null,79.762,89.07400000000001,66.98599999999999,0.59128,0.59634,null,40.779999999999994,40.748,28.12,27.988000000000003,40.955999999999996,28.401999999999997,40.862,40.668,28.414,27.914,null,28.22,40.54,27.952,0.5962,0.59432,null,40.898,40.529999999999994,28.418,28.152,41.416000000000004,28.706,41.518,41.394,28.47,28.07,null,28.436,40.75,28.21]},{"label":"Logrus","data":[6903.2,6809.2,7475.6,null,10550.2,9394.6,9014.8,9540.8,8890.2,11415,10735.6,9476.8,9629.4,null,null,9370,9040.6,3665.8,3516.6,4425.6,null,6611.6,5574.8,5763.8,5789.8,5398.6,7239.8,6723.8,5746.2,5485.2,null,null,5770.6,5315.2,16.628,16.6,0.36854,null,373.04,360.38,354.55999999999995,377.28,351.56,373.62,373.70000000000005,374.76,370.44,null,null,373.5,351.26,16.066,15.840000000000002,0.36296,null,369.26,357.38,347.66,368.12,348.54,367.34000000000003,365.86,365.66,363.38,null,null,370,349.7]}]}}
5 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/rs/logbench
2 |
3 | go 1.21
4 |
5 | require (
6 | github.com/juju/testing v0.0.0-20190613124551-e81189438503
7 | github.com/rs/zerolog v1.30.0
8 | github.com/sirupsen/logrus v1.9.3
9 | go.uber.org/zap v1.25.0
10 | golang.org/x/perf v0.0.0-20201207232921-bdcc6220ee90
11 | )
12 |
13 | require (
14 | github.com/benbjohnson/clock v1.3.0 // indirect
15 | github.com/juju/loggo v1.0.0 // indirect
16 | github.com/mattn/go-colorable v0.1.13 // indirect
17 | github.com/mattn/go-isatty v0.0.19 // indirect
18 | go.uber.org/multierr v1.11.0 // indirect
19 | golang.org/x/sys v0.11.0 // indirect
20 | gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2 // indirect
21 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
22 | gopkg.in/yaml.v2 v2.2.2 // indirect
23 | )
24 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo=
3 | github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes=
4 | github.com/aclements/go-moremath v0.0.0-20161014184102-0ff62e0875ff/go.mod h1:idZL3yvz4kzx1dsBOAC+oYv6L92P1oFEhUXUB1A/lwQ=
5 | github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
6 | github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
7 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
12 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
13 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
14 | github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
15 | github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
16 | github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
17 | github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
18 | github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
19 | github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
20 | github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
21 | github.com/juju/loggo v1.0.0 h1:Y6ZMQOGR9Aj3BGkiWx7HBbIx6zNwNkxhVNOHU2i1bl0=
22 | github.com/juju/loggo v1.0.0/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg=
23 | github.com/juju/testing v0.0.0-20190613124551-e81189438503 h1:ZUgTbk8oHgP0jpMieifGC9Lv47mHn8Pb3mFX3/Ew4iY=
24 | github.com/juju/testing v0.0.0-20190613124551-e81189438503/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
25 | github.com/lunixbochs/vtclean v0.0.0-20160125035106-4fbf7632a2c6/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
26 | github.com/mattn/go-colorable v0.0.6/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
27 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
28 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
29 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
30 | github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
31 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
32 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
33 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
34 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
35 | github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
36 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
37 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
38 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
39 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
40 | github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
41 | github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
42 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
43 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
44 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
45 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
46 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
47 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
48 | go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
49 | go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
50 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
51 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
52 | go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
53 | go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
54 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
55 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
56 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
57 | golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
58 | golang.org/x/perf v0.0.0-20201207232921-bdcc6220ee90 h1:P+M61+mQKVHzooHFlNUTNBfj+TqHiQOGgx2kKL7mHbA=
59 | golang.org/x/perf v0.0.0-20201207232921-bdcc6220ee90/go.mod h1:KRSrLY7jerMEa0Ih7gBheQ3FYDiSx6liMnniX1o3j2g=
60 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
61 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
62 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
63 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
64 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
65 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
66 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
67 | golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
68 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
70 | google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
71 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
72 | google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74 | gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2 h1:+j1SppRob9bAgoYmsdW9NNBdKZfgYuWpqnYHv78Qt8w=
75 | gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
76 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
77 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
78 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
79 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
80 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
81 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
82 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
83 |
--------------------------------------------------------------------------------
/go.version:
--------------------------------------------------------------------------------
1 | GO_VERSION=1.21.0
2 |
--------------------------------------------------------------------------------
/logrus_test.go:
--------------------------------------------------------------------------------
1 | package logbench
2 |
3 | import (
4 | "io"
5 | "time"
6 |
7 | "github.com/sirupsen/logrus"
8 | )
9 |
10 | func init() {
11 | tests["Logrus"] = logrusTester{}
12 | }
13 |
14 | type logrusTester struct {
15 | l logrus.FieldLogger
16 | }
17 |
18 | var (
19 | _ logTesterArray = (*logrusTester)(nil)
20 | )
21 |
22 | func (logrusTester) newLogger(out io.Writer, disabled bool) logTester {
23 | lvl := logrus.DebugLevel
24 | if disabled {
25 | lvl = logrus.PanicLevel
26 | }
27 | return logrusTester{&logrus.Logger{
28 | Out: out,
29 | Formatter: &logrus.JSONFormatter{
30 | DisableTimestamp: true,
31 | FieldMap: logrus.FieldMap{
32 | logrus.FieldKeyTime: "",
33 | logrus.FieldKeyMsg: "message",
34 | },
35 | },
36 | Hooks: make(logrus.LevelHooks),
37 | Level: lvl,
38 | }}
39 | }
40 |
41 | func (t logrusTester) logMsg(msg string) {
42 | t.l.Info(msg)
43 | }
44 |
45 | func (t logrusTester) logFormat(format string, v ...interface{}) bool {
46 | t.l.Infof(format, v...)
47 | return true
48 | }
49 |
50 | func (t logrusTester) withContext(context map[string]interface{}) (logTester, bool) {
51 | return logrusTester{t.l.WithFields(context)}, true
52 | }
53 |
54 | func (t logrusTester) logBool(msg, key string, value bool) bool {
55 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
56 | return true
57 | }
58 |
59 | func (t logrusTester) logInt(msg, key string, value int) bool {
60 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
61 | return true
62 | }
63 |
64 | func (t logrusTester) logFloat32(msg, key string, value float32) bool {
65 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
66 | return true
67 | }
68 |
69 | func (t logrusTester) logFloat64(msg, key string, value float64) bool {
70 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
71 | return true
72 | }
73 |
74 | func (t logrusTester) logTime(msg, key string, value time.Time) bool {
75 | return false
76 | }
77 |
78 | func (t logrusTester) logDuration(msg, key string, value time.Duration) bool {
79 | return false
80 | }
81 |
82 | func (t logrusTester) logError(msg, key string, value error) bool {
83 | return false
84 | }
85 |
86 | func (t logrusTester) logString(msg, key string, value string) bool {
87 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
88 | return true
89 | }
90 |
91 | func (t logrusTester) logObject(msg, key string, value *obj) bool {
92 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
93 | return true
94 | }
95 |
96 | func (t logrusTester) logBools(msg, key string, value []bool) bool {
97 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
98 | return true
99 | }
100 |
101 | func (t logrusTester) logInts(msg, key string, value []int) bool {
102 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
103 | return true
104 | }
105 |
106 | func (t logrusTester) logFloats32(msg, key string, value []float32) bool {
107 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
108 | return true
109 | }
110 |
111 | func (t logrusTester) logFloats64(msg, key string, value []float64) bool {
112 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
113 | return true
114 | }
115 |
116 | func (t logrusTester) logTimes(msg, key string, value []time.Time) bool {
117 | return false
118 | }
119 |
120 | func (t logrusTester) logDurations(msg, key string, value []time.Duration) bool {
121 | return false
122 | }
123 |
124 | func (t logrusTester) logErrors(msg, key string, value []error) bool {
125 | return false
126 | }
127 |
128 | func (t logrusTester) logStrings(msg, key string, value []string) bool {
129 | t.l.WithFields(logrus.Fields{key: value}).Info(msg)
130 | return true
131 | }
132 |
--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------
1 | package logbench
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "io"
8 | "io/ioutil"
9 | "testing"
10 | "time"
11 |
12 | "github.com/juju/testing/checkers"
13 | )
14 |
15 | type tester interface {
16 | newLogger(out io.Writer, disabled bool) logTester
17 | }
18 |
19 | type logTester interface {
20 | logMsg(msg string)
21 | logFormat(format string, v ...interface{}) bool
22 |
23 | withContext(context map[string]interface{}) (logTester, bool)
24 |
25 | logBool(msg, key string, value bool) bool
26 | logInt(msg, key string, value int) bool
27 | logFloat32(msg, key string, value float32) bool
28 | logFloat64(msg, key string, value float64) bool
29 | logTime(msg, key string, value time.Time) bool
30 | logDuration(msg, key string, value time.Duration) bool
31 | logError(msg, key string, value error) bool
32 | logString(msg, key string, value string) bool
33 | logObject(msg, key string, value *obj) bool
34 | }
35 |
36 | type logTesterArray interface {
37 | logBools(msg, key string, value []bool) bool
38 | logInts(msg, key string, value []int) bool
39 | logFloats32(msg, key string, value []float32) bool
40 | logFloats64(msg, key string, value []float64) bool
41 | logTimes(msg, key string, value []time.Time) bool
42 | logDurations(msg, key string, value []time.Duration) bool
43 | logErrors(msg, key string, value []error) bool
44 | logStrings(msg, key string, value []string) bool
45 | }
46 |
47 | var tests = map[string]tester{}
48 |
49 | type obj struct {
50 | Name string `json:"name"`
51 | Count int `json:"count"`
52 | Enabled bool `json:"enabled"`
53 | }
54 |
55 | func (o obj) jsonMap() map[string]interface{} {
56 | return map[string]interface{}{
57 | "name": o.Name,
58 | "count": o.Count,
59 | "enabled": o.Enabled,
60 | }
61 | }
62 |
63 | type objs []*obj
64 |
65 | var (
66 | sampleBools = []bool{true, false, true, false, true, false, true, false, true, false}
67 | sampleInts = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
68 | sampleFloats32 = []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
69 | sampleFloats64 = []float64{1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.0, 0.1}
70 | sampleStrings = fakeStrings(10)
71 | sampleErrors = fakeErrors(10)
72 | sampleTimes = []time.Time{
73 | time.Unix(0, 0),
74 | time.Unix(1, 0),
75 | time.Unix(2, 0),
76 | time.Unix(3, 0),
77 | time.Unix(4, 0),
78 | time.Unix(5, 0),
79 | time.Unix(6, 0),
80 | time.Unix(7, 0),
81 | time.Unix(8, 0),
82 | time.Unix(9, 0),
83 | }
84 | sampleDurations = []time.Duration{
85 | 0 * time.Millisecond,
86 | 1 * time.Millisecond,
87 | 2 * time.Millisecond,
88 | 3 * time.Millisecond,
89 | 4 * time.Millisecond,
90 | 5 * time.Millisecond,
91 | 6 * time.Millisecond,
92 | 7 * time.Millisecond,
93 | 8 * time.Millisecond,
94 | 9 * time.Millisecond,
95 | }
96 | sampleObjects = objs{
97 | &obj{"a", 1, true},
98 | &obj{"b", 2, false},
99 | &obj{"c", 3, true},
100 | &obj{"d", 4, false},
101 | &obj{"e", 5, true},
102 | &obj{"f", 6, false},
103 | &obj{"g", 7, true},
104 | &obj{"h", 8, false},
105 | &obj{"i", 9, true},
106 | &obj{"j", 0, false},
107 | }
108 | sampleString = "some string with a somewhat realistic length"
109 | sampleStringComplex = "some string with \"special ❤️ chars\" and somewhat realistic length"
110 | sampleFormat = "Test format %d %f %s"
111 | sampleFormatArgs = []interface{}{1, 1.0, sampleString}
112 | sampleContext = map[string]interface{}{
113 | "ctx_int": sampleInts[0],
114 | "ctx_string": sampleStrings[0],
115 | "ctx_error": sampleErrors[0],
116 | "ctx_object": sampleObjects[0],
117 | }
118 | )
119 |
120 | func fakeStrings(n int) []string {
121 | strs := make([]string, n)
122 | for i := range strs {
123 | strs[i] = fmt.Sprintf("%s %d", sampleString, i)
124 | }
125 | return strs
126 | }
127 |
128 | func fakeErrors(n int) []error {
129 | errs := make([]error, n)
130 | for i := range errs {
131 | errs[i] = fmt.Errorf("sample error number %d", i)
132 | }
133 | return errs
134 | }
135 |
136 | func Benchmark(b *testing.B) {
137 | for name, t := range tests {
138 | b.Run(name, func(b *testing.B) {
139 | b.Run("Enabled", func(b *testing.B) {
140 | l := t.newLogger(ioutil.Discard, false)
141 | benchmarkContext(b, l)
142 | })
143 | b.Run("Disabled", func(b *testing.B) {
144 | l := t.newLogger(ioutil.Discard, true)
145 | benchmarkContext(b, l)
146 | })
147 | })
148 | }
149 | }
150 |
151 | func benchmarkContext(b *testing.B, l logTester) {
152 | b.Run("NoContext", func(b *testing.B) {
153 | benchmark(b, l)
154 | })
155 | if l, supported := l.withContext(sampleContext); supported {
156 | b.Run("WithContext", func(b *testing.B) {
157 | benchmark(b, l)
158 | })
159 | }
160 | }
161 |
162 | func benchmark(b *testing.B, l logTester) {
163 | b.Run("Msg", func(b *testing.B) {
164 | b.RunParallel(func(pb *testing.PB) {
165 | for pb.Next() {
166 | l.logMsg(sampleString)
167 | }
168 | })
169 | })
170 | b.Run("MsgComplex", func(b *testing.B) {
171 | b.RunParallel(func(pb *testing.PB) {
172 | for pb.Next() {
173 | l.logMsg(sampleStringComplex)
174 | }
175 | })
176 | })
177 | if l.logFormat(sampleFormat, sampleFormatArgs...) {
178 | b.Run("Formatting", func(b *testing.B) {
179 | b.RunParallel(func(pb *testing.PB) {
180 | for pb.Next() {
181 | l.logFormat(sampleFormat, sampleFormatArgs...)
182 | }
183 | })
184 | })
185 | }
186 | b.Run("Fields", func(b *testing.B) {
187 | benchmarkFields(b, l)
188 | })
189 | }
190 |
191 | func benchmarkFields(b *testing.B, l logTester) {
192 | bs := map[string]func(){}
193 | if l.logBool(sampleString, "bool", sampleBools[0]) {
194 | bs["Bool"] = func() {
195 | l.logBool(sampleString, "bool", sampleBools[0])
196 | }
197 | }
198 | if l.logInt(sampleString, "int", sampleInts[0]) {
199 | bs["Int"] = func() {
200 | l.logInt(sampleString, "int", sampleInts[0])
201 | }
202 | }
203 | if l.logFloat32(sampleString, "float32", sampleFloats32[0]) {
204 | bs["Float32"] = func() {
205 | l.logFloat32(sampleString, "float32", sampleFloats32[0])
206 | }
207 | }
208 | if l.logFloat64(sampleString, "float64", sampleFloats64[0]) {
209 | bs["Float64"] = func() {
210 | l.logFloat64(sampleString, "float64", sampleFloats64[0])
211 | }
212 | }
213 | if l.logTime(sampleString, "time", sampleTimes[0]) {
214 | bs["Time"] = func() {
215 | l.logTime(sampleString, "time", sampleTimes[0])
216 | }
217 | }
218 | if l.logDuration(sampleString, "duration", sampleDurations[0]) {
219 | bs["Time"] = func() {
220 | l.logDuration(sampleString, "duration", sampleDurations[0])
221 | }
222 | }
223 | if l.logString(sampleString, "string", sampleStrings[0]) {
224 | bs["String"] = func() {
225 | l.logString(sampleString, "string", sampleStrings[0])
226 | }
227 | }
228 | if l.logError(sampleString, "error", sampleErrors[0]) {
229 | bs["Error"] = func() {
230 | l.logError(sampleString, "error", sampleErrors[0])
231 | }
232 | }
233 | if l.logObject(sampleString, "object", sampleObjects[0]) {
234 | bs["Object"] = func() {
235 | l.logObject(sampleString, "object", sampleObjects[0])
236 | }
237 | }
238 |
239 | if l, ok := l.(logTesterArray); ok {
240 | if l.logBools(sampleString, "bools", sampleBools) {
241 | bs["Bools"] = func() {
242 | l.logBools(sampleString, "bools", sampleBools)
243 | }
244 | }
245 | if l.logInts(sampleString, "ints", sampleInts) {
246 | bs["Ints"] = func() {
247 | l.logInts(sampleString, "ints", sampleInts)
248 | }
249 | }
250 | if l.logFloats32(sampleString, "floats32", sampleFloats32) {
251 | bs["Floats32"] = func() {
252 | l.logFloats32(sampleString, "floats32", sampleFloats32)
253 | }
254 | }
255 | if l.logFloats64(sampleString, "floats64", sampleFloats64) {
256 | bs["Floats64"] = func() {
257 | l.logFloats64(sampleString, "floats64", sampleFloats64)
258 | }
259 | }
260 | if l.logTimes(sampleString, "time", sampleTimes) {
261 | bs["Time"] = func() {
262 | l.logTimes(sampleString, "time", sampleTimes)
263 | }
264 | }
265 | if l.logDurations(sampleString, "duration", sampleDurations) {
266 | bs["Time"] = func() {
267 | l.logDurations(sampleString, "duration", sampleDurations)
268 | }
269 | }
270 | if l.logStrings(sampleString, "strings", sampleStrings) {
271 | bs["Strings"] = func() {
272 | l.logStrings(sampleString, "strings", sampleStrings)
273 | }
274 | }
275 | if l.logErrors(sampleString, "errors", sampleErrors) {
276 | bs["Errors"] = func() {
277 | l.logErrors(sampleString, "errors", sampleErrors)
278 | }
279 | }
280 | }
281 |
282 | b.ResetTimer()
283 | for name, f := range bs {
284 | b.Run(name, func(b *testing.B) {
285 | b.RunParallel(func(pb *testing.PB) {
286 | for pb.Next() {
287 | f()
288 | }
289 | })
290 | })
291 | }
292 | }
293 |
294 | type testCtx struct {
295 | tester logTester
296 | buf *bytes.Buffer
297 | context map[string]interface{}
298 | enabled bool
299 | }
300 |
301 | func Test(t *testing.T) {
302 | for name, lt := range tests {
303 | t.Run(name, func(t *testing.T) {
304 | t.Run("Enabled", func(t *testing.T) {
305 | buf := &bytes.Buffer{}
306 | ctx := &testCtx{
307 | tester: lt.newLogger(buf, false),
308 | buf: buf,
309 | enabled: true,
310 | }
311 | testContext(t, ctx)
312 | })
313 | t.Run("Disabled", func(t *testing.T) {
314 | buf := &bytes.Buffer{}
315 | ctx := &testCtx{
316 | tester: lt.newLogger(buf, true),
317 | buf: buf,
318 | enabled: false,
319 | }
320 | testContext(t, ctx)
321 | })
322 | })
323 | }
324 | }
325 |
326 | func testContext(t *testing.T, ctx *testCtx) {
327 | t.Run("NoContext", func(t *testing.T) {
328 | test(t, ctx)
329 | })
330 | if l, supported := ctx.tester.withContext(sampleContext); supported {
331 | t.Run("WithContext", func(t *testing.T) {
332 | ctx.context = sampleContext
333 | ctx.tester = l
334 | test(t, ctx)
335 | })
336 | }
337 | }
338 |
339 | func test(t *testing.T, ctx *testCtx) {
340 | ctx.tester.logMsg(sampleString)
341 | validate(t, ctx, "Msg", true, map[string]interface{}{"message": sampleString})
342 | ctx.tester.logMsg(sampleStringComplex)
343 | validate(t, ctx, "MsgComplex", true, map[string]interface{}{"message": sampleStringComplex})
344 | validate(t, ctx, "Formatting",
345 | ctx.tester.logFormat(sampleFormat, sampleFormatArgs...),
346 | map[string]interface{}{"message": fmt.Sprintf(sampleFormat, sampleFormatArgs...)})
347 | t.Run("Fields", func(t *testing.T) {
348 | testFields(t, ctx)
349 | })
350 | }
351 |
352 | func testFields(t *testing.T, ctx *testCtx) {
353 | l := ctx.tester
354 | validate(t, ctx, "Bool",
355 | l.logBool(sampleString, "bool", sampleBools[0]),
356 | map[string]interface{}{"message": sampleString, "bool": sampleBools[0]})
357 | validate(t, ctx, "Int",
358 | l.logInt(sampleString, "int", sampleInts[0]),
359 | map[string]interface{}{"message": sampleString, "int": sampleInts[0]})
360 | validate(t, ctx, "Float32",
361 | l.logFloat32(sampleString, "float32", sampleFloats32[0]),
362 | map[string]interface{}{"message": sampleString, "float32": sampleFloats32[0]})
363 | validate(t, ctx, "Float64",
364 | l.logFloat64(sampleString, "float64", sampleFloats64[0]),
365 | map[string]interface{}{"message": sampleString, "float64": sampleFloats64[0]})
366 | validate(t, ctx, "Time",
367 | l.logTime(sampleString, "time", sampleTimes[0]),
368 | map[string]interface{}{"message": sampleString, "time": sampleTimes[0]})
369 | validate(t, ctx, "Duration",
370 | l.logDuration(sampleString, "duration", sampleDurations[0]),
371 | map[string]interface{}{"message": sampleString, "duration": sampleDurations[0]})
372 | validate(t, ctx, "String",
373 | l.logString(sampleString, "string", sampleStrings[0]),
374 | map[string]interface{}{"message": sampleString, "string": sampleStrings[0]})
375 | validate(t, ctx, "Error",
376 | l.logError(sampleString, "error", sampleErrors[0]),
377 | map[string]interface{}{"message": sampleString, "error": sampleErrors[0]})
378 | validate(t, ctx, "Object",
379 | l.logObject(sampleString, "object", sampleObjects[0]),
380 | map[string]interface{}{"message": sampleString, "object": sampleObjects[0]})
381 |
382 | if l, ok := l.(logTesterArray); ok {
383 | validate(t, ctx, "Bools",
384 | l.logBools(sampleString, "bools", sampleBools),
385 | map[string]interface{}{"message": sampleString, "bools": sampleBools})
386 | validate(t, ctx, "Ints",
387 | l.logInts(sampleString, "ints", sampleInts),
388 | map[string]interface{}{"message": sampleString, "ints": sampleInts})
389 | validate(t, ctx, "Floats32",
390 | l.logFloats32(sampleString, "floats32", sampleFloats32),
391 | map[string]interface{}{"message": sampleString, "floats32": sampleFloats32})
392 | validate(t, ctx, "Floats64",
393 | l.logFloats64(sampleString, "floats64", sampleFloats64),
394 | map[string]interface{}{"message": sampleString, "floats64": sampleFloats64})
395 | validate(t, ctx, "Times",
396 | l.logTimes(sampleString, "time", sampleTimes),
397 | map[string]interface{}{"message": sampleString, "time": sampleTimes})
398 | validate(t, ctx, "Durations",
399 | l.logDurations(sampleString, "duration", sampleDurations),
400 | map[string]interface{}{"message": sampleString, "duration": sampleDurations})
401 | validate(t, ctx, "Strings",
402 | l.logStrings(sampleString, "strings", sampleStrings),
403 | map[string]interface{}{"message": sampleString, "strings": sampleStrings})
404 | validate(t, ctx, "Errors",
405 | l.logErrors(sampleString, "errors", sampleErrors),
406 | map[string]interface{}{"message": sampleString, "errors": sampleErrors})
407 | }
408 | }
409 |
410 | func validate(t *testing.T, ctx *testCtx, name string, supported bool, want map[string]interface{}) {
411 | t.Helper()
412 | defer ctx.buf.Reset()
413 | if !supported {
414 | return
415 | }
416 | t.Run(name, func(t *testing.T) {
417 | if !ctx.enabled {
418 | if ctx.buf.Len() != 0 {
419 | t.Errorf("wrote output while disabled: %v", ctx.buf.String())
420 | }
421 | return
422 | }
423 | want["level"] = "info"
424 | for k, v := range ctx.context {
425 | want[k] = v
426 | }
427 | fixTypes(want)
428 | wj, _ := json.Marshal(want)
429 | want = map[string]interface{}{}
430 | json.Unmarshal(wj, &want)
431 | var got map[string]interface{}
432 | if err := json.Unmarshal(ctx.buf.Bytes(), &got); err != nil {
433 | t.Fatalf("invalid JSON output: %v: %v", err, ctx.buf.String())
434 | }
435 | if eq, err := checkers.DeepEqual(got, want); !eq {
436 | t.Errorf("invalid output: %v\ngot: %s\nwant: %s", err, ctx.buf.String(), wj)
437 | }
438 | })
439 | }
440 |
441 | func fixTypes(m map[string]interface{}) {
442 | for k, v := range m {
443 | switch v := v.(type) {
444 | case *obj:
445 | m[k] = v.jsonMap()
446 | case map[string]interface{}:
447 | fixTypes(v)
448 | case time.Time:
449 | m[k] = v.Unix()
450 | case []time.Time:
451 | v2 := make([]int64, len(v))
452 | for i, t := range v {
453 | v2[i] = t.Unix()
454 | }
455 | m[k] = v2
456 | case time.Duration:
457 | m[k] = v / time.Millisecond
458 | case []time.Duration:
459 | v2 := make([]int64, len(v))
460 | for i, d := range v {
461 | v2[i] = int64(d / time.Millisecond)
462 | }
463 | m[k] = v2
464 | case error:
465 | m[k] = v.Error()
466 | case []error:
467 | v2 := make([]string, len(v))
468 | for i, e := range v {
469 | v2[i] = e.Error()
470 | }
471 | m[k] = v2
472 | }
473 | }
474 | }
475 |
--------------------------------------------------------------------------------
/slog_test.go:
--------------------------------------------------------------------------------
1 | package logbench
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "log/slog"
8 | "strings"
9 | "time"
10 | )
11 |
12 | func init() {
13 | tests["slog"] = slogTester{}
14 | }
15 |
16 | type slogTester struct {
17 | l slog.Logger
18 | }
19 |
20 | var (
21 | _ logTesterArray = (*slogTester)(nil)
22 | )
23 |
24 | func (slogTester) newLogger(out io.Writer, disabled bool) logTester {
25 | lvl := slog.LevelInfo
26 | testFixup := func(groups []string, a slog.Attr) slog.Attr {
27 | switch a.Key {
28 | case "time":
29 | return slog.Attr{} // discard timestamps
30 | case "replaceTime-time":
31 | return slog.Attr{Key: "time", Value: slog.Int64Value(a.Value.Time().Unix())}
32 | case "replaceTimes-time":
33 | anyTimes := a.Value.Any().([]time.Time)
34 | anyUnixTimes := make([]int64, len(anyTimes))
35 | for i, anyTime := range anyTimes {
36 | anyUnixTimes[i] = anyTime.Unix()
37 | }
38 | return slog.Attr{Key: "time", Value: slog.AnyValue(anyUnixTimes)}
39 | case "duration":
40 | anyValue := a.Value.Any()
41 | switch anyValue := anyValue.(type) {
42 | case time.Duration:
43 | return slog.Attr{Key: "duration", Value: slog.IntValue(int(anyValue.Milliseconds()))}
44 | case []time.Duration:
45 | anyDurations := anyValue
46 | anyIntDurations := make([]int, len(anyDurations))
47 | for i, anyTime := range anyDurations {
48 | anyIntDurations[i] = int(anyTime.Milliseconds())
49 | }
50 | return slog.Attr{Key: "duration", Value: slog.AnyValue(anyIntDurations)}
51 | default:
52 | return a
53 | }
54 | case "level":
55 | lowerValue := slog.StringValue(strings.ToLower(a.Value.String()))
56 | return slog.Attr{Key: a.Key, Value: lowerValue}
57 | case "msg":
58 | return slog.Attr{Key: "message", Value: a.Value}
59 | case "errors":
60 | valueErrors := a.Value.Any().([]error)
61 | errorStrings := make([]string, len(valueErrors))
62 | for i, err := range valueErrors {
63 | errorStrings[i] = err.Error()
64 | }
65 | return slog.Attr{Key: "errors", Value: slog.AnyValue(errorStrings)}
66 | default:
67 | return a
68 | }
69 | }
70 | var handler slog.Handler = slog.NewJSONHandler(out, &slog.HandlerOptions{Level: lvl, ReplaceAttr: testFixup})
71 | if disabled {
72 | handler = disabledHandler{}
73 | }
74 | return slogTester{*slog.New(handler)}
75 | }
76 |
77 | func (t slogTester) logMsg(msg string) {
78 | t.l.Info(msg)
79 | }
80 |
81 | func (t slogTester) logFormat(format string, v ...interface{}) bool {
82 | t.l.Info(fmt.Sprintf(format, v...))
83 | return true
84 | }
85 |
86 | func (t slogTester) withContext(context map[string]interface{}) (logTester, bool) {
87 | var args = make([]any, len(context)*2)
88 | index := 0
89 | for s, i := range context {
90 | args[index] = s
91 | args[index+1] = i
92 | index += 2
93 | }
94 | return slogTester{*t.l.With(args...)}, true
95 | }
96 |
97 | func (t slogTester) logBool(msg, key string, value bool) bool {
98 | t.l.Info(msg, key, value)
99 | return true
100 | }
101 |
102 | func (t slogTester) logInt(msg, key string, value int) bool {
103 | t.l.Info(msg, key, value)
104 | return true
105 | }
106 |
107 | func (t slogTester) logFloat32(msg, key string, value float32) bool {
108 | t.l.Info(msg, key, value)
109 | return true
110 | }
111 |
112 | func (t slogTester) logFloat64(msg, key string, value float64) bool {
113 | t.l.Info(msg, key, value)
114 | return true
115 | }
116 |
117 | func (t slogTester) logTime(msg, key string, value time.Time) bool {
118 | t.l.Info(msg, "replaceTime-"+key, value)
119 | return true
120 | }
121 |
122 | func (t slogTester) logDuration(msg, key string, value time.Duration) bool {
123 | t.l.Info(msg, key, value)
124 | return true
125 | }
126 |
127 | func (t slogTester) logError(msg, key string, value error) bool {
128 | t.l.Info(msg, key, value)
129 | return true
130 | }
131 |
132 | func (t slogTester) logString(msg, key string, value string) bool {
133 | t.l.Info(msg, key, value)
134 | return true
135 | }
136 |
137 | func (t slogTester) logObject(msg, key string, value *obj) bool {
138 | t.l.Info(msg, key, value)
139 | return true
140 | }
141 |
142 | func (t slogTester) logBools(msg, key string, value []bool) bool {
143 | t.l.Info(msg, key, value)
144 | return true
145 | }
146 |
147 | func (t slogTester) logInts(msg, key string, value []int) bool {
148 | t.l.Info(msg, key, value)
149 | return true
150 | }
151 |
152 | func (t slogTester) logFloats32(msg, key string, value []float32) bool {
153 | t.l.Info(msg, key, value)
154 | return true
155 | }
156 |
157 | func (t slogTester) logFloats64(msg, key string, value []float64) bool {
158 | t.l.Info(msg, key, value)
159 | return true
160 | }
161 |
162 | func (t slogTester) logTimes(msg, key string, value []time.Time) bool {
163 | t.l.Info(msg, "replaceTimes-"+key, value)
164 | return true
165 | }
166 |
167 | func (t slogTester) logDurations(msg, key string, value []time.Duration) bool {
168 | t.l.Info(msg, key, value)
169 | return true
170 | }
171 |
172 | func (t slogTester) logErrors(msg, key string, value []error) bool {
173 | t.l.Info(msg, key, value)
174 | return true
175 | }
176 |
177 | func (t slogTester) logStrings(msg, key string, value []string) bool {
178 | t.l.Info(msg, key, value)
179 | return true
180 | }
181 |
182 | type disabledHandler struct {
183 | }
184 |
185 | func (d disabledHandler) Enabled(ctx context.Context, level slog.Level) bool {
186 | return false
187 | }
188 |
189 | func (d disabledHandler) Handle(ctx context.Context, record slog.Record) error {
190 | return nil
191 | }
192 |
193 | func (d disabledHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
194 | return d
195 | }
196 |
197 | func (d disabledHandler) WithGroup(name string) slog.Handler {
198 | return d
199 | }
200 |
--------------------------------------------------------------------------------
/zap_test.go:
--------------------------------------------------------------------------------
1 | package logbench
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "io/ioutil"
7 | "time"
8 |
9 | "go.uber.org/zap"
10 | "go.uber.org/zap/zapcore"
11 | "go.uber.org/zap/zaptest"
12 | )
13 |
14 | func init() {
15 | tests["Zap"] = zapTester{}
16 | }
17 |
18 | func (o *obj) MarshalLogObject(enc zapcore.ObjectEncoder) error {
19 | enc.AddString("name", o.Name)
20 | enc.AddInt("count", o.Count)
21 | enc.AddBool("enabled", o.Enabled)
22 | return nil
23 | }
24 |
25 | type zapTester struct {
26 | l *zap.Logger
27 | }
28 |
29 | var (
30 | _ logTesterArray = (*zapTester)(nil)
31 | )
32 |
33 | func zapMillisecondDurationEncoder(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
34 | enc.AppendFloat64(float64(d) / float64(time.Millisecond))
35 | }
36 |
37 | func zapEncoder() zapcore.Encoder {
38 | ec := zap.NewProductionEncoderConfig()
39 | ec.EncodeDuration = zapMillisecondDurationEncoder
40 | ec.EncodeTime = zapcore.EpochTimeEncoder
41 | ec.TimeKey = ""
42 | ec.MessageKey = "message"
43 | return zapcore.NewJSONEncoder(ec)
44 | }
45 |
46 | func (zapTester) newLogger(out io.Writer, disabled bool) logTester {
47 | lvl := zap.DebugLevel
48 | if disabled {
49 | lvl = zap.FatalLevel
50 | }
51 | var w zapcore.WriteSyncer = &zaptest.Discarder{}
52 | if out != ioutil.Discard {
53 | w = zapcore.AddSync(out)
54 | }
55 | return zapTester{zap.New(zapcore.NewCore(zapEncoder(), w, lvl))}
56 | }
57 |
58 | func (t zapTester) logMsg(msg string) {
59 | t.l.Info(msg)
60 | }
61 |
62 | func (t zapTester) logFormat(format string, v ...interface{}) bool {
63 | return false
64 | }
65 |
66 | func (t zapTester) withContext(context map[string]interface{}) (logTester, bool) {
67 | l := t.l
68 | for k, v := range context {
69 | switch v := v.(type) {
70 | case int:
71 | l = l.With(zap.Int(k, v))
72 | case string:
73 | l = l.With(zap.String(k, v))
74 | case error:
75 | l = l.With(zap.NamedError(k, v))
76 | case time.Time:
77 | l = l.With(zap.Time(k, v))
78 | case *obj:
79 | l = l.With(zap.Object(k, v))
80 | default:
81 | panic(fmt.Sprintf("zap: unsupported context field type: %v", v))
82 | }
83 | }
84 | return zapTester{l}, true
85 | }
86 |
87 | func (t zapTester) logBool(msg, key string, value bool) bool {
88 | t.l.Info(msg, zap.Bool(key, value))
89 | return true
90 | }
91 |
92 | func (t zapTester) logInt(msg, key string, value int) bool {
93 | t.l.Info(msg, zap.Int(key, value))
94 | return true
95 | }
96 |
97 | func (t zapTester) logFloat32(msg, key string, value float32) bool {
98 | t.l.Info(msg, zap.Float32(key, value))
99 | return true
100 | }
101 |
102 | func (t zapTester) logFloat64(msg, key string, value float64) bool {
103 | t.l.Info(msg, zap.Float64(key, value))
104 | return true
105 | }
106 |
107 | func (t zapTester) logTime(msg, key string, value time.Time) bool {
108 | t.l.Info(msg, zap.Time(key, value))
109 | return true
110 | }
111 |
112 | func (t zapTester) logDuration(msg, key string, value time.Duration) bool {
113 | t.l.Info(msg, zap.Duration(key, value))
114 | return true
115 | }
116 |
117 | func (t zapTester) logError(msg, key string, value error) bool {
118 | t.l.Info(msg, zap.NamedError(key, value))
119 | return true
120 | }
121 |
122 | func (t zapTester) logString(msg, key string, value string) bool {
123 | t.l.Info(msg, zap.String(key, value))
124 | return true
125 | }
126 |
127 | func (t zapTester) logObject(msg, key string, value *obj) bool {
128 | t.l.Info(msg, zap.Object(key, value))
129 | return true
130 | }
131 |
132 | func (t zapTester) logBools(msg, key string, value []bool) bool {
133 | t.l.Info(msg, zap.Bools(key, value))
134 | return true
135 | }
136 |
137 | func (t zapTester) logInts(msg, key string, value []int) bool {
138 | t.l.Info(msg, zap.Ints(key, value))
139 | return true
140 | }
141 |
142 | func (t zapTester) logFloats32(msg, key string, value []float32) bool {
143 | t.l.Info(msg, zap.Float32s(key, value))
144 | return true
145 | }
146 |
147 | func (t zapTester) logFloats64(msg, key string, value []float64) bool {
148 | t.l.Info(msg, zap.Float64s(key, value))
149 | return true
150 | }
151 |
152 | func (t zapTester) logTimes(msg, key string, value []time.Time) bool {
153 | t.l.Info(msg, zap.Times(key, value))
154 | return true
155 | }
156 |
157 | func (t zapTester) logDurations(msg, key string, value []time.Duration) bool {
158 | t.l.Info(msg, zap.Durations(key, value))
159 | return true
160 | }
161 |
162 | func (t zapTester) logErrors(msg, key string, value []error) bool {
163 | return false
164 | }
165 |
166 | func (t zapTester) logStrings(msg, key string, value []string) bool {
167 | t.l.Info(msg, zap.Strings(key, value))
168 | return true
169 | }
170 |
--------------------------------------------------------------------------------
/zapsugar_test.go:
--------------------------------------------------------------------------------
1 | package logbench
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "io/ioutil"
7 | "time"
8 |
9 | "go.uber.org/zap"
10 | "go.uber.org/zap/zapcore"
11 | "go.uber.org/zap/zaptest"
12 | )
13 |
14 | func init() {
15 | tests["ZapSugar"] = zapSugarTester{}
16 | }
17 |
18 | type zapSugarTester struct {
19 | l *zap.SugaredLogger
20 | }
21 |
22 | var (
23 | _ logTesterArray = (*zapSugarTester)(nil)
24 | )
25 |
26 | func (zapSugarTester) newLogger(out io.Writer, disabled bool) logTester {
27 | // TOFIX: use out
28 | lvl := zap.DebugLevel
29 | if disabled {
30 | lvl = zap.FatalLevel
31 | }
32 | var w zapcore.WriteSyncer = &zaptest.Discarder{}
33 | if out != ioutil.Discard {
34 | w = zapcore.AddSync(out)
35 | }
36 | return zapSugarTester{zap.New(zapcore.NewCore(zapEncoder(), w, lvl)).Sugar()}
37 | }
38 |
39 | func (t zapSugarTester) logMsg(msg string) {
40 | t.l.Info(msg)
41 | }
42 |
43 | func (t zapSugarTester) logFormat(format string, v ...interface{}) bool {
44 | t.l.Infof(format, v...)
45 | return true
46 | }
47 |
48 | func (t zapSugarTester) withContext(context map[string]interface{}) (logTester, bool) {
49 | l := t.l
50 | for k, v := range context {
51 | switch v := v.(type) {
52 | case int:
53 | l = l.With(zap.Int(k, v))
54 | case string:
55 | l = l.With(zap.String(k, v))
56 | case error:
57 | l = l.With(zap.NamedError(k, v))
58 | case time.Time:
59 | l = l.With(zap.Time(k, v))
60 | case *obj:
61 | l = l.With(zap.Object(k, v))
62 | default:
63 | panic(fmt.Sprintf("zap: unsupported context field type: %v", v))
64 | }
65 | }
66 | return zapSugarTester{l}, true
67 | }
68 |
69 | func (t zapSugarTester) logBool(msg, key string, value bool) bool {
70 | t.l.Infow(msg, key, value)
71 | return true
72 | }
73 |
74 | func (t zapSugarTester) logInt(msg, key string, value int) bool {
75 | t.l.Infow(msg, key, value)
76 | return true
77 | }
78 |
79 | func (t zapSugarTester) logFloat32(msg, key string, value float32) bool {
80 | t.l.Infow(msg, key, value)
81 | return true
82 | }
83 |
84 | func (t zapSugarTester) logFloat64(msg, key string, value float64) bool {
85 | t.l.Infow(msg, key, value)
86 | return true
87 | }
88 |
89 | func (t zapSugarTester) logTime(msg, key string, value time.Time) bool {
90 | t.l.Infow(msg, key, value)
91 | return true
92 | }
93 |
94 | func (t zapSugarTester) logDuration(msg, key string, value time.Duration) bool {
95 | t.l.Infow(msg, key, value)
96 | return true
97 | }
98 |
99 | func (t zapSugarTester) logError(msg, key string, value error) bool {
100 | t.l.Infow(msg, key, value)
101 | return true
102 | }
103 |
104 | func (t zapSugarTester) logString(msg, key string, value string) bool {
105 | t.l.Infow(msg, key, value)
106 | return true
107 | }
108 |
109 | func (t zapSugarTester) logObject(msg, key string, value *obj) bool {
110 | t.l.Infow(msg, key, value)
111 | return true
112 | }
113 |
114 | func (t zapSugarTester) logBools(msg, key string, value []bool) bool {
115 | t.l.Infow(msg, key, value)
116 | return true
117 | }
118 |
119 | func (t zapSugarTester) logInts(msg, key string, value []int) bool {
120 | t.l.Infow(msg, key, value)
121 | return true
122 | }
123 |
124 | func (t zapSugarTester) logFloats32(msg, key string, value []float32) bool {
125 | t.l.Infow(msg, key, value)
126 | return true
127 | }
128 |
129 | func (t zapSugarTester) logFloats64(msg, key string, value []float64) bool {
130 | t.l.Infow(msg, key, value)
131 | return true
132 | }
133 |
134 | func (t zapSugarTester) logTimes(msg, key string, value []time.Time) bool {
135 | t.l.Infow(msg, key, value)
136 | return true
137 | }
138 |
139 | func (t zapSugarTester) logDurations(msg, key string, value []time.Duration) bool {
140 | t.l.Infow(msg, key, value)
141 | return true
142 | }
143 |
144 | func (t zapSugarTester) logErrors(msg, key string, value []error) bool {
145 | return false
146 | }
147 |
148 | func (t zapSugarTester) logStrings(msg, key string, value []string) bool {
149 | t.l.Infow(msg, key, value)
150 | return true
151 | }
152 |
--------------------------------------------------------------------------------
/zerolog_test.go:
--------------------------------------------------------------------------------
1 | package logbench
2 |
3 | import (
4 | "io"
5 | "time"
6 |
7 | "github.com/rs/zerolog"
8 | )
9 |
10 | func init() {
11 | zerolog.TimeFieldFormat = ""
12 | zerolog.DurationFieldInteger = true
13 | zerolog.MessageFieldName = "message"
14 |
15 | tests["Zerolog"] = zerologTester{}
16 | }
17 |
18 | func (o obj) MarshalZerologObject(e *zerolog.Event) {
19 | e.Str("name", o.Name).
20 | Int("count", o.Count).
21 | Bool("enabled", o.Enabled)
22 | }
23 |
24 | type zerologTester struct {
25 | l zerolog.Logger
26 | }
27 |
28 | var (
29 | _ logTesterArray = (*zerologTester)(nil)
30 | )
31 |
32 | func (zerologTester) newLogger(out io.Writer, disabled bool) logTester {
33 | lvl := zerolog.DebugLevel
34 | if disabled {
35 | lvl = zerolog.Disabled
36 | }
37 | return zerologTester{zerolog.New(out).Level(lvl)}
38 | }
39 |
40 | func (t zerologTester) logMsg(msg string) {
41 | t.l.Info().Msg(msg)
42 | }
43 |
44 | func (t zerologTester) logFormat(format string, v ...interface{}) bool {
45 | t.l.Info().Msgf(format, v...)
46 | return true
47 | }
48 |
49 | func (t zerologTester) withContext(context map[string]interface{}) (logTester, bool) {
50 | return zerologTester{t.l.With().Fields(context).Logger()}, true
51 | }
52 |
53 | func (t zerologTester) logBool(msg, key string, value bool) bool {
54 | t.l.Info().Bool(key, value).Msg(msg)
55 | return true
56 | }
57 |
58 | func (t zerologTester) logInt(msg, key string, value int) bool {
59 | t.l.Info().Int(key, value).Msg(msg)
60 | return true
61 | }
62 |
63 | func (t zerologTester) logFloat32(msg, key string, value float32) bool {
64 | t.l.Info().Float32(key, value).Msg(msg)
65 | return true
66 | }
67 |
68 | func (t zerologTester) logFloat64(msg, key string, value float64) bool {
69 | t.l.Info().Float64(key, value).Msg(msg)
70 | return true
71 | }
72 |
73 | func (t zerologTester) logTime(msg, key string, value time.Time) bool {
74 | t.l.Info().Time(key, value).Msg(msg)
75 | return true
76 | }
77 |
78 | func (t zerologTester) logDuration(msg, key string, value time.Duration) bool {
79 | t.l.Info().Dur(key, value).Msg(msg)
80 | return true
81 | }
82 |
83 | func (t zerologTester) logError(msg, key string, value error) bool {
84 | t.l.Info().AnErr(key, value).Msg(msg)
85 | return true
86 | }
87 |
88 | func (t zerologTester) logString(msg, key string, value string) bool {
89 | t.l.Info().Str(key, value).Msg(msg)
90 | return true
91 | }
92 |
93 | func (t zerologTester) logObject(msg, key string, value *obj) bool {
94 | t.l.Info().Object(key, value).Msg(msg)
95 | return true
96 | }
97 |
98 | func (t zerologTester) logBools(msg, key string, value []bool) bool {
99 | t.l.Info().Bools(key, value).Msg(msg)
100 | return true
101 | }
102 |
103 | func (t zerologTester) logInts(msg, key string, value []int) bool {
104 | t.l.Info().Ints(key, value).Msg(msg)
105 | return true
106 | }
107 |
108 | func (t zerologTester) logFloats32(msg, key string, value []float32) bool {
109 | t.l.Info().Floats32(key, value).Msg(msg)
110 | return true
111 | }
112 |
113 | func (t zerologTester) logFloats64(msg, key string, value []float64) bool {
114 | t.l.Info().Floats64(key, value).Msg(msg)
115 | return true
116 | }
117 |
118 | func (t zerologTester) logTimes(msg, key string, value []time.Time) bool {
119 | t.l.Info().Times(key, value).Msg(msg)
120 | return true
121 | }
122 |
123 | func (t zerologTester) logDurations(msg, key string, value []time.Duration) bool {
124 | t.l.Info().Durs(key, value).Msg(msg)
125 | return true
126 | }
127 |
128 | func (t zerologTester) logErrors(msg, key string, value []error) bool {
129 | t.l.Info().Errs(key, value).Msg(msg)
130 | return true
131 | }
132 |
133 | func (t zerologTester) logStrings(msg, key string, value []string) bool {
134 | t.l.Info().Strs(key, value).Msg(msg)
135 | return true
136 | }
137 |
--------------------------------------------------------------------------------