pax_global_header00006660000000000000000000000064145221256660014523gustar00rootroot0000000000000052 comment=6a2e1c074e6f95c38a274bd78afa4684fcfd3a6c logg-0.4.0/000077500000000000000000000000001452212566600124545ustar00rootroot00000000000000logg-0.4.0/.github/000077500000000000000000000000001452212566600140145ustar00rootroot00000000000000logg-0.4.0/.github/FUNDING.yml000066400000000000000000000000151452212566600156250ustar00rootroot00000000000000github: [bep]logg-0.4.0/.github/workflows/000077500000000000000000000000001452212566600160515ustar00rootroot00000000000000logg-0.4.0/.github/workflows/test.yml000066400000000000000000000016111452212566600175520ustar00rootroot00000000000000on: push: branches: [ master ] pull_request: name: Test jobs: test: strategy: matrix: go-version: [1.20.x,1.21.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest shell: bash - name: Update PATH run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH shell: bash - name: Checkout code uses: actions/checkout@v1 - name: Fmt if: matrix.platform != 'windows-latest' # :( run: "diff <(gofmt -d .) <(printf '')" shell: bash - name: Vet run: go vet ./... - name: Staticcheck run: staticcheck ./... - name: Test run: go test -race ./... logg-0.4.0/.gitignore000066400000000000000000000004141452212566600144430ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/logg-0.4.0/LICENSE000066400000000000000000000021121452212566600134550ustar00rootroot00000000000000(The MIT License) Copyright (c) 2015 TJ Holowaychuk tj@tjholowaychuk.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. logg-0.4.0/README.md000066400000000000000000000236001452212566600137340ustar00rootroot00000000000000 [![Tests on Linux, MacOS and Windows](https://github.com/bep/logg/workflows/Test/badge.svg)](https://github.com/bep/logg/actions?query=workflow:Test) [![Go Report Card](https://goreportcard.com/badge/github.com/bep/logg)](https://goreportcard.com/report/github.com/bep/logg) [![GoDoc](https://godoc.org/github.com/bep/logg?status.svg)](https://godoc.org/github.com/bep/logg) This is a fork of the exellent [Apex Log](https://github.com/apex/log) library. Main changes: * Trim unneeded dependencies. * Make `Fields` into a slice to preserve log order. * Split the old `Interface` in two and remove all but one `Log` method (see below). * This allows for lazy creation of messages in `Log(fmt.Stringer)` and ignoring fields added in `LevelLogger`s with levels below the `Logger`s. * The pointer passed to `HandleLog` is not safe to use outside of the current log chain, and needs to be cloned with `Clone` first if that's needed. * See [Benchmarks](#benchmarks) for more info. This is probably the very fastest structured log library when logging is disabled: image > One can never have enough log libraries! ```go // Logger is the main interface for the logger. type Logger interface { // WithLevel returns a new entry with `level` set. WithLevel(Level) *Entry } // LevelLogger is the logger at a given level. type LevelLogger interface { // Log logs a message at the given level using the string from calling s.String(). // Note that s.String() will not be called if the level is not enabled. Log(s fmt.Stringer) // Logf logs a message at the given level using the format and args from calling fmt.Sprintf(). // Note that fmt.Sprintf() will not be called if the level is not enabled. Logf(format string, a ...any) // WithLevel returns a new entry with `level` set. WithLevel(Level) *Entry // WithFields returns a new entry with the`fields` in fields set. // This is a noop if LevelLogger's level is less than Logger's. WithFields(fields Fielder) *Entry // WithLevel returns a new entry with the field f set with value v // This is a noop if LevelLogger's level is less than Logger's. WithField(f string, v any) *Entry // WithDuration returns a new entry with the "duration" field set // to the given duration in milliseconds. // This is a noop if LevelLogger's level is less than Logger's. WithDuration(time.Duration) *Entry // WithError returns a new entry with the "error" set to `err`. // This is a noop if err is nil or LevelLogger's level is less than Logger's. WithError(error) *Entry } ``` ## Benchmarks Benchmarks below are borrowed and adapted from [Zap](https://github.com/uber-go/zap/tree/master/benchmarks). ### Logging at a disabled level without any structured context ``` name time/op DisabledWithoutFields/apex/log-10 33.9ns ± 0% DisabledWithoutFields/bep/logg-10 0.28ns ± 0% DisabledWithoutFields/sirupsen/logrus-10 6.54ns ± 0% DisabledWithoutFields/rs/zerolog-10 0.31ns ± 0% name alloc/op DisabledWithoutFields/apex/log-10 112B ± 0% DisabledWithoutFields/bep/logg-10 0.00B DisabledWithoutFields/sirupsen/logrus-10 16.0B ± 0% DisabledWithoutFields/rs/zerolog-10 0.00B name allocs/op DisabledWithoutFields/apex/log-10 1.00 ± 0% DisabledWithoutFields/bep/logg-10 0.00 DisabledWithoutFields/sirupsen/logrus-10 1.00 ± 0% DisabledWithoutFields/rs/zerolog-10 0.00 ``` ### Logging at a disabled level with some accumulated context ``` name time/op DisabledAccumulatedContext/apex/log-10 0.29ns ± 0% DisabledAccumulatedContext/bep/logg-10 0.27ns ± 0% DisabledAccumulatedContext/sirupsen/logrus-10 6.61ns ± 0% DisabledAccumulatedContext/rs/zerolog-10 0.32ns ± 0% name alloc/op DisabledAccumulatedContext/apex/log-10 0.00B DisabledAccumulatedContext/bep/logg-10 0.00B DisabledAccumulatedContext/sirupsen/logrus-10 16.0B ± 0% DisabledAccumulatedContext/rs/zerolog-10 0.00B name allocs/op DisabledAccumulatedContext/apex/log-10 0.00 DisabledAccumulatedContext/bep/logg-10 0.00 DisabledAccumulatedContext/sirupsen/logrus-10 1.00 ± 0% DisabledAccumulatedContext/rs/zerolog-10 0.00 ``` ### Logging at a disabled level, adding context at each log site ``` name time/op DisabledAddingFields/apex/log-10 328ns ± 0% DisabledAddingFields/bep/logg-10 0.38ns ± 0% DisabledAddingFields/sirupsen/logrus-10 610ns ± 0% DisabledAddingFields/rs/zerolog-10 10.5ns ± 0% name alloc/op DisabledAddingFields/apex/log-10 886B ± 0% DisabledAddingFields/bep/logg-10 0.00B DisabledAddingFields/sirupsen/logrus-10 1.52kB ± 0% DisabledAddingFields/rs/zerolog-10 24.0B ± 0% name allocs/op DisabledAddingFields/apex/log-10 10.0 ± 0% DisabledAddingFields/bep/logg-10 0.00 DisabledAddingFields/sirupsen/logrus-10 12.0 ± 0% DisabledAddingFields/rs/zerolog-10 1.00 ± 0% ``` ### Logging without any structured context ``` name time/op WithoutFields/apex/log-10 964ns ± 0% WithoutFields/bep/logg-10 100ns ± 0% WithoutFields/go-kit/kit/log-10 232ns ± 0% WithoutFields/inconshreveable/log15-10 2.13µs ± 0% WithoutFields/sirupsen/logrus-10 866ns ± 0% WithoutFields/stdlib.Println-10 7.08ns ± 0% WithoutFields/stdlib.Printf-10 56.4ns ± 0% WithoutFields/rs/zerolog-10 30.9ns ± 0% WithoutFields/rs/zerolog.Formatting-10 1.33µs ± 0% WithoutFields/rs/zerolog.Check-10 32.1ns ± 0% name alloc/op WithoutFields/apex/log-10 352B ± 0% WithoutFields/bep/logg-10 56.0B ± 0% WithoutFields/go-kit/kit/log-10 520B ± 0% WithoutFields/inconshreveable/log15-10 1.43kB ± 0% WithoutFields/sirupsen/logrus-10 1.14kB ± 0% WithoutFields/stdlib.Println-10 16.0B ± 0% WithoutFields/stdlib.Printf-10 136B ± 0% WithoutFields/rs/zerolog-10 0.00B WithoutFields/rs/zerolog.Formatting-10 1.92kB ± 0% WithoutFields/rs/zerolog.Check-10 0.00B name allocs/op WithoutFields/apex/log-10 6.00 ± 0% WithoutFields/bep/logg-10 2.00 ± 0% WithoutFields/go-kit/kit/log-10 9.00 ± 0% WithoutFields/inconshreveable/log15-10 20.0 ± 0% WithoutFields/sirupsen/logrus-10 23.0 ± 0% WithoutFields/stdlib.Println-10 1.00 ± 0% WithoutFields/stdlib.Printf-10 6.00 ± 0% WithoutFields/rs/zerolog-10 0.00 WithoutFields/rs/zerolog.Formatting-10 58.0 ± 0% WithoutFields/rs/zerolog.Check-10 0.00 ``` ### Logging with some accumulated context ``` name time/op AccumulatedContext/apex/log-10 12.7µs ± 0% AccumulatedContext/bep/logg-10 1.52µs ± 0% AccumulatedContext/go-kit/kit/log-10 2.52µs ± 0% AccumulatedContext/inconshreveable/log15-10 9.36µs ± 0% AccumulatedContext/sirupsen/logrus-10 3.41µs ± 0% AccumulatedContext/rs/zerolog-10 37.9ns ± 0% AccumulatedContext/rs/zerolog.Check-10 34.0ns ± 0% AccumulatedContext/rs/zerolog.Formatting-10 1.36µs ± 0% name alloc/op AccumulatedContext/apex/log-10 3.30kB ± 0% AccumulatedContext/bep/logg-10 1.16kB ± 0% AccumulatedContext/go-kit/kit/log-10 3.67kB ± 0% AccumulatedContext/inconshreveable/log15-10 3.31kB ± 0% AccumulatedContext/sirupsen/logrus-10 4.73kB ± 0% AccumulatedContext/rs/zerolog-10 0.00B AccumulatedContext/rs/zerolog.Check-10 0.00B AccumulatedContext/rs/zerolog.Formatting-10 1.92kB ± 0% name allocs/op AccumulatedContext/apex/log-10 53.0 ± 0% AccumulatedContext/bep/logg-10 25.0 ± 0% AccumulatedContext/go-kit/kit/log-10 56.0 ± 0% AccumulatedContext/inconshreveable/log15-10 70.0 ± 0% AccumulatedContext/sirupsen/logrus-10 68.0 ± 0% AccumulatedContext/rs/zerolog-10 0.00 AccumulatedContext/rs/zerolog.Check-10 0.00 AccumulatedContext/rs/zerolog.Formatting-10 58.0 ± 0% ``` ## Logging with additional context at each log site ``` name time/op AddingFields/apex/log-10 13.2µs ± 0% AddingFields/bep/logg-10 1.79µs ± 0% AddingFields/go-kit/kit/log-10 2.23µs ± 0% AddingFields/inconshreveable/log15-10 14.3µs ± 0% AddingFields/sirupsen/logrus-10 4.46µs ± 0% AddingFields/rs/zerolog-10 398ns ± 0% AddingFields/rs/zerolog.Check-10 389ns ± 0% name alloc/op AddingFields/apex/log-10 4.19kB ± 0% AddingFields/bep/logg-10 2.02kB ± 0% AddingFields/go-kit/kit/log-10 3.31kB ± 0% AddingFields/inconshreveable/log15-10 6.68kB ± 0% AddingFields/sirupsen/logrus-10 6.27kB ± 0% AddingFields/rs/zerolog-10 24.0B ± 0% AddingFields/rs/zerolog.Check-10 24.0B ± 0% name allocs/op AddingFields/apex/log-10 63.0 ± 0% AddingFields/bep/logg-10 34.0 ± 0% AddingFields/go-kit/kit/log-10 57.0 ± 0% AddingFields/inconshreveable/log15-10 74.0 ± 0% AddingFields/sirupsen/logrus-10 79.0 ± 0% AddingFields/rs/zerolog-10 1.00 ± 0% AddingFields/rs/zerolog.Check-10 1.00 ± 0% ``` logg-0.4.0/benchmarks/000077500000000000000000000000001452212566600145715ustar00rootroot00000000000000logg-0.4.0/benchmarks/apex_test.go000066400000000000000000000033611452212566600171170ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "github.com/apex/log" "github.com/apex/log/handlers/json" ) func newDisabledApexLog() *log.Logger { return &log.Logger{ Handler: json.New(io.Discard), Level: log.ErrorLevel, } } func newApexLog() *log.Logger { return &log.Logger{ Handler: json.New(io.Discard), Level: log.DebugLevel, } } func fakeApexFields() log.Fields { return log.Fields{ "int": _tenInts[0], "ints": _tenInts, "string": _tenStrings[0], "strings": _tenStrings, "time": _tenTimes[0], "times": _tenTimes, "user1": _oneUser, "user2": _oneUser, "users": _tenUsers, "error": errExample, } } logg-0.4.0/benchmarks/doc.go000066400000000000000000000023341452212566600156670ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Package benchmarks contains only benchmarks comparing zap to other // structured logging libraries. package benchmarks logg-0.4.0/benchmarks/go.mod000066400000000000000000000015631452212566600157040ustar00rootroot00000000000000module github.com/bep/logg/benchmarks go 1.18 replace github.com/bep/logg => ../ require ( github.com/apex/log v1.9.0 github.com/go-kit/log v0.2.0 github.com/rs/zerolog v1.26.0 github.com/sirupsen/logrus v1.8.1 go.uber.org/multierr v1.7.0 go.uber.org/zap v1.19.1 gopkg.in/inconshreveable/log15.v2 v2.0.0-20200109203555-b30bc20e4fd1 ) require ( github.com/benbjohnson/clock v1.2.0 // indirect github.com/bep/clocks v0.5.0 // indirect github.com/bep/logg v0.0.0-20220809094309-f3eda2566f97 github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pkg/errors v0.9.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect ) logg-0.4.0/benchmarks/go.sum000066400000000000000000000333151452212566600157310ustar00rootroot00000000000000github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.2.0 h1:9Re3G2TWxkE06LdMWMpcY6KV81GLXMGiYpPYUPkFAws= github.com/benbjohnson/clock v1.2.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20200109203555-b30bc20e4fd1 h1:iiHuQZCNgYPmFQxd3BBN/Nc5+dAwzZuq5y40s20oQw0= gopkg.in/inconshreveable/log15.v2 v2.0.0-20200109203555-b30bc20e4fd1/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= logg-0.4.0/benchmarks/kit_test.go000066400000000000000000000024221452212566600167460ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "github.com/go-kit/log" ) func newKitLog(fields ...interface{}) log.Logger { return log.With(log.NewJSONLogger(io.Discard), fields...) } logg-0.4.0/benchmarks/log15_test.go000066400000000000000000000024751452212566600171160ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "gopkg.in/inconshreveable/log15.v2" ) func newLog15() log15.Logger { logger := log15.New() logger.SetHandler(log15.StreamHandler(io.Discard, log15.JsonFormat())) return logger } logg-0.4.0/benchmarks/logn_test.go000066400000000000000000000040401452212566600171140ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "github.com/bep/logg" "github.com/bep/logg/handlers/json" ) func newDisabledLoggLog() logg.LevelLogger { logger := logg.New(logg.Options{ Handler: json.New(io.Discard), Level: logg.LevelError, }) return logger.WithLevel(logg.LevelInfo) } func newLoggLog() logg.LevelLogger { logger := logg.New(logg.Options{ Handler: json.New(io.Discard), Level: logg.LevelDebug, }) return logger.WithLevel(logg.LevelDebug) } func fakeLognFields() logg.FieldsFunc { return func() logg.Fields { return logg.Fields{ {Name: "int", Value: _tenInts[0]}, {Name: "ints", Value: _tenInts}, {Name: "string", Value: _tenStrings[0]}, {Name: "strings", Value: _tenStrings}, {Name: "time", Value: _tenTimes[0]}, {Name: "times", Value: _tenTimes}, {Name: "user1", Value: _oneUser}, {Name: "user2", Value: _oneUser}, {Name: "users", Value: _tenUsers}, {Name: "error", Value: errExample}, } } } logg-0.4.0/benchmarks/logrus_test.go000066400000000000000000000034411452212566600174740ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "github.com/sirupsen/logrus" ) func newDisabledLogrus() *logrus.Logger { logger := newLogrus() logger.Level = logrus.ErrorLevel return logger } func newLogrus() *logrus.Logger { return &logrus.Logger{ Out: io.Discard, Formatter: new(logrus.JSONFormatter), Hooks: make(logrus.LevelHooks), Level: logrus.DebugLevel, } } func fakeLogrusFields() logrus.Fields { return logrus.Fields{ "int": _tenInts[0], "ints": _tenInts, "string": _tenStrings[0], "strings": _tenStrings, "time": _tenTimes[0], "times": _tenTimes, "user1": _oneUser, "user2": _oneUser, "users": _tenUsers, "error": errExample, } } logg-0.4.0/benchmarks/scenario_bench_test.go000066400000000000000000000242101452212566600211200ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "log" "testing" "github.com/bep/logg" ) func BenchmarkDisabledWithoutFields(b *testing.B) { b.Logf("Logging at a disabled level without any structured context.") b.Run("apex/log", func(b *testing.B) { logger := newDisabledApexLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("bep/logg", func(b *testing.B) { logger := newDisabledLoggLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { message := logg.StringFunc(func() string { return getMessage(0) }) logger.Log(message) } }) }) b.Run("sirupsen/logrus", func(b *testing.B) { logger := newDisabledLogrus() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("rs/zerolog", func(b *testing.B) { logger := newDisabledZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info().Msg(getMessage(0)) } }) }) } func BenchmarkDisabledAccumulatedContext(b *testing.B) { b.Logf("Logging at a disabled level with some accumulated context.") b.Run("apex/log", func(b *testing.B) { logger := newDisabledApexLog().WithFields(fakeApexFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("bep/logg", func(b *testing.B) { logger := newDisabledLoggLog().WithFields(fakeLognFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { message := logg.StringFunc(func() string { return getMessage(0) }) logger.Log(message) } }) }) b.Run("sirupsen/logrus", func(b *testing.B) { logger := newDisabledLogrus().WithFields(fakeLogrusFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("rs/zerolog", func(b *testing.B) { logger := fakeZerologContext(newDisabledZerolog().With()).Logger() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info().Msg(getMessage(0)) } }) }) } func BenchmarkDisabledAddingFields(b *testing.B) { b.Logf("Logging at a disabled level, adding context at each log site.") b.Run("apex/log", func(b *testing.B) { logger := newDisabledApexLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.WithFields(fakeApexFields()).Info(getMessage(0)) } }) }) b.Run("bep/logg", func(b *testing.B) { logger := newDisabledLoggLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { message := logg.StringFunc(func() string { return getMessage(0) }) logger.WithFields(fakeLognFields()).Log(message) } }) }) b.Run("sirupsen/logrus", func(b *testing.B) { logger := newDisabledLogrus() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.WithFields(fakeLogrusFields()).Info(getMessage(0)) } }) }) b.Run("rs/zerolog", func(b *testing.B) { logger := newDisabledZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { fakeZerologFields(logger.Info()).Msg(getMessage(0)) } }) }) } func BenchmarkWithoutFields(b *testing.B) { b.Logf("Logging without any structured context.") b.Run("apex/log", func(b *testing.B) { logger := newApexLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("bep/logg", func(b *testing.B) { logger := newLoggLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { message := logg.StringFunc(func() string { return getMessage(0) }) logger.Log(message) } }) }) b.Run("go-kit/kit/log", func(b *testing.B) { logger := newKitLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Log(getMessage(0), getMessage(1)) } }) }) b.Run("inconshreveable/log15", func(b *testing.B) { logger := newLog15() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("sirupsen/logrus", func(b *testing.B) { logger := newLogrus() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("stdlib.Println", func(b *testing.B) { logger := log.New(io.Discard, "", log.LstdFlags) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Println(getMessage(0)) } }) }) b.Run("stdlib.Printf", func(b *testing.B) { logger := log.New(io.Discard, "", log.LstdFlags) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Printf("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) } }) }) b.Run("rs/zerolog", func(b *testing.B) { logger := newZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info().Msg(getMessage(0)) } }) }) b.Run("rs/zerolog.Formatting", func(b *testing.B) { logger := newZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info().Msgf("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) } }) }) b.Run("rs/zerolog.Check", func(b *testing.B) { logger := newZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { if e := logger.Info(); e.Enabled() { e.Msg(getMessage(0)) } } }) }) } func BenchmarkAccumulatedContext(b *testing.B) { b.Logf("Logging with some accumulated context.") b.Run("apex/log", func(b *testing.B) { logger := newApexLog().WithFields(fakeApexFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("bep/logg", func(b *testing.B) { logger := newLoggLog().WithFields(fakeLognFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { message := logg.StringFunc(func() string { return getMessage(0) }) logger.Log(message) } }) }) b.Run("go-kit/kit/log", func(b *testing.B) { logger := newKitLog(fakeSugarFields()...) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Log(getMessage(0), getMessage(1)) } }) }) b.Run("inconshreveable/log15", func(b *testing.B) { logger := newLog15().New(fakeSugarFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("sirupsen/logrus", func(b *testing.B) { logger := newLogrus().WithFields(fakeLogrusFields()) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0)) } }) }) b.Run("rs/zerolog", func(b *testing.B) { logger := fakeZerologContext(newZerolog().With()).Logger() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info().Msg(getMessage(0)) } }) }) b.Run("rs/zerolog.Check", func(b *testing.B) { logger := fakeZerologContext(newZerolog().With()).Logger() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { if e := logger.Info(); e.Enabled() { e.Msg(getMessage(0)) } } }) }) b.Run("rs/zerolog.Formatting", func(b *testing.B) { logger := fakeZerologContext(newZerolog().With()).Logger() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info().Msgf("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) } }) }) } func BenchmarkAddingFields(b *testing.B) { b.Logf("Logging with additional context at each log site.") b.Run("apex/log", func(b *testing.B) { logger := newApexLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.WithFields(fakeApexFields()).Info(getMessage(0)) } }) }) b.Run("bep/logg", func(b *testing.B) { logger := newLoggLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { message := logg.StringFunc(func() string { return getMessage(0) }) logger.WithFields(fakeLognFields()).Log(message) } }) }) b.Run("go-kit/kit/log", func(b *testing.B) { logger := newKitLog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Log(fakeSugarFields()...) } }) }) b.Run("inconshreveable/log15", func(b *testing.B) { logger := newLog15() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info(getMessage(0), fakeSugarFields()...) } }) }) b.Run("sirupsen/logrus", func(b *testing.B) { logger := newLogrus() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.WithFields(fakeLogrusFields()).Info(getMessage(0)) } }) }) b.Run("rs/zerolog", func(b *testing.B) { logger := newZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { fakeZerologFields(logger.Info()).Msg(getMessage(0)) } }) }) b.Run("rs/zerolog.Check", func(b *testing.B) { logger := newZerolog() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { if e := logger.Info(); e.Enabled() { fakeZerologFields(e).Msg(getMessage(0)) } } }) }) } logg-0.4.0/benchmarks/zap_test.go000066400000000000000000000073041452212566600167550ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "errors" "fmt" "time" "go.uber.org/multierr" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( errExample = errors.New("fail") _messages = fakeMessages(1000) _tenInts = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} _tenStrings = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"} _tenTimes = []time.Time{ time.Unix(0, 0), time.Unix(1, 0), time.Unix(2, 0), time.Unix(3, 0), time.Unix(4, 0), time.Unix(5, 0), time.Unix(6, 0), time.Unix(7, 0), time.Unix(8, 0), time.Unix(9, 0), } _oneUser = &user{ Name: "Jane Doe", Email: "jane@test.com", CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC), } _tenUsers = users{ _oneUser, _oneUser, _oneUser, _oneUser, _oneUser, _oneUser, _oneUser, _oneUser, _oneUser, _oneUser, } ) func fakeMessages(n int) []string { messages := make([]string, n) for i := range messages { messages[i] = fmt.Sprintf("Test logging, but use a somewhat realistic message length. (#%v)", i) } return messages } func getMessage(iter int) string { return _messages[iter%1000] } type users []*user func (uu users) MarshalLogArray(arr zapcore.ArrayEncoder) error { var err error for i := range uu { err = multierr.Append(err, arr.AppendObject(uu[i])) } return err } type user struct { Name string `json:"name"` Email string `json:"email"` CreatedAt time.Time `json:"created_at"` } func (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("name", u.Name) enc.AddString("email", u.Email) enc.AddInt64("createdAt", u.CreatedAt.UnixNano()) return nil } func fakeFields() []zap.Field { return []zap.Field{ zap.Int("int", _tenInts[0]), zap.Ints("ints", _tenInts), zap.String("string", _tenStrings[0]), zap.Strings("strings", _tenStrings), zap.Time("time", _tenTimes[0]), zap.Times("times", _tenTimes), zap.Object("user1", _oneUser), zap.Object("user2", _oneUser), zap.Array("users", _tenUsers), zap.Error(errExample), } } func fakeSugarFields() []interface{} { return []interface{}{ "int", _tenInts[0], "ints", _tenInts, "string", _tenStrings[0], "strings", _tenStrings, "time", _tenTimes[0], "times", _tenTimes, "user1", _oneUser, "user2", _oneUser, "users", _tenUsers, "error", errExample, } } func fakeFmtArgs() []interface{} { // Need to keep this a function instead of a package-global var so that we // pay the cast-to-interface{} penalty on each call. return []interface{}{ _tenInts[0], _tenInts, _tenStrings[0], _tenStrings, _tenTimes[0], _tenTimes, _oneUser, _oneUser, _tenUsers, errExample, } } logg-0.4.0/benchmarks/zerolog_test.go000066400000000000000000000044421452212566600176440ustar00rootroot00000000000000// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package benchmarks import ( "io" "github.com/rs/zerolog" ) func newZerolog() zerolog.Logger { return zerolog.New(io.Discard).With().Timestamp().Logger() } func newDisabledZerolog() zerolog.Logger { return newZerolog().Level(zerolog.Disabled) } func (u *user) MarshalZerologObject(e *zerolog.Event) { e.Str("name", u.Name). Str("email", u.Email). Int64("createdAt", u.CreatedAt.UnixNano()) } func (uu users) MarshalZerologArray(a *zerolog.Array) { for _, u := range uu { a.Object(u) } } func fakeZerologFields(e *zerolog.Event) *zerolog.Event { return e. Int("int", _tenInts[0]). Ints("ints", _tenInts). Str("string", _tenStrings[0]). Strs("strings", _tenStrings). Time("time", _tenTimes[0]). Times("times", _tenTimes). Object("user1", _oneUser). Object("user2", _oneUser). Array("users", _tenUsers). Err(errExample) } func fakeZerologContext(c zerolog.Context) zerolog.Context { return c. Int("int", _tenInts[0]). Ints("ints", _tenInts). Str("string", _tenStrings[0]). Strs("strings", _tenStrings). Time("time", _tenTimes[0]). Times("times", _tenTimes). Object("user1", _oneUser). Object("user2", _oneUser). Array("users", _tenUsers). Err(errExample) } logg-0.4.0/doc.go000066400000000000000000000001141452212566600135440ustar00rootroot00000000000000/* package logg implements a simple structured logging API. */ package logg logg-0.4.0/entry.go000066400000000000000000000065771452212566600141630ustar00rootroot00000000000000package logg import ( "fmt" "strings" "time" ) // assert interface compliance. var ( _ LevelLogger = (*Entry)(nil) ) // Entry represents a single log entry at a given log level. type Entry struct { logger *logger Level Level `json:"level"` Timestamp time.Time `json:"timestamp"` Fields Fields `json:"fields,omitempty"` Message string `json:"message"` fieldsAddedCounter int } // NewEntry returns a new entry for `log`. func NewEntry(log *logger) *Entry { return &Entry{ logger: log, } } func (e Entry) WithLevel(level Level) *Entry { e.Level = level return &e } func (e *Entry) WithFields(fielder Fielder) *Entry { if e.isLevelDisabled() { return e } x := *e fields := fielder.Fields() x.fieldsAddedCounter += len(fields) x.Fields = append(x.Fields, fields...) if x.fieldsAddedCounter > 100 { // This operation will eventually also be performed on the final entry, // do it here to avoid the slice to grow indefinitely. x.mergeFields() x.fieldsAddedCounter = 0 } return &x } func (e *Entry) WithField(key string, value any) *Entry { if e.isLevelDisabled() { return e } return e.WithFields(Fields{{key, value}}) } func (e *Entry) WithDuration(d time.Duration) *Entry { if e.isLevelDisabled() { return e } return e.WithField("duration", d.Milliseconds()) } // WithError returns a new entry with the "error" set to `err`. // // The given error may implement .Fielder, if it does the method // will add all its `.Fields()` into the returned entry. func (e *Entry) WithError(err error) *Entry { if err == nil || e.isLevelDisabled() { return e } ctx := e.WithField("error", err.Error()) if s, ok := err.(stackTracer); ok { frame := s.StackTrace()[0] name := fmt.Sprintf("%n", frame) file := fmt.Sprintf("%+s", frame) line := fmt.Sprintf("%d", frame) parts := strings.Split(file, "\n\t") if len(parts) > 1 { file = parts[1] } ctx = ctx.WithField("source", fmt.Sprintf("%s: %s:%s", name, file, line)) } if f, ok := err.(Fielder); ok { ctx = ctx.WithFields(f) } return ctx } func (e *Entry) isLevelDisabled() bool { return e.Level < e.logger.Level } // Log a message at the given level. func (e *Entry) Log(s fmt.Stringer) { e.logger.log(e, s) } // Log a message at the given level. func (e *Entry) Logf(format string, a ...any) { e.logger.log(e, StringFunc(func() string { return fmt.Sprintf(format, a...) })) } // Clone returns a new Entry with the same fields. func (e *Entry) Clone() *Entry { x := *e x.Fields = make(Fields, len(e.Fields)) copy(x.Fields, e.Fields) return &x } func (e *Entry) reset() { e.logger = nil e.Level = 0 e.Fields = e.Fields[:0] e.Message = "" e.Timestamp = time.Time{} } // Remove any early entries with the same name. func (e *Entry) mergeFields() { n := 0 for i, f := range e.Fields { keep := true for j := i + 1; j < len(e.Fields); j++ { if e.Fields[j].Name == f.Name { keep = false break } } if keep { e.Fields[n] = f n++ } } e.Fields = e.Fields[:n] } // finalize populates dst with Level and Fields merged from e and Message and Timestamp set. func (e *Entry) finalize(dst *Entry, msg string) { dst.Message = msg dst.Timestamp = e.logger.Clock.Now() dst.Level = e.Level if cap(dst.Fields) < len(e.Fields) { dst.Fields = make(Fields, len(e.Fields)) } else { dst.Fields = dst.Fields[:len(e.Fields)] } copy(dst.Fields, e.Fields) dst.mergeFields() } logg-0.4.0/entry_test.go000066400000000000000000000061531452212566600152100ustar00rootroot00000000000000package logg_test import ( "fmt" "testing" "time" "github.com/bep/logg" "github.com/bep/logg/handlers" "github.com/bep/logg/handlers/memory" qt "github.com/frankban/quicktest" ) func TestEntry_WithFields(t *testing.T) { h := memory.New() a := logg.New(logg.Options{Handler: h, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithFields(logg.Fields{{"foo", "bar"}}) c := a.WithFields(logg.Fields{{"foo", "hello"}, {"bar", "world"}}) d := c.WithFields(logg.Fields{{"baz", "jazz"}}) qt.Assert(t, b.Fields, qt.DeepEquals, logg.Fields{{"foo", "bar"}}) qt.Assert(t, c.Fields, qt.DeepEquals, logg.Fields{{"foo", "hello"}, {"bar", "world"}}) qt.Assert(t, d.Fields, qt.DeepEquals, logg.Fields{{"foo", "hello"}, {"bar", "world"}, {"baz", "jazz"}}) c.Log(logg.String("upload")) e := h.Entries[0] qt.Assert(t, "upload", qt.Equals, e.Message) qt.Assert(t, logg.Fields{{"foo", "hello"}, {"bar", "world"}}, qt.DeepEquals, e.Fields) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) qt.Assert(t, time.Now().IsZero(), qt.IsFalse) } func TestEntry_WithManyFieldsWithSameName(t *testing.T) { h := memory.New() a := logg.New(logg.Options{Handler: h, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithFields(logg.Fields{{"foo", "bar"}}) for i := 0; i < 100; i++ { b = b.WithFields(logg.Fields{{"foo", "bar"}}) } b.Log(logg.String("upload")) e := h.Entries[0] qt.Assert(t, "upload", qt.Equals, e.Message) qt.Assert(t, logg.Fields{{"foo", "bar"}}, qt.DeepEquals, e.Fields) } func TestEntry_WithField(t *testing.T) { h := memory.New() a := logg.New(logg.Options{Handler: h, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithField("foo", "baz").WithField("foo", "bar") b.Log(logg.String("upload")) qt.Assert(t, a.Fields, qt.IsNil) qt.Assert(t, h.Entries[0].Fields, qt.DeepEquals, logg.Fields{{"foo", "bar"}}) } func TestEntry_WithError(t *testing.T) { a := logg.New(logg.Options{Handler: handlers.Discard, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithError(fmt.Errorf("boom")) qt.Assert(t, a.Fields, qt.IsNil) qt.Assert(t, b.Fields, qt.DeepEquals, logg.Fields{{"error", "boom"}}) } func TestEntry_WithError_fields(t *testing.T) { a := logg.New(logg.Options{Handler: handlers.Discard, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithError(errFields("boom")) qt.Assert(t, a.Fields, qt.IsNil) qt.Assert(t, b.Fields, qt.DeepEquals, logg.Fields{ {"error", "boom"}, {"reason", "timeout"}, }) } func TestEntry_WithError_nil(t *testing.T) { a := logg.New(logg.Options{Handler: handlers.Discard, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithError(nil) qt.Assert(t, a.Fields, qt.IsNil) qt.Assert(t, b.Fields, qt.IsNil) } func TestEntry_WithDuration(t *testing.T) { a := logg.New(logg.Options{Handler: handlers.Discard, Level: logg.LevelInfo}).WithLevel(logg.LevelInfo) b := a.WithDuration(time.Second * 2) qt.Assert(t, b.Fields, qt.DeepEquals, logg.Fields{{"duration", int64(2000)}}) } type errFields string func (ef errFields) Error() string { return string(ef) } func (ef errFields) Fields() logg.Fields { return logg.Fields{{"reason", "timeout"}} } logg-0.4.0/example_test.go000066400000000000000000000035531452212566600155030ustar00rootroot00000000000000package logg_test import ( "bytes" "fmt" "strings" "time" "github.com/bep/logg" "github.com/bep/logg/handlers/text" ) func Example() { var buff bytes.Buffer // Create a new logger. l := logg.New( logg.Options{ Level: logg.LevelInfo, Handler: text.New(&buff, text.Options{Separator: " "}), }, ) // Create a new log context. infoLogger := l.WithLevel(logg.LevelInfo) // Logg some user activity. userLogger := infoLogger.WithField("user", "foo").WithField("id", "123") userLogger.Log(logg.String("logged in")) userLogger.WithField("file", "jokes.txt").Log(logg.String("uploaded")) userLogger.WithField("file", "morejokes.txt").Log(logg.String("uploaded")) fmt.Print(buff.String()) // Output: // INFO logged in user=foo id=123 // INFO uploaded user=foo id=123 file=jokes.txt // INFO uploaded user=foo id=123 file=morejokes.txt } func Example_lazy_evaluation() { var buff bytes.Buffer // Create a new logger. l := logg.New( logg.Options{ Level: logg.LevelError, Handler: text.New(&buff, text.Options{Separator: "|"}), }, ) errorLogger := l.WithLevel(logg.LevelError) // Info is below the logger's level, so // nothing will be printed. infoLogger := l.WithLevel(logg.LevelInfo) // Simulate a busy loop. for i := 0; i < 999; i++ { ctx := infoLogger.WithFields( logg.NewFieldsFunc( // This func will never be invoked with the current logger's level. func() logg.Fields { return logg.Fields{ {"field", strings.Repeat("x", 9999)}, } }), ) ctx.Log(logg.StringFunc( // This func will never be invoked with the current logger's level. func() string { return "log message: " + strings.Repeat("x", 9999) }, )) } errorLogger.WithDuration(32 * time.Second).Log(logg.String("something took too long")) fmt.Print(buff.String()) // Output: // ERROR|something took too long|duration=32000 } logg-0.4.0/go.mod000066400000000000000000000010121452212566600135540ustar00rootroot00000000000000module github.com/bep/logg go 1.18 require ( github.com/bep/clocks v0.5.0 github.com/fatih/color v1.13.0 github.com/frankban/quicktest v1.14.3 github.com/mattn/go-colorable v0.1.12 github.com/pkg/errors v0.9.1 ) require ( github.com/google/go-cmp v0.5.8 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect ) logg-0.4.0/go.sum000066400000000000000000000065271452212566600136210ustar00rootroot00000000000000github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= logg-0.4.0/handler.go000066400000000000000000000017211452212566600144210ustar00rootroot00000000000000package logg // Handler is used to handle log events, outputting them to // stdio or sending them to remote services. See the "handlers" // directory for implementations. // // It is left up to Handlers to implement thread-safety. type Handler interface { // HandleLog is invoked for each log event. // Note that if the Entry is going to be used after the call to HandleLog // in the handler chain returns, it must be cloned with Clone(). See // the memory.Handler implementation for an example. // // The Entry can be modified if needed, e.g. when passed down via // a multi.Handler (e.g. to sanitize the data). HandleLog(e *Entry) error } // The HandlerFunc type is an adapter to allow the use of ordinary functions as // log handlers. If f is a function with the appropriate signature, // HandlerFunc(f) is a Handler object that calls f. type HandlerFunc func(*Entry) error // HandleLog calls f(e). func (f HandlerFunc) HandleLog(e *Entry) error { return f(e) } logg-0.4.0/handlers/000077500000000000000000000000001452212566600142545ustar00rootroot00000000000000logg-0.4.0/handlers/cli/000077500000000000000000000000001452212566600150235ustar00rootroot00000000000000logg-0.4.0/handlers/cli/cli.go000066400000000000000000000030361452212566600161230ustar00rootroot00000000000000// Package cli implements a colored text handler suitable for command-line interfaces. package cli import ( "fmt" "io" "os" "sync" "github.com/bep/logg" "github.com/fatih/color" colorable "github.com/mattn/go-colorable" ) // Default handler outputting to stderr. var Default = New(os.Stderr) var bold = color.New(color.Bold) // Colors mapping. var Colors = [...]*color.Color{ logg.LevelTrace: color.New(color.FgWhite), logg.LevelDebug: color.New(color.FgWhite), logg.LevelInfo: color.New(color.FgBlue), logg.LevelWarn: color.New(color.FgYellow), logg.LevelError: color.New(color.FgRed), } // Strings mapping. var Strings = [...]string{ logg.LevelTrace: "•", logg.LevelDebug: "•", logg.LevelInfo: "•", logg.LevelWarn: "•", logg.LevelError: "⨯", } // Handler implementation. type Handler struct { mu sync.Mutex Writer io.Writer Padding int } // New handler. func New(w io.Writer) *Handler { if f, ok := w.(*os.File); ok { return &Handler{ Writer: colorable.NewColorable(f), Padding: 3, } } return &Handler{ Writer: w, Padding: 3, } } // HandleLog implements logg.Handler. func (h *Handler) HandleLog(e *logg.Entry) error { color := Colors[e.Level] level := Strings[e.Level] h.mu.Lock() defer h.mu.Unlock() color.Fprintf(h.Writer, "%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message) for _, field := range e.Fields { if field.Name == "source" { continue } fmt.Fprintf(h.Writer, " %s=%v", color.Sprint(field.Name), field.Value) } fmt.Fprintln(h.Writer) return nil } logg-0.4.0/handlers/handlers.go000066400000000000000000000002701452212566600164020ustar00rootroot00000000000000package handlers import "github.com/bep/logg" // Discard is a no-op handler that discards all log messages. var Discard = logg.HandlerFunc(func(e *logg.Entry) error { return nil }) logg-0.4.0/handlers/json/000077500000000000000000000000001452212566600152255ustar00rootroot00000000000000logg-0.4.0/handlers/json/json.go000066400000000000000000000012031452212566600165210ustar00rootroot00000000000000// Package json implements a JSON handler. package json import ( "encoding/json" "io" "github.com/bep/logg" ) type Handler struct { w io.Writer } // New Handler implementation for JSON logging. // Eeach log Entry is written as a single JSON object, no more than one write to w. // The writer w should be safe for concurrent use by multiple // goroutines if the returned Handler will be used concurrently. func New(w io.Writer) *Handler { return &Handler{ w, } } // HandleLog implements logg.Handler. func (h *Handler) HandleLog(e *logg.Entry) error { enc := json.NewEncoder(h.w) enc.SetEscapeHTML(false) return enc.Encode(e) } logg-0.4.0/handlers/json/json_test.go000066400000000000000000000020021452212566600175560ustar00rootroot00000000000000package json_test import ( "bytes" "testing" qt "github.com/frankban/quicktest" "github.com/bep/clocks" "github.com/bep/logg" "github.com/bep/logg/handlers/json" ) func TestJSONHandler(t *testing.T) { var buf bytes.Buffer l := logg.New( logg.Options{ Level: logg.LevelInfo, Handler: json.New(&buf), Clock: clocks.Fixed(clocks.TimeCupFinalNorway1976), }) info := l.WithLevel(logg.LevelInfo) info.WithField("user", "tj").WithField("id", "123").Log(logg.String("hello")) info.Log(logg.String("world")) info.WithLevel(logg.LevelError).Log(logg.String("boom")) expected := "{\"level\":\"info\",\"timestamp\":\"1976-10-24T12:15:02.127686412Z\",\"fields\":[{\"name\":\"user\",\"value\":\"tj\"},{\"name\":\"id\",\"value\":\"123\"}],\"message\":\"hello\"}\n{\"level\":\"info\",\"timestamp\":\"1976-10-24T12:15:02.127686412Z\",\"message\":\"world\"}\n{\"level\":\"error\",\"timestamp\":\"1976-10-24T12:15:02.127686412Z\",\"message\":\"boom\"}\n" qt.Assert(t, buf.String(), qt.Equals, expected) } logg-0.4.0/handlers/level/000077500000000000000000000000001452212566600153635ustar00rootroot00000000000000logg-0.4.0/handlers/level/level.go000066400000000000000000000007401452212566600170220ustar00rootroot00000000000000// Package level implements a level filter handler. package level import "github.com/bep/logg" // Handler implementation. type Handler struct { Level logg.Level Handler logg.Handler } // New handler. func New(h logg.Handler, level logg.Level) *Handler { return &Handler{ Level: level, Handler: h, } } // HandleLog implements logg.Handler. func (h *Handler) HandleLog(e *logg.Entry) error { if e.Level < h.Level { return nil } return h.Handler.HandleLog(e) } logg-0.4.0/handlers/level/level_test.go000066400000000000000000000011131452212566600200540ustar00rootroot00000000000000package level_test import ( "testing" qt "github.com/frankban/quicktest" "github.com/bep/logg" "github.com/bep/logg/handlers/level" "github.com/bep/logg/handlers/memory" ) func TestLevel(t *testing.T) { h := memory.New() l := logg.New( logg.Options{Level: logg.LevelError, Handler: level.New(h, logg.LevelError)}, ) info := l.WithLevel(logg.LevelInfo) info.Log(logg.String("hello")) info.Log(logg.String("world")) info.WithLevel(logg.LevelError).Log(logg.String("boom")) qt.Assert(t, h.Entries, qt.HasLen, 1) qt.Assert(t, "boom", qt.Equals, h.Entries[0].Message) } logg-0.4.0/handlers/memory/000077500000000000000000000000001452212566600155645ustar00rootroot00000000000000logg-0.4.0/handlers/memory/memory.go000066400000000000000000000010001452212566600174120ustar00rootroot00000000000000// Package memory implements an in-memory handler useful for testing, as the // entries can be accessed after writes. package memory import ( "sync" "github.com/bep/logg" ) // Handler implementation. type Handler struct { mu sync.Mutex Entries []*logg.Entry } // New handler. func New() *Handler { return &Handler{} } // HandleLog implements logg.Handler. func (h *Handler) HandleLog(e *logg.Entry) error { h.mu.Lock() defer h.mu.Unlock() h.Entries = append(h.Entries, e.Clone()) return nil } logg-0.4.0/handlers/multi/000077500000000000000000000000001452212566600154065ustar00rootroot00000000000000logg-0.4.0/handlers/multi/multi.go000066400000000000000000000012171452212566600170700ustar00rootroot00000000000000// Package multi implements a handler which invokes a number of handlers. package multi import ( "github.com/bep/logg" ) // Handler implementation. type Handler struct { Handlers []logg.Handler } // New handler. func New(h ...logg.Handler) *Handler { return &Handler{ Handlers: h, } } // HandleLog implements logg.Handler. func (h *Handler) HandleLog(e *logg.Entry) error { for _, handler := range h.Handlers { // TODO(tj): maybe just write to stderr here, definitely not ideal // to miss out logging to a more critical handler if something // goes wrong if err := handler.HandleLog(e); err != nil { return err } } return nil } logg-0.4.0/handlers/multi/multi_test.go000066400000000000000000000035301452212566600201270ustar00rootroot00000000000000package multi_test import ( "testing" "github.com/bep/logg" "github.com/bep/logg/handlers/memory" "github.com/bep/logg/handlers/multi" qt "github.com/frankban/quicktest" ) func TestMulti(t *testing.T) { a := memory.New() b := memory.New() l := logg.New(logg.Options{ Level: logg.LevelInfo, Handler: multi.New(a, b), }) info := l.WithLevel(logg.LevelInfo) info.WithField("user", "tj").WithField("id", "123").Log(logg.String("hello")) info.Log(logg.String("world")) info.WithLevel(logg.LevelError).Log(logg.String("boom")) qt.Assert(t, a.Entries, qt.HasLen, 3) qt.Assert(t, b.Entries, qt.HasLen, 3) } func TestMultiModifyEntry(t *testing.T) { var a logg.HandlerFunc = func(e *logg.Entry) error { e.Message += "-modified" e.Fields = append(e.Fields, logg.Field{Name: "added", Value: "value"}) return nil } b := memory.New() l := logg.New( logg.Options{ Level: logg.LevelInfo, Handler: multi.New(a, b), }) l.WithLevel(logg.LevelInfo).WithField("initial", "value").Log(logg.String("text")) qt.Assert(t, b.Entries, qt.HasLen, 1) qt.Assert(t, b.Entries[0].Message, qt.Equals, "text-modified") qt.Assert(t, b.Entries[0].Fields, qt.HasLen, 2) qt.Assert(t, b.Entries[0].Fields[0].Name, qt.Equals, "initial") qt.Assert(t, b.Entries[0].Fields[1].Name, qt.Equals, "added") } func TestStopEntry(t *testing.T) { var a logg.HandlerFunc = func(e *logg.Entry) error { if e.Fields[0].Value == "v2" { return logg.ErrStopLogEntry } return nil } b := memory.New() l := logg.New( logg.Options{ Level: logg.LevelInfo, Handler: multi.New(a, b), }) l.WithLevel(logg.LevelInfo).WithField("v", "v1").Log(logg.String("text")) l.WithLevel(logg.LevelInfo).WithField("v", "v2").Log(logg.String("text")) l.WithLevel(logg.LevelInfo).WithField("v", "v3").Log(logg.String("text")) qt.Assert(t, b.Entries, qt.HasLen, 2) } logg-0.4.0/handlers/text/000077500000000000000000000000001452212566600152405ustar00rootroot00000000000000logg-0.4.0/handlers/text/text.go000066400000000000000000000017741452212566600165640ustar00rootroot00000000000000// Package text implements a development-friendly textual handler. package text import ( "fmt" "io" "os" "strings" "github.com/bep/logg" ) // Default handler outputting to stderr. var Default = New(os.Stderr, Options{}) // Handler implementation. type Handler struct { opts Options w io.Writer } // Options holds options for the text handler. type Options struct { // Separator is the separator between fields. // Default is " ". Separator string } // New handler. func New(w io.Writer, opts Options) *Handler { if opts.Separator == "" { opts.Separator = " " } return &Handler{ w: w, opts: opts, } } // HandleLog implements logg.Handler. func (h *Handler) HandleLog(e *logg.Entry) error { fields := make([]string, len(e.Fields)) for i, f := range e.Fields { fields[i] = fmt.Sprintf("%s=%v", f.Name, f.Value) } fmt.Fprintf(h.w, "%s%s%s%s%s\n", strings.ToUpper(e.Level.String()), h.opts.Separator, e.Message, h.opts.Separator, strings.Join(fields, h.opts.Separator)) return nil } logg-0.4.0/handlers/text/text_test.go000066400000000000000000000013021452212566600176060ustar00rootroot00000000000000package text_test import ( "bytes" "testing" qt "github.com/frankban/quicktest" "github.com/bep/logg" "github.com/bep/logg/handlers/text" ) func TestTextHandler(t *testing.T) { var buf bytes.Buffer l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: text.New(&buf, text.Options{Separator: "|"})}) info := l.WithLevel(logg.LevelInfo) info.WithField("user", "tj").WithField("id", "123").Log(logg.String("hello")) info.WithField("user", "tj").Log(logg.String("world")) info.WithField("user", "tj").WithLevel(logg.LevelError).Log(logg.String("boom")) expected := "INFO|hello|user=tj|id=123\nINFO|world|user=tj\nERROR|boom|user=tj\n" qt.Assert(t, buf.String(), qt.Equals, expected) } logg-0.4.0/interfaces.go000066400000000000000000000026541452212566600151350ustar00rootroot00000000000000package logg import ( "fmt" "time" ) // Logger is the main interface for the logger. type Logger interface { // WithLevel returns a new entry with `level` set. WithLevel(Level) *Entry } // LevelLogger is the logger at a given level. type LevelLogger interface { // Log logs a message at the given level using the string from calling s.String(). // Note that s.String() will not be called if the level is not enabled. Log(s fmt.Stringer) // Logf logs a message at the given level using the format and args from calling fmt.Sprintf(). // Note that fmt.Sprintf() will not be called if the level is not enabled. Logf(format string, a ...any) // WithLevel returns a new entry with `level` set. WithLevel(Level) *Entry // WithFields returns a new entry with the`fields` in fields set. // This is a noop if LevelLogger's level is less than Logger's. WithFields(fields Fielder) *Entry // WithLevel returns a new entry with the field f set with value v // This is a noop if LevelLogger's level is less than Logger's. WithField(f string, v any) *Entry // WithDuration returns a new entry with the "duration" field set // to the given duration in milliseconds. // This is a noop if LevelLogger's level is less than Logger's. WithDuration(time.Duration) *Entry // WithError returns a new entry with the "error" set to `err`. // This is a noop if err is nil or LevelLogger's level is less than Logger's. WithError(error) *Entry } logg-0.4.0/levels.go000066400000000000000000000025761452212566600143070ustar00rootroot00000000000000package logg import ( "bytes" "errors" "strings" ) // ErrInvalidLevel is returned if the severity level is invalid. var ErrInvalidLevel = errors.New("invalid level") // Level of severity. type Level int // Log levels. const ( LevelInvalid Level = iota LevelTrace LevelDebug LevelInfo LevelWarn LevelError ) var levelNames = [...]string{ LevelTrace: "trace", LevelDebug: "debug", LevelInfo: "info", LevelWarn: "warn", LevelError: "error", } var levelStrings = map[string]Level{ "trace": LevelTrace, "debug": LevelDebug, "info": LevelInfo, "warn": LevelWarn, "warning": LevelWarn, "error": LevelError, } // String implementation. func (l Level) String() string { return levelNames[l] } // MarshalJSON implementation. func (l Level) MarshalJSON() ([]byte, error) { return []byte(`"` + l.String() + `"`), nil } // UnmarshalJSON implementation. func (l *Level) UnmarshalJSON(b []byte) error { v, err := ParseLevel(string(bytes.Trim(b, `"`))) if err != nil { return err } *l = v return nil } // ParseLevel parses level string. func ParseLevel(s string) (Level, error) { l, ok := levelStrings[strings.ToLower(s)] if !ok { return LevelInvalid, ErrInvalidLevel } return l, nil } // MustParseLevel parses level string or panics. func MustParseLevel(s string) Level { l, err := ParseLevel(s) if err != nil { panic("invalid log level") } return l } logg-0.4.0/levels_test.go000066400000000000000000000023571452212566600153430ustar00rootroot00000000000000package logg import ( "encoding/json" "testing" qt "github.com/frankban/quicktest" ) func TestParseLevel(t *testing.T) { cases := []struct { String string Level Level }{ {"trace", LevelTrace}, {"debug", LevelDebug}, {"info", LevelInfo}, {"warn", LevelWarn}, {"warning", LevelWarn}, {"error", LevelError}, } for _, c := range cases { t.Run(c.String, func(t *testing.T) { l, err := ParseLevel(c.String) qt.Assert(t, err, qt.IsNil, qt.Commentf("parse")) qt.Assert(t, l, qt.Equals, c.Level) }) } t.Run("invalid", func(t *testing.T) { l, err := ParseLevel("something") qt.Assert(t, err, qt.Equals, ErrInvalidLevel) qt.Assert(t, l, qt.Equals, LevelInvalid) }) } func TestLevel_MarshalJSON(t *testing.T) { e := Entry{ Message: "hello", Level: LevelInfo, } expect := `{"level":"info","timestamp":"0001-01-01T00:00:00Z","message":"hello"}` b, err := json.Marshal(e) qt.Assert(t, err, qt.IsNil) qt.Assert(t, string(b), qt.Equals, expect) } func TestLevel_UnmarshalJSON(t *testing.T) { s := `{"fields":[],"level":"info","timestamp":"0001-01-01T00:00:00Z","message":"hello"}` e := new(Entry) err := json.Unmarshal([]byte(s), e) qt.Assert(t, err, qt.IsNil) qt.Assert(t, e.Level, qt.Equals, LevelInfo) } logg-0.4.0/logger.go000066400000000000000000000067221452212566600142710ustar00rootroot00000000000000package logg import ( "fmt" stdlog "log" "time" "github.com/bep/clocks" ) // assert interface compliance. var _ Logger = (*logger)(nil) // String implements fmt.Stringer and can be used directly in // the log methods. type String string // StringFunc is a function that returns a string. // It also implements the fmt.Stringer interface and // can therefore be used as argument to the log methods. type StringFunc func() string func (f StringFunc) String() string { return f() } func (s String) String() string { return string(s) } // Fielder is an interface for providing fields to custom types. type Fielder interface { Fields() Fields } func NewFieldsFunc(fn func() Fields) FieldsFunc { return FieldsFunc(fn) } type FieldsFunc func() Fields func (f FieldsFunc) Fields() Fields { return f() } // Field holds a named value. type Field struct { Name string `json:"name"` Value any `json:"value"` } // Fields represents a slice of entry level data used for structured logging. type Fields []Field // Fields implements Fielder. func (f Fields) Fields() Fields { return f } // Options is the set of options used to configure a logger. type Options struct { // Level is the minimum level to log at. // If not set, defaults to InfoLevel. Level Level // Handler is the log handler to use. Handler Handler // Clock is the clock to use for timestamps. // If not set, the system clock is used. Clock Clock } // New returns a new logger. func New(cfg Options) Logger { if cfg.Handler == nil { panic("handler cannot be nil") } if cfg.Level <= 0 || cfg.Level > LevelError { panic("log level is out of range") } if cfg.Clock == nil { cfg.Clock = clocks.System() } if cfg.Level == 0 { cfg.Level = LevelInfo } return &logger{ Handler: cfg.Handler, Level: cfg.Level, Clock: cfg.Clock, } } // logger represents a logger with configurable Level and Handler. type logger struct { Handler Handler Level Level Clock Clock } // Clock provides the current time. type Clock interface { Now() time.Time } // WithLevel returns a new entry with `level` set. func (l *logger) WithLevel(level Level) *Entry { return NewEntry(l).WithLevel(level) } // WithFields returns a new entry with `fields` set. func (l *logger) WithFields(fields Fielder) *Entry { return NewEntry(l).WithFields(fields.Fields()) } // WithField returns a new entry with the `key` and `value` set. // // Note that the `key` should not have spaces in it - use camel // case or underscores func (l *logger) WithField(key string, value any) *Entry { return NewEntry(l).WithField(key, value) } // WithDuration returns a new entry with the "duration" field set // to the given duration in milliseconds. func (l *logger) WithDuration(d time.Duration) *Entry { return NewEntry(l).WithDuration(d) } // WithError returns a new entry with the "error" set to `err`. func (l *logger) WithError(err error) *Entry { return NewEntry(l).WithError(err) } // ErrStopLogEntry is a sentinel error that can be returned from a // handler to stop the entry from being passed to the next handler. var ErrStopLogEntry = fmt.Errorf("stop log entry") // log the message, invoking the handler. func (l *logger) log(e *Entry, s fmt.Stringer) { if e.Level < l.Level { return } finalized := objectPools.GetEntry() defer objectPools.PutEntry(finalized) e.finalize(finalized, s.String()) if err := l.Handler.HandleLog(finalized); err != nil { if err != ErrStopLogEntry { stdlog.Printf("error logging: %s", err) } } } logg-0.4.0/logger_test.go000066400000000000000000000165331452212566600153310ustar00rootroot00000000000000package logg_test import ( "fmt" "strings" "testing" "github.com/bep/logg" "github.com/bep/logg/handlers" "github.com/bep/logg/handlers/memory" qt "github.com/frankban/quicktest" ) func TestLogger_Log(t *testing.T) { h := memory.New() l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: h}) a := l.WithLevel(logg.LevelInfo) a.Log(logg.String("logged in Tobi")) e := h.Entries[0] qt.Assert(t, "logged in Tobi", qt.Equals, e.Message) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) } func TestLogger_Logf(t *testing.T) { h := memory.New() l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: h}) a := l.WithLevel(logg.LevelInfo) a.Logf("logged in %s", "Tobi") e := h.Entries[0] qt.Assert(t, "logged in Tobi", qt.Equals, e.Message) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) } func TestLogger_levels(t *testing.T) { h := memory.New() l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: h}) l.WithLevel(logg.LevelTrace).Log(logg.String("uploading")) l.WithLevel(logg.LevelDebug).Log(logg.String("uploading")) l.WithLevel(logg.LevelInfo).Log(logg.String("upload complete")) qt.Assert(t, len(h.Entries), qt.Equals, 1) e := h.Entries[0] qt.Assert(t, "upload complete", qt.Equals, e.Message) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) } func TestLogger_WithFields(t *testing.T) { h := memory.New() l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: h}) info := l.WithLevel(logg.LevelInfo).WithFields(logg.Fields{{"file", "sloth.png"}}) info.WithLevel(logg.LevelDebug).Log(logg.String("uploading")) info.Log(logg.String("upload complete")) qt.Assert(t, len(h.Entries), qt.Equals, 1) e := h.Entries[0] qt.Assert(t, "upload complete", qt.Equals, e.Message) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) qt.Assert(t, e.Fields, qt.DeepEquals, logg.Fields{{"file", "sloth.png"}}) } func TestLogger_WithField(t *testing.T) { h := memory.New() l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: h}) info := l.WithLevel(logg.LevelInfo).WithField("file", "sloth.png").WithField("user", "Tobi") info.WithLevel(logg.LevelDebug).Log(logg.String("uploading")) info.Log(logg.String("upload complete")) qt.Assert(t, len(h.Entries), qt.Equals, 1) e := h.Entries[0] qt.Assert(t, "upload complete", qt.Equals, e.Message) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) qt.Assert(t, e.Fields, qt.DeepEquals, logg.Fields{{"file", "sloth.png"}, {"user", "Tobi"}}) } func TestLogger_HandlerFunc(t *testing.T) { h := memory.New() f := func(e *logg.Entry) error { return h.HandleLog(e) } l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: logg.HandlerFunc(f)}) info := l.WithLevel(logg.LevelInfo) info.Log(logg.String("logged in Tobi")) e := h.Entries[0] qt.Assert(t, "logged in Tobi", qt.Equals, e.Message) qt.Assert(t, logg.LevelInfo, qt.Equals, e.Level) } func BenchmarkLogger_small(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) for i := 0; i < b.N; i++ { info.Log(logg.String("login")) } } func BenchmarkLogger_medium(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) for i := 0; i < b.N; i++ { info.WithFields(logg.Fields{ {"file", "sloth.png"}, {"type", "image/png"}, {"size", 1 << 20}, }).Log(logg.String("upload")) } } func BenchmarkLogger_large(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) err := fmt.Errorf("boom") for i := 0; i < b.N; i++ { info.WithFields(logg.Fields{ {"file", "sloth.png"}, {"type", "image/png"}, {"size", 1 << 20}, }). WithFields(logg.Fields{ {"some", "more"}, {"data", "here"}, {"whatever", "blah blah"}, {"more", "stuff"}, {"context", "such useful"}, {"much", "fun"}, }). WithError(err).Log(logg.String("upload failed")) } } func BenchmarkLogger_common_context(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) for i := 0; i < 3; i++ { info = info.WithField(fmt.Sprintf("context%d", i), "value") } b.ResetTimer() for i := 0; i < b.N; i++ { info.Log(logg.String("upload")) } } func BenchmarkLogger_common_context_many_fields(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) for i := 0; i < 42; i++ { info = info.WithField(fmt.Sprintf("context%d", i), "value") } b.ResetTimer() for i := 0; i < b.N; i++ { info.Log(logg.String("upload")) } } func BenchmarkLogger_context_many_fields_duplicate_names_with_field(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) b.ResetTimer() for i := 0; i < b.N; i++ { info := l.WithLevel(logg.LevelInfo) for i := 0; i < 9999; i++ { info = info.WithField("name", "value") } info.Log(logg.String("upload")) } } func BenchmarkLogger_context_many_fields_duplicate_names_with_fields(b *testing.B) { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) b.ResetTimer() for i := 0; i < b.N; i++ { info := l.WithLevel(logg.LevelInfo) for i := 0; i < 3333; i++ { info = info.WithFields(logg.Fields{{"name", "value"}, {"name", "value"}, {"name", "value"}}) } info.Log(logg.String("upload")) } } func BenchmarkLogger_levels(b *testing.B) { doWork := func(l logg.LevelLogger) { for i := 0; i < 10; i++ { l.Log(logg.StringFunc( func() string { return fmt.Sprintf("loging value %s and %s.", "value1", strings.Repeat("value2", i+1)) }, )) } } b.Run("level not met", func(b *testing.B) { for i := 0; i < b.N; i++ { l := logg.New(logg.Options{Level: logg.LevelError, Handler: handlers.Discard}) error := l.WithLevel(logg.LevelInfo) doWork(error) } }) b.Run("level not met, one field", func(b *testing.B) { for i := 0; i < b.N; i++ { l := logg.New(logg.Options{Level: logg.LevelError, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) info = info.WithField("file", "sloth.png") doWork(info) } }) b.Run("level not met, many fields", func(b *testing.B) { for i := 0; i < b.N; i++ { l := logg.New(logg.Options{Level: logg.LevelError, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) info = info.WithField("file", "sloth.png") for i := 0; i < 32; i++ { info = info.WithField(fmt.Sprintf("field%d", i), "value") } doWork(info) } }) b.Run("level met", func(b *testing.B) { for i := 0; i < b.N; i++ { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) for j := 0; j < 10; j++ { doWork(info) } } }) b.Run("level met, one field", func(b *testing.B) { for i := 0; i < b.N; i++ { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) info = info.WithField("file", "sloth.png") doWork(info) } }) b.Run("level met, many fields", func(b *testing.B) { for i := 0; i < b.N; i++ { l := logg.New(logg.Options{Level: logg.LevelInfo, Handler: handlers.Discard}) info := l.WithLevel(logg.LevelInfo) info = info.WithField("file", "sloth.png") for i := 0; i < 32; i++ { info = info.WithField(fmt.Sprintf("field%d", i), "value") } doWork(info) } }) } logg-0.4.0/objectpools.go000066400000000000000000000006551452212566600153340ustar00rootroot00000000000000package logg import "sync" var objectPools = &objectPoolsHolder{ entryPool: &sync.Pool{ New: func() any { return &Entry{} }, }, } type objectPoolsHolder struct { // This is only used for the event copy passed to HandleLog. entryPool *sync.Pool } func (h *objectPoolsHolder) GetEntry() *Entry { return h.entryPool.Get().(*Entry) } func (h *objectPoolsHolder) PutEntry(e *Entry) { e.reset() h.entryPool.Put(e) } logg-0.4.0/stack.go000066400000000000000000000002071452212566600141070ustar00rootroot00000000000000package logg import "github.com/pkg/errors" // stackTracer interface. type stackTracer interface { StackTrace() errors.StackTrace }