package starlog import ( "context" "fmt" "io" "time" ) type sinkSyncer interface { Sync() error } type writerSyncer interface { Sync() error } func mergeLifecycleError(current error, next error) error { if next == nil { return current } if current == nil { return next } return fmt.Errorf("%v; %w", current, next) } func WaitAsyncDrain(ctx context.Context) error { if ctx == nil { ctx = context.Background() } for { stackMu.Lock() current := stacks started := stackStarted stackMu.Unlock() if !started || current == nil || current.Len() == 0 { return nil } select { case <-ctx.Done(): return ctx.Err() case <-time.After(5 * time.Millisecond): } } } func (logger *StarLogger) Flush() error { if logger == nil || logger.logcore == nil { return nil } logger.logcore.mu.Lock() previousSwitching := logger.logcore.switching logger.logcore.switching = false logger.logcore.writePendingLocked() logger.logcore.switching = previousSwitching logger.logcore.mu.Unlock() return nil } func (logger *StarLogger) Sync() error { if logger == nil || logger.logcore == nil { return nil } if err := logger.Flush(); err != nil { return err } logger.logcore.mu.Lock() sink := logger.logcore.sink writer := logger.logcore.output logger.logcore.mu.Unlock() var err error if sink != nil { if syncer, ok := sink.(sinkSyncer); ok { err = mergeLifecycleError(err, syncer.Sync()) } return err } if writer != nil { if syncer, ok := writer.(writerSyncer); ok { err = mergeLifecycleError(err, syncer.Sync()) } } return err } // Close flushes/syncs and closes archive-managed file/sink/writer resources. // It does not wait for async handler queue; use Shutdown for graceful app exit. func (logger *StarLogger) Close() error { if logger == nil || logger.logcore == nil { return nil } var err error StopArchive(logger) err = mergeLifecycleError(err, logger.Sync()) err = mergeLifecycleError(err, CloseLogFile(logger)) logger.logcore.mu.Lock() sink := logger.logcore.sink writer := logger.logcore.output entryHandler := logger.logcore.entryHandler logger.logcore.mu.Unlock() if sink != nil { err = mergeLifecycleError(err, sink.Close()) } else if writer != nil { if closer, ok := writer.(io.Closer); ok { err = mergeLifecycleError(err, closer.Close()) } } if entryHandler != nil { if closer, ok := entryHandler.(interface{ Close() error }); ok { err = mergeLifecycleError(err, closer.Close()) } } logger.StopWrite() return err } func (logger *StarLogger) Shutdown(ctx context.Context) error { if logger == nil || logger.logcore == nil { return nil } var err error err = mergeLifecycleError(err, logger.Flush()) err = mergeLifecycleError(err, WaitAsyncDrain(ctx)) StopStacks() err = mergeLifecycleError(err, logger.Close()) return err }