Add slog attributes at pool, worker, and connection levels.
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -29,6 +31,12 @@ type MockOutgoingData struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type ExpectedLog struct {
|
||||
Level slog.Level
|
||||
Msg string
|
||||
Attrs map[string]any
|
||||
}
|
||||
|
||||
// Setup
|
||||
|
||||
func SetupTestSocket(t *testing.T) (
|
||||
@@ -117,3 +125,109 @@ func Never(t *testing.T, condition func() bool, msg string) {
|
||||
t.Helper()
|
||||
assert.Never(t, condition, NegativeTestTimeout, TestTick, msg)
|
||||
}
|
||||
|
||||
// Logging Helpers
|
||||
|
||||
func AssertLogSequence(t *testing.T, records []slog.Record, expected []ExpectedLog) {
|
||||
t.Helper()
|
||||
|
||||
recIndex := 0
|
||||
for expIndex, exp := range expected {
|
||||
found := false
|
||||
|
||||
for recIndex < len(records) {
|
||||
rec := records[recIndex]
|
||||
|
||||
if rec.Level == exp.Level && strings.Contains(rec.Message, exp.Msg) {
|
||||
allAttrsMatch := true
|
||||
for key, expectedValue := range exp.Attrs {
|
||||
if !AssertAttributePresent(t, rec, key, expectedValue) {
|
||||
allAttrsMatch = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if allAttrsMatch {
|
||||
found = true
|
||||
recIndex++
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
recIndex++
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf(
|
||||
"expected log not found: index=%d level=%v msg=%q attrs=%v",
|
||||
expIndex, exp.Level, exp.Msg, exp.Attrs,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FindLogRecord(records []slog.Record, level slog.Level, msgSnippet string) *slog.Record {
|
||||
for i := range records {
|
||||
if records[i].Level == level && strings.Contains(records[i].Message, msgSnippet) {
|
||||
return &records[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AssertAttributePresent(t *testing.T, record slog.Record, key string, expectedValue any) bool {
|
||||
t.Helper()
|
||||
|
||||
var found bool
|
||||
var actualValue any
|
||||
|
||||
record.Attrs(func(attr slog.Attr) bool {
|
||||
if attr.Key == key {
|
||||
found = true
|
||||
actualValue = attr.Value.Any()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if !found {
|
||||
t.Fatalf("attribute %q not found in log record", key)
|
||||
return false
|
||||
}
|
||||
|
||||
if !logValuesEqual(actualValue, expectedValue) {
|
||||
t.Errorf("attribute %q: expected=%v actual=%v", key, expectedValue, actualValue)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func logValuesEqual(a, b any) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
aInt, aOk := toInt64(a)
|
||||
bInt, bOk := toInt64(b)
|
||||
if aOk && bOk {
|
||||
return aInt == bInt
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toInt64(v any) (int64, bool) {
|
||||
switch val := v.(type) {
|
||||
case int:
|
||||
return int64(val), true
|
||||
case int64:
|
||||
return val, true
|
||||
case int32:
|
||||
return int64(val), true
|
||||
case int16:
|
||||
return int64(val), true
|
||||
case int8:
|
||||
return int64(val), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
+16
-7
@@ -79,20 +79,24 @@ func (m *MockSocket) SetCloseHandler(h func(code int, text string) error) {
|
||||
// Logging mocks
|
||||
|
||||
type MockSlogHandler struct {
|
||||
records []slog.Record
|
||||
records *[]slog.Record
|
||||
attrs []slog.Attr
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMockSlogHandler() *MockSlogHandler {
|
||||
records := make([]slog.Record, 0)
|
||||
return &MockSlogHandler{
|
||||
records: make([]slog.Record, 0),
|
||||
records: &records,
|
||||
attrs: make([]slog.Attr, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockSlogHandler) Handle(ctx context.Context, record slog.Record) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.records = append(m.records, record)
|
||||
record.AddAttrs(m.attrs...)
|
||||
*m.records = append(*m.records, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -101,7 +105,12 @@ func (m *MockSlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
}
|
||||
|
||||
func (m *MockSlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return m
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return &MockSlogHandler{
|
||||
records: m.records, // shared records slice
|
||||
attrs: append(m.attrs, attrs...),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockSlogHandler) WithGroup(name string) slog.Handler {
|
||||
@@ -111,13 +120,13 @@ func (m *MockSlogHandler) WithGroup(name string) slog.Handler {
|
||||
func (m *MockSlogHandler) GetRecords() []slog.Record {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
result := make([]slog.Record, len(m.records))
|
||||
copy(result, m.records)
|
||||
result := make([]slog.Record, len(*m.records))
|
||||
copy(result, *m.records)
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *MockSlogHandler) Clear() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.records = make([]slog.Record, 0)
|
||||
*m.records = make([]slog.Record, 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user