161 lines
4.1 KiB
Go
161 lines
4.1 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 (
|
|
"errors"
|
|
"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)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageFileLoggerCloseIsTerminal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dir := t.TempDir()
|
|
fl := Setup(dir)
|
|
logPath := filepath.Join(dir, logSubDir, logFileName)
|
|
fl.Logger.Info("before close")
|
|
if err := fl.Close(); err != nil {
|
|
t.Fatalf("close logger: %v", err)
|
|
}
|
|
if err := os.Remove(logPath); err != nil {
|
|
t.Fatalf("remove closed log file: %v", err)
|
|
}
|
|
if err := fl.writer.open(); !errors.Is(err, os.ErrClosed) {
|
|
t.Fatalf("open after close = %v, want os.ErrClosed", err)
|
|
}
|
|
fl.writer.mu.Lock()
|
|
err := fl.writer.reopenLocked()
|
|
fl.writer.mu.Unlock()
|
|
if !errors.Is(err, os.ErrClosed) {
|
|
t.Fatalf("reopen after close = %v, want os.ErrClosed", err)
|
|
}
|
|
|
|
n, err := fl.Writer().Write([]byte("late write"))
|
|
if n != 0 || !errors.Is(err, os.ErrClosed) {
|
|
t.Fatalf("write after close = (%d, %v), want (0, os.ErrClosed)", n, err)
|
|
}
|
|
fl.Logger.Info("late structured write")
|
|
if _, err := os.Stat(logPath); !os.IsNotExist(err) {
|
|
t.Fatalf("closed logger recreated log file: %v", err)
|
|
}
|
|
if err := fl.Close(); err != nil {
|
|
t.Fatalf("second close: %v", err)
|
|
}
|
|
}
|