mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-12-23 22:11:10 +02:00
300 lines
6.9 KiB
Go
300 lines
6.9 KiB
Go
package logger
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/testutil"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type FormatterStructuredTestSuite struct {
|
|
testutil.LazySuite
|
|
|
|
buf testutil.LazyObj[*bytes.Buffer]
|
|
config testutil.LazyObj[*Config]
|
|
handler testutil.LazyObj[*Handler]
|
|
logger testutil.LazyObj[*slog.Logger]
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) SetupTest() {
|
|
s.buf, _ = testutil.NewLazySuiteObj(
|
|
s,
|
|
func() (*bytes.Buffer, error) {
|
|
return new(bytes.Buffer), nil
|
|
},
|
|
)
|
|
|
|
s.config, _ = testutil.NewLazySuiteObj(
|
|
s,
|
|
func() (*Config, error) {
|
|
cfg := NewDefaultConfig()
|
|
cfg.Format = FormatStructured
|
|
return &cfg, nil
|
|
},
|
|
)
|
|
|
|
s.handler, _ = testutil.NewLazySuiteObj(
|
|
s,
|
|
func() (*Handler, error) {
|
|
return NewHandler(s.buf(), s.config()), nil
|
|
},
|
|
)
|
|
|
|
s.logger, _ = testutil.NewLazySuiteObj(
|
|
s,
|
|
func() (*slog.Logger, error) {
|
|
return slog.New(s.handler()), nil
|
|
},
|
|
)
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) SetupSubTest() {
|
|
s.ResetLazyObjects()
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) checkNextEntry(lvl, msg string) {
|
|
str, err := s.buf().ReadString('\n')
|
|
s.Require().NoError(err)
|
|
|
|
const timeLen = 19 + 7 // +7 for key, separator, and quotes
|
|
lvlLen := len(lvl) + 10 // +10 for key, separator, quotes, and spaces
|
|
prefixLen := timeLen + lvlLen
|
|
|
|
s.Require().GreaterOrEqual(len(str), prefixLen)
|
|
|
|
timePart := str[:timeLen]
|
|
levelPart := str[timeLen:prefixLen]
|
|
|
|
now := time.Now()
|
|
t, err := time.ParseInLocation(time.DateTime, timePart[6:timeLen-1], now.Location())
|
|
s.Require().NoError(err)
|
|
s.Require().WithinDuration(time.Now(), t, time.Minute)
|
|
|
|
s.Equal(` level="`+lvl+`" `, levelPart)
|
|
|
|
// Check the message
|
|
s.Equal(msg+"\n", str[prefixLen:])
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) TestLevel() {
|
|
type testEntry struct {
|
|
level slog.Level
|
|
levelName string
|
|
message string
|
|
}
|
|
|
|
testEntries := []testEntry{
|
|
{level: slog.LevelDebug, levelName: "DEBUG", message: "Debug message"},
|
|
{level: slog.LevelInfo, levelName: "INFO", message: "Info message"},
|
|
{level: slog.LevelWarn, levelName: "WARNING", message: "Warning message"},
|
|
{level: slog.LevelError, levelName: "ERROR", message: "Error message"},
|
|
{level: LevelCritical, levelName: "CRITICAL", message: "Critical message"},
|
|
}
|
|
|
|
testCases := []struct {
|
|
level slog.Level
|
|
entries []testEntry
|
|
}{
|
|
{level: slog.LevelDebug, entries: testEntries},
|
|
{level: slog.LevelInfo, entries: testEntries[1:]},
|
|
{level: slog.LevelWarn, entries: testEntries[2:]},
|
|
{level: slog.LevelError, entries: testEntries[3:]},
|
|
{level: LevelCritical, entries: testEntries[4:]},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
s.Run(tc.level.String(), func() {
|
|
s.config().Level = tc.level
|
|
|
|
for _, entry := range testEntries {
|
|
s.logger().Log(s.T().Context(), entry.level, entry.message)
|
|
}
|
|
|
|
for _, entry := range tc.entries {
|
|
s.checkNextEntry(entry.levelName, fmt.Sprintf(`msg="%s"`, entry.message))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) TestAttributes() {
|
|
s.logger().Info(
|
|
"Test message",
|
|
slog.String("string", "value"),
|
|
slog.Int("int", -100),
|
|
slog.Uint64("uint64", 200),
|
|
slog.Float64("float64", 3.14),
|
|
slog.Bool("bool", true),
|
|
slog.Time("time", time.Date(1984, 1, 2, 3, 4, 5, 6, time.UTC)),
|
|
slog.Duration("duration", time.Minute),
|
|
slog.Any("err", errors.New("error value")),
|
|
slog.Any("any", struct {
|
|
Field1 string
|
|
Field2 int
|
|
}{"value", 42}),
|
|
)
|
|
|
|
s.checkNextEntry(
|
|
"INFO",
|
|
strings.Join([]string{
|
|
`msg="Test message"`,
|
|
`string="value"`,
|
|
`int=-100`,
|
|
`uint64=200`,
|
|
`float64=3.14`,
|
|
`bool=true`,
|
|
`time="1984-01-02 03:04:05"`,
|
|
`duration="1m0s"`,
|
|
`err="error value"`,
|
|
`any="{Field1:value Field2:42}"`,
|
|
}, " "),
|
|
)
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) TestGroups() {
|
|
s.Run("LastGroupNotEmpty", func() {
|
|
s.logger().
|
|
With(
|
|
slog.String("string", "value"),
|
|
slog.Int("int", -100),
|
|
).
|
|
WithGroup("group1").
|
|
With(
|
|
slog.Uint64("uint64", 200),
|
|
slog.Float64("float64", 3.14),
|
|
).
|
|
WithGroup("group2").
|
|
With(slog.Group("group3",
|
|
slog.Bool("bool", true),
|
|
slog.Time("time", time.Date(1984, 1, 2, 3, 4, 5, 6, time.UTC)),
|
|
)).
|
|
With(slog.Group("empty_group")).
|
|
WithGroup("group4").
|
|
Info(
|
|
"Test message",
|
|
slog.Duration("duration", time.Minute),
|
|
slog.Any("any", struct {
|
|
Field1 string
|
|
Field2 int
|
|
}{"value", 42}),
|
|
)
|
|
|
|
s.checkNextEntry(
|
|
"INFO",
|
|
strings.Join([]string{
|
|
`msg="Test message"`,
|
|
`string="value"`,
|
|
`int=-100`,
|
|
`group1.uint64=200`,
|
|
`group1.float64=3.14`,
|
|
`group1.group2.group3.bool=true`,
|
|
`group1.group2.group3.time="1984-01-02 03:04:05"`,
|
|
`group1.group2.group4.duration="1m0s"`,
|
|
`group1.group2.group4.any="{Field1:value Field2:42}"`,
|
|
}, " "),
|
|
)
|
|
})
|
|
|
|
s.Run("LastGroupsEmpty", func() {
|
|
s.logger().
|
|
With(
|
|
slog.String("string", "value"),
|
|
slog.Int("int", -100),
|
|
).
|
|
WithGroup("group1").
|
|
With(
|
|
slog.Uint64("uint64", 200),
|
|
slog.Float64("float64", 3.14),
|
|
).
|
|
WithGroup("group2").
|
|
With(slog.Group("group3",
|
|
slog.Bool("bool", true),
|
|
slog.Time("time", time.Date(1984, 1, 2, 3, 4, 5, 6, time.UTC)),
|
|
)).
|
|
With(slog.Group("empty_group")).
|
|
WithGroup("group4").
|
|
WithGroup("group5").
|
|
Info("Test message")
|
|
|
|
s.checkNextEntry(
|
|
"INFO",
|
|
strings.Join([]string{
|
|
`msg="Test message"`,
|
|
`string="value"`,
|
|
`int=-100`,
|
|
`group1.uint64=200`,
|
|
`group1.float64=3.14`,
|
|
`group1.group2.group3.bool=true`,
|
|
`group1.group2.group3.time="1984-01-02 03:04:05"`,
|
|
}, " "),
|
|
)
|
|
})
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) TestQuoting() {
|
|
s.logger().Info(
|
|
"Test message",
|
|
"key", "value",
|
|
"key with spaces", "value with spaces",
|
|
`key"with"quotes`, `value"with"quotes`,
|
|
"key\nwith\nnewlines", "value\nwith\nnewlines",
|
|
slog.Group("group name", slog.String("key", "value")),
|
|
)
|
|
|
|
s.checkNextEntry(
|
|
"INFO",
|
|
strings.Join([]string{
|
|
`msg="Test message"`,
|
|
`key="value"`,
|
|
`"key with spaces"="value with spaces"`,
|
|
`"key\"with\"quotes"="value\"with\"quotes"`,
|
|
`"key\nwith\nnewlines"="value\nwith\nnewlines"`,
|
|
`"group name.key"="value"`,
|
|
}, " "),
|
|
)
|
|
}
|
|
|
|
func (s *FormatterStructuredTestSuite) TestSpecialFields() {
|
|
s.logger().Info(
|
|
"Test message",
|
|
"stack", "stack value\nwith new lines",
|
|
"key1", "value1",
|
|
"error", errors.New("error value"),
|
|
"key2", "value2",
|
|
"source", "source value",
|
|
"key3", "value3",
|
|
slog.Group(
|
|
"group",
|
|
"stack", "stack in group",
|
|
"error", "error in group",
|
|
"source", "source in group",
|
|
),
|
|
)
|
|
|
|
s.checkNextEntry(
|
|
"INFO",
|
|
strings.Join([]string{
|
|
`msg="Test message"`,
|
|
`key1="value1"`,
|
|
`key2="value2"`,
|
|
`key3="value3"`,
|
|
`group.stack="stack in group"`,
|
|
`group.error="error in group"`,
|
|
`group.source="source in group"`,
|
|
`error="error value"`,
|
|
`source="source value"`,
|
|
`stack="stack value\nwith new lines"`,
|
|
}, " "),
|
|
)
|
|
}
|
|
|
|
func TestFormatterStructured(t *testing.T) {
|
|
suite.Run(t, new(FormatterStructuredTestSuite))
|
|
}
|