171 lines
3.7 KiB
Go
171 lines
3.7 KiB
Go
|
|
package starlog
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"path/filepath"
|
||
|
|
"sync/atomic"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
type syncWriteBuffer struct {
|
||
|
|
bytes.Buffer
|
||
|
|
syncCalls uint64
|
||
|
|
}
|
||
|
|
|
||
|
|
func (buffer *syncWriteBuffer) Sync() error {
|
||
|
|
atomic.AddUint64(&buffer.syncCalls, 1)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type closeSink struct {
|
||
|
|
closeCalls uint64
|
||
|
|
}
|
||
|
|
|
||
|
|
func (sink *closeSink) Write(data []byte) error {
|
||
|
|
_ = data
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (sink *closeSink) Close() error {
|
||
|
|
atomic.AddUint64(&sink.closeCalls, 1)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFlushDrainsPendingWrites(t *testing.T) {
|
||
|
|
var output bytes.Buffer
|
||
|
|
logger := newStructuredTestLogger(&output)
|
||
|
|
logger.SetSwitching(true)
|
||
|
|
logger.Infoln("pending")
|
||
|
|
|
||
|
|
if output.Len() != 0 {
|
||
|
|
t.Fatalf("pending logs should not be written while switching=true")
|
||
|
|
}
|
||
|
|
if err := logger.Flush(); err != nil {
|
||
|
|
t.Fatalf("Flush failed: %v", err)
|
||
|
|
}
|
||
|
|
if output.Len() == 0 {
|
||
|
|
t.Fatalf("Flush should write buffered logs")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSyncCallsWriterSync(t *testing.T) {
|
||
|
|
var writer syncWriteBuffer
|
||
|
|
logger := NewStarlog(&writer)
|
||
|
|
logger.SetShowStd(false)
|
||
|
|
logger.SetShowColor(false)
|
||
|
|
logger.SetShowOriginFile(false)
|
||
|
|
logger.SetShowFuncName(false)
|
||
|
|
logger.SetShowFlag(false)
|
||
|
|
|
||
|
|
logger.Infoln("sync")
|
||
|
|
if err := logger.Sync(); err != nil {
|
||
|
|
t.Fatalf("Sync failed: %v", err)
|
||
|
|
}
|
||
|
|
if atomic.LoadUint64(&writer.syncCalls) == 0 {
|
||
|
|
t.Fatalf("Sync should call underlying writer Sync method")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCloseClosesSinkAndStopsWrite(t *testing.T) {
|
||
|
|
sink := &closeSink{}
|
||
|
|
logger := NewStarlog(nil)
|
||
|
|
logger.SetShowStd(false)
|
||
|
|
logger.SetShowColor(false)
|
||
|
|
logger.SetShowOriginFile(false)
|
||
|
|
logger.SetShowFuncName(false)
|
||
|
|
logger.SetShowFlag(false)
|
||
|
|
logger.SetSink(sink)
|
||
|
|
|
||
|
|
if err := logger.Close(); err != nil {
|
||
|
|
t.Fatalf("Close failed: %v", err)
|
||
|
|
}
|
||
|
|
if atomic.LoadUint64(&sink.closeCalls) == 0 {
|
||
|
|
t.Fatalf("Close should call sink.Close")
|
||
|
|
}
|
||
|
|
if !logger.IsWriteStopped() {
|
||
|
|
t.Fatalf("Close should stop writer")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWaitAsyncDrainContextTimeout(t *testing.T) {
|
||
|
|
stackMu.Lock()
|
||
|
|
stackStarted = true
|
||
|
|
stacks = newStarChanStack(1)
|
||
|
|
stackStopChan = nil
|
||
|
|
stackDoneChan = nil
|
||
|
|
stackMu.Unlock()
|
||
|
|
defer func() {
|
||
|
|
stackMu.Lock()
|
||
|
|
if stacks != nil {
|
||
|
|
_ = stacks.Close()
|
||
|
|
}
|
||
|
|
stackStarted = false
|
||
|
|
stacks = nil
|
||
|
|
stackStopChan = nil
|
||
|
|
stackDoneChan = nil
|
||
|
|
stackMu.Unlock()
|
||
|
|
}()
|
||
|
|
|
||
|
|
if err := stacks.Push("x"); err != nil {
|
||
|
|
t.Fatalf("prepare queue failed: %v", err)
|
||
|
|
}
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
||
|
|
defer cancel()
|
||
|
|
err := WaitAsyncDrain(ctx)
|
||
|
|
if !errors.Is(err, context.DeadlineExceeded) {
|
||
|
|
t.Fatalf("WaitAsyncDrain should return context deadline, got %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestShutdownStopsAsyncStacks(t *testing.T) {
|
||
|
|
resetAsyncMetricsForTest()
|
||
|
|
defer func() {
|
||
|
|
resetAsyncMetricsForTest()
|
||
|
|
StopStacks()
|
||
|
|
}()
|
||
|
|
|
||
|
|
logger := NewStarlog(nil)
|
||
|
|
logger.SetShowStd(false)
|
||
|
|
handled := make(chan struct{}, 1)
|
||
|
|
logger.SetHandler(func(LogData) {
|
||
|
|
select {
|
||
|
|
case handled <- struct{}{}:
|
||
|
|
default:
|
||
|
|
}
|
||
|
|
})
|
||
|
|
logger.Infoln("shutdown")
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||
|
|
defer cancel()
|
||
|
|
if err := logger.Shutdown(ctx); err != nil {
|
||
|
|
t.Fatalf("Shutdown failed: %v", err)
|
||
|
|
}
|
||
|
|
select {
|
||
|
|
case <-handled:
|
||
|
|
case <-time.After(200 * time.Millisecond):
|
||
|
|
t.Fatalf("async handler should complete before shutdown")
|
||
|
|
}
|
||
|
|
|
||
|
|
stackMu.Lock()
|
||
|
|
started := stackStarted
|
||
|
|
stackMu.Unlock()
|
||
|
|
if started {
|
||
|
|
t.Fatalf("Shutdown should stop async stacks")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCloseManagedLogFileNoDoubleCloseError(t *testing.T) {
|
||
|
|
logger := NewStarlog(nil)
|
||
|
|
logger.SetShowStd(false)
|
||
|
|
logPath := filepath.Join(testBinDir(t), "lifecycle_close.log")
|
||
|
|
if err := SetLogFile(logPath, logger, false); err != nil {
|
||
|
|
t.Fatalf("SetLogFile failed: %v", err)
|
||
|
|
}
|
||
|
|
if err := logger.Close(); err != nil {
|
||
|
|
t.Fatalf("Close should not fail for managed log file: %v", err)
|
||
|
|
}
|
||
|
|
}
|