code.gitea.io/gitea@v1.19.3/modules/log/writer.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package log
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"regexp"
    11  	"strings"
    12  	"sync"
    13  )
    14  
    15  type byteArrayWriter []byte
    16  
    17  func (b *byteArrayWriter) Write(p []byte) (int, error) {
    18  	*b = append(*b, p...)
    19  	return len(p), nil
    20  }
    21  
    22  // WriterLogger represent a basic logger for Gitea
    23  type WriterLogger struct {
    24  	out io.WriteCloser
    25  	mu  sync.Mutex
    26  
    27  	Level           Level  `json:"level"`
    28  	StacktraceLevel Level  `json:"stacktraceLevel"`
    29  	Flags           int    `json:"flags"`
    30  	Prefix          string `json:"prefix"`
    31  	Colorize        bool   `json:"colorize"`
    32  	Expression      string `json:"expression"`
    33  	regexp          *regexp.Regexp
    34  }
    35  
    36  // NewWriterLogger creates a new WriterLogger from the provided WriteCloser.
    37  // Optionally the level can be changed at the same time.
    38  func (logger *WriterLogger) NewWriterLogger(out io.WriteCloser, level ...Level) {
    39  	logger.mu.Lock()
    40  	defer logger.mu.Unlock()
    41  	logger.out = out
    42  	switch logger.Flags {
    43  	case 0:
    44  		logger.Flags = LstdFlags
    45  	case -1:
    46  		logger.Flags = 0
    47  	}
    48  	if len(level) > 0 {
    49  		logger.Level = level[0]
    50  	}
    51  	logger.createExpression()
    52  }
    53  
    54  func (logger *WriterLogger) createExpression() {
    55  	if len(logger.Expression) > 0 {
    56  		var err error
    57  		logger.regexp, err = regexp.Compile(logger.Expression)
    58  		if err != nil {
    59  			logger.regexp = nil
    60  		}
    61  	}
    62  }
    63  
    64  // GetLevel returns the logging level for this logger
    65  func (logger *WriterLogger) GetLevel() Level {
    66  	return logger.Level
    67  }
    68  
    69  // GetStacktraceLevel returns the stacktrace logging level for this logger
    70  func (logger *WriterLogger) GetStacktraceLevel() Level {
    71  	return logger.StacktraceLevel
    72  }
    73  
    74  // Copy of cheap integer to fixed-width decimal to ascii from logger.
    75  func itoa(buf *[]byte, i, wid int) {
    76  	var logger [20]byte
    77  	bp := len(logger) - 1
    78  	for i >= 10 || wid > 1 {
    79  		wid--
    80  		q := i / 10
    81  		logger[bp] = byte('0' + i - q*10)
    82  		bp--
    83  		i = q
    84  	}
    85  	// i < 10
    86  	logger[bp] = byte('0' + i)
    87  	*buf = append(*buf, logger[bp:]...)
    88  }
    89  
    90  func (logger *WriterLogger) createMsg(buf *[]byte, event *Event) {
    91  	*buf = append(*buf, logger.Prefix...)
    92  	t := event.time
    93  	if logger.Flags&(Ldate|Ltime|Lmicroseconds) != 0 {
    94  		if logger.Colorize {
    95  			*buf = append(*buf, fgCyanBytes...)
    96  		}
    97  		if logger.Flags&LUTC != 0 {
    98  			t = t.UTC()
    99  		}
   100  		if logger.Flags&Ldate != 0 {
   101  			year, month, day := t.Date()
   102  			itoa(buf, year, 4)
   103  			*buf = append(*buf, '/')
   104  			itoa(buf, int(month), 2)
   105  			*buf = append(*buf, '/')
   106  			itoa(buf, day, 2)
   107  			*buf = append(*buf, ' ')
   108  		}
   109  		if logger.Flags&(Ltime|Lmicroseconds) != 0 {
   110  			hour, min, sec := t.Clock()
   111  			itoa(buf, hour, 2)
   112  			*buf = append(*buf, ':')
   113  			itoa(buf, min, 2)
   114  			*buf = append(*buf, ':')
   115  			itoa(buf, sec, 2)
   116  			if logger.Flags&Lmicroseconds != 0 {
   117  				*buf = append(*buf, '.')
   118  				itoa(buf, t.Nanosecond()/1e3, 6)
   119  			}
   120  			*buf = append(*buf, ' ')
   121  		}
   122  		if logger.Colorize {
   123  			*buf = append(*buf, resetBytes...)
   124  		}
   125  
   126  	}
   127  	if logger.Flags&(Lshortfile|Llongfile) != 0 {
   128  		if logger.Colorize {
   129  			*buf = append(*buf, fgGreenBytes...)
   130  		}
   131  		file := event.filename
   132  		if logger.Flags&Lmedfile == Lmedfile {
   133  			startIndex := len(file) - 20
   134  			if startIndex > 0 {
   135  				file = "..." + file[startIndex:]
   136  			}
   137  		} else if logger.Flags&Lshortfile != 0 {
   138  			startIndex := strings.LastIndexByte(file, '/')
   139  			if startIndex > 0 && startIndex < len(file) {
   140  				file = file[startIndex+1:]
   141  			}
   142  		}
   143  		*buf = append(*buf, file...)
   144  		*buf = append(*buf, ':')
   145  		itoa(buf, event.line, -1)
   146  		if logger.Flags&(Lfuncname|Lshortfuncname) != 0 {
   147  			*buf = append(*buf, ':')
   148  		} else {
   149  			if logger.Colorize {
   150  				*buf = append(*buf, resetBytes...)
   151  			}
   152  			*buf = append(*buf, ' ')
   153  		}
   154  	}
   155  	if logger.Flags&(Lfuncname|Lshortfuncname) != 0 {
   156  		if logger.Colorize {
   157  			*buf = append(*buf, fgGreenBytes...)
   158  		}
   159  		funcname := event.caller
   160  		if logger.Flags&Lshortfuncname != 0 {
   161  			lastIndex := strings.LastIndexByte(funcname, '.')
   162  			if lastIndex > 0 && len(funcname) > lastIndex+1 {
   163  				funcname = funcname[lastIndex+1:]
   164  			}
   165  		}
   166  		*buf = append(*buf, funcname...)
   167  		if logger.Colorize {
   168  			*buf = append(*buf, resetBytes...)
   169  		}
   170  		*buf = append(*buf, ' ')
   171  
   172  	}
   173  	if logger.Flags&(Llevel|Llevelinitial) != 0 {
   174  		level := strings.ToUpper(event.level.String())
   175  		if logger.Colorize {
   176  			*buf = append(*buf, levelToColor[event.level]...)
   177  		}
   178  		*buf = append(*buf, '[')
   179  		if logger.Flags&Llevelinitial != 0 {
   180  			*buf = append(*buf, level[0])
   181  		} else {
   182  			*buf = append(*buf, level...)
   183  		}
   184  		*buf = append(*buf, ']')
   185  		if logger.Colorize {
   186  			*buf = append(*buf, resetBytes...)
   187  		}
   188  		*buf = append(*buf, ' ')
   189  	}
   190  
   191  	msg := []byte(event.msg)
   192  	if len(msg) > 0 && msg[len(msg)-1] == '\n' {
   193  		msg = msg[:len(msg)-1]
   194  	}
   195  
   196  	pawMode := allowColor
   197  	if !logger.Colorize {
   198  		pawMode = removeColor
   199  	}
   200  
   201  	baw := byteArrayWriter(*buf)
   202  	(&protectedANSIWriter{
   203  		w:    &baw,
   204  		mode: pawMode,
   205  	}).Write(msg)
   206  	*buf = baw
   207  
   208  	if event.stacktrace != "" && logger.StacktraceLevel <= event.level {
   209  		lines := bytes.Split([]byte(event.stacktrace), []byte("\n"))
   210  		if len(lines) > 1 {
   211  			for _, line := range lines {
   212  				*buf = append(*buf, "\n\t"...)
   213  				*buf = append(*buf, line...)
   214  			}
   215  		}
   216  		*buf = append(*buf, '\n')
   217  	}
   218  	*buf = append(*buf, '\n')
   219  }
   220  
   221  // LogEvent logs the event to the internal writer
   222  func (logger *WriterLogger) LogEvent(event *Event) error {
   223  	if logger.Level > event.level {
   224  		return nil
   225  	}
   226  
   227  	logger.mu.Lock()
   228  	defer logger.mu.Unlock()
   229  	if !logger.Match(event) {
   230  		return nil
   231  	}
   232  	var buf []byte
   233  	logger.createMsg(&buf, event)
   234  	_, err := logger.out.Write(buf)
   235  	return err
   236  }
   237  
   238  // Match checks if the given event matches the logger's regexp expression
   239  func (logger *WriterLogger) Match(event *Event) bool {
   240  	if logger.regexp == nil {
   241  		return true
   242  	}
   243  	if logger.regexp.Match([]byte(fmt.Sprintf("%s:%d:%s", event.filename, event.line, event.caller))) {
   244  		return true
   245  	}
   246  	// Match on the non-colored msg - therefore strip out colors
   247  	var msg []byte
   248  	baw := byteArrayWriter(msg)
   249  	(&protectedANSIWriter{
   250  		w:    &baw,
   251  		mode: removeColor,
   252  	}).Write([]byte(event.msg))
   253  	msg = baw
   254  	return logger.regexp.Match(msg)
   255  }
   256  
   257  // Close the base logger
   258  func (logger *WriterLogger) Close() {
   259  	logger.mu.Lock()
   260  	defer logger.mu.Unlock()
   261  	if logger.out != nil {
   262  		logger.out.Close()
   263  	}
   264  }
   265  
   266  // GetName returns empty for these provider loggers
   267  func (logger *WriterLogger) GetName() string {
   268  	return ""
   269  }