- Add internal/logging package with JSON file logger - Support log rotation at 10MB with 3 backup files - Add MultiHandler for dual output (stderr + file) - Include automatic secret redaction in log entries - Add transport-level request/response logging helpers
115 lines
3.3 KiB
Go
115 lines
3.3 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"log/slog"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestMultiHandlerWritesToBoth(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var buf1, buf2 bytes.Buffer
|
|
h1 := slog.NewTextHandler(&buf1, &slog.HandlerOptions{Level: slog.LevelDebug})
|
|
h2 := slog.NewJSONHandler(&buf2, &slog.HandlerOptions{Level: slog.LevelDebug})
|
|
|
|
logger := slog.New(NewMultiHandler(h1, h2))
|
|
logger.Info("hello", "key", "value")
|
|
|
|
if !strings.Contains(buf1.String(), "hello") {
|
|
t.Errorf("handler1 missing message: %s", buf1.String())
|
|
}
|
|
if !strings.Contains(buf2.String(), "hello") {
|
|
t.Errorf("handler2 missing message: %s", buf2.String())
|
|
}
|
|
}
|
|
|
|
func TestMultiHandlerRespectsIndividualLevels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var debugBuf, warnBuf bytes.Buffer
|
|
debugHandler := slog.NewTextHandler(&debugBuf, &slog.HandlerOptions{Level: slog.LevelDebug})
|
|
warnHandler := slog.NewTextHandler(&warnBuf, &slog.HandlerOptions{Level: slog.LevelWarn})
|
|
|
|
logger := slog.New(NewMultiHandler(debugHandler, warnHandler))
|
|
|
|
logger.Debug("debug msg")
|
|
logger.Warn("warn msg")
|
|
|
|
// debugBuf should have both
|
|
if !strings.Contains(debugBuf.String(), "debug msg") {
|
|
t.Error("debug handler should capture debug msg")
|
|
}
|
|
if !strings.Contains(debugBuf.String(), "warn msg") {
|
|
t.Error("debug handler should capture warn msg")
|
|
}
|
|
|
|
// warnBuf should only have warn
|
|
if strings.Contains(warnBuf.String(), "debug msg") {
|
|
t.Error("warn handler should NOT capture debug msg")
|
|
}
|
|
if !strings.Contains(warnBuf.String(), "warn msg") {
|
|
t.Error("warn handler should capture warn msg")
|
|
}
|
|
}
|
|
|
|
func TestMultiHandlerEnabled(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var buf bytes.Buffer
|
|
warnHandler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelWarn})
|
|
debugHandler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
|
|
|
|
mh := NewMultiHandler(warnHandler, debugHandler)
|
|
|
|
// Should be enabled at DEBUG because at least one handler accepts it
|
|
if !mh.Enabled(context.Background(), slog.LevelDebug) {
|
|
t.Error("should be enabled at DEBUG when one handler accepts it")
|
|
}
|
|
}
|
|
|
|
func TestMultiHandlerWithAttrs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var buf bytes.Buffer
|
|
h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
|
|
mh := NewMultiHandler(h)
|
|
|
|
logger := slog.New(mh.WithAttrs([]slog.Attr{slog.String("component", "test")}))
|
|
logger.Info("with attrs")
|
|
|
|
if !strings.Contains(buf.String(), "component") {
|
|
t.Error("WithAttrs not propagated")
|
|
}
|
|
}
|
|
|
|
func TestMultiHandlerWithGroup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var buf bytes.Buffer
|
|
h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
|
|
mh := NewMultiHandler(h)
|
|
|
|
logger := slog.New(mh.WithGroup("grp"))
|
|
logger.Info("grouped", "k", "v")
|
|
|
|
if !strings.Contains(buf.String(), "grp") {
|
|
t.Error("WithGroup not propagated")
|
|
}
|
|
}
|