Skip to content

Commit

Permalink
Merge pull request #10 from iamd3vil/ref/var-args
Browse files Browse the repository at this point in the history
feat: Use variadic arguments for fields instead of passing a map.
  • Loading branch information
mr-karan authored Jul 7, 2022
2 parents e21fed0 + a391cda commit 03e3acc
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 170 deletions.
57 changes: 24 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,44 @@
[![Go Report Card](https://goreportcard.com/badge/zerodha/logf)](https://goreportcard.com/report/zerodha/logf)
[![GitHub Actions](https://github.com/zerodha/logf/actions/workflows/build.yml/badge.svg)](https://github.com/zerodha/logf/actions/workflows/build.yml)


logf is a **high performance** logging library for Go applications with a **minimal** API overhead. It emits **structured logs** ([`logfmt`](https://brandur.org/logfmt) style) in human readable and machine friendly way. `logf` aims to be customisable without providing an overwhelming amount of things to configure.
logf is a **high performance**, **zero alloc** logging library for Go applications with a **minimal** API overhead. It emits **structured logs** ([`logfmt`](https://brandur.org/logfmt) style) in human readable and machine friendly way. `logf` aims to be customisable without providing an overwhelming amount of things to configure.

## Example

```go
package main

import (
"errors"
"os"
"time"

"github.com/zerodha/logf"
)

func main() {
logger := logf.New()
logger := logf.New(os.Stderr)

// Basic log.
logger.Info("starting app")

// Enable colored output.
logger.SetColorOutput(true)

// Change verbosity on the fly.
logger.SetLevel(logf.DebugLevel)
logger.Debug("meant for debugging app")

// Add extra keys to the log.
logger.WithFields(logf.Fields{
"component": "api",
"user": "karan",
}).Info("logging with some extra metadata")
logger.Info("logging with some extra metadata", "component", "api", "user", "karan")

// Log with error key.
logger.WithError(errors.New("this is a dummy error")).Error("error fetching details")
logger.Error("error fetching details", "error", "this is a dummy error")

// Enable `caller` field in the log and specify the number of frames to skip to get the caller.
// Enable `caller` field in the log and specify the number of frames to skip to get the caller.
logger.SetCallerFrame(true, 3)
// Change the default timestamp format.
logger.SetTimestampFormat(time.RFC3339Nano)

// Create a logger and add fields which will be logged in every line.
requestLogger := logger.WithFields(logf.Fields{"request_id": "3MG91VKP", "ip": "1.1.1.1", "method": "GET"})
requestLogger.Info("request success")
requestLogger.Warn("this isn't supposed to happen")

// Log the error and set exit code as 1.
logger.Fatal("goodbye world")
}
Expand All @@ -57,13 +52,10 @@ func main() {
### Text Output

```bash
timestamp=2022-06-26T11:56:46+05:30 level=info message=starting app caller=/home/karan/Code/Personal/logf/examples/main.go:13
timestamp=2022-06-26T11:56:46+05:30 level=debug message=meant for debugging app caller=/home/karan/Code/Personal/logf/examples/main.go:17 level=debug message=meant for debugging app timestamp=2022-06-26T11:56:46+05:30 caller=/home/karan/Code/Personal/logf/examples/main.go:17
timestamp=2022-06-26T11:56:46+05:30 level=info message=logging with some extra metadata component=api user=karan caller=/home/karan/Code/Personal/logf/examples/main.go:23
timestamp=2022-06-26T11:56:46+05:30 level=error message=error fetching details error=this is a dummy error caller=/home/karan/Code/Personal/logf/examples/main.go:26
timestamp=2022-06-26T11:56:46.412189111+05:30 level=info message=request success ip=1.1.1.1 method=GET request_id=3MG91VKP
timestamp=2022-06-26T11:56:46.412204619+05:30 level=warn message=this isn't supposed to happen ip=1.1.1.1 level=warn message=this isn't supposed to happen method=GET request_id=3MG91VKP timestamp=2022-06-26T11:56:46.412204619+05:30
timestamp=2022-06-26T11:56:46.412218628+05:30 level=fatal message=goodbye world ip=1.1.1.1 level=fatal message=goodbye world method=GET request_id=3MG91VKP timestamp=2022-06-26T11:56:46.412218628+05:30
timestamp=2022-07-07T12:09:10.221+05:30 level=info message="starting app"
timestamp=2022-07-07T12:09:10.221+05:30 level=info message="logging with some extra metadata" component=api user=karan
timestamp=2022-07-07T12:09:10.221+05:30 level=error message="error fetching details" error="this is a dummy error"
timestamp=2022-07-07T12:09:10.221+05:30 level=fatal message="goodbye world"
```

### Console Output
Expand All @@ -88,22 +80,21 @@ You can run benchmarks with `make bench`.
### No Colors (Default)

```
BenchmarkNoField-8 7140398 175.3 ns/op 0 B/op 0 allocs/op
BenchmarkOneField-8 5061104 235.0 ns/op 336 B/op 2 allocs/op
BenchmarkThreeFields-8 4499778 265.4 ns/op 336 B/op 2 allocs/op
BenchmarkErrorField-8 4768662 252.4 ns/op 368 B/op 4 allocs/op
BenchmarkHugePayload-8 1784425 670.0 ns/op 1031 B/op 5 allocs/op
BenchmarkThreeFields_WithCaller-8 2144788 564.7 ns/op 704 B/op 8 allocs/op
BenchmarkNoField-8 7219110 173.0 ns/op 0 B/op 0 allocs/op
BenchmarkOneField-8 6421900 176.3 ns/op 0 B/op 0 allocs/op
BenchmarkThreeFields-8 5485582 221.3 ns/op 0 B/op 0 allocs/op
BenchmarkHugePayload-8 975226 1659 ns/op 0 B/op 0 allocs/op
BenchmarkThreeFields_WithCaller-8 1390599 906.4 ns/op 0 B/op 0 allocs/op
BenchmarkNoField_WithColor-8 1580092 644.2 ns/op 0 B/op 0 allocs/op
```

### With Colors

```
BenchmarkNoField_WithColor-8 6236416 197.8 ns/op 0 B/op 0 allocs/op
BenchmarkOneField_WithColor-8 4316852 296.9 ns/op 336 B/op 2 allocs/op
BenchmarkThreeFields_WithColor-8 2447352 517.2 ns/op 336 B/op 2 allocs/op
BenchmarkErrorField_WithColor-8 2673672 454.1 ns/op 368 B/op 4 allocs/op
BenchmarkHugePayload_WithColor-8 1000000 1252 ns/op 1032 B/op 5 allocs/op
BenchmarkNoField_WithColor-8 1580092 644.2 ns/op 0 B/op 0 allocs/op
BenchmarkOneField_WithColor-8 1810801 689.9 ns/op 0 B/op 0 allocs/op
BenchmarkThreeFields_WithColor-8 1592907 740.8 ns/op 0 B/op 0 allocs/op
BenchmarkHugePayload_WithColor-8 991813 1224 ns/op 0 B/op 0 allocs/op
```

For a comparison with existing popular libs, visit [uber-go/zap#performance](https://github.com/uber-go/zap#performance).
Expand Down
80 changes: 37 additions & 43 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func BenchmarkOneField(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{"stack": "testing"}).Info("hello world")
logger.Info("hello world", "stack", "testing")
}
})
}
Expand All @@ -37,11 +37,9 @@ func BenchmarkThreeFields(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{
"component": "api",
"method": "GET",
"bytes": 1 << 18,
}).Info("request completed")
logger.Info("request completed",
"component", "api", "method", "GET", "bytes", 1<<18,
)
}
})
}
Expand All @@ -55,7 +53,7 @@ func BenchmarkErrorField(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithError(fakeErr).Error("request failed")
logger.Error("request fields", "error", fakeErr)
}
})
}
Expand All @@ -67,18 +65,18 @@ func BenchmarkHugePayload(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{
"id": 11,
"title": "perfume Oil",
"description": "Mega Discount, Impression of A...",
"price": 13,
"discountPercentage": 8.4,
"rating": 4.26,
"stock": 65,
"brand": "Impression of Acqua Di Gio",
"category": "fragrances",
"thumbnail": "https://dummyjson.com/image/i/products/11/thumbnail.jpg",
}).Info("fetched details")
logger.Info("fetched details",
"id", 11,
"title", "perfume Oil",
"description", "Mega Discount, Impression of A...",
"price", 13,
"discountPercentage", 8.4,
"rating", 4.26,
"stock", 65,
"brand", "Impression of Acqua Di Gio",
"category", "fragrances",
"thumbnail", "https://dummyjson.com/image/i/products/11/thumbnail.jpg",
)
}
})
}
Expand All @@ -91,11 +89,9 @@ func BenchmarkThreeFields_WithCaller(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{
"component": "api",
"method": "GET",
"bytes": 1 << 18,
}).Info("request completed")
logger.Info("request completed",
"component", "api", "method", "GET", "bytes", 1<<18,
)
}
})
}
Expand All @@ -120,7 +116,7 @@ func BenchmarkOneField_WithColor(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{"stack": "testing"}).Info("hello world")
logger.Info("hello world", "stack", "testing")
}
})
}
Expand All @@ -133,11 +129,9 @@ func BenchmarkThreeFields_WithColor(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{
"component": "api",
"method": "GET",
"bytes": 1 << 18,
}).Info("request completed")
logger.Info("request completed",
"component", "api", "method", "GET", "bytes", 1<<18,
)
}
})
}
Expand All @@ -152,7 +146,7 @@ func BenchmarkErrorField_WithColor(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithError(fakeErr).Error("request failed")
logger.Error("request fields", "error", fakeErr)
}
})
}
Expand All @@ -165,18 +159,18 @@ func BenchmarkHugePayload_WithColor(b *testing.B) {

b.RunParallel(func(p *testing.PB) {
for p.Next() {
logger.WithFields(logf.Fields{
"id": 11,
"title": "perfume Oil",
"description": "Mega Discount, Impression of A...",
"price": 13,
"discountPercentage": 8.4,
"rating": 4.26,
"stock": 65,
"brand": "Impression of Acqua Di Gio",
"category": "fragrances",
"thumbnail": "https://dummyjson.com/image/i/products/11/thumbnail.jpg",
}).Info("fetched details")
logger.Info("fetched details",
"id", 11,
"title", "perfume Oil",
"description", "Mega Discount, Impression of A...",
"price", 13,
"discountPercentage", 8.4,
"rating", 4.26,
"stock", 65,
"brand", "Impression of Acqua Di Gio",
"category", "fragrances",
"thumbnail", "https://dummyjson.com/image/i/products/11/thumbnail.jpg",
)
}
})
}
13 changes: 2 additions & 11 deletions examples/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"errors"
"os"
"time"

Expand All @@ -22,24 +21,16 @@ func main() {
logger.Debug("meant for debugging app")

// Add extra keys to the log.
logger.WithFields(logf.Fields{
"component": "api",
"user": "karan",
}).Info("logging with some extra metadata")
logger.Info("logging with some extra metadata", "component", "api", "user", "karan")

// Log with error key.
logger.WithError(errors.New("this is a dummy error")).Error("error fetching details")
logger.Error("error fetching details", "error", "this is a dummy error")

// Enable `caller` field in the log and specify the number of frames to skip to get the caller.
logger.SetCallerFrame(true, 3)
// Change the default timestamp format.
logger.SetTimestampFormat(time.RFC3339Nano)

// Create a logger and add fields which will be logged in every line.
requestLogger := logger.WithFields(logf.Fields{"request_id": "3MG91VKP", "ip": "1.1.1.1", "method": "method=GET"})
requestLogger.Info("request success")
requestLogger.Warn("this isn't supposed to happen")

// Log the error and set exit code as 1.
logger.Fatal("goodbye world")
}
35 changes: 0 additions & 35 deletions field_logger.go

This file was deleted.

Loading

0 comments on commit 03e3acc

Please sign in to comment.