- 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
124 lines
3.0 KiB
Go
124 lines
3.0 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 (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSetupCreatesLogFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dir := t.TempDir()
|
|
fl := Setup(dir)
|
|
defer fl.Close()
|
|
|
|
fl.Logger.Info("test message", "key", "value")
|
|
fl.Close()
|
|
|
|
logPath := filepath.Join(dir, logSubDir, logFileName)
|
|
data, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read log file: %v", err)
|
|
}
|
|
if !strings.Contains(string(data), "test message") {
|
|
t.Errorf("log file does not contain 'test message': %s", data)
|
|
}
|
|
if !strings.Contains(string(data), `"key":"value"`) {
|
|
t.Errorf("log file does not contain structured key: %s", data)
|
|
}
|
|
}
|
|
|
|
func TestSetupInvalidDirReturnsNopLogger(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fl := Setup("/nonexistent/path/that/cannot/be/created")
|
|
defer fl.Close()
|
|
|
|
// Should not panic
|
|
fl.Logger.Info("should be discarded")
|
|
}
|
|
|
|
func TestRotatingWriterRotatesOnSize(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dir := t.TempDir()
|
|
logPath := filepath.Join(dir, "test.log")
|
|
w := newRotatingWriter(logPath, 100, 2) // 100 bytes max
|
|
if err := w.open(); err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
|
|
// Write enough data to trigger rotation
|
|
payload := strings.Repeat("x", 60) + "\n"
|
|
w.Write([]byte(payload)) // 61 bytes
|
|
w.Write([]byte(payload)) // triggers rotate at 122 bytes
|
|
|
|
// Should have created a backup
|
|
if _, err := os.Stat(logPath + ".1"); err != nil {
|
|
t.Errorf("backup .1 not created: %v", err)
|
|
}
|
|
|
|
// Write more to trigger second rotation
|
|
w.Write([]byte(payload))
|
|
w.Write([]byte(payload))
|
|
w.close()
|
|
|
|
if _, err := os.Stat(logPath + ".2"); err != nil {
|
|
t.Errorf("backup .2 not created: %v", err)
|
|
}
|
|
|
|
// Active log should be small (last write only)
|
|
info, _ := os.Stat(logPath)
|
|
if info.Size() > 100 {
|
|
t.Errorf("active log too large after rotation: %d bytes", info.Size())
|
|
}
|
|
}
|
|
|
|
func TestRotatingWriterMaxBackups(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dir := t.TempDir()
|
|
logPath := filepath.Join(dir, "test.log")
|
|
w := newRotatingWriter(logPath, 50, 2)
|
|
if err := w.open(); err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
|
|
payload := strings.Repeat("a", 51) + "\n"
|
|
|
|
// Force multiple rotations
|
|
for i := 0; i < 5; i++ {
|
|
w.Write([]byte(payload))
|
|
}
|
|
w.close()
|
|
|
|
// .3 should NOT exist (maxBackups=2)
|
|
if _, err := os.Stat(logPath + ".3"); err == nil {
|
|
t.Error("backup .3 should not exist with maxBackups=2")
|
|
}
|
|
}
|
|
|
|
func TestCloseOnNilLogger(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var fl *FileLogger
|
|
if err := fl.Close(); err != nil {
|
|
t.Errorf("Close on nil should not error: %v", err)
|
|
}
|
|
}
|