Adding upstream version 3.10.8.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
37e9b6d587
commit
03bfe4079e
356 changed files with 28857 additions and 0 deletions
24
logger/context.go
Normal file
24
logger/context.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
const (
|
||||
loggerKey key = iota + 1
|
||||
)
|
||||
|
||||
func ContextSetLogger(ctx context.Context, value Interface) context.Context {
|
||||
return context.WithValue(ctx, loggerKey, value)
|
||||
}
|
||||
|
||||
func ContextGetLogger(ctx context.Context) Interface {
|
||||
value, _ := ctx.Value(loggerKey).(Interface)
|
||||
return value
|
||||
}
|
20
logger/context_test.go
Normal file
20
logger/context_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_LoggerContext(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
assert.Nil(t, ContextGetLogger(ctx))
|
||||
logger := NewLogger()
|
||||
ctx = ContextSetLogger(ctx, logger)
|
||||
assert.EqualValues(t, logger, ContextGetLogger(ctx))
|
||||
}
|
70
logger/interface.go
Normal file
70
logger/interface.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type Level int
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
Message Level = iota
|
||||
Trace Level = iota
|
||||
Debug Level = iota
|
||||
Info Level = iota
|
||||
Warn Level = iota
|
||||
Error Level = iota
|
||||
Fatal Level = iota
|
||||
)
|
||||
|
||||
var toString = map[Level]string{
|
||||
Message: "message",
|
||||
Trace: "trace",
|
||||
Debug: "debug",
|
||||
Info: "info",
|
||||
Warn: "warn",
|
||||
Error: "error",
|
||||
Fatal: "fatal",
|
||||
}
|
||||
|
||||
func (l Level) String() string {
|
||||
s, ok := toString[l]
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
return "undefined"
|
||||
}
|
||||
|
||||
type Fun func(string, ...any)
|
||||
|
||||
type CaptureInterface interface {
|
||||
Interface
|
||||
GetBuffer() *bytes.Buffer
|
||||
String() string
|
||||
Reset()
|
||||
}
|
||||
|
||||
type MessageInterface interface {
|
||||
Message(string, ...any)
|
||||
Trace(string, ...any)
|
||||
Debug(string, ...any)
|
||||
Info(string, ...any)
|
||||
Warn(string, ...any)
|
||||
Error(string, ...any)
|
||||
Fatal(string, ...any)
|
||||
Log(skip int, level Level, message string, args ...any)
|
||||
}
|
||||
|
||||
type ManageInterface interface {
|
||||
SetLevel(level Level)
|
||||
GetLevel() Level
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
MessageInterface
|
||||
ManageInterface
|
||||
}
|
230
logger/logger.go
Normal file
230
logger/logger.go
Normal file
|
@ -0,0 +1,230 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type logger struct {
|
||||
levels map[Level]int
|
||||
writer io.Writer
|
||||
logger *slog.Logger
|
||||
level Level
|
||||
levelVar *slog.LevelVar
|
||||
}
|
||||
|
||||
var levels = map[Level]int{
|
||||
Trace: int(slog.LevelDebug) - 1,
|
||||
Debug: int(slog.LevelDebug),
|
||||
Info: int(slog.LevelInfo),
|
||||
Warn: int(slog.LevelWarn),
|
||||
Error: int(slog.LevelError),
|
||||
Fatal: int(slog.LevelError + 1),
|
||||
}
|
||||
|
||||
var levelList = []Level{
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Fatal,
|
||||
}
|
||||
|
||||
func MoreVerbose(level Level) *Level {
|
||||
position := levelToPosition(level) - 1
|
||||
if position > 0 {
|
||||
return &levelList[position]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LessVerbose(level Level) *Level {
|
||||
position := levelToPosition(level) + 1
|
||||
if position < len(levelList) {
|
||||
return &levelList[position]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func levelToPosition(level Level) int {
|
||||
var i int
|
||||
for i = 0; i < len(levelList); i++ {
|
||||
if levelList[i] == level {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i >= len(levelList) {
|
||||
panic(fmt.Errorf("unknown verbosity %v", level))
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func NewLogger() Interface {
|
||||
l := &logger{}
|
||||
l.Init()
|
||||
return l
|
||||
}
|
||||
|
||||
type captureLogger struct {
|
||||
logger
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewCaptureLogger() CaptureInterface {
|
||||
l := &captureLogger{}
|
||||
l.buf = new(bytes.Buffer)
|
||||
l.writer = l.buf
|
||||
l.Init()
|
||||
return l
|
||||
}
|
||||
|
||||
func (o *captureLogger) String() string {
|
||||
return o.buf.String()
|
||||
}
|
||||
|
||||
func (o *captureLogger) GetBuffer() *bytes.Buffer {
|
||||
return o.buf
|
||||
}
|
||||
|
||||
func (o *captureLogger) Reset() {
|
||||
o.buf.Reset()
|
||||
}
|
||||
|
||||
var filenamePrefix string
|
||||
|
||||
func init() {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
filenamePrefix = strings.TrimSuffix(filename, "logger.go")
|
||||
if filenamePrefix == filename {
|
||||
// in case the source code file is moved, we can not trim the suffix, the code above should also be updated.
|
||||
panic("unable to detect correct package prefix, please update file: " + filename)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *logger) Log(skip int, level Level, format string, args ...any) {
|
||||
slogLevel := levels[level]
|
||||
logger := o.logger
|
||||
if !logger.Handler().Enabled(context.Background(), slog.Level(slogLevel)) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(2+skip, pcs[:]) // 2 is to skip [Callers(), Log()]
|
||||
r := slog.NewRecord(time.Now(), slog.Level(slogLevel), fmt.Sprintf(format, args...), pcs[0])
|
||||
_ = logger.Handler().Handle(context.Background(), r)
|
||||
}
|
||||
|
||||
func (o *logger) Init() {
|
||||
replace := func(groups []string, a slog.Attr) slog.Attr {
|
||||
if a.Key == slog.TimeKey && len(groups) == 0 {
|
||||
return slog.Attr{}
|
||||
}
|
||||
if a.Key == slog.SourceKey {
|
||||
source := a.Value.Any().(*slog.Source)
|
||||
var function string
|
||||
dot := strings.LastIndex(source.Function, ".")
|
||||
if dot >= 0 {
|
||||
function = ":" + source.Function[dot+1:]
|
||||
}
|
||||
source.File = strings.TrimPrefix(source.File, projectPackagePrefix) + function
|
||||
}
|
||||
return a
|
||||
}
|
||||
o.levelVar = new(slog.LevelVar)
|
||||
if o.writer == nil {
|
||||
o.writer = os.Stdout
|
||||
}
|
||||
o.logger = slog.New(slog.NewTextHandler(o.writer, &slog.HandlerOptions{
|
||||
Level: o.levelVar,
|
||||
AddSource: true,
|
||||
ReplaceAttr: replace,
|
||||
}))
|
||||
}
|
||||
|
||||
func (o *logger) SetLevel(level Level) {
|
||||
o.level = level
|
||||
o.levelVar.Set(slog.Level(levels[o.level]))
|
||||
}
|
||||
|
||||
func (o *logger) GetLevel() Level {
|
||||
return o.level
|
||||
}
|
||||
|
||||
func (o *logger) SetWriter(out io.Writer) {
|
||||
o.writer = out
|
||||
o.Init()
|
||||
}
|
||||
|
||||
func (o *logger) Message(message string, args ...any) { o.Log(1, Info, message, args...) }
|
||||
func (o *logger) Trace(message string, args ...any) { o.Log(1, Trace, message, args...) }
|
||||
func (o *logger) Debug(message string, args ...any) { o.Log(1, Debug, message, args...) }
|
||||
func (o *logger) Info(message string, args ...any) { o.Log(1, Info, message, args...) }
|
||||
func (o *logger) Warn(message string, args ...any) { o.Log(1, Warn, message, args...) }
|
||||
func (o *logger) Error(message string, args ...any) { o.Log(1, Error, message, args...) }
|
||||
func (o *logger) Fatal(message string, args ...any) { o.Log(1, Fatal, message, args...) }
|
||||
|
||||
type Logger struct {
|
||||
logger Interface
|
||||
}
|
||||
|
||||
func (o *Logger) GetLogger() Interface { return o.logger }
|
||||
|
||||
func (o *Logger) SetLogger(logger Interface) { o.logger = logger }
|
||||
|
||||
func (o *Logger) SetLevel(level Level) { o.logger.SetLevel(level) }
|
||||
|
||||
func (o *Logger) GetLevel() Level { return o.logger.GetLevel() }
|
||||
|
||||
func (o *Logger) Message(message string, args ...any) {
|
||||
o.logger.Log(1, Message, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Trace(message string, args ...any) {
|
||||
o.logger.Log(1, Trace, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Debug(message string, args ...any) {
|
||||
o.logger.Log(1, Debug, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Info(message string, args ...any) {
|
||||
o.logger.Log(1, Info, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Warn(message string, args ...any) {
|
||||
o.logger.Log(1, Warn, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Error(message string, args ...any) {
|
||||
o.logger.Log(1, Error, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Fatal(message string, args ...any) {
|
||||
o.logger.Log(1, Fatal, message, args...)
|
||||
}
|
||||
|
||||
func (o *Logger) Log(skip int, level Level, message string, args ...any) {
|
||||
o.logger.Log(skip+1, level, message, args...)
|
||||
}
|
||||
|
||||
var projectPackagePrefix string
|
||||
|
||||
func init() {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
projectPackagePrefix = strings.TrimSuffix(filename, "logger/logger.go")
|
||||
if projectPackagePrefix == filename {
|
||||
// in case the source code file is moved, we can not trim the suffix, the code above should also be updated.
|
||||
panic("unable to detect correct package prefix, please update file: " + filename)
|
||||
}
|
||||
}
|
29
logger/logger_test.go
Normal file
29
logger/logger_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Logger(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
expected string
|
||||
level Level
|
||||
call func(MessageInterface, string, ...any)
|
||||
}{
|
||||
{expected: "level=INFO ", level: Info, call: func(logger MessageInterface, message string, args ...any) { logger.Message(message, args...) }},
|
||||
{expected: "level=DEBUG-1 ", level: Trace, call: func(logger MessageInterface, message string, args ...any) { logger.Trace(message, args...) }},
|
||||
{expected: "level=DEBUG ", level: Debug, call: func(logger MessageInterface, message string, args ...any) { logger.Debug(message, args...) }},
|
||||
{expected: "level=INFO ", level: Info, call: func(logger MessageInterface, message string, args ...any) { logger.Info(message, args...) }},
|
||||
{expected: "level=WARN ", level: Warn, call: func(logger MessageInterface, message string, args ...any) { logger.Warn(message, args...) }},
|
||||
{expected: "level=ERROR ", level: Error, call: func(logger MessageInterface, message string, args ...any) { logger.Error(message, args...) }},
|
||||
{expected: "level=ERROR+1 ", level: Fatal, call: func(logger MessageInterface, message string, args ...any) { logger.Fatal(message, args...) }},
|
||||
} {
|
||||
t.Run(testCase.expected+testCase.level.String(), func(t *testing.T) {
|
||||
testLoggerCase(t, testCase.expected, testCase.level, testCase.call)
|
||||
})
|
||||
}
|
||||
}
|
54
logger/logger_test_helper.go
Normal file
54
logger/logger_test_helper.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testLoggerCase(t *testing.T, expected string, level Level, loggerFunc func(MessageInterface, string, ...any)) {
|
||||
logger := NewCaptureLogger()
|
||||
logger.SetLevel(level)
|
||||
messages := []string{
|
||||
"MESSAGE HERE",
|
||||
}
|
||||
moreVerbose := MoreVerbose(level)
|
||||
if moreVerbose != nil {
|
||||
messages = append(messages, "MESSAGE MORE VERBOSE")
|
||||
}
|
||||
lessVerbose := LessVerbose(level)
|
||||
if lessVerbose != nil {
|
||||
messages = append(messages, "MESSAGE LESS VERBOSE")
|
||||
}
|
||||
|
||||
loggerFunc(logger, "MESSAGE %s", "HERE")
|
||||
if moreVerbose != nil {
|
||||
logger.Log(1, *moreVerbose, "MESSAGE %s", "MORE VERBOSE")
|
||||
}
|
||||
if lessVerbose != nil {
|
||||
logger.Log(1, *lessVerbose, "MESSAGE %s", "LESS VERBOSE")
|
||||
}
|
||||
|
||||
i := 0
|
||||
assert.Contains(t, logger.String(), messages[i])
|
||||
if moreVerbose != nil {
|
||||
i++
|
||||
require.True(t, len(messages) > i)
|
||||
assert.NotContains(t, logger.String(), messages[i])
|
||||
}
|
||||
if lessVerbose != nil {
|
||||
i++
|
||||
require.True(t, len(messages) > i)
|
||||
assert.Contains(t, logger.String(), messages[i])
|
||||
}
|
||||
|
||||
assert.Contains(t, logger.String(), expected)
|
||||
// verifies the call stack is calculated correctly and this is the
|
||||
// reason for having this function in a separate file
|
||||
assert.Contains(t, logger.String(), "logger_test.go")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue