pax_global_header 0000666 0000000 0000000 00000000064 14550243076 0014520 g ustar 00root root 0000000 0000000 52 comment=5a5b0af7a8eca56f76341aa8ff3406714af6d9b8 console-slog-0.3.1/ 0000775 0000000 0000000 00000000000 14550243076 0014125 5 ustar 00root root 0000000 0000000 console-slog-0.3.1/.github/ 0000775 0000000 0000000 00000000000 14550243076 0015465 5 ustar 00root root 0000000 0000000 console-slog-0.3.1/.github/dependabot.yml 0000664 0000000 0000000 00000000770 14550243076 0020321 0 ustar 00root root 0000000 0000000 # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" console-slog-0.3.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14550243076 0017522 5 ustar 00root root 0000000 0000000 console-slog-0.3.1/.github/workflows/go.yml 0000664 0000000 0000000 00000001216 14550243076 0020652 0 ustar 00root root 0000000 0000000 # This workflow will build a golang project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go name: Build on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.21' - name: Build run: go build -v ./... - name: Test run: go test -race -coverprofile=coverage.txt -covermode=atomic -v ./... - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 console-slog-0.3.1/.gitignore 0000664 0000000 0000000 00000000736 14550243076 0016123 0 ustar 00root root 0000000 0000000 # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore # # 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/ # Go workspace file go.work console-slog-0.3.1/LICENSE 0000664 0000000 0000000 00000002067 14550243076 0015137 0 ustar 00root root 0000000 0000000 MIT License Copyright (c) 2023 Pierre-Henri Symoneaux 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. console-slog-0.3.1/README.md 0000664 0000000 0000000 00000006025 14550243076 0015407 0 ustar 00root root 0000000 0000000 # console-slog [](https://pkg.go.dev/github.com/phsym/console-slog) [](https://raw.githubusercontent.com/phsym/console-slog/master/LICENSE) [](https://github.com/phsym/slog-console/actions/workflows/go.yml) [](https://codecov.io/gh/phsym/console-slog) [](https://goreportcard.com/report/github.com/phsym/console-slog) A handler for slog that prints colorized logs, similar to zerolog's console writer output without sacrificing performances. ## Installation ```bash go get github.com/phsym/console-slog@latest ``` ## Example ```go package main import ( "errors" "log/slog" "os" "github.com/phsym/console-slog" ) func main() { logger := slog.New( console.NewHandler(os.Stderr, &console.HandlerOptions{Level: slog.LevelDebug}), ) slog.SetDefault(logger) slog.Info("Hello world!", "foo", "bar") slog.Debug("Debug message") slog.Warn("Warning message") slog.Error("Error message", "err", errors.New("the error")) logger = logger.With("foo", "bar"). WithGroup("the-group"). With("bar", "baz") logger.Info("group info", "attr", "value") } ```  When setting `console.HandlerOptions.AddSource` to `true`: ```go console.NewHandler(os.Stderr, &console.HandlerOptions{Level: slog.LevelDebug, AddSource: true}) ```  ## Performances See [benchmark file](./bench_test.go) for details. The handler itself performs quite well compared to std-lib's handlers. It does no allocation: ``` goos: linux goarch: amd64 pkg: github.com/phsym/console-slog cpu: Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz BenchmarkHandlers/dummy-4 128931026 8.732 ns/op 0 B/op 0 allocs/op BenchmarkHandlers/console-4 849837 1294 ns/op 0 B/op 0 allocs/op BenchmarkHandlers/std-text-4 542583 2097 ns/op 4 B/op 2 allocs/op BenchmarkHandlers/std-json-4 583784 1911 ns/op 120 B/op 3 allocs/op ``` However, the go 1.21.0 `slog.Logger` adds some overhead: ``` goos: linux goarch: amd64 pkg: github.com/phsym/console-slog cpu: Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz BenchmarkLoggers/dummy-4 1239873 893.2 ns/op 128 B/op 1 allocs/op BenchmarkLoggers/console-4 483354 2338 ns/op 128 B/op 1 allocs/op BenchmarkLoggers/std-text-4 368828 3141 ns/op 132 B/op 3 allocs/op BenchmarkLoggers/std-json-4 393322 2909 ns/op 248 B/op 4 allocs/op ``` console-slog-0.3.1/bench_test.go 0000664 0000000 0000000 00000004050 14550243076 0016571 0 ustar 00root root 0000000 0000000 package console import ( "context" "errors" "io" "log/slog" "testing" "time" ) type DummyHandler struct{} func (*DummyHandler) Enabled(context.Context, slog.Level) bool { return true } func (*DummyHandler) Handle(context.Context, slog.Record) error { return nil } func (h *DummyHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return h } func (h *DummyHandler) WithGroup(name string) slog.Handler { return h } var handlers = []struct { name string hdl slog.Handler }{ {"dummy", &DummyHandler{}}, {"console", NewHandler(io.Discard, &HandlerOptions{Level: slog.LevelDebug, AddSource: false})}, {"std-text", slog.NewTextHandler(io.Discard, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: false})}, {"std-json", slog.NewJSONHandler(io.Discard, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: false})}, } var attrs = []slog.Attr{ slog.String("foo", "bar"), slog.Int("int", 12), slog.Duration("dur", 3*time.Second), slog.Bool("bool", true), slog.Float64("float", 23.7), slog.Time("thetime", time.Now()), slog.Any("err", errors.New("yo")), slog.Group("empty"), slog.Group("group", slog.String("bar", "baz")), } var attrsAny = func() (a []any) { for _, attr := range attrs { a = append(a, attr) } return }() func BenchmarkHandlers(b *testing.B) { ctx := context.Background() rec := slog.NewRecord(time.Now(), slog.LevelInfo, "hello", 0) rec.AddAttrs(attrs...) for _, tc := range handlers { b.Run(tc.name, func(b *testing.B) { l := tc.hdl.WithAttrs(attrs).WithGroup("test").WithAttrs(attrs) // Warm-up _ = l.Handle(ctx, rec) b.ResetTimer() for i := 0; i < b.N; i++ { _ = l.Handle(ctx, rec) } }) } } func BenchmarkLoggers(b *testing.B) { for _, tc := range handlers { ctx := context.Background() b.Run(tc.name, func(b *testing.B) { l := slog.New(tc.hdl).With(attrsAny...).WithGroup("test").With(attrsAny...) // Warm-up l.LogAttrs(ctx, slog.LevelInfo, "hello", attrs...) b.ResetTimer() for i := 0; i < b.N; i++ { l.LogAttrs(ctx, slog.LevelInfo, "hello", attrs...) } }) } } console-slog-0.3.1/buffer.go 0000664 0000000 0000000 00000003165 14550243076 0015732 0 ustar 00root root 0000000 0000000 package console import ( "io" "slices" "strconv" "time" ) type buffer []byte func (b *buffer) Grow(n int) { *b = slices.Grow(*b, n) } func (b *buffer) Bytes() []byte { return *b } func (b *buffer) String() string { return string(*b) } func (b *buffer) Len() int { return len(*b) } func (b *buffer) Cap() int { return cap(*b) } func (b *buffer) WriteTo(dst io.Writer) (int64, error) { l := len(*b) if l == 0 { return 0, nil } n, err := dst.Write(*b) if err != nil { return int64(n), err } if n < l { return int64(n), io.ErrShortWrite } b.Reset() return int64(n), nil } func (b *buffer) Reset() { *b = (*b)[:0] } func (b *buffer) Clone() buffer { return append(buffer(nil), *b...) } func (b *buffer) Clip() { *b = slices.Clip(*b) } func (b *buffer) copy(src *buffer) { if src.Len() > 0 { b.Append(src.Bytes()) } } func (b *buffer) Append(data []byte) { *b = append(*b, data...) } func (b *buffer) AppendString(s string) { *b = append(*b, s...) } // func (b *buffer) AppendQuotedString(s string) { // b.buff = strconv.AppendQuote(b.buff, s) // } func (b *buffer) AppendByte(byt byte) { *b = append(*b, byt) } func (b *buffer) AppendTime(t time.Time, format string) { *b = t.AppendFormat(*b, format) } func (b *buffer) AppendInt(i int64) { *b = strconv.AppendInt(*b, i, 10) } func (b *buffer) AppendUint(i uint64) { *b = strconv.AppendUint(*b, i, 10) } func (b *buffer) AppendFloat(i float64) { *b = strconv.AppendFloat(*b, i, 'g', -1, 64) } func (b *buffer) AppendBool(i bool) { *b = strconv.AppendBool(*b, i) } func (b *buffer) AppendDuration(d time.Duration) { *b = appendDuration(*b, d) } console-slog-0.3.1/buffer_test.go 0000664 0000000 0000000 00000005551 14550243076 0016772 0 ustar 00root root 0000000 0000000 package console import ( "bytes" "errors" "io" "testing" "time" ) func TestBuffer_Append(t *testing.T) { b := new(buffer) AssertZero(t, b.Len()) b.AppendString("foobar") AssertEqual(t, 6, b.Len()) b.AppendString("baz") AssertEqual(t, 9, b.Len()) AssertEqual(t, "foobarbaz", b.String()) b.AppendByte('.') AssertEqual(t, 10, b.Len()) AssertEqual(t, "foobarbaz.", b.String()) b.AppendBool(true) b.AppendBool(false) b.AppendFloat(3.14) b.AppendInt(42) b.AppendUint(12) b.Append([]byte("foo")) b.AppendDuration(1 * time.Second) now := time.Now() b.AppendTime(now, time.RFC3339) AssertEqual(t, "foobarbaz.truefalse3.144212foo1s"+now.Format(time.RFC3339), b.String()) } func TestBuffer_WriteTo(t *testing.T) { dest := bytes.Buffer{} b := new(buffer) n, err := b.WriteTo(&dest) AssertNoError(t, err) AssertZero(t, n) b.AppendString("foobar") n, err = b.WriteTo(&dest) AssertEqual(t, len("foobar"), int(n)) AssertNoError(t, err) AssertEqual(t, "foobar", dest.String()) AssertZero(t, b.Len()) } func TestBuffer_Clone(t *testing.T) { b := new(buffer) b.AppendString("foobar") b2 := b.Clone() AssertEqual(t, b.String(), b2.String()) AssertNotEqual(t, &b.Bytes()[0], &b2.Bytes()[0]) } func TestBuffer_Copy(t *testing.T) { b := new(buffer) b.AppendString("foobar") b2 := new(buffer) b2.copy(b) AssertEqual(t, b.String(), b2.String()) AssertNotEqual(t, &b.Bytes()[0], &b2.Bytes()[0]) } func TestBuffer_Reset(t *testing.T) { b := new(buffer) b.AppendString("foobar") AssertEqual(t, "foobar", b.String()) AssertEqual(t, len("foobar"), b.Len()) bufCap := b.Cap() b.Reset() AssertZero(t, b.Len()) AssertEqual(t, bufCap, b.Cap()) } func TestBuffer_Grow(t *testing.T) { b := new(buffer) AssertZero(t, b.Cap()) b.Grow(12) AssertGreaterOrEqual(t, 12, b.Cap()) b.Grow(6) AssertGreaterOrEqual(t, 12, b.Cap()) b.Grow(24) AssertGreaterOrEqual(t, 24, b.Cap()) } func TestBuffer_Clip(t *testing.T) { b := new(buffer) b.AppendString("foobar") b.Grow(12) AssertGreaterOrEqual(t, 12, b.Cap()) b.Clip() AssertEqual(t, "foobar", b.String()) AssertEqual(t, len("foobar"), b.Cap()) } func TestBuffer_WriteTo_Err(t *testing.T) { w := writerFunc(func(b []byte) (int, error) { return 0, errors.New("nope") }) b := new(buffer) b.AppendString("foobar") _, err := b.WriteTo(w) AssertError(t, err) w = writerFunc(func(b []byte) (int, error) { return 0, nil }) _, err = b.WriteTo(w) AssertError(t, err) if !errors.Is(err, io.ErrShortWrite) { t.Fatalf("Expected io.ErrShortWrite, go %T", err) } } func BenchmarkBuffer(b *testing.B) { data := []byte("foobarbaz") b.Run("std", func(b *testing.B) { buf := bytes.Buffer{} for i := 0; i < b.N; i++ { buf.Write(data) buf.WriteByte('.') buf.Reset() } }) b.Run("buffer", func(b *testing.B) { buf := buffer{} for i := 0; i < b.N; i++ { buf.Append(data) buf.AppendByte('.') buf.Reset() } }) } console-slog-0.3.1/doc/ 0000775 0000000 0000000 00000000000 14550243076 0014672 5 ustar 00root root 0000000 0000000 console-slog-0.3.1/doc/img/ 0000775 0000000 0000000 00000000000 14550243076 0015446 5 ustar 00root root 0000000 0000000 console-slog-0.3.1/doc/img/output-with-source.png 0000664 0000000 0000000 00000067274 14550243076 0022003 0 ustar 00root root 0000000 0000000 PNG IHDR R x sBIT|d tEXtSoftware gnome-screenshot> (tEXtCreation Time mer. 16 aot 2023 10:07:583x IDATx}tS VF˕@aA>v@f/ Eapfz.3nk:pNBЬ@ƝzԺZX,رyIX[V<{~<.2w²v@F拌`e͚5U+2222222_`A'`ݺu՟+2c5q7gMi~ٶ_`_4$/ٯ5nٖ,pV'כ>dddd>+(WѰf=%vF{Z]F1U! )"^DwTQMUN29B8-_4%GeP)ZCd$5r;F]Dwojaì˂TYgٟ~Q_ef > $nƮ⋜K_g+{|KS5g(?0iT%؆Ëab4w ̩~_T"Jԧ.)Ua) +{G<߉=H_S9k8 ̮pQWYLA]UigsWt oԗ,I02Oddddd} ,5Q[xF lN P۪*o9˯jb%U1$}b!oyc$TրOb"Sy&ʍA.HwLPʪJb._*s k&<0q:.w)3/'\8B莹$ T8ṁ/ڸҾ|NoܞpN9o\zcC?yQDlep~hW$H8ݾbd#V/y@ ###) hi3ayL2Vp#v4Y?"8A>&E,o|c@Ձ(%d/]i V,%6TFN]_`VLD&h(4l=)s T!pc2oAn$@;8YPԬ7c#znr.%&P{;K1VMK=;kbVM HVgc;l{.ؾb]+AtV+9B`{34dbM/yi)v+mb %VZEHV'!b{. _SK^x-6v;> ( 0{D_MGq9`7?ӕPA(9<}nʽ* ulo&ꜹn-eB=Omqh }3z$W gUR|T;n(Fu2% qTnRClNgz+jĊA&l5FM>=%Er`$h!)P Hƈ EFz ޱӓnD00|GPF[,FzK/|>@H-;عs';ȴ@qsh_knab7%_1نrtCiG ,,qr0hÄYK0F#0 يQ1%"/B4) H;tv3u eZ,5A"da*.h4 ++ &*X)N[d݄:&6S^Iky4(a$v۩.p&Xڽ=wF,^RLr)LHZ}ilvd/k:Jia&xYT 4ղv̑K3(&\evr5<]7%r͑ ۨ;!tjA8w+XZ)'d֓0K.߄G$`/T,"&Szh4GwZPemᗧr^UMZR+tx8:jWX+ {+!dddd>JTJMļ dA$cplg3^bqi$Qy
wq!s-/ASkӍ0WYCQ!>W!UsuQ귐zcYIFmL(4iq]n9jʭ#]uMx[~)Gv5_C\iq~ruz%N *A@QVɬ`Xe`(3R88SaIQaY6~Z^k{X;0)Ȓ5InZF
hwfXjԨD2L S+K+Lsw
HQVF ~Lo(Un9ox:D+A)?r *m[V>cCiANW2ciQ9ȣV9 8y$l}"z&DTRğdUh,Ё^gqT^gqƾyx8<3?
fXf|] yYwS>d8YFFFμ>12#T"8(( e֟p()0>UJCTc2\wHOf*_aQ'|N6QdMZ~& bI &"u&?};_}y
V!DOzlBoƔ≂uO{u+$o%X滑4S0+
PFe-#\s "|mZ>=:9ݘL