|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// 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 (
|
|
|
|
|
"context"
|
|
|
|
|
"errs"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"syncs/queue"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type (
|
|
|
|
|
|
|
|
|
|
ActiveLogger struct {
|
|
|
|
|
loggerObject
|
|
|
|
|
extenders EntryExtenders
|
|
|
|
|
filters EntryFilters
|
|
|
|
|
|
|
|
|
|
scopes loggerScopes
|
|
|
|
|
loggers []Logger
|
|
|
|
|
queue queue.GQueue[*Entry]
|
|
|
|
|
|
|
|
|
|
ctx context.Context
|
|
|
|
|
w sync.WaitGroup
|
|
|
|
|
asyncMode bool
|
|
|
|
|
entryChan chan *Entry
|
|
|
|
|
|
|
|
|
|
logErrors bool
|
|
|
|
|
|
|
|
|
|
onErrorFn ActiveLoggerErrorFnc
|
|
|
|
|
|
|
|
|
|
onEntryErrorFn ActiveLoggerEntryFnc
|
|
|
|
|
onEntryFatalFn ActiveLoggerEntryFnc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActiveLoggerFnc func(manager *ActiveLogger) error
|
|
|
|
|
|
|
|
|
|
ActiveLoggerErrorFnc func(manager *ActiveLogger, err error, logger Logger)
|
|
|
|
|
|
|
|
|
|
ActiveLoggerEntryFnc func(manager *ActiveLogger, entry *Entry)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func NewActiveLogger(level Level, options ...ActiveLoggerFnc) (*ActiveLogger,error) {
|
|
|
|
|
l := &ActiveLogger{
|
|
|
|
|
queue : *queue.NewGQueue[*Entry](false),
|
|
|
|
|
ctx : context.Background(),
|
|
|
|
|
logErrors : true,
|
|
|
|
|
onEntryErrorFn: nopEntryHandler,
|
|
|
|
|
onEntryFatalFn: nopEntryHandler,
|
|
|
|
|
}
|
|
|
|
|
l.loggerObject = *newLoggerObject(G.RootLoggerName,level,l.log)
|
|
|
|
|
for _,o := range options {
|
|
|
|
|
if er := o(l); er != nil {
|
|
|
|
|
return nil,er
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return l,nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) Start () error {
|
|
|
|
|
l.Lock()
|
|
|
|
|
if l.state == StateStarted {
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return G.ErrLoggerStarted.WrapWithFieldString(entryFieldLogger,l.name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l.state == StateStopped {
|
|
|
|
|
l.errors = l.errors.Reset()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if l.asyncMode {
|
|
|
|
|
if l.ctx.Err() != nil {
|
|
|
|
|
return errs.NewErrCause(G.ErrLoggerNotStarted.WrapWithFieldString(entryFieldLogger,l.name),l.ctx.Err())
|
|
|
|
|
}
|
|
|
|
|
l.entryChan = make(chan *Entry)
|
|
|
|
|
go l.runLogger()
|
|
|
|
|
}
|
|
|
|
|
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 *ActiveLogger) Stop () error {
|
|
|
|
|
l.Lock()
|
|
|
|
|
if l.state != StateStarted {
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return G.ErrLoggerNotStarted
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if l.asyncMode {
|
|
|
|
|
close(l.entryChan)
|
|
|
|
|
l.entryChan = nil
|
|
|
|
|
}
|
|
|
|
|
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 *ActiveLogger) 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 *ActiveLogger) 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 *ActiveLogger) 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 *ActiveLogger) NewLogger(name string, level Level, options ...LoggerCoreFnc) error {
|
|
|
|
|
lg,er := NewLoggerCore(name,level,options...)
|
|
|
|
|
if er != nil {
|
|
|
|
|
return er
|
|
|
|
|
}
|
|
|
|
|
return l.AddLogger(lg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) 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 *ActiveLogger) 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 *ActiveLogger) 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 *ActiveLogger) Log (entry *Entry) error {
|
|
|
|
|
if entry == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if entry.Logger == nil {
|
|
|
|
|
entry.Logger = l
|
|
|
|
|
}
|
|
|
|
|
return l.logEntry(entry)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) log (level Level, msg string, err error,options []EntryFnc) {
|
|
|
|
|
_ = l.logEntry(AcquireEntry(l, level, msg, err, EntryWithExtender(l.extenders)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) logEntry(entry *Entry) error {
|
|
|
|
|
l.Lock()
|
|
|
|
|
|
|
|
|
|
if l.state != StateStarted {
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return G.ErrLoggerNotStarted
|
|
|
|
|
}
|
|
|
|
|
if !l.level.Has(entry.Level) {
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if entry.Excludes.Has(l.name) {
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if !l.filters.Filter(entry) {
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !l.asyncMode {
|
|
|
|
|
er := l.dispatchEntry(entry)
|
|
|
|
|
l.Unlock()
|
|
|
|
|
return er
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.w.Add(1)
|
|
|
|
|
l.entryChan <- entry
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) dispatchEntry(entry *Entry) error {
|
|
|
|
|
|
|
|
|
|
er := l.handleLoggers(entry,func(c Logger, e *Entry) error {
|
|
|
|
|
if c.State() == StateStarted {
|
|
|
|
|
return c.Log(e)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
},G.FmtMsgLogErr)
|
|
|
|
|
|
|
|
|
|
if entry.Logger == l {
|
|
|
|
|
if entry.Level == LevelFatal {
|
|
|
|
|
l.onEntryFatalFn(l, entry)
|
|
|
|
|
}
|
|
|
|
|
if entry.Level == LevelError {
|
|
|
|
|
l.onEntryErrorFn(l, entry)
|
|
|
|
|
}
|
|
|
|
|
ReleaseEntry(entry)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.handleQueue()
|
|
|
|
|
|
|
|
|
|
return er
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) handleError (err error, entry *Entry, logger Logger) {
|
|
|
|
|
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 {
|
|
|
|
|
go l.onErrorFn(l,err,logger)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) handleQueue() {
|
|
|
|
|
for en := l.queue.Pop(); en != nil; {
|
|
|
|
|
_ = l.dispatchEntry(en)
|
|
|
|
|
en = l.queue.Pop()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) 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,fmt.Sprintf(msg,nm)).WrapWithFieldString(errFieldLogger,nm)
|
|
|
|
|
if l.logErrors {
|
|
|
|
|
en = AcquireEntry(l,LevelError,ms,ne,
|
|
|
|
|
EntryWithExclude (nm),
|
|
|
|
|
EntryWithField (entryFieldLogger,nm,true))
|
|
|
|
|
}
|
|
|
|
|
l.handleError(ne,en,co)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errs.NewErrMultiIf(el)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) findLogger(name string) int {
|
|
|
|
|
for i :=0; i < len(l.loggers); i++ {
|
|
|
|
|
if l.loggers[i].Name() == name {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *ActiveLogger) runLogger () {
|
|
|
|
|
//var w sync.WaitGroup
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <- l.ctx.Done():
|
|
|
|
|
l.w.Wait()
|
|
|
|
|
//Esperamos que todas las entradas sean
|
|
|
|
|
//despachadas y llamamos a Stop
|
|
|
|
|
_ = l.Stop()
|
|
|
|
|
return
|
|
|
|
|
case e,ok := <- l.entryChan:
|
|
|
|
|
if !ok {
|
|
|
|
|
l.w.Wait()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
l.w.Add(1)
|
|
|
|
|
_ = l.dispatchEntry(e)
|
|
|
|
|
l.Unlock()
|
|
|
|
|
l.w.Done()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithLogErrors (enable bool) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-log-errors")
|
|
|
|
|
}
|
|
|
|
|
a.logErrors = enable
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithFilters (configFilters EntryFiltersFnc) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-filters")
|
|
|
|
|
}
|
|
|
|
|
configFilters(&a.filters)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithOnFatal (onFatal ActiveLoggerEntryFnc) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-on-fatal")
|
|
|
|
|
}
|
|
|
|
|
a.onEntryFatalFn = onFatal
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithOnError (onError ActiveLoggerErrorFnc) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-on-error")
|
|
|
|
|
}
|
|
|
|
|
a.onErrorFn = onError
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithOnEntryError (onError ActiveLoggerEntryFnc) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-on-entry-error")
|
|
|
|
|
}
|
|
|
|
|
if onError == nil {
|
|
|
|
|
a.onEntryErrorFn = nopEntryHandler
|
|
|
|
|
} else {
|
|
|
|
|
a.onEntryErrorFn = onError
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithOnEntryFatal (onError ActiveLoggerEntryFnc) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-on-entry-error")
|
|
|
|
|
}
|
|
|
|
|
if onError == nil {
|
|
|
|
|
a.onEntryFatalFn = nopEntryHandler
|
|
|
|
|
} else {
|
|
|
|
|
a.onEntryFatalFn = onError
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithExtender (extender ...EntryExtender) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-extender")
|
|
|
|
|
}
|
|
|
|
|
a.extenders = append(a.extenders,extender...)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ActiveLoggerWithContext (ctx context.Context, asyncMode bool) ActiveLoggerFnc {
|
|
|
|
|
return func(a *ActiveLogger) error {
|
|
|
|
|
if a.State() != StateCreated {
|
|
|
|
|
return G.ErrInvalidState.WrapWithMessage("config-active-logger-with-context")
|
|
|
|
|
}
|
|
|
|
|
if ctx == nil {
|
|
|
|
|
ctx = context.Background()
|
|
|
|
|
} else if ctx.Err() != nil {
|
|
|
|
|
return *errs.NewErrCause(G.ErrInvalidContext.WrapWithMessage("config-active-logger-with-context"),ctx.Err())
|
|
|
|
|
}
|
|
|
|
|
a.ctx = ctx
|
|
|
|
|
if asyncMode {
|
|
|
|
|
a.asyncMode = true
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func nopEntryHandler (_ *ActiveLogger,_ *Entry) {}
|