123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- // 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
- ReleaseReopen() error
- }
-
- // 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
- }
-
- // Close this ChannelledLog
- func (l *ChannelledLog) Close() {
- l.close <- true
- <-l.closed
- }
-
- // Flush this ChannelledLog
- func (l *ChannelledLog) Flush() {
- l.flush <- true
- }
-
- // ReleaseReopen this ChannelledLog
- func (l *ChannelledLog) ReleaseReopen() error {
- return l.loggerProvider.ReleaseReopen()
- }
-
- // 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
- paused 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),
- paused: 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
- }
-
- // Pause pauses this Logger
- func (m *MultiChannelledLog) Pause() {
- m.paused <- true
- }
-
- // Resume resumes this Logger
- func (m *MultiChannelledLog) Resume() {
- m.paused <- false
- }
-
- // ReleaseReopen causes this logger to tell its subloggers to release and reopen
- func (m *MultiChannelledLog) ReleaseReopen() error {
- m.mutex.Lock()
- defer m.mutex.Unlock()
- var accumulatedErr error
- for _, logger := range m.loggers {
- if err := logger.ReleaseReopen(); err != nil {
- if accumulatedErr == nil {
- accumulatedErr = fmt.Errorf("Error whilst reopening: %s Error: %v", logger.GetName(), err)
- } else {
- accumulatedErr = fmt.Errorf("Error whilst reopening: %s Error: %v & %v", logger.GetName(), err, accumulatedErr)
- }
- }
- }
- return accumulatedErr
- }
-
- // Start processing the MultiChannelledLog
- func (m *MultiChannelledLog) Start() {
- m.mutex.Lock()
- if m.started {
- m.mutex.Unlock()
- return
- }
- m.started = true
- m.mutex.Unlock()
- paused := false
- for {
- if paused {
- select {
- case paused = <-m.paused:
- if !paused {
- m.ResetLevel()
- }
- 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
- }
- continue
- }
- select {
- case paused = <-m.paused:
- if paused && m.level < INFO {
- m.level = INFO
- }
- 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(100 * time.Millisecond):
- // 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
- }
|