# 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", "") 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", ""). AddReplacer("user@example.com", "") logger.Infoln("login token:", "SOME_SECRET") ``` This writes `` 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).