- add godoc for SameLevel and Equal - update package docs and READMEs with new helpers - refresh unreleased release notes - add tests for Printf, HTTP method levels, and level constructors
This commit is contained in:
@@ -135,6 +135,23 @@ 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")
|
||||
```
|
||||
|
||||
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 severity matters and `Equal`
|
||||
if the full configuration, including colors and attributes, must match.
|
||||
|
||||
## Message replacement
|
||||
|
||||
`AddReplacer(old, new)` replaces matching text in every message before the
|
||||
@@ -157,6 +174,7 @@ An empty `old` value is ignored.
|
||||
|
||||
- `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.
|
||||
|
||||
@@ -135,6 +135,24 @@ httpCache.SetForeground256Color(214)
|
||||
Так как setter'ы `LogLevel` изменяют уровень на месте, их нужно вызывать на переменной, а не на временном результате `NewLogLevel(...)`.
|
||||
Короткие формы вроде `SetFgColor` и `SetBgColor` сохранены для обратной совместимости.
|
||||
|
||||
Для стандартных severity также есть отдельные helper'ы:
|
||||
|
||||
```go
|
||||
access := sneklog.NewInfoLogLevelWithColors("access", sneklog.FgCyan, sneklog.BgNone)
|
||||
audit := sneklog.NewWarnLogLevel("audit")
|
||||
```
|
||||
|
||||
Для HTTP-методов доступны готовые уровни:
|
||||
|
||||
```go
|
||||
level := sneklog.LogLevelForMethod(http.MethodPost)
|
||||
logger.Print(level, "POST /users")
|
||||
```
|
||||
|
||||
Если нужно сравнить только severity, используйте `SameLevel`. Если нужно
|
||||
полное совпадение конфигурации уровня, включая цвета и атрибуты, используйте
|
||||
`Equal`.
|
||||
|
||||
## Замена сообщений
|
||||
|
||||
`AddReplacer(old, new)` заменяет найденный текст в каждом сообщении до того,
|
||||
@@ -156,6 +174,7 @@ logger.Infoln("login token:", "SOME_SECRET")
|
||||
|
||||
- `Info`, `Warn`, `Error`, `Debug`, `Fatal` принимают список значений.
|
||||
- `Infof`, `Warnf`, `Errorf`, `Debugf`, `Fatalf` используют `fmt.Sprintf`.
|
||||
- `Printf(level, format, args...)` форматирует сообщение для явного `LogLevel`.
|
||||
- Методы `*ln` добавляют семантику перевода строки, что удобно для `stdout`, Docker и line-based collectors.
|
||||
- `Fatal`, `Fatalf` и `Fatalln` вызывают `os.Exit(1)` после записи сообщения.
|
||||
- `AddReplacer` маскирует или переписывает текст сообщений перед отправкой в writer'ы.
|
||||
|
||||
+5
-2
@@ -1,6 +1,6 @@
|
||||
# v2.2.0
|
||||
# Unreleased (planned v2.2.0)
|
||||
|
||||
This release adds new public APIs for working with log levels, introduces HTTP method-specific log levels, and improves package documentation.
|
||||
This release expands the public LogLevel API, adds HTTP method helpers, and improves package documentation and test coverage.
|
||||
|
||||
### Added
|
||||
|
||||
@@ -28,15 +28,18 @@ This release adds new public APIs for working with log levels, introduces HTTP m
|
||||
- `HTTPUnknownLevel`
|
||||
- Added `LogLevelForMethod(method string) LogLevel` to map HTTP methods to predefined log levels.
|
||||
- Added `Logger.Printf(level, format, args...)` for formatted logging with an explicit log level.
|
||||
- Added `LogLevel.SameLevel` and `LogLevel.Equal` for severity-only and full level comparisons.
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored log level definitions into a dedicated `levels.go` file.
|
||||
- Unified `Logger.Print` and `Logger.Println` through a shared internal implementation without changing their behavior.
|
||||
- Expanded test coverage for `Logger.Printf`, HTTP level mapping, and new level constructors.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Improved GoDoc coverage for exported types, constructors, formatters, colors, HTTP helpers, and time layouts.
|
||||
- Documented level comparison helpers and explicit-level formatted logging.
|
||||
- Expanded inline documentation for formatter tokens and default formatter behavior.
|
||||
|
||||
### Compatibility
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
// passed to writers. Call SetFormatter on a writer to customize timestamps,
|
||||
// traceback fields, colors, and message layout.
|
||||
//
|
||||
// The package also provides helper constructors for common severities such as
|
||||
// NewInfoLogLevel and NewErrorLogLevel, plus predefined HTTP method-specific
|
||||
// levels such as HTTPGetLevel and HTTPPostLevel. Use LogLevelForMethod to map
|
||||
// an HTTP method to one of those predefined levels.
|
||||
//
|
||||
// Basic usage:
|
||||
//
|
||||
// logger := sneklog.CreateLogger().
|
||||
@@ -41,4 +46,7 @@
|
||||
// the logger and are closed by Logger.Close. Writers created from existing
|
||||
// io.Writer values through CreateTextWriter or CreateJsonWriter remain owned by
|
||||
// the caller and are not closed by the logger.
|
||||
//
|
||||
// LogLevel values can be compared with SameLevel when only severity matters or
|
||||
// with Equal when the full level configuration must match.
|
||||
package sneklog
|
||||
|
||||
@@ -196,3 +196,50 @@ func (l *LogLevel) GetAttributes() []Attribute {
|
||||
copy(attrs, l.attrs)
|
||||
return attrs
|
||||
}
|
||||
|
||||
// SameLevel reports whether two log levels have the same severity index.
|
||||
func (l *LogLevel) SameLevel(other LogLevel) bool {
|
||||
return l.n == other.n
|
||||
}
|
||||
|
||||
// Equal reports whether two log levels have identical configuration.
|
||||
func (l *LogLevel) Equal(other LogLevel) bool {
|
||||
if l.t != other.t || l.n != other.n {
|
||||
return false
|
||||
}
|
||||
if l.fg != other.fg || l.bg != other.bg {
|
||||
return false
|
||||
}
|
||||
|
||||
if l.fg256 != other.fg256 || l.bg256 != other.bg256 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(l.attrs) != len(other.attrs) {
|
||||
return false
|
||||
}
|
||||
for i := range l.attrs {
|
||||
if l.attrs[i] != other.attrs[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(l.fgRgb) != len(other.fgRgb) {
|
||||
return false
|
||||
}
|
||||
for i := range l.fgRgb {
|
||||
if l.fgRgb[i] != other.fgRgb[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(l.bgRgb) != len(other.bgRgb) {
|
||||
return false
|
||||
}
|
||||
for i := range l.bgRgb {
|
||||
if l.bgRgb[i] != other.bgRgb[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+100
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -100,6 +101,105 @@ func TestLoggerPreservesMessageTypesWithoutReplacers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerPrintfFormatsMessage(t *testing.T) {
|
||||
writer := &stubLoggerWriter{}
|
||||
logger := CreateLogger().
|
||||
SetLevel(DEBUG).
|
||||
AddWriter(writer)
|
||||
|
||||
logger.Printf(INFO, "status=%d %s", 200, "ok")
|
||||
|
||||
if writer.printCalls != 1 {
|
||||
t.Fatalf("Printf() should write once, got %d calls", writer.printCalls)
|
||||
}
|
||||
if len(writer.messages) != 1 {
|
||||
t.Fatalf("Printf() should produce one formatted message, got %d parts", len(writer.messages))
|
||||
}
|
||||
if got, ok := writer.messages[0].(string); !ok || got != "status=200 ok" {
|
||||
t.Fatalf("Printf() should format message, got %#v", writer.messages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogLevelForMethodReturnsExpectedLevels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
method string
|
||||
want LogLevel
|
||||
}{
|
||||
{name: "get", method: http.MethodGet, want: HTTPGetLevel},
|
||||
{name: "head", method: http.MethodHead, want: HTTPHeadLevel},
|
||||
{name: "post", method: http.MethodPost, want: HTTPPostLevel},
|
||||
{name: "put", method: http.MethodPut, want: HTTPPutLevel},
|
||||
{name: "patch", method: http.MethodPatch, want: HTTPPatchLevel},
|
||||
{name: "delete", method: http.MethodDelete, want: HTTPDeleteLevel},
|
||||
{name: "options", method: http.MethodOptions, want: HTTPOptionsLevel},
|
||||
{name: "connect", method: http.MethodConnect, want: HTTPConnectLevel},
|
||||
{name: "trace", method: http.MethodTrace, want: HTTPTraceLevel},
|
||||
{name: "unknown", method: "PROPFIND", want: HTTPUnknownLevel},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := LogLevelForMethod(tt.method)
|
||||
if got.n != tt.want.n || got.t != tt.want.t || got.fg != tt.want.fg || got.bg != tt.want.bg {
|
||||
t.Fatalf("LogLevelForMethod(%q) = %#v, want %#v", tt.method, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLevelConstructorsSetExpectedSeverity(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
got LogLevel
|
||||
want LogLevel
|
||||
}{
|
||||
{name: "info", got: NewInfoLogLevel("custom"), want: INFO},
|
||||
{name: "warn", got: NewWarnLogLevel("custom"), want: WARN},
|
||||
{name: "error", got: NewErrorLogLevel("custom"), want: ERROR},
|
||||
{name: "fatal", got: NewFatalLogLevel("custom"), want: FATAL},
|
||||
{name: "debug", got: NewDebugLogLevel("custom"), want: DEBUG},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.got.n != tt.want.n {
|
||||
t.Fatalf("%s severity = %d, want %d", tt.name, tt.got.n, tt.want.n)
|
||||
}
|
||||
if tt.got.GetName() != "custom" {
|
||||
t.Fatalf("%s constructor should preserve name, got %q", tt.name, tt.got.GetName())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLevelConstructorsWithColorsSetExpectedFields(t *testing.T) {
|
||||
info := NewInfoLogLevelWithColors("info-custom", FgBlue, BgYellow)
|
||||
if info.n != INFO.n || info.GetName() != "info-custom" || info.fg != FgBlue || info.bg != BgYellow {
|
||||
t.Fatalf("NewInfoLogLevelWithColors() = %#v", info)
|
||||
}
|
||||
|
||||
warn := NewWarnLogLevelWithColors("warn-custom", FgMagenta, BgCyan)
|
||||
if warn.n != WARN.n || warn.GetName() != "warn-custom" || warn.fg != FgMagenta || warn.bg != BgCyan {
|
||||
t.Fatalf("NewWarnLogLevelWithColors() = %#v", warn)
|
||||
}
|
||||
|
||||
errLevel := NewErrorLogLevelWithColors("error-custom", FgRed, BgWhite)
|
||||
if errLevel.n != ERROR.n || errLevel.GetName() != "error-custom" || errLevel.fg != FgRed || errLevel.bg != BgWhite {
|
||||
t.Fatalf("NewErrorLogLevelWithColors() = %#v", errLevel)
|
||||
}
|
||||
|
||||
fatal := NewFatalLogLevelWithColors("fatal-custom", FgHiRed, BgBlack)
|
||||
if fatal.n != FATAL.n || fatal.GetName() != "fatal-custom" || fatal.fg != FgHiRed || fatal.bg != BgBlack {
|
||||
t.Fatalf("NewFatalLogLevelWithColors() = %#v", fatal)
|
||||
}
|
||||
|
||||
debug := NewDebugLogLevelWithColors("debug-custom", FgGreen, BgDefault)
|
||||
if debug.n != DEBUG.n || debug.GetName() != "debug-custom" || debug.fg != FgGreen || debug.bg != BgDefault {
|
||||
t.Fatalf("NewDebugLogLevelWithColors() = %#v", debug)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTextWriterCloseOnNonCloserIsNoOp(t *testing.T) {
|
||||
writer := CreateTextWriter(&bytes.Buffer{})
|
||||
if err := writer.Close(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user