gitea

Зеркало из https://github.com/go-gitea/gitea
Форк
0
/
logger_impl.go 
240 строк · 6.2 Кб
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
}
241

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.