feat: add precise diagnostics and optional client copy logging controls
This commit is contained in:
133
logx/timeZone_test.go
Normal file
133
logx/timeZone_test.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoggerTimeZoneIsolation(t *testing.T) {
|
||||
originalLocal := time.Local
|
||||
stamp := time.Date(2026, 7, 1, 0, 0, 0, 123456000, time.UTC)
|
||||
for _, format := range []string{"json", "text"} {
|
||||
t.Run(format, func(t *testing.T) {
|
||||
var a, b bytes.Buffer
|
||||
sh, err := New(Config{TimeZone: "Asia/Shanghai", Format: format, Writer: &a})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sh.Close()
|
||||
utc, err := New(Config{TimeZone: "UTC", Format: format, Writer: &b})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer utc.Close()
|
||||
r := slog.NewRecord(stamp, slog.LevelInfo, "timestamp", 0)
|
||||
r.AddAttrs(slog.Time("eventTime", stamp), slog.String("password", "secret"))
|
||||
if err := sh.With("service", "first").WithGroup("data").logger.Handler().Handle(context.Background(), r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := utc.logger.Handler().Handle(context.Background(), r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(a.String(), "08:00:00") || !strings.Contains(a.String(), "+08:00") || !strings.Contains(b.String(), "00:00:00") || strings.Contains(b.String(), "+08:00") {
|
||||
t.Fatalf("a=%s b=%s", a.String(), b.String())
|
||||
}
|
||||
if strings.Contains(a.String(), "secret") || strings.Contains(b.String(), "secret") {
|
||||
t.Fatal("redaction lost")
|
||||
}
|
||||
if format == "json" {
|
||||
var out map[string]any
|
||||
if err := json.Unmarshal(a.Bytes(), &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out["time"] != "2026-07-01T08:00:00.123456+08:00" || out["data"].(map[string]any)["eventTime"] != "2026-07-01T00:00:00.123456Z" {
|
||||
t.Fatal(out)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if time.Local != originalLocal {
|
||||
t.Fatal("global time.Local changed")
|
||||
}
|
||||
}
|
||||
func TestLoggerTimeZoneValidationAndDST(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "not-created", "app.log")
|
||||
if _, err := New(Config{TimeZone: "Secret/Not_A_Zone", File: path}); err == nil || strings.Contains(err.Error(), "Secret") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Dir(path)); !os.IsNotExist(err) {
|
||||
t.Fatal("invalid zone opened writer")
|
||||
}
|
||||
var out bytes.Buffer
|
||||
l, err := New(Config{TimeZone: "America/New_York", Writer: &out})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
for _, month := range []time.Month{time.January, time.July} {
|
||||
r := slog.NewRecord(time.Date(2026, month, 1, 12, 0, 0, 0, time.UTC), slog.LevelInfo, "dst", 0)
|
||||
if err := l.logger.Handler().Handle(context.Background(), r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(out.String(), "-05:00") || !strings.Contains(out.String(), "-04:00") {
|
||||
t.Fatal(out.String())
|
||||
}
|
||||
// Explicit empty location preserves an incoming record's original zone.
|
||||
out.Reset()
|
||||
plain, _ := New(Config{Writer: &out})
|
||||
defer plain.Close()
|
||||
r := slog.NewRecord(time.Date(2026, 1, 1, 0, 0, 0, 0, time.FixedZone("original", 7200)), slog.LevelInfo, "unchanged", 0)
|
||||
plain.logger.Handler().Handle(context.Background(), r)
|
||||
if !strings.Contains(out.String(), "+02:00") {
|
||||
t.Fatal(out.String())
|
||||
}
|
||||
}
|
||||
func TestLoggerTimeZoneConcurrentChildren(t *testing.T) {
|
||||
var out bytes.Buffer
|
||||
l, err := New(Config{TimeZone: "Asia/Shanghai", Writer: &out})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 20; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
child := l.With("worker", i).WithGroup("event")
|
||||
for n := 0; n < 10; n++ {
|
||||
child.Standard(slog.LevelInfo).Print("message")
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if strings.Count(out.String(), "+08:00") != 200 {
|
||||
t.Fatal("timestamps missing")
|
||||
}
|
||||
}
|
||||
func BenchmarkLoggerTimeZone(b *testing.B) {
|
||||
for _, zone := range []string{"", "UTC", "Asia/Shanghai"} {
|
||||
b.Run(zone, func(b *testing.B) {
|
||||
l, err := New(Config{TimeZone: zone, Writer: io.Discard})
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.InfoContext(context.Background(), "benchmark", "count", 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user