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 }