diff options
Diffstat (limited to 'modules/log/event.go')
-rw-r--r-- | modules/log/event.go | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/modules/log/event.go b/modules/log/event.go new file mode 100644 index 0000000000..2ec1f9587d --- /dev/null +++ b/modules/log/event.go @@ -0,0 +1,335 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package log + +import ( + "fmt" + "sync" + "time" +) + +// Event represents a logging event +type Event struct { + level Level + msg string + caller string + filename string + line int + time time.Time + stacktrace string +} + +// EventLogger represents the behaviours of a logger +type EventLogger interface { + LogEvent(event *Event) error + Close() + Flush() + GetLevel() Level + GetStacktraceLevel() Level + GetName() string +} + +// ChannelledLog represents a cached channel to a LoggerProvider +type ChannelledLog struct { + name string + provider string + queue chan *Event + loggerProvider LoggerProvider + flush chan bool + close chan bool + closed chan bool +} + +// NewChannelledLog a new logger instance with given logger provider and config. +func NewChannelledLog(name, provider, config string, bufferLength int64) (*ChannelledLog, error) { + if log, ok := providers[provider]; ok { + l := &ChannelledLog{ + queue: make(chan *Event, bufferLength), + flush: make(chan bool), + close: make(chan bool), + closed: make(chan bool), + } + l.loggerProvider = log() + if err := l.loggerProvider.Init(config); err != nil { + return nil, err + } + l.name = name + l.provider = provider + go l.Start() + return l, nil + } + return nil, ErrUnknownProvider{provider} +} + +// Start processing the ChannelledLog +func (l *ChannelledLog) Start() { + for { + select { + case event, ok := <-l.queue: + if !ok { + l.closeLogger() + return + } + l.loggerProvider.LogEvent(event) + case _, ok := <-l.flush: + if !ok { + l.closeLogger() + return + } + l.loggerProvider.Flush() + case _, _ = <-l.close: + l.closeLogger() + return + } + } +} + +// LogEvent logs an event to this ChannelledLog +func (l *ChannelledLog) LogEvent(event *Event) error { + select { + case l.queue <- event: + return nil + case <-time.After(60 * time.Second): + // We're blocked! + return ErrTimeout{ + Name: l.name, + Provider: l.provider, + } + } +} + +func (l *ChannelledLog) closeLogger() { + l.loggerProvider.Flush() + l.loggerProvider.Close() + l.closed <- true + return +} + +// Close this ChannelledLog +func (l *ChannelledLog) Close() { + l.close <- true + <-l.closed +} + +// Flush this ChannelledLog +func (l *ChannelledLog) Flush() { + l.flush <- true +} + +// GetLevel gets the level of this ChannelledLog +func (l *ChannelledLog) GetLevel() Level { + return l.loggerProvider.GetLevel() +} + +// GetStacktraceLevel gets the level of this ChannelledLog +func (l *ChannelledLog) GetStacktraceLevel() Level { + return l.loggerProvider.GetStacktraceLevel() +} + +// GetName returns the name of this ChannelledLog +func (l *ChannelledLog) GetName() string { + return l.name +} + +// MultiChannelledLog represents a cached channel to a LoggerProvider +type MultiChannelledLog struct { + name string + bufferLength int64 + queue chan *Event + mutex sync.Mutex + loggers map[string]EventLogger + flush chan bool + close chan bool + started bool + level Level + stacktraceLevel Level + closed chan bool +} + +// NewMultiChannelledLog a new logger instance with given logger provider and config. +func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog { + m := &MultiChannelledLog{ + name: name, + queue: make(chan *Event, bufferLength), + flush: make(chan bool), + bufferLength: bufferLength, + loggers: make(map[string]EventLogger), + level: NONE, + stacktraceLevel: NONE, + close: make(chan bool), + closed: make(chan bool), + } + return m +} + +// AddLogger adds a logger to this MultiChannelledLog +func (m *MultiChannelledLog) AddLogger(logger EventLogger) error { + m.mutex.Lock() + name := logger.GetName() + if _, has := m.loggers[name]; has { + m.mutex.Unlock() + return ErrDuplicateName{name} + } + m.loggers[name] = logger + if logger.GetLevel() < m.level { + m.level = logger.GetLevel() + } + if logger.GetStacktraceLevel() < m.stacktraceLevel { + m.stacktraceLevel = logger.GetStacktraceLevel() + } + m.mutex.Unlock() + go m.Start() + return nil +} + +// DelLogger removes a sub logger from this MultiChannelledLog +// NB: If you delete the last sublogger this logger will simply drop +// log events +func (m *MultiChannelledLog) DelLogger(name string) bool { + m.mutex.Lock() + logger, has := m.loggers[name] + if !has { + m.mutex.Unlock() + return false + } + delete(m.loggers, name) + m.internalResetLevel() + m.mutex.Unlock() + logger.Flush() + logger.Close() + return true +} + +// GetEventLogger returns a sub logger from this MultiChannelledLog +func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger { + m.mutex.Lock() + defer m.mutex.Unlock() + return m.loggers[name] +} + +// GetEventLoggerNames returns a list of names +func (m *MultiChannelledLog) GetEventLoggerNames() []string { + m.mutex.Lock() + defer m.mutex.Unlock() + var keys []string + for k := range m.loggers { + keys = append(keys, k) + } + return keys +} + +func (m *MultiChannelledLog) closeLoggers() { + m.mutex.Lock() + for _, logger := range m.loggers { + logger.Flush() + logger.Close() + } + m.mutex.Unlock() + m.closed <- true + return +} + +// Start processing the MultiChannelledLog +func (m *MultiChannelledLog) Start() { + m.mutex.Lock() + if m.started { + m.mutex.Unlock() + return + } + m.started = true + m.mutex.Unlock() + for { + select { + case event, ok := <-m.queue: + if !ok { + m.closeLoggers() + return + } + m.mutex.Lock() + for _, logger := range m.loggers { + err := logger.LogEvent(event) + if err != nil { + fmt.Println(err) + } + } + m.mutex.Unlock() + case _, ok := <-m.flush: + if !ok { + m.closeLoggers() + return + } + m.mutex.Lock() + for _, logger := range m.loggers { + logger.Flush() + } + m.mutex.Unlock() + case <-m.close: + m.closeLoggers() + return + } + } +} + +// LogEvent logs an event to this MultiChannelledLog +func (m *MultiChannelledLog) LogEvent(event *Event) error { + select { + case m.queue <- event: + return nil + case <-time.After(60 * time.Second): + // We're blocked! + return ErrTimeout{ + Name: m.name, + Provider: "MultiChannelledLog", + } + } +} + +// Close this MultiChannelledLog +func (m *MultiChannelledLog) Close() { + m.close <- true + <-m.closed +} + +// Flush this ChannelledLog +func (m *MultiChannelledLog) Flush() { + m.flush <- true +} + +// GetLevel gets the level of this MultiChannelledLog +func (m *MultiChannelledLog) GetLevel() Level { + return m.level +} + +// GetStacktraceLevel gets the level of this MultiChannelledLog +func (m *MultiChannelledLog) GetStacktraceLevel() Level { + return m.stacktraceLevel +} + +func (m *MultiChannelledLog) internalResetLevel() Level { + m.level = NONE + for _, logger := range m.loggers { + level := logger.GetLevel() + if level < m.level { + m.level = level + } + level = logger.GetStacktraceLevel() + if level < m.stacktraceLevel { + m.stacktraceLevel = level + } + } + return m.level +} + +// ResetLevel will reset the level of this MultiChannelledLog +func (m *MultiChannelledLog) ResetLevel() Level { + m.mutex.Lock() + defer m.mutex.Unlock() + return m.internalResetLevel() +} + +// GetName gets the name of this MultiChannelledLog +func (m *MultiChannelledLog) GetName() string { + return m.name +} |