https://github.com/bool64/ctxd
Contextualized structured logging and error handling for Go
https://github.com/bool64/ctxd
go structured-logging
Last synced: about 2 months ago
JSON representation
Contextualized structured logging and error handling for Go
- Host: GitHub
- URL: https://github.com/bool64/ctxd
- Owner: bool64
- License: mit
- Created: 2020-11-27T21:57:39.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-04-25T10:01:39.000Z (almost 2 years ago)
- Last Synced: 2024-06-20T00:29:10.098Z (almost 2 years ago)
- Topics: go, structured-logging
- Language: Go
- Homepage: https://pkg.go.dev/github.com/bool64/ctxd
- Size: 45.9 KB
- Stars: 8
- Watchers: 1
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Contextualized Structured Logging and Error Handling for Go
This library provides context-driven structured error and logger contracts.
[](https://github.com/bool64/ctxd/actions?query=branch%3Amaster+workflow%3Atest-unit)
[](https://codecov.io/gh/bool64/ctxd)
[](https://pkg.go.dev/github.com/bool64/ctxd)
[](https://wakatime.com/badge/github/bool64/ctxd)


## Usage
* Create an adapter for your logger that implements `ctxd.Logger` or use [`zapctxd`](https://github.com/bool64/zapctxd)
that is built around awesome [`go.uber.org/zap`](https://pkg.go.dev/go.uber.org/zap).
* Add fields to context and pass it around.
* Use context for last-mile logging or error emitting.
## Example
### Structured Logging
```go
logger := ctxd.LoggerMock{}
// Once instrumented context can aid logger with structured information.
ctx := ctxd.AddFields(context.Background(), "foo", "bar")
logger.Info(ctx, "something happened")
// Also context contributes additional information to structured errors.
err := ctxd.NewError(ctx, "something failed", "baz", "quux")
ctxd.LogError(ctx, err, logger.Error)
fmt.Print(logger.String())
// Output:
// info: something happened {"foo":"bar"}
// error: something failed {"baz":"quux","foo":"bar"}
```
Logger can be instrumented with persistent fields that are affecting every context.
```go
lm := ctxd.LoggerMock{}
var globalLogger ctxd.Logger = &lm
localLogger := ctxd.LoggerWithFields(globalLogger, "local", 123)
ctx1 := ctxd.AddFields(context.Background(),
"ctx", 1,
"foo", "bar",
)
ctx2 := ctxd.AddFields(context.Background(), "ctx", 2)
localLogger.Info(ctx1, "hello", "he", "lo")
localLogger.Warn(ctx2, "bye", "by", "ee")
fmt.Print(lm.String())
// Output:
// info: hello {"ctx":1,"foo":"bar","he":"lo","local":123}
// warn: bye {"by":"ee","ctx":2,"local":123}
```
### Handling Errors
```go
ctx := context.Background()
// Elaborate context with fields.
ctx = ctxd.AddFields(ctx,
"field1", 1,
"field2", "abc",
)
// Add more fields when creating error.
err := ctxd.NewError(ctx, "something failed",
"field3", 3.0,
)
err2 := ctxd.WrapError(
// You can use same or any other context when wrapping error.
ctxd.AddFields(context.Background(), "field5", "V"),
err, "wrapped",
"field4", true)
// Setup your logger.
var (
lm = ctxd.LoggerMock{}
logger ctxd.Logger = &lm
)
// Inspect error fields.
var se ctxd.StructuredError
if errors.As(err, &se) {
fmt.Printf("error fields: %v\n", se.Fields())
}
// Log errors.
ctxd.LogError(ctx, err, logger.Error)
ctxd.LogError(ctx, err2, logger.Warn)
fmt.Print(lm.String())
// Output:
// error fields: map[field1:1 field2:abc field3:3]
// error: something failed {"field1":1,"field2":"abc","field3":3}
// warn: wrapped: something failed {"field1":1,"field2":"abc","field3":3,"field4":true,"field5":"V"}
```