code.gitea.io/gitea@v1.22.3/modules/log/logger_impl.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package log
     5  
     6  import (
     7  	"context"
     8  	"runtime"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"code.gitea.io/gitea/modules/json"
    15  	"code.gitea.io/gitea/modules/util"
    16  )
    17  
    18  type LoggerImpl struct {
    19  	LevelLogger
    20  
    21  	ctx       context.Context
    22  	ctxCancel context.CancelFunc
    23  
    24  	level           atomic.Int32
    25  	stacktraceLevel atomic.Int32
    26  
    27  	eventWriterMu sync.RWMutex
    28  	eventWriters  map[string]EventWriter
    29  }
    30  
    31  var (
    32  	_ BaseLogger  = (*LoggerImpl)(nil)
    33  	_ LevelLogger = (*LoggerImpl)(nil)
    34  )
    35  
    36  // SendLogEvent sends a log event to all writers
    37  func (l *LoggerImpl) SendLogEvent(event *Event) {
    38  	l.eventWriterMu.RLock()
    39  	defer l.eventWriterMu.RUnlock()
    40  
    41  	if len(l.eventWriters) == 0 {
    42  		FallbackErrorf("[no logger writer]: %s", event.MsgSimpleText)
    43  		return
    44  	}
    45  
    46  	// the writers have their own goroutines, the message arguments (with Stringer) shouldn't be used in other goroutines
    47  	// so the event message must be formatted here
    48  	msgFormat, msgArgs := event.msgFormat, event.msgArgs
    49  	event.msgFormat, event.msgArgs = "(already processed by formatters)", nil
    50  
    51  	for _, w := range l.eventWriters {
    52  		if event.Level < w.GetLevel() {
    53  			continue
    54  		}
    55  		formatted := &EventFormatted{
    56  			Origin: event,
    57  			Msg:    w.Base().FormatMessage(w.Base().Mode, event, msgFormat, msgArgs...),
    58  		}
    59  		select {
    60  		case w.Base().Queue <- formatted:
    61  		default:
    62  			bs, _ := json.Marshal(event)
    63  			FallbackErrorf("log writer %q queue is full, event: %v", w.GetWriterName(), string(bs))
    64  		}
    65  	}
    66  }
    67  
    68  // syncLevelInternal syncs the level of the logger with the levels of the writers
    69  func (l *LoggerImpl) syncLevelInternal() {
    70  	lowestLevel := NONE
    71  	for _, w := range l.eventWriters {
    72  		if w.GetLevel() < lowestLevel {
    73  			lowestLevel = w.GetLevel()
    74  		}
    75  	}
    76  	l.level.Store(int32(lowestLevel))
    77  
    78  	lowestLevel = NONE
    79  	for _, w := range l.eventWriters {
    80  		if w.Base().Mode.StacktraceLevel < lowestLevel {
    81  			lowestLevel = w.GetLevel()
    82  		}
    83  	}
    84  	l.stacktraceLevel.Store(int32(lowestLevel))
    85  }
    86  
    87  // removeWriterInternal removes a writer from the logger, and stops it if it's not shared
    88  func (l *LoggerImpl) removeWriterInternal(w EventWriter) {
    89  	if !w.Base().shared {
    90  		eventWriterStopWait(w) // only stop non-shared writers, shared writers are managed by the manager
    91  	}
    92  	delete(l.eventWriters, w.GetWriterName())
    93  }
    94  
    95  // AddWriters adds writers to the logger, and starts them. Existing writers will be replaced by new ones.
    96  func (l *LoggerImpl) AddWriters(writer ...EventWriter) {
    97  	l.eventWriterMu.Lock()
    98  	defer l.eventWriterMu.Unlock()
    99  	l.addWritersInternal(writer...)
   100  }
   101  
   102  func (l *LoggerImpl) addWritersInternal(writer ...EventWriter) {
   103  	for _, w := range writer {
   104  		if old, ok := l.eventWriters[w.GetWriterName()]; ok {
   105  			l.removeWriterInternal(old)
   106  		}
   107  	}
   108  
   109  	for _, w := range writer {
   110  		l.eventWriters[w.GetWriterName()] = w
   111  		eventWriterStartGo(l.ctx, w, false)
   112  	}
   113  
   114  	l.syncLevelInternal()
   115  }
   116  
   117  // RemoveWriter removes a writer from the logger, and the writer is closed and flushed if it is not shared
   118  func (l *LoggerImpl) RemoveWriter(modeName string) error {
   119  	l.eventWriterMu.Lock()
   120  	defer l.eventWriterMu.Unlock()
   121  
   122  	w, ok := l.eventWriters[modeName]
   123  	if !ok {
   124  		return util.ErrNotExist
   125  	}
   126  
   127  	l.removeWriterInternal(w)
   128  	l.syncLevelInternal()
   129  	return nil
   130  }
   131  
   132  // ReplaceAllWriters replaces all writers from the logger, non-shared writers are closed and flushed
   133  func (l *LoggerImpl) ReplaceAllWriters(writer ...EventWriter) {
   134  	l.eventWriterMu.Lock()
   135  	defer l.eventWriterMu.Unlock()
   136  
   137  	for _, w := range l.eventWriters {
   138  		l.removeWriterInternal(w)
   139  	}
   140  	l.eventWriters = map[string]EventWriter{}
   141  	l.addWritersInternal(writer...)
   142  }
   143  
   144  // DumpWriters dumps the writers as a JSON map, it's used for debugging and display purposes.
   145  func (l *LoggerImpl) DumpWriters() map[string]any {
   146  	l.eventWriterMu.RLock()
   147  	defer l.eventWriterMu.RUnlock()
   148  
   149  	writers := make(map[string]any, len(l.eventWriters))
   150  	for k, w := range l.eventWriters {
   151  		bs, err := json.Marshal(w.Base().Mode)
   152  		if err != nil {
   153  			FallbackErrorf("marshal writer %q to dump failed: %v", k, err)
   154  			continue
   155  		}
   156  		m := map[string]any{}
   157  		_ = json.Unmarshal(bs, &m)
   158  		m["WriterType"] = w.GetWriterType()
   159  		writers[k] = m
   160  	}
   161  	return writers
   162  }
   163  
   164  // Close closes the logger, non-shared writers are closed and flushed
   165  func (l *LoggerImpl) Close() {
   166  	l.ReplaceAllWriters()
   167  	l.ctxCancel()
   168  }
   169  
   170  // IsEnabled returns true if the logger is enabled: it has a working level and has writers
   171  // Fatal is not considered as enabled, because it's a special case and the process just exits
   172  func (l *LoggerImpl) IsEnabled() bool {
   173  	l.eventWriterMu.RLock()
   174  	defer l.eventWriterMu.RUnlock()
   175  	return l.level.Load() < int32(FATAL) && len(l.eventWriters) > 0
   176  }
   177  
   178  // Log prepares the log event, if the level matches, the event will be sent to the writers
   179  func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
   180  	if Level(l.level.Load()) > level {
   181  		return
   182  	}
   183  
   184  	event := &Event{
   185  		Time:   time.Now(),
   186  		Level:  level,
   187  		Caller: "?()",
   188  	}
   189  
   190  	pc, filename, line, ok := runtime.Caller(skip + 1)
   191  	if ok {
   192  		fn := runtime.FuncForPC(pc)
   193  		if fn != nil {
   194  			event.Caller = fn.Name() + "()"
   195  		}
   196  	}
   197  	event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line
   198  
   199  	if l.stacktraceLevel.Load() <= int32(level) {
   200  		event.Stacktrace = Stack(skip + 1)
   201  	}
   202  
   203  	labels := getGoroutineLabels()
   204  	if labels != nil {
   205  		event.GoroutinePid = labels["pid"]
   206  	}
   207  
   208  	// get a simple text message without color
   209  	msgArgs := make([]any, len(logArgs))
   210  	copy(msgArgs, logArgs)
   211  
   212  	// handle LogStringer values
   213  	for i, v := range msgArgs {
   214  		if cv, ok := v.(*ColoredValue); ok {
   215  			if s, ok := cv.v.(LogStringer); ok {
   216  				cv.v = logStringFormatter{v: s}
   217  			}
   218  		} else if s, ok := v.(LogStringer); ok {
   219  			msgArgs[i] = logStringFormatter{v: s}
   220  		}
   221  	}
   222  
   223  	event.MsgSimpleText = colorSprintf(false, format, msgArgs...)
   224  	event.msgFormat = format
   225  	event.msgArgs = msgArgs
   226  	l.SendLogEvent(event)
   227  }
   228  
   229  func (l *LoggerImpl) GetLevel() Level {
   230  	return Level(l.level.Load())
   231  }
   232  
   233  func NewLoggerWithWriters(ctx context.Context, name string, writer ...EventWriter) *LoggerImpl {
   234  	l := &LoggerImpl{}
   235  	l.ctx, l.ctxCancel = newProcessTypedContext(ctx, "Logger: "+name)
   236  	l.LevelLogger = BaseLoggerToGeneralLogger(l)
   237  	l.eventWriters = map[string]EventWriter{}
   238  	l.AddWriters(writer...)
   239  	return l
   240  }