From 25ef889ce1dba1f36d2730976a35fe7a3f296249 Mon Sep 17 00:00:00 2001 From: "andrea.rinaldi" Date: Tue, 3 Feb 2026 10:31:33 +0100 Subject: [PATCH 1/7] perf: sets up a loggers comparison test --- go.mod | 7 +++ go.sum | 20 +++++++++ logs/encoders/base.go | 34 +++++++++++++-- test/comparison_test.go | 96 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 test/comparison_test.go diff --git a/go.mod b/go.mod index 4ea58d4..1cc56de 100644 --- a/go.mod +++ b/go.mod @@ -9,5 +9,12 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.34.0 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/sys v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index fe3d5ca..bf040f2 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,33 @@ +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/logs/encoders/base.go b/logs/encoders/base.go index d673227..433ae1a 100644 --- a/logs/encoders/base.go +++ b/logs/encoders/base.go @@ -32,16 +32,38 @@ func (b *baseEncoder) castAndConcatenateInto(buf *bytes.Buffer, args ...any) { switch v := arg.(type) { case string: buf.WriteString(v) - case rune: - buf.WriteRune(v) + case []byte: + buf.Write(v) case int: buf.Write(strconv.AppendInt(buf.AvailableBuffer(), int64(v), 10)) + case int8: + buf.Write(strconv.AppendInt(buf.AvailableBuffer(), int64(v), 10)) + case int16: + buf.Write(strconv.AppendInt(buf.AvailableBuffer(), int64(v), 10)) + case int32: + buf.Write(strconv.AppendInt(buf.AvailableBuffer(), int64(v), 10)) case int64: buf.Write(strconv.AppendInt(buf.AvailableBuffer(), v, 10)) + case uint: + buf.Write(strconv.AppendUint(buf.AvailableBuffer(), uint64(v), 10)) + case uint8: + buf.Write(strconv.AppendUint(buf.AvailableBuffer(), uint64(v), 10)) + case uint16: + buf.Write(strconv.AppendUint(buf.AvailableBuffer(), uint64(v), 10)) + case uint32: + buf.Write(strconv.AppendUint(buf.AvailableBuffer(), uint64(v), 10)) + case uint64: + buf.Write(strconv.AppendUint(buf.AvailableBuffer(), v, 10)) + case float32: + buf.Write(strconv.AppendFloat(buf.AvailableBuffer(), float64(v), 'f', -1, 32)) case float64: buf.Write(strconv.AppendFloat(buf.AvailableBuffer(), v, 'f', -1, 64)) case bool: - buf.Write(strconv.AppendBool(buf.AvailableBuffer(), v)) + if v { + buf.WriteString("true") + } else { + buf.WriteString("false") + } case fmt.Stringer: buf.WriteString(v.String()) case error: @@ -67,7 +89,11 @@ func (b *baseEncoder) castToString(arg any) string { case float64: return strconv.FormatFloat(v, 'f', -1, 64) case bool: - return strconv.FormatBool(v) + if v { + return "true" + } + + return "false" case fmt.Stringer: return v.String() case error: diff --git a/test/comparison_test.go b/test/comparison_test.go new file mode 100644 index 0000000..36c771f --- /dev/null +++ b/test/comparison_test.go @@ -0,0 +1,96 @@ +package test + +import ( + "io" + "os" + "testing" + + "github.com/pho3b/tiny-logger/logs" + "github.com/rs/zerolog" + "github.com/sirupsen/logrus" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// devNullFile is used for tiny-logger which requires an *os.File +var devNullFile *os.File + +func init() { + var err error + // Open /dev/null (or NUL on Windows) to discard output for tiny-logger + devNullFile, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) + if err != nil { + panic(err) + } +} + +// 1. Tiny Logger (Project) +func BenchmarkTinyLogger(b *testing.B) { + // Initialize tiny-logger + logger := logs.NewLogger() + // Redirect to /dev/null to measure logger overhead only, not I/O + logger.SetLogFile(devNullFile) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + // Simulating a log with a message and a few fields + logger.Info("Benchmark message", "iteration", i, "active", true) + } +} + +// 2. Logrus (Standard-like popular logger) +func BenchmarkLogrus(b *testing.B) { + logger := logrus.New() + logger.Out = io.Discard + logger.SetFormatter(&logrus.TextFormatter{DisableColors: true}) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + logger.WithFields(logrus.Fields{ + "iteration": i, + "active": true, + }).Info("Benchmark message") + } +} + +// 3. Zerolog (Zero Allocation Logger) +func BenchmarkZerolog(b *testing.B) { + logger := zerolog.New(io.Discard) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + logger.Info(). + Any("iteration", i). + Any("active", true). + Any("Benchmark message", "") + } +} + +// 4. Zap (Uber's fast logger) +func BenchmarkZap(b *testing.B) { + // Configure Zap to discard output + encoderConfig := zap.NewProductionEncoderConfig() + core := zapcore.NewCore( + zapcore.NewJSONEncoder(encoderConfig), + zapcore.AddSync(io.Discard), + zap.InfoLevel, + ) + logger := zap.New(core) + defer logger.Sync() + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + logger.Info("Benchmark message", + zap.Any("iteration", i), + zap.Any("active", true), + ) + } +} From 8f76498eb7c0c983b4b8ec44d17e25d2a90baab3 Mon Sep 17 00:00:00 2001 From: arinaldi Date: Tue, 3 Feb 2026 19:54:13 +0100 Subject: [PATCH 2/7] feat: adds benchmark comparison and updates rune casting --- README.md | 2 + go.mod | 16 +++---- go.sum | 14 +++--- internal/services/constants.go | 5 -- internal/services/json_marshaler.go | 5 ++ internal/services/json_mashaler_test.go | 1 - internal/services/yaml_marshaler.go | 2 + logs/encoders/base.go | 32 ++++++++++--- logs/encoders/base_test.go | 8 ++-- logs/logger_test.go | 47 +++++++++++++++++++ makefile | 10 ++-- ...ark_test.go => encoders_benchmark_test.go} | 40 ++++++---------- test/functions.go | 11 +++++ ...son_test.go => loggers_comparison_test.go} | 27 +++-------- 14 files changed, 143 insertions(+), 77 deletions(-) delete mode 100644 internal/services/constants.go rename test/{benchmark_test.go => encoders_benchmark_test.go} (71%) rename test/{comparison_test.go => loggers_comparison_test.go} (76%) diff --git a/README.md b/README.md index eff356f..a238791 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A fast, lightweight, zero-dependency logging solution for Go applications that p simplicity. Compatible with Go version 1.18.x and above +"TinyLogger is the fastest way to log loosely typed data (interfaces)" + ## ✅ Key Features - **Lightweight**: No external dependencies mean faster builds and smaller binaries
diff --git a/go.mod b/go.mod index 1cc56de..4bcfbdc 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,20 @@ module github.com/pho3b/tiny-logger -go 1.23.2 +go 1.24.0 require ( + github.com/rs/zerolog v1.34.0 + github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 + go.uber.org/zap v1.27.1 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rs/zerolog v1.34.0 // indirect - github.com/sirupsen/logrus v1.9.4 // indirect - go.uber.org/multierr v1.10.0 // indirect - go.uber.org/zap v1.27.1 // indirect - golang.org/x/sys v0.13.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/sys v0.40.0 // indirect ) diff --git a/go.sum b/go.sum index bf040f2..9162290 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,13 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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= @@ -17,8 +19,8 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= @@ -26,8 +28,8 @@ go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/services/constants.go b/internal/services/constants.go deleted file mode 100644 index 1c5da00..0000000 --- a/internal/services/constants.go +++ /dev/null @@ -1,5 +0,0 @@ -package services - -const jsonCharOverhead = 80 -const yamlCharOverhead = 70 -const averageExtraLen = 30 diff --git a/internal/services/json_marshaler.go b/internal/services/json_marshaler.go index b0646a2..6304ff1 100644 --- a/internal/services/json_marshaler.go +++ b/internal/services/json_marshaler.go @@ -6,6 +6,11 @@ import ( "strconv" ) +const ( + jsonCharOverhead = 80 + averageExtraLen = 30 +) + // JsonLogEntry represents a structured log entry that can be marshaled to JSON format. // All fields except Message are optional and will be omitted if empty. type JsonLogEntry struct { diff --git a/internal/services/json_mashaler_test.go b/internal/services/json_mashaler_test.go index ee66347..b3d6b8b 100644 --- a/internal/services/json_mashaler_test.go +++ b/internal/services/json_mashaler_test.go @@ -143,7 +143,6 @@ func TestJsonMarshaler_Marshal_OnlyTime(t *testing.T) { m.MarshalInto(buf, entry) got := buf.String() - // Expecting "ts" key instead of "datetime" want := `{"level":"info","time":"16:00","msg":"only time"}` if got != want { t.Errorf("Marshal() = %q, want %q", got, want) diff --git a/internal/services/yaml_marshaler.go b/internal/services/yaml_marshaler.go index 8cc8ced..59bacbf 100644 --- a/internal/services/yaml_marshaler.go +++ b/internal/services/yaml_marshaler.go @@ -6,6 +6,8 @@ import ( "strconv" ) +const yamlCharOverhead = 70 + // YamlLogEntry represents a structured log entry that can be marshaled to YAML format. // All fields except Message are optional and will be omitted if empty. type YamlLogEntry struct { diff --git a/logs/encoders/base.go b/logs/encoders/base.go index 433ae1a..e2e2b14 100644 --- a/logs/encoders/base.go +++ b/logs/encoders/base.go @@ -19,7 +19,9 @@ type baseEncoder struct { bufferSyncPool sync.Pool } -// castAndConcatenateInto writes all the given arguments cast to string and concatenated by a white space into the given buffer. +// castAndConcatenateInto writes all the given arguments cast to string and concatenated by a white space +// into the given buffer. +// The function uses the slower fmt.Sprint only for unknown types func (b *baseEncoder) castAndConcatenateInto(buf *bytes.Buffer, args ...any) { argsLen := len(args) buf.Grow(averageWordLen * argsLen) @@ -61,31 +63,50 @@ func (b *baseEncoder) castAndConcatenateInto(buf *bytes.Buffer, args ...any) { case bool: if v { buf.WriteString("true") - } else { - buf.WriteString("false") + break } + + buf.WriteString("false") case fmt.Stringer: buf.WriteString(v.String()) case error: buf.WriteString(v.Error()) default: - // Using the slower fmt.Sprint only for unknown types buf.WriteString(fmt.Sprint(v)) } } } // castToString is a fast casting method that returns the given argument as a string. +// It uses the slow fmt.Sprint only for unknown types func (b *baseEncoder) castToString(arg any) string { switch v := arg.(type) { case string: return v - case rune: + case []byte: return string(v) case int: return strconv.Itoa(v) + case int8: + return strconv.FormatInt(int64(v), 10) + case int16: + return strconv.FormatInt(int64(v), 10) + case int32: + return strconv.FormatInt(int64(v), 10) case int64: return strconv.FormatInt(v, 10) + case uint: + return strconv.FormatUint(uint64(v), 10) + case uint8: + return strconv.FormatUint(uint64(v), 10) + case uint16: + return strconv.FormatUint(uint64(v), 10) + case uint32: + return strconv.FormatUint(uint64(v), 10) + case uint64: + return strconv.FormatUint(v, 10) + case float32: + return strconv.FormatFloat(float64(v), 'f', -1, 32) case float64: return strconv.FormatFloat(v, 'f', -1, 64) case bool: @@ -99,7 +120,6 @@ func (b *baseEncoder) castToString(arg any) string { case error: return v.Error() default: - // Using the slower fmt.Sprint only for unknown types return fmt.Sprint(v) } } diff --git a/logs/encoders/base_test.go b/logs/encoders/base_test.go index 6925561..7efae09 100644 --- a/logs/encoders/base_test.go +++ b/logs/encoders/base_test.go @@ -40,6 +40,8 @@ func TestBuildMsg(t *testing.T) { result = encoder.castToString(int64(43)) assert.Equal(t, "43", result) result = encoder.castToString(int32(32234)) + assert.Equal(t, "32234", result) + result = encoder.castToString("緪") assert.Equal(t, "緪", result) result = encoder.castToString(int8(3)) assert.Equal(t, "3", result) @@ -56,7 +58,7 @@ func TestBuildMsgWithCastAndConcatenateInto(t *testing.T) { buf := &bytes.Buffer{} // Test with multiple arguments - encoder.castAndConcatenateInto(buf, "This", "is", 'a', "test") + encoder.castAndConcatenateInto(buf, "This", "is", "a", "test") assert.Equal(t, "This is a test", buf.String()) buf.Reset() @@ -67,7 +69,7 @@ func TestBuildMsgWithCastAndConcatenateInto(t *testing.T) { // Test with various argument types encoder.castAndConcatenateInto(buf, "str", '\n', 2, 2.3, true, nil) - assert.Equal(t, "str \n 2 2.3 true ", buf.String()) + assert.Equal(t, "str 10 2 2.3 true ", buf.String()) buf.Reset() // Test with no arguments @@ -81,7 +83,7 @@ func TestBuildMsgWithCastAndConcatenateInto(t *testing.T) { buf.Reset() // Test with rune and int64 types and struct - encoder.castAndConcatenateInto(buf, 'A', int64(43), errors.New("my error")) + encoder.castAndConcatenateInto(buf, "A", int64(43), errors.New("my error")) assert.Equal(t, "A 43 my error", buf.String()) buf.Reset() } diff --git a/logs/logger_test.go b/logs/logger_test.go index 6a95677..ce6618e 100644 --- a/logs/logger_test.go +++ b/logs/logger_test.go @@ -473,6 +473,53 @@ func TestLogger_LogsRedirectedToFile(t *testing.T) { assert.Contains(t, contentStr, "error message") } +func TestLogger_NestedStructParameterCorrectLogging(t *testing.T) { + type Address struct { + Street string + City string + Zip int + } + + type Contact struct { + Email string + Phone string + ad Address + } + + type User struct { + ID int + Name string + Address Address // Named field + Contact // Embedded (Promoted) field + } + + user := User{ + ID: 1, + Name: "Alice", + Address: Address{ + Street: "123 Go Lane", + City: "Tech City", + Zip: 90210, + }, + Contact: Contact{ + Email: "alice@example.com", + Phone: "555-0199", + ad: Address{ + Street: "123 Go Lane", + City: "Tech City", + Zip: 90210, + }, + }, + } + + logger := NewLogger().SetEncoder(shared.JsonEncoderType) + outMsg := test.CaptureOutput(func() { logger.Debug(user) }) + assert.Contains(t, + outMsg, + "{1 Alice {123 Go Lane Tech City 90210} {alice@example.com 555-0199 {123 Go Lane Tech City 90210}}}", + ) +} + func createMockOutFile(fileName string) *os.File { file, err := os.OpenFile(fmt.Sprintf("./%s", fileName), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { diff --git a/makefile b/makefile index 854cc38..ea1ac04 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,4 @@ -.PHONY: test test-coverage test-benchmark +.PHONY: test test-coverage test-benchmark encoders-benchmark loggers-comparison-benchmark ARCH=amd64 @@ -10,6 +10,10 @@ test: test-coverage: CGO_ENABLED=1 GOARCH=${ARCH} go test -cover -coverprofile=coverage.out ./... -race && go tool cover -html=coverage.out +# Run encoders benchmark tests +encoders-benchmark: + CGO_ENABLED=1 GOARCH=${ARCH} go test ./test/encoders_benchmark_test.go ./test/functions.go -bench=. -benchmem -benchtime=5s -cpu=8 + # Run benchmark tests -test-benchmark: - CGO_ENABLED=1 GOARCH=${ARCH} go test ./test/benchmark_test.go -bench=. -benchmem -benchtime=8s -cpu=8 | grep /op +loggers-comparison-benchmark: + CGO_ENABLED=1 GOARCH=${ARCH} go test ./test/loggers_comparison_test.go ./test/functions.go -bench=. -benchmem -benchtime=5s -cpu=8 \ No newline at end of file diff --git a/test/benchmark_test.go b/test/encoders_benchmark_test.go similarity index 71% rename from test/benchmark_test.go rename to test/encoders_benchmark_test.go index 6ce8a30..da07d2e 100644 --- a/test/benchmark_test.go +++ b/test/encoders_benchmark_test.go @@ -1,7 +1,6 @@ package test import ( - "fmt" "testing" "github.com/pho3b/tiny-logger/logs" @@ -12,13 +11,11 @@ func BenchmarkDefaultEncoderAllPropertiesDisabled(b *testing.B) { b.ReportAllocs() logger := logs.NewLogger(). - SetEncoder(shared.DefaultEncoderType).ShowLogLevel(false) + SetEncoder(shared.DefaultEncoderType).ShowLogLevel(false).SetLogFile(initDevNullFile()) for i := 0; i < b.N; i++ { - logger.Debug("DEFAULT encoder", "all-properties-enabled", false, "id", 2) + logger.Debug("DEFAULT encoder", "all-properties-enabled", false, "id", i) } - - fmt.Print("Default_Encoder_All_Properties_Disabled:") } func BenchmarkDefaultEncoderAllPropertiesEnabled(b *testing.B) { @@ -28,26 +25,23 @@ func BenchmarkDefaultEncoderAllPropertiesEnabled(b *testing.B) { SetEncoder(shared.DefaultEncoderType). ShowLogLevel(true). AddDateTime(true). - EnableColors(true) + EnableColors(true). + SetLogFile(initDevNullFile()) for i := 0; i < b.N; i++ { - logger.Debug("DEFAULT encoder", "all-properties-enabled", true, "id", 2) + logger.Debug("DEFAULT encoder", "all-properties-enabled", true, "id", i) } - - fmt.Print("Default_Encoder_All_Properties_Enabled: ") } func BenchmarkJsonEncoderAllPropertiesDisabled(b *testing.B) { b.ReportAllocs() logger := logs.NewLogger(). - SetEncoder(shared.JsonEncoderType).ShowLogLevel(false) + SetEncoder(shared.JsonEncoderType).ShowLogLevel(false).SetLogFile(initDevNullFile()) for i := 0; i < b.N; i++ { - logger.Debug("JSON encoder", "all-properties-enabled", false, "id", 2) + logger.Debug("JSON encoder", "all-properties-enabled", false, "id", i) } - - fmt.Print("Json_Encoder_All_Properties_Disabled: ") } func BenchmarkJsonEncoderAllPropertiesEnabled(b *testing.B) { @@ -56,26 +50,23 @@ func BenchmarkJsonEncoderAllPropertiesEnabled(b *testing.B) { logger := logs.NewLogger(). SetEncoder(shared.JsonEncoderType). ShowLogLevel(true). - AddDateTime(true) + AddDateTime(true). + SetLogFile(initDevNullFile()) for i := 0; i < b.N; i++ { - logger.Debug("JSON encoder", "all-properties-enabled", true, "id", 2) + logger.Debug("JSON encoder", "all-properties-enabled", true, "id", i) } - - fmt.Print("Json_Encoder_All_Properties_Enabled: ") } func BenchmarkYamlEncoderAllPropertiesDisabled(b *testing.B) { b.ReportAllocs() logger := logs.NewLogger(). - SetEncoder(shared.YamlEncoderType).ShowLogLevel(false) + SetEncoder(shared.YamlEncoderType).ShowLogLevel(false).SetLogFile(initDevNullFile()) for i := 0; i < b.N; i++ { - logger.Debug("YAML encoder", "all-properties-enabled", false, "id", 2) + logger.Debug("YAML encoder", "all-properties-enabled", false, "id", i) } - - fmt.Print("Yaml_Encoder_All_Properties_Disabled: ") } func BenchmarkYamlEncoderAllPropertiesEnabled(b *testing.B) { @@ -84,11 +75,10 @@ func BenchmarkYamlEncoderAllPropertiesEnabled(b *testing.B) { logger := logs.NewLogger(). SetEncoder(shared.YamlEncoderType). ShowLogLevel(true). - AddDateTime(true) + AddDateTime(true). + SetLogFile(initDevNullFile()) for i := 0; i < b.N; i++ { - logger.Debug("YAML encoder", "all-properties-enabled", true, "id", 2) + logger.Debug("YAML encoder", "all-properties-enabled", true, "id", i) } - - fmt.Print("Yaml_Encoder_All_Properties_Enabled: ") } diff --git a/test/functions.go b/test/functions.go index 41969c4..679789b 100644 --- a/test/functions.go +++ b/test/functions.go @@ -38,3 +38,14 @@ func CaptureErrorOutput(f func()) string { _, _ = buf.ReadFrom(r) return buf.String() } + +func initDevNullFile() *os.File { + var err error + // Open /dev/null (or NUL on Windows) to discard output for tiny-logger + devNullFile, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0) + if err != nil { + panic(err) + } + + return devNullFile +} diff --git a/test/comparison_test.go b/test/loggers_comparison_test.go similarity index 76% rename from test/comparison_test.go rename to test/loggers_comparison_test.go index 36c771f..d65a251 100644 --- a/test/comparison_test.go +++ b/test/loggers_comparison_test.go @@ -1,35 +1,22 @@ package test import ( - "io" - "os" "testing" "github.com/pho3b/tiny-logger/logs" + "github.com/pho3b/tiny-logger/shared" "github.com/rs/zerolog" "github.com/sirupsen/logrus" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) -// devNullFile is used for tiny-logger which requires an *os.File -var devNullFile *os.File - -func init() { - var err error - // Open /dev/null (or NUL on Windows) to discard output for tiny-logger - devNullFile, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) - if err != nil { - panic(err) - } -} - // 1. Tiny Logger (Project) func BenchmarkTinyLogger(b *testing.B) { // Initialize tiny-logger - logger := logs.NewLogger() + logger := logs.NewLogger().SetEncoder(shared.JsonEncoderType).AddDateTime(true) // Redirect to /dev/null to measure logger overhead only, not I/O - logger.SetLogFile(devNullFile) + logger.SetLogFile(initDevNullFile()) b.ReportAllocs() b.ResetTimer() @@ -43,7 +30,7 @@ func BenchmarkTinyLogger(b *testing.B) { // 2. Logrus (Standard-like popular logger) func BenchmarkLogrus(b *testing.B) { logger := logrus.New() - logger.Out = io.Discard + logger.Out = initDevNullFile() logger.SetFormatter(&logrus.TextFormatter{DisableColors: true}) b.ReportAllocs() @@ -59,7 +46,7 @@ func BenchmarkLogrus(b *testing.B) { // 3. Zerolog (Zero Allocation Logger) func BenchmarkZerolog(b *testing.B) { - logger := zerolog.New(io.Discard) + logger := zerolog.New(initDevNullFile()) b.ReportAllocs() b.ResetTimer() @@ -68,7 +55,7 @@ func BenchmarkZerolog(b *testing.B) { logger.Info(). Any("iteration", i). Any("active", true). - Any("Benchmark message", "") + Msg("Benchmark message") } } @@ -78,7 +65,7 @@ func BenchmarkZap(b *testing.B) { encoderConfig := zap.NewProductionEncoderConfig() core := zapcore.NewCore( zapcore.NewJSONEncoder(encoderConfig), - zapcore.AddSync(io.Discard), + zapcore.AddSync(initDevNullFile()), zap.InfoLevel, ) logger := zap.New(core) From 4d84cbfe16e277a06246acffc69c4fd9ef0647f2 Mon Sep 17 00:00:00 2001 From: arinaldi Date: Wed, 4 Feb 2026 13:12:46 +0100 Subject: [PATCH 3/7] docs: updates the README.md file --- README.md | 65 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index a238791..86a892e 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,27 @@ # Tiny Logger -A fast, lightweight, zero-dependency logging solution for Go applications that prioritizes performance and -simplicity. -Compatible with Go version 1.18.x and above +A fast, lightweight, zero-dependency logging solution for Go applications that prioritizes performance and simplicity. + +The library is extremely optimized to log loosely typed data (interfaces), so you won't have to specify the correct data type before logging them. +I know you can reach more raw speed using other Go logging libray and specifying the type of the data to log beforehead, but when I created the tiny-logger i wanted to make it as fast as possible while keeping it very compact and simple to use. +In my experience there are cases where the only thing you know is raw speed yes, but majority of projects benefits from having something that is really small, easy to use and also perform very fast. -"TinyLogger is the fastest way to log loosely typed data (interfaces)" +Compatible with Go version 1.18.x and above -## ✅ Key Features +## Key Features -- **Lightweight**: No external dependencies mean faster builds and smaller binaries
- The dependencies that you see in the `go.mod` file are not included in the final binary since they are only used in `_test` files. -- **Simplicity**: Clean API design with a minimal learning curve. You'll set it up in seconds. +- **Lightweight**: The library has no dependencies, the code you see is all that runs. + NOTE: The only dpes you'll see in the `go.mod` file are not included in the final binary since they are only used in `_test` files. +- **Simplicity**: I made the API to have a minimal learning curve. You'll set it up in seconds. - **Performance**: The library is benchmarked to be very fast. It implements custom JSON and YAML marshaling specifically optimized for logging - - Up to 1.4x faster JSON marshaling than `encoding/json` - - Up to 5x faster YAML marshaling than `gopkg.in/yaml.v3` + - Up to 1.4x faster JSON marshaling than `encoding/json` + - Up to 5x faster YAML marshaling than `gopkg.in/yaml.v3` - **Color Support**: Built-in ANSI color support for terminal output - **Thread-Safe**: Concurrent-safe logging with atomic operations - **Time-Optimized**: Efficient date/time print built-int logic with minimal allocations -- **Reliability**: Thoroughly tested with high test coverage -- **Maintainability**: A small, focused codebase makes it easy to understand and modify at will -## 🎯 Use Examples +## Use Examples ````go /******************** Basic Logging methods usage ********************/ @@ -80,30 +80,33 @@ terminal graphical visualization time. | **YAML Encoder** | All Properties OFF | 535.3 | 80 | 1 | | | All Properties ON | 557.1 | 104 | 1 | +## Benchmark Results -## 🤝 Contributing +**System Environment:** -Contributions are welcome, Here's how you can help: +- **OS:** Linux +- **Arch:** AMD64 +- **CPU:** 12th Gen Intel(R) Core(TM) i9-12900K -1. Fork the repository -2. Clone your fork: -3. Create a new branch: +| Logger | Iterations | Time / Op | Bytes / Op | Allocs / Op | +| :--- | :--- | :--- | :--- | :--- | +| **TinyLogger** | 17,625,723 | **339.9 ns** | **88 B** | **2** | +| Zerolog | 12,983,034 | 460.2 ns | 232 B | 5 | +| Zap | 10,391,967 | 578.3 ns | 136 B | 2 | +| Logrus | 3,607,248 | 1692 ns | 1241 B | 21 | -```bash - git checkout -b feat/your-feature-name - ``` +## Contributing -- **Code Style** - - Follow standard Go formatting (`go fmt`) - - Use meaningful variable names - - Add comments for non-obvious code sections - - Write tests for new functionality +Contributes to this project are really welcome, here's how you can do it -- **Testing** - - Run tests: `make test` - - Run benchmarks: `make test-benchmark` - - Ensure test coverage remains high; it can be checked using `make test-coverage` + 1. Fork the repository + 2. Clone your fork + 3. Create a new branch + ```bash git checkout -b your-feature-name``` + 4. Testing + Take a look at the [makefile](./makefile). + You can use the contained commands to run `unit-test`, check the `testing coverage` and monitor the `benchamrks` of the library. ## 📝 License -MIT License—see [LICENSE](https://mit-license.org/) file for details +MIT License—see [LICENSE](https://mit-license.org/) file for details From 9fffa121979124634205f13d2af51fd00d0d3a71 Mon Sep 17 00:00:00 2001 From: arinaldi Date: Wed, 4 Feb 2026 19:51:05 +0100 Subject: [PATCH 4/7] docs: README update --- README.md | 78 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 86a892e..d0d2357 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ A fast, lightweight, zero-dependency logging solution for Go applications that prioritizes performance and simplicity. -The library is extremely optimized to log loosely typed data (interfaces), so you won't have to specify the correct data type before logging them. -I know you can reach more raw speed using other Go logging libray and specifying the type of the data to log beforehead, but when I created the tiny-logger i wanted to make it as fast as possible while keeping it very compact and simple to use. -In my experience there are cases where the only thing you know is raw speed yes, but majority of projects benefits from having something that is really small, easy to use and also perform very fast. +The library is extremely optimized for loggging loosely-typed data (interfaces), so you don't need to specify concrete types before logging. +I know you can get higher raw speed using other Go logging libraries and specifying the type of the data to log beforehead, but when I created the tiny-logger I wanted to make it as fast as possible while keeping it compact and simple to use. +I think that a lot of projects can benefit from having a logging library that is compact, fast, easy to use, and simple to modify at will. Since the codebase is so small, it won't take days for you to understand it. -Compatible with Go version 1.18.x and above +The project is compatible with **Go version 1.18.x** and above ## Key Features @@ -66,47 +66,59 @@ logger.SetDateTimeFormat(shared.UnixTimestamp) logger.Debug("This is my Debug log", "Test arg") // stdout: 1690982143.000000 This is my Debug log Test arg ```` -## 📊 Benchmark Results - -This is the result of running the `./test/benchmark_test.go` benchmark on my machine, (ns/op)times do not include the -terminal graphical visualization time. - -| Encoder | Configuration | ns/op | B/op | allocs/op | -|---------------------|--------------------|-------|------|-----------| -| **Default Encoder** | All Properties OFF | 490.3 | 80 | 1 | -| | All Properties ON | 511.2 | 104 | 1 | -| **JSON Encoder** | All Properties OFF | 513.3 | 80 | 1 | -| | All Properties ON | 536.5 | 104 | 1 | -| **YAML Encoder** | All Properties OFF | 535.3 | 80 | 1 | -| | All Properties ON | 557.1 | 104 | 1 | - ## Benchmark Results -**System Environment:** - -- **OS:** Linux -- **Arch:** AMD64 -- **CPU:** 12th Gen Intel(R) Core(TM) i9-12900K - -| Logger | Iterations | Time / Op | Bytes / Op | Allocs / Op | -| :--- | :--- | :--- | :--- | :--- | -| **TinyLogger** | 17,625,723 | **339.9 ns** | **88 B** | **2** | -| Zerolog | 12,983,034 | 460.2 ns | 232 B | 5 | -| Zap | 10,391,967 | 578.3 ns | 136 B | 2 | -| Logrus | 3,607,248 | 1692 ns | 1241 B | 21 | +1. Benchmarks of the **loggers-comparison-benchmark** command contained in the [makefile](./makefile) + + - **OS:** Linux + - **Arch:** AMD64 + - **CPU:** 12th Gen Intel(R) Core(TM) i9-12900K + + | Logger | Iterations | Time / Op | Bytes / Op | Allocs / Op | + | :--- | :--- | :--- | :--- | :--- | + | **TinyLogger** | 17,625,723 | **339.9 ns** | **88 B** | **2** | + | Zerolog | 12,983,034 | 460.2 ns | 232 B | 5 | + | Zap | 10,391,967 | 578.3 ns | 136 B | 2 | + | Logrus | 3,607,248 | 1692 ns | 1241 B | 21 | + + - **OS:** Darwin (macOS) + - **Arch:** AMD64 + - **CPU:** VirtualApple @ 2.50GHz + + | Logger | Iterations | Time / Op | Bytes / Op | Allocs / Op | + | :--- | :--- | :--- | :--- | :--- | + | **TinyLogger** | 6,091,185 | **972.9 ns** | **88 B** | **2** | + | Zerolog | 4,922,115 | 1220 ns | 232 B | 5 | + | Zap | 3,938,301 | 1517 ns | 136 B | 2 | + | Logrus | 1,814,809 | 3291 ns | 1241 B | 21 | + +2. Benchmarks of the **encoders-benchmark** command contained in the [makefile](./makefile) + + - **OS:** Linux + - **Arch:** AMD64 + - **CPU:** 12th Gen Intel(R) Core(TM) i9-12900K + + | Logger | Iterations | Time / Op | Bytes / Op | Allocs / Op | + | :--- | :--- | :--- | :--- | :--- | + | DefaultEncoder DisabledProperties | 18336217 | 298.7 ns | 88 B | 2 | + | DefaultEncoder EnabledProperties | 18336217 | 334.3 ns | 88 B | 2 | + | JsonEncoder DisabledProperties | 17974824 | 316.0 ns | 88 B | 2 | + | JsonEncoder EnabledProperties | 17488896 | 344.2 ns | 88 B | 2 | + | YamlEncoder DisabledProperties | 17625220 | 342.8 ns | 88 B | 2 | + | YamlEncoder EnabledProperties | 16005187 | 373.3 ns | 88 B | 2 | ## Contributing -Contributes to this project are really welcome, here's how you can do it +Contributions to this project are really welcome, here's how you can do it: 1. Fork the repository 2. Clone your fork 3. Create a new branch ```bash git checkout -b your-feature-name``` - 4. Testing + 4. Local Tests Take a look at the [makefile](./makefile). You can use the contained commands to run `unit-test`, check the `testing coverage` and monitor the `benchamrks` of the library. -## 📝 License +## License MIT License—see [LICENSE](https://mit-license.org/) file for details From c4124c4047a3b2c31d49bb8401f462e3d4efc3f1 Mon Sep 17 00:00:00 2001 From: arinaldi Date: Wed, 4 Feb 2026 20:07:50 +0100 Subject: [PATCH 5/7] docs: README update --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d0d2357..93fad1e 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,26 @@ A fast, lightweight, zero-dependency logging solution for Go applications that prioritizes performance and simplicity. -The library is extremely optimized for loggging loosely-typed data (interfaces), so you don't need to specify concrete types before logging. -I know you can get higher raw speed using other Go logging libraries and specifying the type of the data to log beforehead, but when I created the tiny-logger I wanted to make it as fast as possible while keeping it compact and simple to use. -I think that a lot of projects can benefit from having a logging library that is compact, fast, easy to use, and simple to modify at will. Since the codebase is so small, it won't take days for you to understand it. +The library is extremely optimized to log loosely-typed data (interfaces), so you won't have to specify concrete types before logging. -The project is compatible with **Go version 1.18.x** and above +I know that higher raw speed can be reached using other Go logging solutions, but when I created tiny-logger, I wanted to build something as fast as possible without compromising on simplicity of use. +There are many projects that can benefit from having a logging library that is compact, fast, easy to use, and simple to modify. +Since the codebase is so small, it won't take long for you to understand it. + +The project is compatible with **Go version 1.18.x** and above. ## Key Features - **Lightweight**: The library has no dependencies, the code you see is all that runs. - NOTE: The only dpes you'll see in the `go.mod` file are not included in the final binary since they are only used in `_test` files. -- **Simplicity**: I made the API to have a minimal learning curve. You'll set it up in seconds. + NOTE: The only dependencies you'll see in the `go.mod` file are not included in the final binary since they are only used in `_test` files. +- **Simplicity**: I designed the API to have a minimal learning curve. You'll set it up in seconds. - **Performance**: The library is benchmarked to be very fast. It implements custom JSON and YAML marshaling specifically optimized for logging - Up to 1.4x faster JSON marshaling than `encoding/json` - Up to 5x faster YAML marshaling than `gopkg.in/yaml.v3` - **Color Support**: Built-in ANSI color support for terminal output - **Thread-Safe**: Concurrent-safe logging with atomic operations -- **Time-Optimized**: Efficient date/time print built-int logic with minimal allocations +- **Time-Optimized**: Efficient date/time formatting with minimal allocations ## Use Examples From e9f8e8166bb93faf6974e9db5f1fa1defe322607 Mon Sep 17 00:00:00 2001 From: arinaldi Date: Thu, 5 Feb 2026 00:19:14 +0100 Subject: [PATCH 6/7] docs: README update --- makefile => Makefile | 0 README.md | 28 +++++++++++++++++----------- test/mocks.go | 1 + 3 files changed, 18 insertions(+), 11 deletions(-) rename makefile => Makefile (100%) diff --git a/makefile b/Makefile similarity index 100% rename from makefile rename to Makefile diff --git a/README.md b/README.md index 93fad1e..728798e 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ A fast, lightweight, zero-dependency logging solution for Go applications that prioritizes performance and simplicity. -The library is extremely optimized to log loosely-typed data (interfaces), so you won't have to specify concrete types before logging. +This library is extremely optimized to log loosely-typed data (interfaces), so you won't have to specify concrete types before logging. I know that higher raw speed can be reached using other Go logging solutions, but when I created tiny-logger, I wanted to build something as fast as possible without compromising on simplicity of use. There are many projects that can benefit from having a logging library that is compact, fast, easy to use, and simple to modify. -Since the codebase is so small, it won't take long for you to understand it. +Since the codebase is so small, it won't take long for you to understand it and modify it at will. The project is compatible with **Go version 1.18.x** and above. @@ -19,9 +19,10 @@ The project is compatible with **Go version 1.18.x** and above. specifically optimized for logging - Up to 1.4x faster JSON marshaling than `encoding/json` - Up to 5x faster YAML marshaling than `gopkg.in/yaml.v3` -- **Color Support**: Built-in ANSI color support for terminal output -- **Thread-Safe**: Concurrent-safe logging with atomic operations -- **Time-Optimized**: Efficient date/time formatting with minimal allocations +- **Color Support**: Built-in ANSI color support for terminal output. +- **Thread-Safe**: Concurrent-safe logging with atomic operations. +- **Time-Optimized**: Efficient date/time formatting with minimal allocations. +- **Memory-Efficient**: Heap allocations and log sizes are kept to a minimum to avoid triggering the garbage collector. ## Use Examples @@ -68,9 +69,14 @@ logger.SetDateTimeFormat(shared.UnixTimestamp) logger.Debug("This is my Debug log", "Test arg") // stdout: 1690982143.000000 This is my Debug log Test arg ```` -## Benchmark Results +## Benchmarks + +1. Benchmarks of the **loggers-comparison-benchmark** in [Makefile](./Makefile) + + **NOTE:** These benchmarks intentionally log loosely-typed data across all four compared libraries. + As mentioned above, libraries like **zerolog** can achieve higher performance when logging strictly-defined data types. + However, since high-speed typed logging was not the primary goal of **tiny-logger**, I wanted to evaluate how it performs against industry-standard libraries when handling arbitrary data types. -1. Benchmarks of the **loggers-comparison-benchmark** command contained in the [makefile](./makefile) - **OS:** Linux - **Arch:** AMD64 @@ -94,7 +100,7 @@ logger.Debug("This is my Debug log", "Test arg") // stdout: 1690982143.000000 Th | Zap | 3,938,301 | 1517 ns | 136 B | 2 | | Logrus | 1,814,809 | 3291 ns | 1241 B | 21 | -2. Benchmarks of the **encoders-benchmark** command contained in the [makefile](./makefile) +2. Benchmarks of the **encoders-benchmark** command contained in the [Makefile](./Makefile) - **OS:** Linux - **Arch:** AMD64 @@ -111,15 +117,15 @@ logger.Debug("This is my Debug log", "Test arg") // stdout: 1690982143.000000 Th ## Contributing -Contributions to this project are really welcome, here's how you can do it: +Contributions to this project are very welcome, here's how you can do it: 1. Fork the repository 2. Clone your fork 3. Create a new branch ```bash git checkout -b your-feature-name``` 4. Local Tests - Take a look at the [makefile](./makefile). - You can use the contained commands to run `unit-test`, check the `testing coverage` and monitor the `benchamrks` of the library. + Take a look at the [Makefile](./Makefile). + You can use the commands provided to run `test`, check the `test-coverage` and monitor the library's `benchmarks`. ## License diff --git a/test/mocks.go b/test/mocks.go index ea18b9c..72fc06c 100644 --- a/test/mocks.go +++ b/test/mocks.go @@ -25,6 +25,7 @@ func (m *LoggerConfigMock) GetLogLvlIntValue() int8 { func (m *LoggerConfigMock) GetDateTimeEnabled() (dateEnabled bool, timeEnabled bool) { return m.DateEnabled, m.TimeEnabled } + func (m *LoggerConfigMock) GetColorsEnabled() bool { return m.ColorsEnabled } From 20c63dd6631d22242c2d43aa8f4a47ef399afce4 Mon Sep 17 00:00:00 2001 From: arinaldi Date: Thu, 5 Feb 2026 00:28:10 +0100 Subject: [PATCH 7/7] chore: makefile update --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ea1ac04..f85a737 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,6 @@ test-coverage: encoders-benchmark: CGO_ENABLED=1 GOARCH=${ARCH} go test ./test/encoders_benchmark_test.go ./test/functions.go -bench=. -benchmem -benchtime=5s -cpu=8 -# Run benchmark tests +# Run commercial loggers benchmark comparison tests loggers-comparison-benchmark: CGO_ENABLED=1 GOARCH=${ARCH} go test ./test/loggers_comparison_test.go ./test/functions.go -bench=. -benchmem -benchtime=5s -cpu=8 \ No newline at end of file