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  }