package starlog import ( "context" "b612.me/starlog/internal/observerx" ) type Observer struct { buffer *observerx.Buffer } func (observer *Observer) ensureBuffer() *observerx.Buffer { if observer == nil { return nil } if observer.buffer == nil { observer.buffer = observerx.NewBuffer() } return observer.buffer } func NewObserver() *Observer { return &Observer{ buffer: observerx.NewBuffer(), } } func NewObserverWithLimit(limit int) *Observer { observer := NewObserver() observer.SetLimit(limit) return observer } func (observer *Observer) Handle(ctx context.Context, entry *Entry) error { _ = ctx if observer == nil || entry == nil { return nil } buffer := observer.ensureBuffer() if buffer == nil { return nil } entryCopy := *entry entryCopy.Fields = cloneFields(entry.Fields) buffer.Add(entryCopy) return nil } func (observer *Observer) SetLimit(limit int) { if observer == nil { return } buffer := observer.ensureBuffer() if buffer == nil { return } buffer.SetLimit(limit) } func (observer *Observer) Limit() int { if observer == nil { return 0 } buffer := observer.ensureBuffer() if buffer == nil { return 0 } return buffer.Limit() } func (observer *Observer) Count() int { if observer == nil { return 0 } buffer := observer.ensureBuffer() if buffer == nil { return 0 } return buffer.Count() } func (observer *Observer) Dropped() uint64 { if observer == nil { return 0 } buffer := observer.ensureBuffer() if buffer == nil { return 0 } return buffer.Dropped() } func (observer *Observer) Entries() []Entry { if observer == nil { return nil } buffer := observer.ensureBuffer() if buffer == nil { return nil } items := buffer.Snapshot() result := make([]Entry, 0, len(items)) for _, raw := range items { entry, ok := raw.(Entry) if !ok { continue } item := entry item.Fields = cloneFields(entry.Fields) result = append(result, item) } return result } func (observer *Observer) Last() (Entry, bool) { if observer == nil { return Entry{}, false } buffer := observer.ensureBuffer() if buffer == nil { return Entry{}, false } raw, ok := buffer.Last() if !ok { return Entry{}, false } entry, ok := raw.(Entry) if !ok { return Entry{}, false } entry.Fields = cloneFields(entry.Fields) return entry, true } func (observer *Observer) TakeAll() []Entry { if observer == nil { return nil } buffer := observer.ensureBuffer() if buffer == nil { return nil } items := buffer.TakeAll() if len(items) == 0 { return nil } result := make([]Entry, 0, len(items)) for _, raw := range items { entry, ok := raw.(Entry) if !ok { continue } item := entry item.Fields = cloneFields(entry.Fields) result = append(result, item) } return result } func (observer *Observer) Reset() { if observer == nil { return } buffer := observer.ensureBuffer() if buffer == nil { return } buffer.Reset() } type testHookHandler struct { observer *Observer next Handler } func (handler *testHookHandler) Handle(ctx context.Context, entry *Entry) error { if handler == nil { return nil } var firstErr error if handler.observer != nil { if err := handler.observer.Handle(ctx, entry); err != nil && firstErr == nil { firstErr = err } } if handler.next != nil { if err := handler.next.Handle(ctx, entry); err != nil && firstErr == nil { firstErr = err } } return firstErr } type TestHook struct { logger *StarLogger observer *Observer previous Handler handler Handler } func NewTestHook(logger *StarLogger) *TestHook { if logger == nil { return nil } observer := NewObserver() previous := logger.GetEntryHandler() wrapper := &testHookHandler{ observer: observer, next: previous, } logger.SetEntryHandler(wrapper) return &TestHook{ logger: logger, observer: observer, previous: previous, handler: wrapper, } } func (hook *TestHook) Observer() *Observer { if hook == nil { return nil } return hook.observer } func (hook *TestHook) Entries() []Entry { if hook == nil || hook.observer == nil { return nil } return hook.observer.Entries() } func (hook *TestHook) Count() int { if hook == nil || hook.observer == nil { return 0 } return hook.observer.Count() } func (hook *TestHook) Last() (Entry, bool) { if hook == nil || hook.observer == nil { return Entry{}, false } return hook.observer.Last() } func (hook *TestHook) Reset() { if hook == nil || hook.observer == nil { return } hook.observer.Reset() } // Close tries to restore the previous entry handler. // It returns false when current handler was replaced externally. func (hook *TestHook) Close() bool { if hook == nil || hook.logger == nil { return false } current := hook.logger.GetEntryHandler() if current != hook.handler { return false } hook.logger.SetEntryHandler(hook.previous) return true }