- 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(...)`.
|
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.
|
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
|
## Message replacement
|
||||||
|
|
||||||
`AddReplacer(old, new)` replaces matching text in every message before the
|
`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.
|
- `Info`, `Warn`, `Error`, `Debug`, and `Fatal` accept a list of values.
|
||||||
- `Infof`, `Warnf`, `Errorf`, `Debugf`, and `Fatalf` use `fmt.Sprintf`.
|
- `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.
|
- 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.
|
- `Fatal`, `Fatalf`, and `Fatalln` call `os.Exit(1)` after writing the message.
|
||||||
- `AddReplacer` masks or rewrites message text before records are sent to writers.
|
- `AddReplacer` masks or rewrites message text before records are sent to writers.
|
||||||
|
|||||||
@@ -135,6 +135,24 @@ httpCache.SetForeground256Color(214)
|
|||||||
Так как setter'ы `LogLevel` изменяют уровень на месте, их нужно вызывать на переменной, а не на временном результате `NewLogLevel(...)`.
|
Так как setter'ы `LogLevel` изменяют уровень на месте, их нужно вызывать на переменной, а не на временном результате `NewLogLevel(...)`.
|
||||||
Короткие формы вроде `SetFgColor` и `SetBgColor` сохранены для обратной совместимости.
|
Короткие формы вроде `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)` заменяет найденный текст в каждом сообщении до того,
|
`AddReplacer(old, new)` заменяет найденный текст в каждом сообщении до того,
|
||||||
@@ -156,6 +174,7 @@ logger.Infoln("login token:", "SOME_SECRET")
|
|||||||
|
|
||||||
- `Info`, `Warn`, `Error`, `Debug`, `Fatal` принимают список значений.
|
- `Info`, `Warn`, `Error`, `Debug`, `Fatal` принимают список значений.
|
||||||
- `Infof`, `Warnf`, `Errorf`, `Debugf`, `Fatalf` используют `fmt.Sprintf`.
|
- `Infof`, `Warnf`, `Errorf`, `Debugf`, `Fatalf` используют `fmt.Sprintf`.
|
||||||
|
- `Printf(level, format, args...)` форматирует сообщение для явного `LogLevel`.
|
||||||
- Методы `*ln` добавляют семантику перевода строки, что удобно для `stdout`, Docker и line-based collectors.
|
- Методы `*ln` добавляют семантику перевода строки, что удобно для `stdout`, Docker и line-based collectors.
|
||||||
- `Fatal`, `Fatalf` и `Fatalln` вызывают `os.Exit(1)` после записи сообщения.
|
- `Fatal`, `Fatalf` и `Fatalln` вызывают `os.Exit(1)` после записи сообщения.
|
||||||
- `AddReplacer` маскирует или переписывает текст сообщений перед отправкой в writer'ы.
|
- `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
|
### Added
|
||||||
|
|
||||||
@@ -28,15 +28,18 @@ This release adds new public APIs for working with log levels, introduces HTTP m
|
|||||||
- `HTTPUnknownLevel`
|
- `HTTPUnknownLevel`
|
||||||
- Added `LogLevelForMethod(method string) LogLevel` to map HTTP methods to predefined log levels.
|
- 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 `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
|
### Changed
|
||||||
|
|
||||||
- Refactored log level definitions into a dedicated `levels.go` file.
|
- 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.
|
- 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
|
### Documentation
|
||||||
|
|
||||||
- Improved GoDoc coverage for exported types, constructors, formatters, colors, HTTP helpers, and time layouts.
|
- 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.
|
- Expanded inline documentation for formatter tokens and default formatter behavior.
|
||||||
|
|
||||||
### Compatibility
|
### Compatibility
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
// passed to writers. Call SetFormatter on a writer to customize timestamps,
|
// passed to writers. Call SetFormatter on a writer to customize timestamps,
|
||||||
// traceback fields, colors, and message layout.
|
// 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:
|
// Basic usage:
|
||||||
//
|
//
|
||||||
// logger := sneklog.CreateLogger().
|
// logger := sneklog.CreateLogger().
|
||||||
@@ -41,4 +46,7 @@
|
|||||||
// the logger and are closed by Logger.Close. Writers created from existing
|
// the logger and are closed by Logger.Close. Writers created from existing
|
||||||
// io.Writer values through CreateTextWriter or CreateJsonWriter remain owned by
|
// io.Writer values through CreateTextWriter or CreateJsonWriter remain owned by
|
||||||
// the caller and are not closed by the logger.
|
// 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
|
package sneklog
|
||||||
|
|||||||
@@ -196,3 +196,50 @@ func (l *LogLevel) GetAttributes() []Attribute {
|
|||||||
copy(attrs, l.attrs)
|
copy(attrs, l.attrs)
|
||||||
return 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"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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) {
|
func TestCreateTextWriterCloseOnNonCloserIsNoOp(t *testing.T) {
|
||||||
writer := CreateTextWriter(&bytes.Buffer{})
|
writer := CreateTextWriter(&bytes.Buffer{})
|
||||||
if err := writer.Close(); err != nil {
|
if err := writer.Close(); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user