271 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a MIT-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package log
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
type byteArrayWriter []byte
 | 
						|
 | 
						|
func (b *byteArrayWriter) Write(p []byte) (int, error) {
 | 
						|
	*b = append(*b, p...)
 | 
						|
	return len(p), nil
 | 
						|
}
 | 
						|
 | 
						|
// WriterLogger represent a basic logger for Gitea
 | 
						|
type WriterLogger struct {
 | 
						|
	out io.WriteCloser
 | 
						|
	mu  sync.Mutex
 | 
						|
 | 
						|
	Level           Level  `json:"level"`
 | 
						|
	StacktraceLevel Level  `json:"stacktraceLevel"`
 | 
						|
	Flags           int    `json:"flags"`
 | 
						|
	Prefix          string `json:"prefix"`
 | 
						|
	Colorize        bool   `json:"colorize"`
 | 
						|
	Expression      string `json:"expression"`
 | 
						|
	regexp          *regexp.Regexp
 | 
						|
}
 | 
						|
 | 
						|
// NewWriterLogger creates a new WriterLogger from the provided WriteCloser.
 | 
						|
// Optionally the level can be changed at the same time.
 | 
						|
func (logger *WriterLogger) NewWriterLogger(out io.WriteCloser, level ...Level) {
 | 
						|
	logger.mu.Lock()
 | 
						|
	defer logger.mu.Unlock()
 | 
						|
	logger.out = out
 | 
						|
	switch logger.Flags {
 | 
						|
	case 0:
 | 
						|
		logger.Flags = LstdFlags
 | 
						|
	case -1:
 | 
						|
		logger.Flags = 0
 | 
						|
	}
 | 
						|
	if len(level) > 0 {
 | 
						|
		logger.Level = level[0]
 | 
						|
	}
 | 
						|
	logger.createExpression()
 | 
						|
}
 | 
						|
 | 
						|
func (logger *WriterLogger) createExpression() {
 | 
						|
	if len(logger.Expression) > 0 {
 | 
						|
		var err error
 | 
						|
		logger.regexp, err = regexp.Compile(logger.Expression)
 | 
						|
		if err != nil {
 | 
						|
			logger.regexp = nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// GetLevel returns the logging level for this logger
 | 
						|
func (logger *WriterLogger) GetLevel() Level {
 | 
						|
	return logger.Level
 | 
						|
}
 | 
						|
 | 
						|
// GetStacktraceLevel returns the stacktrace logging level for this logger
 | 
						|
func (logger *WriterLogger) GetStacktraceLevel() Level {
 | 
						|
	return logger.StacktraceLevel
 | 
						|
}
 | 
						|
 | 
						|
// Copy of cheap integer to fixed-width decimal to ascii from logger.
 | 
						|
func itoa(buf *[]byte, i int, wid int) {
 | 
						|
	var logger [20]byte
 | 
						|
	bp := len(logger) - 1
 | 
						|
	for i >= 10 || wid > 1 {
 | 
						|
		wid--
 | 
						|
		q := i / 10
 | 
						|
		logger[bp] = byte('0' + i - q*10)
 | 
						|
		bp--
 | 
						|
		i = q
 | 
						|
	}
 | 
						|
	// i < 10
 | 
						|
	logger[bp] = byte('0' + i)
 | 
						|
	*buf = append(*buf, logger[bp:]...)
 | 
						|
}
 | 
						|
 | 
						|
func (logger *WriterLogger) createMsg(buf *[]byte, event *Event) {
 | 
						|
	*buf = append(*buf, logger.Prefix...)
 | 
						|
	t := event.time
 | 
						|
	if logger.Flags&(Ldate|Ltime|Lmicroseconds) != 0 {
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, fgCyanBytes...)
 | 
						|
		}
 | 
						|
		if logger.Flags&LUTC != 0 {
 | 
						|
			t = t.UTC()
 | 
						|
		}
 | 
						|
		if logger.Flags&Ldate != 0 {
 | 
						|
			year, month, day := t.Date()
 | 
						|
			itoa(buf, year, 4)
 | 
						|
			*buf = append(*buf, '/')
 | 
						|
			itoa(buf, int(month), 2)
 | 
						|
			*buf = append(*buf, '/')
 | 
						|
			itoa(buf, day, 2)
 | 
						|
			*buf = append(*buf, ' ')
 | 
						|
		}
 | 
						|
		if logger.Flags&(Ltime|Lmicroseconds) != 0 {
 | 
						|
			hour, min, sec := t.Clock()
 | 
						|
			itoa(buf, hour, 2)
 | 
						|
			*buf = append(*buf, ':')
 | 
						|
			itoa(buf, min, 2)
 | 
						|
			*buf = append(*buf, ':')
 | 
						|
			itoa(buf, sec, 2)
 | 
						|
			if logger.Flags&Lmicroseconds != 0 {
 | 
						|
				*buf = append(*buf, '.')
 | 
						|
				itoa(buf, t.Nanosecond()/1e3, 6)
 | 
						|
			}
 | 
						|
			*buf = append(*buf, ' ')
 | 
						|
		}
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, resetBytes...)
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
	if logger.Flags&(Lshortfile|Llongfile) != 0 {
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, fgGreenBytes...)
 | 
						|
		}
 | 
						|
		file := event.filename
 | 
						|
		if logger.Flags&Lmedfile == Lmedfile {
 | 
						|
			startIndex := len(file) - 20
 | 
						|
			if startIndex > 0 {
 | 
						|
				file = "..." + file[startIndex:]
 | 
						|
			}
 | 
						|
		} else if logger.Flags&Lshortfile != 0 {
 | 
						|
			startIndex := strings.LastIndexByte(file, '/')
 | 
						|
			if startIndex > 0 && startIndex < len(file) {
 | 
						|
				file = file[startIndex+1:]
 | 
						|
			}
 | 
						|
		}
 | 
						|
		*buf = append(*buf, file...)
 | 
						|
		*buf = append(*buf, ':')
 | 
						|
		itoa(buf, event.line, -1)
 | 
						|
		if logger.Flags&(Lfuncname|Lshortfuncname) != 0 {
 | 
						|
			*buf = append(*buf, ':')
 | 
						|
		} else {
 | 
						|
			if logger.Colorize {
 | 
						|
				*buf = append(*buf, resetBytes...)
 | 
						|
			}
 | 
						|
			*buf = append(*buf, ' ')
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if logger.Flags&(Lfuncname|Lshortfuncname) != 0 {
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, fgGreenBytes...)
 | 
						|
		}
 | 
						|
		funcname := event.caller
 | 
						|
		if logger.Flags&Lshortfuncname != 0 {
 | 
						|
			lastIndex := strings.LastIndexByte(funcname, '.')
 | 
						|
			if lastIndex > 0 && len(funcname) > lastIndex+1 {
 | 
						|
				funcname = funcname[lastIndex+1:]
 | 
						|
			}
 | 
						|
		}
 | 
						|
		*buf = append(*buf, funcname...)
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, resetBytes...)
 | 
						|
		}
 | 
						|
		*buf = append(*buf, ' ')
 | 
						|
 | 
						|
	}
 | 
						|
	if logger.Flags&(Llevel|Llevelinitial) != 0 {
 | 
						|
		level := strings.ToUpper(event.level.String())
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, levelToColor[event.level]...)
 | 
						|
		}
 | 
						|
		*buf = append(*buf, '[')
 | 
						|
		if logger.Flags&Llevelinitial != 0 {
 | 
						|
			*buf = append(*buf, level[0])
 | 
						|
		} else {
 | 
						|
			*buf = append(*buf, level...)
 | 
						|
		}
 | 
						|
		*buf = append(*buf, ']')
 | 
						|
		if logger.Colorize {
 | 
						|
			*buf = append(*buf, resetBytes...)
 | 
						|
		}
 | 
						|
		*buf = append(*buf, ' ')
 | 
						|
	}
 | 
						|
 | 
						|
	var msg = []byte(event.msg)
 | 
						|
	if len(msg) > 0 && msg[len(msg)-1] == '\n' {
 | 
						|
		msg = msg[:len(msg)-1]
 | 
						|
	}
 | 
						|
 | 
						|
	pawMode := allowColor
 | 
						|
	if !logger.Colorize {
 | 
						|
		pawMode = removeColor
 | 
						|
	}
 | 
						|
 | 
						|
	baw := byteArrayWriter(*buf)
 | 
						|
	(&protectedANSIWriter{
 | 
						|
		w:    &baw,
 | 
						|
		mode: pawMode,
 | 
						|
	}).Write(msg)
 | 
						|
	*buf = baw
 | 
						|
 | 
						|
	if event.stacktrace != "" && logger.StacktraceLevel <= event.level {
 | 
						|
		lines := bytes.Split([]byte(event.stacktrace), []byte("\n"))
 | 
						|
		if len(lines) > 1 {
 | 
						|
			for _, line := range lines {
 | 
						|
				*buf = append(*buf, "\n\t"...)
 | 
						|
				*buf = append(*buf, line...)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		*buf = append(*buf, '\n')
 | 
						|
	}
 | 
						|
	*buf = append(*buf, '\n')
 | 
						|
}
 | 
						|
 | 
						|
// LogEvent logs the event to the internal writer
 | 
						|
func (logger *WriterLogger) LogEvent(event *Event) error {
 | 
						|
	if logger.Level > event.level {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	logger.mu.Lock()
 | 
						|
	defer logger.mu.Unlock()
 | 
						|
	if !logger.Match(event) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	var buf []byte
 | 
						|
	logger.createMsg(&buf, event)
 | 
						|
	_, err := logger.out.Write(buf)
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// Match checks if the given event matches the logger's regexp expression
 | 
						|
func (logger *WriterLogger) Match(event *Event) bool {
 | 
						|
	if logger.regexp == nil {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	if logger.regexp.Match([]byte(fmt.Sprintf("%s:%d:%s", event.filename, event.line, event.caller))) {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	// Match on the non-colored msg - therefore strip out colors
 | 
						|
	var msg []byte
 | 
						|
	baw := byteArrayWriter(msg)
 | 
						|
	(&protectedANSIWriter{
 | 
						|
		w:    &baw,
 | 
						|
		mode: removeColor,
 | 
						|
	}).Write([]byte(event.msg))
 | 
						|
	msg = baw
 | 
						|
	return logger.regexp.Match(msg)
 | 
						|
}
 | 
						|
 | 
						|
// Close the base logger
 | 
						|
func (logger *WriterLogger) Close() {
 | 
						|
	logger.mu.Lock()
 | 
						|
	defer logger.mu.Unlock()
 | 
						|
	if logger.out != nil {
 | 
						|
		logger.out.Close()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// GetName returns empty for these provider loggers
 | 
						|
func (logger *WriterLogger) GetName() string {
 | 
						|
	return ""
 | 
						|
}
 |