starlog/observer.go
2026-03-19 16:37:57 +08:00

269 lines
4.8 KiB
Go

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
}