You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

event_writer_base.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package log
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "regexp"
  9. "runtime/pprof"
  10. "time"
  11. )
  12. // EventWriterBase is the base interface for most event writers
  13. // It provides default implementations for most methods
  14. type EventWriterBase interface {
  15. Base() *EventWriterBaseImpl
  16. GetWriterType() string
  17. GetWriterName() string
  18. GetLevel() Level
  19. Run(ctx context.Context)
  20. }
  21. type EventWriterBaseImpl struct {
  22. writerType string
  23. Name string
  24. Mode *WriterMode
  25. Queue chan *EventFormatted
  26. FormatMessage EventFormatter // format the Event to a message and write it to output
  27. OutputWriteCloser io.WriteCloser // it will be closed when the event writer is stopped
  28. GetPauseChan func() chan struct{}
  29. shared bool
  30. stopped chan struct{}
  31. }
  32. var _ EventWriterBase = (*EventWriterBaseImpl)(nil)
  33. func (b *EventWriterBaseImpl) Base() *EventWriterBaseImpl {
  34. return b
  35. }
  36. func (b *EventWriterBaseImpl) GetWriterType() string {
  37. return b.writerType
  38. }
  39. func (b *EventWriterBaseImpl) GetWriterName() string {
  40. return b.Name
  41. }
  42. func (b *EventWriterBaseImpl) GetLevel() Level {
  43. return b.Mode.Level
  44. }
  45. // Run is the default implementation for EventWriter.Run
  46. func (b *EventWriterBaseImpl) Run(ctx context.Context) {
  47. defer b.OutputWriteCloser.Close()
  48. var exprRegexp *regexp.Regexp
  49. if b.Mode.Expression != "" {
  50. var err error
  51. if exprRegexp, err = regexp.Compile(b.Mode.Expression); err != nil {
  52. FallbackErrorf("unable to compile expression %q for writer %q: %v", b.Mode.Expression, b.Name, err)
  53. }
  54. }
  55. handlePaused := func() {
  56. if pause := b.GetPauseChan(); pause != nil {
  57. select {
  58. case <-pause:
  59. case <-ctx.Done():
  60. }
  61. }
  62. }
  63. for {
  64. select {
  65. case <-ctx.Done():
  66. return
  67. case event, ok := <-b.Queue:
  68. if !ok {
  69. return
  70. }
  71. handlePaused()
  72. if exprRegexp != nil {
  73. fileLineCaller := fmt.Sprintf("%s:%d:%s", event.Origin.Filename, event.Origin.Line, event.Origin.Caller)
  74. matched := exprRegexp.MatchString(fileLineCaller) || exprRegexp.MatchString(event.Origin.MsgSimpleText)
  75. if !matched {
  76. continue
  77. }
  78. }
  79. var err error
  80. switch msg := event.Msg.(type) {
  81. case string:
  82. _, err = b.OutputWriteCloser.Write([]byte(msg))
  83. case []byte:
  84. _, err = b.OutputWriteCloser.Write(msg)
  85. case io.WriterTo:
  86. _, err = msg.WriteTo(b.OutputWriteCloser)
  87. default:
  88. _, err = b.OutputWriteCloser.Write([]byte(fmt.Sprint(msg)))
  89. }
  90. if err != nil {
  91. FallbackErrorf("unable to write log message of %q (%v): %v", b.Name, err, event.Msg)
  92. }
  93. }
  94. }
  95. }
  96. func NewEventWriterBase(name, writerType string, mode WriterMode) *EventWriterBaseImpl {
  97. if mode.BufferLen == 0 {
  98. mode.BufferLen = 1000
  99. }
  100. if mode.Level == UNDEFINED {
  101. mode.Level = INFO
  102. }
  103. if mode.StacktraceLevel == UNDEFINED {
  104. mode.StacktraceLevel = NONE
  105. }
  106. b := &EventWriterBaseImpl{
  107. writerType: writerType,
  108. Name: name,
  109. Mode: &mode,
  110. Queue: make(chan *EventFormatted, mode.BufferLen),
  111. GetPauseChan: GetManager().GetPauseChan, // by default, use the global pause channel
  112. FormatMessage: EventFormatTextMessage,
  113. }
  114. return b
  115. }
  116. // eventWriterStartGo use "go" to start an event worker's Run method
  117. func eventWriterStartGo(ctx context.Context, w EventWriter, shared bool) {
  118. if w.Base().stopped != nil {
  119. return // already started
  120. }
  121. w.Base().shared = shared
  122. w.Base().stopped = make(chan struct{})
  123. ctxDesc := "Logger: EventWriter: " + w.GetWriterName()
  124. if shared {
  125. ctxDesc = "Logger: EventWriter (shared): " + w.GetWriterName()
  126. }
  127. writerCtx, writerCancel := newProcessTypedContext(ctx, ctxDesc)
  128. go func() {
  129. defer writerCancel()
  130. defer close(w.Base().stopped)
  131. pprof.SetGoroutineLabels(writerCtx)
  132. w.Run(writerCtx)
  133. }()
  134. }
  135. // eventWriterStopWait stops an event writer and waits for it to finish flushing (with a timeout)
  136. func eventWriterStopWait(w EventWriter) {
  137. close(w.Base().Queue)
  138. select {
  139. case <-w.Base().stopped:
  140. case <-time.After(2 * time.Second):
  141. FallbackErrorf("unable to stop log writer %q in time, skip", w.GetWriterName())
  142. }
  143. }