mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-01 19:20:31 +00:00
Redact and rate-limit error tracker fan-out, cap panic stack capture, sanitise stdlib fallback output, strip contexts in Info/Debug, sync the replaced logger, add Sync, UpdateLoggerE and CatchPanicRethrow, cache the PID, and add tests.
130 lines
3.0 KiB
Go
130 lines
3.0 KiB
Go
package logger
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"log"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
errortracking "github.com/bitechdev/ResolveSpec/pkg/errortracking"
|
|
)
|
|
|
|
type fakeTracker struct {
|
|
mu sync.Mutex
|
|
msgs []string
|
|
}
|
|
|
|
func (f *fakeTracker) CaptureError(context.Context, error, errortracking.Severity, map[string]interface{}) {
|
|
}
|
|
func (f *fakeTracker) CaptureMessage(_ context.Context, m string, _ errortracking.Severity, _ map[string]interface{}) {
|
|
f.mu.Lock()
|
|
f.msgs = append(f.msgs, m)
|
|
f.mu.Unlock()
|
|
}
|
|
func (f *fakeTracker) CapturePanic(context.Context, interface{}, []byte, map[string]interface{}) {}
|
|
func (f *fakeTracker) Flush(int) bool { return true }
|
|
func (f *fakeTracker) Close() error { return nil }
|
|
|
|
func TestStdlibFallbackSafe(t *testing.T) {
|
|
swapLogger(nil)
|
|
var buf bytes.Buffer
|
|
log.SetOutput(&buf)
|
|
defer log.SetOutput(nil)
|
|
|
|
Info("100%s done\nFAKE line", "")
|
|
Info("%d%% %s", 5, context.Background())
|
|
out := buf.String()
|
|
if strings.Count(out, "\n") != 2 {
|
|
t.Fatalf("log injection: %q", out)
|
|
}
|
|
}
|
|
|
|
func TestContextStrippedFromInfo(t *testing.T) {
|
|
swapLogger(nil)
|
|
var buf bytes.Buffer
|
|
log.SetOutput(&buf)
|
|
defer log.SetOutput(nil)
|
|
Info("saved %s", "widget", context.WithValue(context.Background(), "k", "secret"))
|
|
if strings.Contains(buf.String(), "EXTRA") || strings.Contains(buf.String(), "secret") {
|
|
t.Fatalf("context leaked: %q", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestRedact(t *testing.T) {
|
|
in := "connect postgres://admin:hunter2@db:5432/x failed password=abc123 Authorization: Bearer eyJ.abc-d"
|
|
out := redact(in)
|
|
for _, leak := range []string{"hunter2", "abc123", "eyJ.abc-d"} {
|
|
if strings.Contains(out, leak) {
|
|
t.Errorf("%q leaked in %q", leak, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTrackerRateLimitAndRedaction(t *testing.T) {
|
|
swapLogger(zap.NewNop().Sugar())
|
|
ft := &fakeTracker{}
|
|
InitErrorTracking(ft)
|
|
defer InitErrorTracking(nil)
|
|
|
|
for i := 0; i < 100; i++ {
|
|
Error("same template %d password=x", i)
|
|
}
|
|
if len(ft.msgs) != 1 {
|
|
t.Fatalf("dedup failed: %d events", len(ft.msgs))
|
|
}
|
|
if strings.Contains(ft.msgs[0], "password=x") {
|
|
t.Fatalf("not redacted: %q", ft.msgs[0])
|
|
}
|
|
}
|
|
|
|
func TestConcurrentUpdateAndLog(t *testing.T) {
|
|
InitErrorTracking(&fakeTracker{})
|
|
defer InitErrorTracking(nil)
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 4; i++ {
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 50; j++ {
|
|
cfg := zap.NewProductionConfig()
|
|
cfg.OutputPaths = []string{"stderr"}
|
|
_ = UpdateLoggerE(&cfg)
|
|
}
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 200; j++ {
|
|
Error("e %d", j)
|
|
_ = CloseErrorTracking()
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
_ = Sync()
|
|
}
|
|
|
|
func TestCatchPanicRethrow(t *testing.T) {
|
|
swapLogger(zap.NewNop().Sugar())
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Fatal("expected re-panic")
|
|
}
|
|
}()
|
|
func() {
|
|
defer CatchPanicRethrow("x")()
|
|
panic("boom")
|
|
}()
|
|
}
|
|
|
|
func TestCatchPanicSwallows(t *testing.T) {
|
|
swapLogger(zap.NewNop().Sugar())
|
|
func() {
|
|
defer CatchPanic("x")()
|
|
panic("boom")
|
|
}()
|
|
}
|