Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/go-simpler/sloggen
🪵 Generate domain-specific wrappers for log/slog
https://github.com/go-simpler/sloggen
codegen go logging slog tool
Last synced: 3 months ago
JSON representation
🪵 Generate domain-specific wrappers for log/slog
- Host: GitHub
- URL: https://github.com/go-simpler/sloggen
- Owner: go-simpler
- License: mpl-2.0
- Created: 2023-08-22T00:03:07.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-07-11T19:32:33.000Z (4 months ago)
- Last Synced: 2024-07-11T22:38:12.509Z (4 months ago)
- Topics: codegen, go, logging, slog, tool
- Language: Go
- Homepage: https://go-simpler.org/sloggen
- Size: 69.3 KB
- Stars: 11
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-slog - sloggen
README
# sloggen
[![checks](https://github.com/go-simpler/sloggen/actions/workflows/checks.yml/badge.svg)](https://github.com/go-simpler/sloggen/actions/workflows/checks.yml)
[![pkg.go.dev](https://pkg.go.dev/badge/go-simpler.org/sloggen.svg)](https://pkg.go.dev/go-simpler.org/sloggen)
[![goreportcard](https://goreportcard.com/badge/go-simpler.org/sloggen)](https://goreportcard.com/report/go-simpler.org/sloggen)
[![codecov](https://codecov.io/gh/go-simpler/sloggen/branch/main/graph/badge.svg)](https://codecov.io/gh/go-simpler/sloggen)Generate domain-specific wrappers for `log/slog`.
## 📌 About
When using `log/slog` in a production-grade project, it is useful to write helpers to prevent typos in the keys:
```go
slog.Info("a user has logged in", "user_id", 42)
slog.Info("a user has logged out", "user_ip", 42) // oops :(
```Depending on your code style, these can be simple constants (if you prefer key-value pairs)...
```go
const UserId = "user_id"
```...or custom `slog.Attr` constructors (if you're a safety/performance advocate):
```go
func UserId(value int) slog.Attr { return slog.Int("user_id", value) }
````sloggen` generates such helpers for you, so you don't have to write them manually.
---
The default `log/slog` levels cover most use cases, but at some point you may want to introduce custom levels that better suit your app.
At first glance, this is as simple as defining a constant:```go
const LevelAlert = slog.Level(12)
```However, custom levels are treated differently than the first-class citizens `Debug`/`Info`/`Warn`/`Error`:
```go
slog.Log(nil, LevelAlert, "msg") // want "ALERT msg"; got "ERROR+4 msg"
````sloggen` solves this inconvenience by generating not only the levels themselves, but also the necessary helpers.
Unfortunately, the only way to use such levels is the `Log` method, which is quite verbose.
`sloggen` can generate a custom `Logger` type so that custom levels can be used just like the builtin ones:```go
// before:
logger.Log(nil, LevelAlert, "msg", "key", "value")
// after:
logger.Alert("msg", "key", "value")
```Additionally, there are options to choose the API style of the arguments (`...any` or `...slog.Attr`) and to add/remove `context.Context` as the first parameter.
This allows you to adjust the logging API to your own code style without sacrificing convenience.> [!tip]
> Various API rules for `log/slog` can be enforced by the [`sloglint`][1] linter. Give it a try too!## 🚀 Features
* Generate key constants and `slog.Attr` constructors
* Generate custom levels with helpers for parsing/printing
* Generate a custom `Logger` type with methods for custom levels
* Codegen-based, so no runtime dependency introduced## 📦 Install
Add the following directive to any `.go` file and run `go generate ./...`.
```go
//go:generate go run go-simpler.org/sloggen@ [flags]
```Where `` is the version of `sloggen` itself (use `latest` for automatic updates) and `[flags]` is the list of [available options](#help).
## 📋 Usage
There are two ways to provide options to `sloggen`: CLI flags and a `.yml` config file.
The former works best for few options and requires only a single `//go:generate` directive.
For many options it may be more convenient to use a config file, since `go generate` does not support multiline commands.
The config file can also be reused between several (micro)services if they share the same domain.To get started, see the [`example_test.go`](example_test.go) file and the [`example`](example) directory.
### Key constants
The `-c` flag (or the `consts` field) is used to generate a key constant.
For example, `-c=used_id` results in:```go
const UserId = "user_id"
```### Attribute constructors
The `-a` flag (or the `attrs` field) is used to generate a custom `slog.Attr` constructor.
For example, `-a=used_id:int` results in:```go
func UserId(value int) slog.Attr { return slog.Int("user_id", value) }
```### Custom levels
The `-l` flag (or the `levels` field) is used to generate a custom `slog.Level`.
For example, `-l=alert:12` results in:```go
const LevelAlert = slog.Level(12)func ParseLevel(s string) (slog.Level, error) {...}
func RenameLevels(_ []string, attr slog.Attr) slog.Attr {...}
```The `ParseLevel` function should be used to parse the level from a string (e.g. from an environment variable):
```go
level, err := slogx.ParseLevel("ALERT")
```The `RenameLevels` function should be used as `slog.HandlerOptions.ReplaceAttr` to print custom level names correctly:
```go
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
ReplaceAttr: slogx.RenameLevels,
}))
```### Custom Logger
The `-logger` flag (or the `logger` field) is used to generate a custom `Logger` type with methods for custom levels.
The `-api` flag (or the `logger.api` field) is used to choose the API style of the arguments: `any` for `...any` (key-value pairs) and `attr` for `...slog.Attr`.
The `-ctx` flag (or the `logger.ctx` field) is used to add or remove `context.Context` as the first parameter.
For example, `-l=alert:12 -logger -api=attr -ctx` results in:
```go
type Logger struct{ handler slog.Handler }func New(h slog.Handler) *Logger { return &Logger{handler: h} }
func (l *Logger) Alert(ctx context.Context, msg string, attrs ...slog.Attr) {...}
```The generated `Logger` has all the utility methods of the original `slog.Logger`, including `Enabled()`, `With()` and `WithGroup()`.
Since `Logger` is just a frontend, you can always fall back to `slog.Logger` (e.g. to pass it to a library) using the `Handler()` method:
```go
slog.New(logger.Handler())
```### Help
```shell
Usage: sloggen [flags]Flags:
-config read config from the file instead of flags
-dir change the working directory before generating files
-pkg the name for the generated package (default: slogx)
-i add import
-l add level
-c add constant
-a add attribute
-logger generate a custom Logger type
-api the API style for the Logger's methods (default: any)
-ctx add context.Context to the Logger's methods
-h, -help print this message and quit
```For the description of the config file fields, see [`.slog.example.yml`](.slog.example.yml).
[1]: https://github.com/go-simpler/sloglint