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 }