FILE / ScuroNeko/SNekLog
README.md
Исходный файл и его история в репозитории.
Golang lint / lint (push) Successful in 1m36s
- add threshold-aware LogLevel constructors - add Logger.SetThresholdMode and SameThreshold - preserve legacy SameLevel behavior for compatibility - extend tests for threshold mode and compatibility - update README and release notes for v2.2.0
225 lines
6.1 KiB
Markdown
225 lines
6.1 KiB
Markdown
# SNekLog (ScuroNeko Logger)
|
|
|
|
Small structured logger for Go with text and JSON output, multiple writers, and optional traceback metadata.
|
|
|
|
Russian version: [README_ru.md](README_ru.md)
|
|
|
|
## Features
|
|
|
|
- Fan out the same record to multiple destinations.
|
|
- Text and JSON writers.
|
|
- `stdout`, files, and arbitrary external `io.Writer` values.
|
|
- Optional timestamps for text output.
|
|
- Compact traceback metadata for text writers and full traceback slices for JSON.
|
|
- Message replacement for masking secrets or normalizing output.
|
|
- Explicit ownership rules for writer closing.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
go get git.scuroneko.dev/scuroneko/sneklog/v2
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"git.scuroneko.dev/scuroneko/sneklog/v2"
|
|
)
|
|
|
|
func main() {
|
|
logger := sneklog.NewLogger().
|
|
SetName("API").
|
|
SetLevel(sneklog.DEBUG).
|
|
AddReplacer("SOME_SECRET", "<redacted>")
|
|
|
|
text := logger.CreateTextStdoutWriter()
|
|
jsonFile, err := logger.CreateJsonFileWriter("logs/app.json")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
logger.AddWriters(text, jsonFile)
|
|
|
|
logger.Infoln("service started")
|
|
logger.Warnln("cache miss")
|
|
logger.Errorln("request failed")
|
|
logger.Debugln("debug details")
|
|
logger.Infoln("token", "SOME_SECRET")
|
|
|
|
if err := logger.Close(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Defaults
|
|
|
|
`NewLogger()` starts with:
|
|
|
|
- `Prefix("LOG")`
|
|
- `Level(sneklog.FATAL)`
|
|
- `JsonPretty(false)`
|
|
- text formatter: `sneklog.DefaultTextFormatter`
|
|
- JSON formatter: `sneklog.DefaultJsonFormatter`
|
|
|
|
`CreateLogger()` is still available for backward compatibility.
|
|
|
|
Important: with the current level ordering, `Level(sneklog.FATAL)` allows `INFO`, `WARN`, `ERROR`, and `FATAL`, but not `DEBUG`. Use `Level(sneklog.DEBUG)` to enable every level.
|
|
|
|
If you want classic threshold filtering instead, call `SetThresholdMode(true)` and
|
|
configure levels with `NewThresholdLogLevel(...)` or
|
|
`NewThresholdLogLevelWithColors(...)`.
|
|
|
|
## Writers and ownership
|
|
|
|
`Logger.Close()` only closes writers created by the logger itself:
|
|
|
|
- `CreateTextFileWriter(...)`
|
|
- `CreateJsonFileWriter(...)`
|
|
|
|
The following writers remain owned by the caller and are not closed by `Logger.Close()`:
|
|
|
|
- `CreateTextWriter(existingWriter)`
|
|
- `CreateJsonWriter(existingWriter)`
|
|
- `CreateTextStdoutWriter()`
|
|
- `CreateJsonStdoutWriter()`
|
|
|
|
This makes it safe to plug in `bytes.Buffer`, network writers, and other externally managed resources.
|
|
|
|
## Output formats
|
|
|
|
Text writers render records like:
|
|
|
|
```text
|
|
2026-03-17T14:05:09+03:00 info API: service started
|
|
```
|
|
|
|
Use `SetFormatter` on a writer to customize timestamps, traceback fields, colors, and message layout.
|
|
|
|
JSON writers emit objects with this shape:
|
|
|
|
```json
|
|
{
|
|
"time": "2026-03-17T14:05:09.123456789+03:00",
|
|
"level": "info",
|
|
"prefix": "API",
|
|
"message": "service started",
|
|
"traceback": [
|
|
{
|
|
"method": "main",
|
|
"filename": "main.go",
|
|
"line": 27,
|
|
"signature": "main.main",
|
|
"fullPath": "/path/to/main.go"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
When `JsonPretty(true)` is enabled, JSON is indented.
|
|
|
|
## Custom levels and colors
|
|
|
|
You can define your own levels and assign ANSI, 256-color, or RGB colors, plus text attributes such as `Bold` or `Italic`.
|
|
|
|
```go
|
|
httpDelete := sneklog.NewLogLevel(0, "delete")
|
|
httpDelete.SetBackgroundRGB(128, 0, 0)
|
|
httpDelete.AddAttribute(sneklog.Italic).AddAttribute(sneklog.Bold)
|
|
|
|
httpCache := sneklog.NewLogLevel(0, "cache")
|
|
httpCache.SetForeground256Color(214)
|
|
```
|
|
|
|
Because `LogLevel` setters mutate the level in place, call them on a variable, not on a temporary value returned by `NewLogLevel(...)`.
|
|
Short forms such as `SetFgColor` and `SetBgColor` remain available for backward compatibility.
|
|
|
|
Common severities also have dedicated helpers:
|
|
|
|
```go
|
|
access := sneklog.NewInfoLogLevelWithColors("access", sneklog.FgCyan, sneklog.BgNone)
|
|
audit := sneklog.NewWarnLogLevel("audit")
|
|
```
|
|
|
|
These helpers keep the legacy severity index for backward compatibility and also
|
|
assign default threshold values:
|
|
|
|
- `INFO`: `th=10`
|
|
- `WARN`: `th=20`
|
|
- `ERROR`: `th=30`
|
|
- `FATAL`: `th=40`
|
|
- `DEBUG`: `th=0`
|
|
|
|
To define custom threshold-based levels explicitly:
|
|
|
|
```go
|
|
trace := sneklog.NewThresholdLogLevelWithColors(4, 5, "trace", sneklog.FgHiBlack, sneklog.BgNone)
|
|
audit := sneklog.NewThresholdLogLevel(1, 25, "audit")
|
|
|
|
logger := sneklog.NewLogger().
|
|
SetLevel(audit).
|
|
SetThresholdMode(true)
|
|
|
|
logger.Print(trace, "verbose trace") // filtered out
|
|
logger.Print(audit, "audit event") // allowed
|
|
```
|
|
|
|
HTTP method-specific predefined levels are available out of the box:
|
|
|
|
```go
|
|
level := sneklog.LogLevelForMethod(http.MethodPost)
|
|
logger.Print(level, "POST /users")
|
|
```
|
|
|
|
When comparing levels:
|
|
|
|
- use `SameLevel` if only the legacy severity index matters;
|
|
- use `SameThreshold` if only threshold filtering matters;
|
|
- use `Equal` if the full configuration, including threshold, colors, and attributes, must match.
|
|
|
|
## Message replacement
|
|
|
|
`AddReplacer(old, new)` replaces matching text in every message before the
|
|
record reaches any writer. Replacement rules are applied in the order they are
|
|
added.
|
|
|
|
```go
|
|
logger := sneklog.CreateLogger().
|
|
Level(sneklog.DEBUG).
|
|
AddReplacer("SOME_SECRET", "<redacted>").
|
|
AddReplacer("user@example.com", "<email>")
|
|
|
|
logger.Infoln("login token:", "SOME_SECRET")
|
|
```
|
|
|
|
This writes `<redacted>` instead of `SOME_SECRET` in both text and JSON output.
|
|
An empty `old` value is ignored.
|
|
|
|
## API summary
|
|
|
|
- `Info`, `Warn`, `Error`, `Debug`, and `Fatal` accept a list of values.
|
|
- `Infof`, `Warnf`, `Errorf`, `Debugf`, and `Fatalf` use `fmt.Sprintf`.
|
|
- `Printf(level, format, args...)` formats a message for an explicit `LogLevel`.
|
|
- The `*ln` methods preserve newline semantics, which is useful for `stdout`, Docker, and line-based collectors.
|
|
- `Fatal`, `Fatalf`, and `Fatalln` call `os.Exit(1)` after writing the message.
|
|
- `AddReplacer` masks or rewrites message text before records are sent to writers.
|
|
|
|
## Traceback behavior
|
|
|
|
- Text writers use the nearest user stack frame.
|
|
- JSON writers receive the full traceback slice.
|
|
- Internal `sneklog` frames and `runtime` frames are filtered out.
|
|
|
|
## Repository example
|
|
|
|
See [examples/main.go](examples/main.go).
|
|
|
|
## License
|
|
|
|
This project is licensed under GNU GPLv3. See [LICENSE](LICENSE).
|