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