// ------------------------------------------------------------------------ // Project active2 // Active Thing (activething.com) git.activething.com/go // // File name logger_manager.go // Created by DEV // Modified 26/04/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ // 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 alogs import ( "errs" "fmt" "strings" "sync" "sync/atomic" "syncs/queue" ) type ( LoggerManager struct { sync.RWMutex filters EntryFilters extenders EntryExtenders queue queue.GQueue[*Entry] scopes loggerScopes loggers []Logger async bool chanLog chan *Entry level Level name string state State count int64 logErrors bool errors errs.ErrorList onErrorFn LoggerManagerErrorFnc onFatalFn LoggerManagerEntryFnc } LoggerManagerFnc func(manager *LoggerManager) LoggerManagerErrorFnc func(manager *LoggerManager, err error) LoggerManagerEntryFnc func(manager *LoggerManager, entry *Entry) ) func NewLoggerManager(name string, level Level, options ...LoggerManagerFnc) *LoggerManager { l := &LoggerManager{ filters : *NewEntryFilters(), level : level, state : StateCreated, logErrors: true, queue : *queue.NewGQueue[*Entry](false), } for _,o := range options { o(l) } return l } func (l *LoggerManager) Name() string { return l.name } func (l *LoggerManager) Level() Level { l.RLock() lv := l.level l.RUnlock() return lv } func (l *LoggerManager) SetLevel (level Level) { l.Lock() l.level = level l.Unlock() } func (l *LoggerManager) Count () int64 { return atomic.LoadInt64(&l.count) } func (l *LoggerManager) incCount () { atomic.AddInt64(&l.count,1) } func (l *LoggerManager) State () State { l.RLock() s := l.state l.RUnlock() return s } func (l *LoggerManager) Errors () errs.ErrorList { l.RLock() ls := l.errors l.RUnlock() return ls } func (l *LoggerManager) Start () error { l.Lock() if l.state == StateStarted { l.Unlock() return G.ErrLoggerStarted } if l.state == StateStopped { l.errors = l.errors.Reset() } l.state = StateStarted er := l.handleLoggers(nil,func(c Logger, _ *Entry) error { return c.Start() }, G.FmtMsgStartErr,) if er != nil { l.handleQueue() } l.Unlock() return er } func (l *LoggerManager) Stop () error { l.Lock() if l.state != StateStarted { l.Unlock() return G.ErrLoggerNotStarted } l.state = StateStopped er := l.handleLoggers(nil,func(c Logger, _ *Entry) error { return c.Stop() }, G.FmtMsgStopErr) if er != nil { l.handleQueue() } l.Unlock() return er } func (l *LoggerManager) Flush () error { l.Lock() er := l.handleLoggers(nil,func(c Logger,_ *Entry) error { return c.Flush() }, G.FmtMsgFlushErr) if er != nil { l.handleQueue() } l.Unlock() return er } func (l *LoggerManager) GetLogger(name string) Logger { l.RLock() var co Logger if p := l.findLogger(name); p != -1 { co = l.loggers[p] } l.RUnlock() return co } func (l *LoggerManager) AddLogger(logger Logger) error { l.Lock() nm := logger.Name() if p := l.findLogger(nm); p != -1 { l.Unlock() return G.ErrDuplicatedLogger.WrapWithFieldString(errFieldLogger,nm) } var er error l.loggers = append(l.loggers,logger) if l.state == StateStarted { er = logger.Start() } l.Unlock() return er } func (l *LoggerManager) DelLogger(name string) error { l.Lock() p := l.findLogger(name) if p != -1 { l.Unlock() return G.ErrNotExistsLogger.WrapWithFieldString(entryFieldLogger,name) } er := l.loggers[p].Stop() l.loggers = append(l.loggers[:p],l.loggers[p+1:]...) l.Unlock() return er } func (l *LoggerManager) GetScope(name string) *LoggerScope { ns := strings.Split(G.ScopeNameTransforms.Transform(name),g.scopeSepStr) l.RLock() sc := l.scopes.getScope(ns[0]) if sc != nil && len(ns) > 1 { sc = sc.getScope(ns[1:]) } l.RUnlock() return sc } func (l *LoggerManager) AddScope(name string, options ...LoggerScopeFnc) (*LoggerScope,error) { nm := G.ScopeNameTransforms.Transform(name) ns := strings.Split(nm,g.scopeSepStr) l.Lock() sc := l.scopes.getScope(ns[0]) if sc == nil { sc = newLoggerScope(nm,l.level,l,"",nil,options) l.scopes = append(l.scopes,sc) if len(ns) == 1 { l.Unlock() return sc,nil } } else if len(ns) == 1 { l.Unlock() return nil,G.ErrDuplicatedScope.WrapWithFieldString(errFieldScope,ns[0]) } l.Unlock() return sc.newScope(ns[1:],options) } func (l *LoggerManager) Fatal(msg string, err error, ops ...EntryFnc){ l.log(LevelFatal,msg,err,ops) } func (l *LoggerManager) Error(msg string, err error, ops ...EntryFnc){ l.log(LevelError,msg,err,ops) } func (l *LoggerManager) Warn (msg string, ops ...EntryFnc){ l.log(LevelWarn,msg,nil,ops) } func (l *LoggerManager) Info (msg string, ops ...EntryFnc){ l.log(LevelInfo,msg,nil,ops) } func (l *LoggerManager) Debug(msg string, ops ...EntryFnc){ l.log(LevelDebug,msg,nil,ops) } func (l *LoggerManager) Data (msg string, data any, ops ...EntryFnc){ l.log(LevelData,msg,nil,[]EntryFnc{EntryWithData(data,ops...)}) } func (l *LoggerManager) Log (entry *Entry) error { if entry == nil { return nil } l.Lock() if l.state != StateStarted { l.Unlock() return G.ErrLoggerNotStarted } if !l.level.Has(entry.Level) { l.Unlock() return nil } er := l.logEntry(entry,false) if er != nil { l.handleQueue() } l.Unlock() return er } func (l *LoggerManager) log (level Level, msg string, err error,options []EntryFnc) { l.Lock() if l.state != StateStarted || !l.level.Has(level){ l.Unlock() return } er := l.logEntry(AcquireEntry(l,level,msg,err,options...),true) if er != nil { l.handleQueue() } l.Unlock() return } func (l *LoggerManager) logEntry(entry *Entry, self bool) error { if !l.filters.Filter(entry) { return nil } if self { l.incCount() } else if entry.Logger == nil { entry.Logger = l } l.extenders.ExtendEntry(entry) er := l.handleLoggers(entry,func(c Logger, e *Entry) error { return c.Log(e) },G.FmtMsgLogErr) if self { if entry.Level == LevelFatal && l.onFatalFn != nil { l.onFatalFn(l, entry) } ReleaseEntry(entry) } return er } func (l *LoggerManager) handleError (err error, entry *Entry) { if err == nil { return } l.errors = l.errors.Append(err) if l.logErrors { if entry == nil { entry = AcquireEntry(l,LevelError,err.Error(),err) } l.queue.Push(entry) } if l.onErrorFn != nil { l.onErrorFn(l,err) } } func (l *LoggerManager) handleQueue() { for en := l.queue.Pop(); en != nil; { _ = l.logEntry(en,true) en = l.queue.Pop() } } func (l *LoggerManager) handleLoggers(entry *Entry, doFn func(c Logger, e *Entry) error, msg string) error { var el errs.ErrorList for _,co := range l.loggers { if er := doFn(co,entry); er != nil { var en *Entry nm := co.Name() ms := fmt.Sprintf(msg,nm) ne := errs.NewErrMessage(er,msg) if l.logErrors { en = AcquireEntry(l,LevelError,ms,ne, EntryWithExtender(l.extenders), EntryWithExclude (nm), EntryWithField (entryFieldLogger,nm,true)) } l.handleError(er,en) } } return errs.NewErrMultiIf(el) } func (l *LoggerManager) findLogger(name string) int { for i :=0; i < len(l.loggers); i++ { if l.loggers[i].Name() == name { return i } } return -1 } func (l *LoggerManager) runLogger () { for { select { case e := <- l.chanLog : //todo } } } func LoggerManagerWithFilters (configFilters EntryFiltersFnc) LoggerManagerFnc { return func(a *LoggerManager) { configFilters(&a.filters) } } func LoggerManagerWithOnFatal (onFatal LoggerManagerEntryFnc) LoggerManagerFnc { return func(a *LoggerManager) { a.onFatalFn = onFatal } } func LoggerManagerWithOnError (onError LoggerManagerErrorFnc) LoggerManagerFnc { return func(a *LoggerManager) { a.onErrorFn = onError } } func LoggerManagerWithExtender (extender ...EntryExtender) LoggerManagerFnc { return func(a *LoggerManager) { a.extenders = append(a.extenders,extender...) } }