123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- // Copyright 2023 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package log
-
- import (
- "context"
- "runtime"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/util"
- )
-
- type LoggerImpl struct {
- LevelLogger
-
- ctx context.Context
- ctxCancel context.CancelFunc
-
- level atomic.Int32
- stacktraceLevel atomic.Int32
-
- eventWriterMu sync.RWMutex
- eventWriters map[string]EventWriter
- }
-
- var (
- _ BaseLogger = (*LoggerImpl)(nil)
- _ LevelLogger = (*LoggerImpl)(nil)
- )
-
- // SendLogEvent sends a log event to all writers
- func (l *LoggerImpl) SendLogEvent(event *Event) {
- l.eventWriterMu.RLock()
- defer l.eventWriterMu.RUnlock()
-
- if len(l.eventWriters) == 0 {
- FallbackErrorf("[no logger writer]: %s", event.MsgSimpleText)
- return
- }
-
- // the writers have their own goroutines, the message arguments (with Stringer) shouldn't be used in other goroutines
- // so the event message must be formatted here
- msgFormat, msgArgs := event.msgFormat, event.msgArgs
- event.msgFormat, event.msgArgs = "(already processed by formatters)", nil
-
- for _, w := range l.eventWriters {
- if event.Level < w.GetLevel() {
- continue
- }
- formatted := &EventFormatted{
- Origin: event,
- Msg: w.Base().FormatMessage(w.Base().Mode, event, msgFormat, msgArgs...),
- }
- select {
- case w.Base().Queue <- formatted:
- default:
- bs, _ := json.Marshal(event)
- FallbackErrorf("log writer %q queue is full, event: %v", w.GetWriterName(), string(bs))
- }
- }
- }
-
- // syncLevelInternal syncs the level of the logger with the levels of the writers
- func (l *LoggerImpl) syncLevelInternal() {
- lowestLevel := NONE
- for _, w := range l.eventWriters {
- if w.GetLevel() < lowestLevel {
- lowestLevel = w.GetLevel()
- }
- }
- l.level.Store(int32(lowestLevel))
-
- lowestLevel = NONE
- for _, w := range l.eventWriters {
- if w.Base().Mode.StacktraceLevel < lowestLevel {
- lowestLevel = w.GetLevel()
- }
- }
- l.stacktraceLevel.Store(int32(lowestLevel))
- }
-
- // removeWriterInternal removes a writer from the logger, and stops it if it's not shared
- func (l *LoggerImpl) removeWriterInternal(w EventWriter) {
- if !w.Base().shared {
- eventWriterStopWait(w) // only stop non-shared writers, shared writers are managed by the manager
- }
- delete(l.eventWriters, w.GetWriterName())
- }
-
- // AddWriters adds writers to the logger, and starts them. Existing writers will be replaced by new ones.
- func (l *LoggerImpl) AddWriters(writer ...EventWriter) {
- l.eventWriterMu.Lock()
- defer l.eventWriterMu.Unlock()
-
- for _, w := range writer {
- if old, ok := l.eventWriters[w.GetWriterName()]; ok {
- l.removeWriterInternal(old)
- }
- }
-
- for _, w := range writer {
- l.eventWriters[w.GetWriterName()] = w
- eventWriterStartGo(l.ctx, w, false)
- }
-
- l.syncLevelInternal()
- }
-
- // RemoveWriter removes a writer from the logger, and the writer is closed and flushed if it is not shared
- func (l *LoggerImpl) RemoveWriter(modeName string) error {
- l.eventWriterMu.Lock()
- defer l.eventWriterMu.Unlock()
-
- w, ok := l.eventWriters[modeName]
- if !ok {
- return util.ErrNotExist
- }
-
- l.removeWriterInternal(w)
- l.syncLevelInternal()
- return nil
- }
-
- // RemoveAllWriters removes all writers from the logger, non-shared writers are closed and flushed
- func (l *LoggerImpl) RemoveAllWriters() *LoggerImpl {
- l.eventWriterMu.Lock()
- defer l.eventWriterMu.Unlock()
-
- for _, w := range l.eventWriters {
- l.removeWriterInternal(w)
- }
- l.eventWriters = map[string]EventWriter{}
- l.syncLevelInternal()
- return l
- }
-
- // DumpWriters dumps the writers as a JSON map, it's used for debugging and display purposes.
- func (l *LoggerImpl) DumpWriters() map[string]any {
- l.eventWriterMu.RLock()
- defer l.eventWriterMu.RUnlock()
-
- writers := make(map[string]any, len(l.eventWriters))
- for k, w := range l.eventWriters {
- bs, err := json.Marshal(w.Base().Mode)
- if err != nil {
- FallbackErrorf("marshal writer %q to dump failed: %v", k, err)
- continue
- }
- m := map[string]any{}
- _ = json.Unmarshal(bs, &m)
- m["WriterType"] = w.GetWriterType()
- writers[k] = m
- }
- return writers
- }
-
- // Close closes the logger, non-shared writers are closed and flushed
- func (l *LoggerImpl) Close() {
- l.RemoveAllWriters()
- l.ctxCancel()
- }
-
- // IsEnabled returns true if the logger is enabled: it has a working level and has writers
- // Fatal is not considered as enabled, because it's a special case and the process just exits
- func (l *LoggerImpl) IsEnabled() bool {
- l.eventWriterMu.RLock()
- defer l.eventWriterMu.RUnlock()
- return l.level.Load() < int32(FATAL) && len(l.eventWriters) > 0
- }
-
- // Log prepares the log event, if the level matches, the event will be sent to the writers
- func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
- if Level(l.level.Load()) > level {
- return
- }
-
- event := &Event{
- Time: time.Now(),
- Level: level,
- Caller: "?()",
- }
-
- pc, filename, line, ok := runtime.Caller(skip + 1)
- if ok {
- fn := runtime.FuncForPC(pc)
- if fn != nil {
- event.Caller = fn.Name() + "()"
- }
- }
- event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line
-
- if l.stacktraceLevel.Load() <= int32(level) {
- event.Stacktrace = Stack(skip + 1)
- }
-
- labels := getGoroutineLabels()
- if labels != nil {
- event.GoroutinePid = labels["pid"]
- }
-
- // get a simple text message without color
- msgArgs := make([]any, len(logArgs))
- copy(msgArgs, logArgs)
-
- // handle LogStringer values
- for i, v := range msgArgs {
- if cv, ok := v.(*ColoredValue); ok {
- if s, ok := cv.v.(LogStringer); ok {
- cv.v = logStringFormatter{v: s}
- }
- } else if s, ok := v.(LogStringer); ok {
- msgArgs[i] = logStringFormatter{v: s}
- }
- }
-
- event.MsgSimpleText = colorSprintf(false, format, msgArgs...)
- event.msgFormat = format
- event.msgArgs = msgArgs
- l.SendLogEvent(event)
- }
-
- func (l *LoggerImpl) GetLevel() Level {
- return Level(l.level.Load())
- }
-
- func NewLoggerWithWriters(ctx context.Context, name string, writer ...EventWriter) *LoggerImpl {
- l := &LoggerImpl{}
- l.ctx, l.ctxCancel = newProcessTypedContext(ctx, "Logger: "+name)
- l.LevelLogger = BaseLoggerToGeneralLogger(l)
- l.eventWriters = map[string]EventWriter{}
- l.syncLevelInternal()
- l.AddWriters(writer...)
- return l
- }
|