code.gitea.io/gitea@v1.19.3/modules/log/event.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package log
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"runtime/pprof"
    10  	"sync"
    11  	"time"
    12  
    13  	"code.gitea.io/gitea/modules/process"
    14  )
    15  
    16  // Event represents a logging event
    17  type Event struct {
    18  	level      Level
    19  	msg        string
    20  	caller     string
    21  	filename   string
    22  	line       int
    23  	time       time.Time
    24  	stacktrace string
    25  }
    26  
    27  // EventLogger represents the behaviours of a logger
    28  type EventLogger interface {
    29  	LogEvent(event *Event) error
    30  	Close()
    31  	Flush()
    32  	GetLevel() Level
    33  	GetStacktraceLevel() Level
    34  	GetName() string
    35  	ReleaseReopen() error
    36  }
    37  
    38  // ChannelledLog represents a cached channel to a LoggerProvider
    39  type ChannelledLog struct {
    40  	ctx            context.Context
    41  	finished       context.CancelFunc
    42  	name           string
    43  	provider       string
    44  	queue          chan *Event
    45  	loggerProvider LoggerProvider
    46  	flush          chan bool
    47  	close          chan bool
    48  	closed         chan bool
    49  }
    50  
    51  // NewChannelledLog a new logger instance with given logger provider and config.
    52  func NewChannelledLog(parent context.Context, name, provider, config string, bufferLength int64) (*ChannelledLog, error) {
    53  	if log, ok := providers[provider]; ok {
    54  
    55  		l := &ChannelledLog{
    56  			queue:  make(chan *Event, bufferLength),
    57  			flush:  make(chan bool),
    58  			close:  make(chan bool),
    59  			closed: make(chan bool),
    60  		}
    61  		l.loggerProvider = log()
    62  		if err := l.loggerProvider.Init(config); err != nil {
    63  			return nil, err
    64  		}
    65  		l.name = name
    66  		l.provider = provider
    67  		l.ctx, _, l.finished = process.GetManager().AddTypedContext(parent, fmt.Sprintf("Logger: %s(%s)", l.name, l.provider), process.SystemProcessType, false)
    68  		go l.Start()
    69  		return l, nil
    70  	}
    71  	return nil, ErrUnknownProvider{provider}
    72  }
    73  
    74  // Start processing the ChannelledLog
    75  func (l *ChannelledLog) Start() {
    76  	pprof.SetGoroutineLabels(l.ctx)
    77  	defer l.finished()
    78  	for {
    79  		select {
    80  		case event, ok := <-l.queue:
    81  			if !ok {
    82  				l.closeLogger()
    83  				return
    84  			}
    85  			l.loggerProvider.LogEvent(event)
    86  		case _, ok := <-l.flush:
    87  			if !ok {
    88  				l.closeLogger()
    89  				return
    90  			}
    91  			l.emptyQueue()
    92  			l.loggerProvider.Flush()
    93  		case <-l.close:
    94  			l.emptyQueue()
    95  			l.closeLogger()
    96  			return
    97  		}
    98  	}
    99  }
   100  
   101  // LogEvent logs an event to this ChannelledLog
   102  func (l *ChannelledLog) LogEvent(event *Event) error {
   103  	select {
   104  	case l.queue <- event:
   105  		return nil
   106  	case <-time.After(60 * time.Second):
   107  		// We're blocked!
   108  		return ErrTimeout{
   109  			Name:     l.name,
   110  			Provider: l.provider,
   111  		}
   112  	}
   113  }
   114  
   115  func (l *ChannelledLog) emptyQueue() bool {
   116  	for {
   117  		select {
   118  		case event, ok := <-l.queue:
   119  			if !ok {
   120  				return false
   121  			}
   122  			l.loggerProvider.LogEvent(event)
   123  		default:
   124  			return true
   125  		}
   126  	}
   127  }
   128  
   129  func (l *ChannelledLog) closeLogger() {
   130  	l.loggerProvider.Flush()
   131  	l.loggerProvider.Close()
   132  	l.closed <- true
   133  }
   134  
   135  // Close this ChannelledLog
   136  func (l *ChannelledLog) Close() {
   137  	l.close <- true
   138  	<-l.closed
   139  }
   140  
   141  // Flush this ChannelledLog
   142  func (l *ChannelledLog) Flush() {
   143  	l.flush <- true
   144  }
   145  
   146  // ReleaseReopen this ChannelledLog
   147  func (l *ChannelledLog) ReleaseReopen() error {
   148  	return l.loggerProvider.ReleaseReopen()
   149  }
   150  
   151  // GetLevel gets the level of this ChannelledLog
   152  func (l *ChannelledLog) GetLevel() Level {
   153  	return l.loggerProvider.GetLevel()
   154  }
   155  
   156  // GetStacktraceLevel gets the level of this ChannelledLog
   157  func (l *ChannelledLog) GetStacktraceLevel() Level {
   158  	return l.loggerProvider.GetStacktraceLevel()
   159  }
   160  
   161  // GetName returns the name of this ChannelledLog
   162  func (l *ChannelledLog) GetName() string {
   163  	return l.name
   164  }
   165  
   166  // MultiChannelledLog represents a cached channel to a LoggerProvider
   167  type MultiChannelledLog struct {
   168  	ctx             context.Context
   169  	finished        context.CancelFunc
   170  	name            string
   171  	bufferLength    int64
   172  	queue           chan *Event
   173  	rwmutex         sync.RWMutex
   174  	loggers         map[string]EventLogger
   175  	flush           chan bool
   176  	close           chan bool
   177  	started         bool
   178  	level           Level
   179  	stacktraceLevel Level
   180  	closed          chan bool
   181  	paused          chan bool
   182  }
   183  
   184  // NewMultiChannelledLog a new logger instance with given logger provider and config.
   185  func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog {
   186  	ctx, _, finished := process.GetManager().AddTypedContext(context.Background(), fmt.Sprintf("Logger: %s", name), process.SystemProcessType, false)
   187  
   188  	m := &MultiChannelledLog{
   189  		ctx:             ctx,
   190  		finished:        finished,
   191  		name:            name,
   192  		queue:           make(chan *Event, bufferLength),
   193  		flush:           make(chan bool),
   194  		bufferLength:    bufferLength,
   195  		loggers:         make(map[string]EventLogger),
   196  		level:           NONE,
   197  		stacktraceLevel: NONE,
   198  		close:           make(chan bool),
   199  		closed:          make(chan bool),
   200  		paused:          make(chan bool),
   201  	}
   202  	return m
   203  }
   204  
   205  // AddLogger adds a logger to this MultiChannelledLog
   206  func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
   207  	m.rwmutex.Lock()
   208  	name := logger.GetName()
   209  	if _, has := m.loggers[name]; has {
   210  		m.rwmutex.Unlock()
   211  		return ErrDuplicateName{name}
   212  	}
   213  	m.loggers[name] = logger
   214  	if logger.GetLevel() < m.level {
   215  		m.level = logger.GetLevel()
   216  	}
   217  	if logger.GetStacktraceLevel() < m.stacktraceLevel {
   218  		m.stacktraceLevel = logger.GetStacktraceLevel()
   219  	}
   220  	m.rwmutex.Unlock()
   221  	go m.Start()
   222  	return nil
   223  }
   224  
   225  // DelLogger removes a sub logger from this MultiChannelledLog
   226  // NB: If you delete the last sublogger this logger will simply drop
   227  // log events
   228  func (m *MultiChannelledLog) DelLogger(name string) bool {
   229  	m.rwmutex.Lock()
   230  	logger, has := m.loggers[name]
   231  	if !has {
   232  		m.rwmutex.Unlock()
   233  		return false
   234  	}
   235  	delete(m.loggers, name)
   236  	m.internalResetLevel()
   237  	m.rwmutex.Unlock()
   238  	logger.Flush()
   239  	logger.Close()
   240  	return true
   241  }
   242  
   243  // GetEventLogger returns a sub logger from this MultiChannelledLog
   244  func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger {
   245  	m.rwmutex.RLock()
   246  	defer m.rwmutex.RUnlock()
   247  	return m.loggers[name]
   248  }
   249  
   250  // GetEventProvider returns a sub logger provider content from this MultiChannelledLog
   251  func (m *MultiChannelledLog) GetLoggerProviderContent(name string) (string, error) {
   252  	channelledLogger := m.GetEventLogger(name).(*ChannelledLog)
   253  	return channelledLogger.loggerProvider.Content()
   254  }
   255  
   256  // GetEventLoggerNames returns a list of names
   257  func (m *MultiChannelledLog) GetEventLoggerNames() []string {
   258  	m.rwmutex.RLock()
   259  	defer m.rwmutex.RUnlock()
   260  	var keys []string
   261  	for k := range m.loggers {
   262  		keys = append(keys, k)
   263  	}
   264  	return keys
   265  }
   266  
   267  func (m *MultiChannelledLog) closeLoggers() {
   268  	m.rwmutex.Lock()
   269  	for _, logger := range m.loggers {
   270  		logger.Flush()
   271  		logger.Close()
   272  	}
   273  	m.rwmutex.Unlock()
   274  	m.closed <- true
   275  }
   276  
   277  // Pause pauses this Logger
   278  func (m *MultiChannelledLog) Pause() {
   279  	m.paused <- true
   280  }
   281  
   282  // Resume resumes this Logger
   283  func (m *MultiChannelledLog) Resume() {
   284  	m.paused <- false
   285  }
   286  
   287  // ReleaseReopen causes this logger to tell its subloggers to release and reopen
   288  func (m *MultiChannelledLog) ReleaseReopen() error {
   289  	m.rwmutex.Lock()
   290  	defer m.rwmutex.Unlock()
   291  	var accumulatedErr error
   292  	for _, logger := range m.loggers {
   293  		if err := logger.ReleaseReopen(); err != nil {
   294  			if accumulatedErr == nil {
   295  				accumulatedErr = fmt.Errorf("Error whilst reopening: %s Error: %w", logger.GetName(), err)
   296  			} else {
   297  				accumulatedErr = fmt.Errorf("Error whilst reopening: %s Error: %v & %w", logger.GetName(), err, accumulatedErr)
   298  			}
   299  		}
   300  	}
   301  	return accumulatedErr
   302  }
   303  
   304  // Start processing the MultiChannelledLog
   305  func (m *MultiChannelledLog) Start() {
   306  	m.rwmutex.Lock()
   307  	if m.started {
   308  		m.rwmutex.Unlock()
   309  		return
   310  	}
   311  	pprof.SetGoroutineLabels(m.ctx)
   312  	defer m.finished()
   313  
   314  	m.started = true
   315  	m.rwmutex.Unlock()
   316  	paused := false
   317  	for {
   318  		if paused {
   319  			select {
   320  			case paused = <-m.paused:
   321  				if !paused {
   322  					m.ResetLevel()
   323  				}
   324  			case _, ok := <-m.flush:
   325  				if !ok {
   326  					m.closeLoggers()
   327  					return
   328  				}
   329  				m.rwmutex.RLock()
   330  				for _, logger := range m.loggers {
   331  					logger.Flush()
   332  				}
   333  				m.rwmutex.RUnlock()
   334  			case <-m.close:
   335  				m.closeLoggers()
   336  				return
   337  			}
   338  			continue
   339  		}
   340  		select {
   341  		case paused = <-m.paused:
   342  			if paused && m.level < INFO {
   343  				m.level = INFO
   344  			}
   345  		case event, ok := <-m.queue:
   346  			if !ok {
   347  				m.closeLoggers()
   348  				return
   349  			}
   350  			m.rwmutex.RLock()
   351  			for _, logger := range m.loggers {
   352  				err := logger.LogEvent(event)
   353  				if err != nil {
   354  					fmt.Println(err)
   355  				}
   356  			}
   357  			m.rwmutex.RUnlock()
   358  		case _, ok := <-m.flush:
   359  			if !ok {
   360  				m.closeLoggers()
   361  				return
   362  			}
   363  			m.emptyQueue()
   364  			m.rwmutex.RLock()
   365  			for _, logger := range m.loggers {
   366  				logger.Flush()
   367  			}
   368  			m.rwmutex.RUnlock()
   369  		case <-m.close:
   370  			m.emptyQueue()
   371  			m.closeLoggers()
   372  			return
   373  		}
   374  	}
   375  }
   376  
   377  func (m *MultiChannelledLog) emptyQueue() bool {
   378  	for {
   379  		select {
   380  		case event, ok := <-m.queue:
   381  			if !ok {
   382  				return false
   383  			}
   384  			m.rwmutex.RLock()
   385  			for _, logger := range m.loggers {
   386  				err := logger.LogEvent(event)
   387  				if err != nil {
   388  					fmt.Println(err)
   389  				}
   390  			}
   391  			m.rwmutex.RUnlock()
   392  		default:
   393  			return true
   394  		}
   395  	}
   396  }
   397  
   398  // LogEvent logs an event to this MultiChannelledLog
   399  func (m *MultiChannelledLog) LogEvent(event *Event) error {
   400  	select {
   401  	case m.queue <- event:
   402  		return nil
   403  	case <-time.After(100 * time.Millisecond):
   404  		// We're blocked!
   405  		return ErrTimeout{
   406  			Name:     m.name,
   407  			Provider: "MultiChannelledLog",
   408  		}
   409  	}
   410  }
   411  
   412  // Close this MultiChannelledLog
   413  func (m *MultiChannelledLog) Close() {
   414  	m.close <- true
   415  	<-m.closed
   416  }
   417  
   418  // Flush this ChannelledLog
   419  func (m *MultiChannelledLog) Flush() {
   420  	m.flush <- true
   421  }
   422  
   423  // GetLevel gets the level of this MultiChannelledLog
   424  func (m *MultiChannelledLog) GetLevel() Level {
   425  	m.rwmutex.RLock()
   426  	defer m.rwmutex.RUnlock()
   427  	return m.level
   428  }
   429  
   430  // GetStacktraceLevel gets the level of this MultiChannelledLog
   431  func (m *MultiChannelledLog) GetStacktraceLevel() Level {
   432  	m.rwmutex.RLock()
   433  	defer m.rwmutex.RUnlock()
   434  	return m.stacktraceLevel
   435  }
   436  
   437  func (m *MultiChannelledLog) internalResetLevel() Level {
   438  	m.level = NONE
   439  	for _, logger := range m.loggers {
   440  		level := logger.GetLevel()
   441  		if level < m.level {
   442  			m.level = level
   443  		}
   444  		level = logger.GetStacktraceLevel()
   445  		if level < m.stacktraceLevel {
   446  			m.stacktraceLevel = level
   447  		}
   448  	}
   449  	return m.level
   450  }
   451  
   452  // ResetLevel will reset the level of this MultiChannelledLog
   453  func (m *MultiChannelledLog) ResetLevel() Level {
   454  	m.rwmutex.Lock()
   455  	defer m.rwmutex.Unlock()
   456  	return m.internalResetLevel()
   457  }
   458  
   459  // GetName gets the name of this MultiChannelledLog
   460  func (m *MultiChannelledLog) GetName() string {
   461  	return m.name
   462  }