123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- // 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"
- )
-
- // These flags define which text to prefix to each log entry generated
- // by the Logger. Bits are or'ed together to control what's printed.
- // There is no control over the order they appear (the order listed
- // here) or the format they present (as described in the comments).
- // The prefix is followed by a colon only if more than time is stated
- // is specified. For example, flags Ldate | Ltime
- // produce, 2009/01/23 01:23:23 message.
- // The standard is:
- // 2009/01/23 01:23:23 ...a/b/c/d.go:23:runtime.Caller() [I]: message
- const (
- Ldate = 1 << iota // the date in the local time zone: 2009/01/23
- Ltime // the time in the local time zone: 01:23:23
- Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
- Llongfile // full file name and line number: /a/b/c/d.go:23
- Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
- Lfuncname // function name of the caller: runtime.Caller()
- Lshortfuncname // last part of the function name
- LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
- Llevelinitial // Initial character of the provided level in brackets eg. [I] for info
- Llevel // Provided level in brackets [INFO]
-
- // Last 20 characters of the filename
- Lmedfile = Lshortfile | Llongfile
-
- // LstdFlags is the initial value for the standard logger
- LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial
- )
-
- var flagFromString = map[string]int{
- "none": 0,
- "date": Ldate,
- "time": Ltime,
- "microseconds": Lmicroseconds,
- "longfile": Llongfile,
- "shortfile": Lshortfile,
- "funcname": Lfuncname,
- "shortfuncname": Lshortfuncname,
- "utc": LUTC,
- "levelinitial": Llevelinitial,
- "level": Llevel,
- "medfile": Lmedfile,
- "stdflags": LstdFlags,
- }
-
- // FlagsFromString takes a comma separated list of flags and returns
- // the flags for this string
- func FlagsFromString(from string) int {
- flags := 0
- for _, flag := range strings.Split(strings.ToLower(from), ",") {
- f, ok := flagFromString[strings.TrimSpace(flag)]
- if ok {
- flags = flags | f
- }
- }
- return flags
- }
-
- type byteArrayWriter []byte
-
- func (b *byteArrayWriter) Write(p []byte) (int, error) {
- *b = append(*b, p...)
- return len(p), nil
- }
-
- // BaseLogger represent a basic logger for Gitea
- type BaseLogger 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
- }
-
- func (b *BaseLogger) createLogger(out io.WriteCloser, level ...Level) {
- b.mu.Lock()
- defer b.mu.Unlock()
- b.out = out
- switch b.Flags {
- case 0:
- b.Flags = LstdFlags
- case -1:
- b.Flags = 0
- }
- if len(level) > 0 {
- b.Level = level[0]
- }
- b.createExpression()
- }
-
- func (b *BaseLogger) createExpression() {
- if len(b.Expression) > 0 {
- var err error
- b.regexp, err = regexp.Compile(b.Expression)
- if err != nil {
- b.regexp = nil
- }
- }
- }
-
- // GetLevel returns the logging level for this logger
- func (b *BaseLogger) GetLevel() Level {
- return b.Level
- }
-
- // GetStacktraceLevel returns the stacktrace logging level for this logger
- func (b *BaseLogger) GetStacktraceLevel() Level {
- return b.StacktraceLevel
- }
-
- // Copy of cheap integer to fixed-width decimal to ascii from logger.
- func itoa(buf *[]byte, i int, wid int) {
- var b [20]byte
- bp := len(b) - 1
- for i >= 10 || wid > 1 {
- wid--
- q := i / 10
- b[bp] = byte('0' + i - q*10)
- bp--
- i = q
- }
- // i < 10
- b[bp] = byte('0' + i)
- *buf = append(*buf, b[bp:]...)
- }
-
- func (b *BaseLogger) createMsg(buf *[]byte, event *Event) {
- *buf = append(*buf, b.Prefix...)
- t := event.time
- if b.Flags&(Ldate|Ltime|Lmicroseconds) != 0 {
- if b.Colorize {
- *buf = append(*buf, fgCyanBytes...)
- }
- if b.Flags&LUTC != 0 {
- t = t.UTC()
- }
- if b.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 b.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 b.Flags&Lmicroseconds != 0 {
- *buf = append(*buf, '.')
- itoa(buf, t.Nanosecond()/1e3, 6)
- }
- *buf = append(*buf, ' ')
- }
- if b.Colorize {
- *buf = append(*buf, resetBytes...)
- }
-
- }
- if b.Flags&(Lshortfile|Llongfile) != 0 {
- if b.Colorize {
- *buf = append(*buf, fgGreenBytes...)
- }
- file := event.filename
- if b.Flags&Lmedfile == Lmedfile {
- startIndex := len(file) - 20
- if startIndex > 0 {
- file = "..." + file[startIndex:]
- }
- } else if b.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 b.Flags&(Lfuncname|Lshortfuncname) != 0 {
- *buf = append(*buf, ':')
- } else {
- if b.Colorize {
- *buf = append(*buf, resetBytes...)
- }
- *buf = append(*buf, ' ')
- }
- }
- if b.Flags&(Lfuncname|Lshortfuncname) != 0 {
- if b.Colorize {
- *buf = append(*buf, fgGreenBytes...)
- }
- funcname := event.caller
- if b.Flags&Lshortfuncname != 0 {
- lastIndex := strings.LastIndexByte(funcname, '.')
- if lastIndex > 0 && len(funcname) > lastIndex+1 {
- funcname = funcname[lastIndex+1:]
- }
- }
- *buf = append(*buf, funcname...)
- if b.Colorize {
- *buf = append(*buf, resetBytes...)
- }
- *buf = append(*buf, ' ')
-
- }
- if b.Flags&(Llevel|Llevelinitial) != 0 {
- level := strings.ToUpper(event.level.String())
- if b.Colorize {
- *buf = append(*buf, levelToColor[event.level]...)
- }
- *buf = append(*buf, '[')
- if b.Flags&Llevelinitial != 0 {
- *buf = append(*buf, level[0])
- } else {
- *buf = append(*buf, level...)
- }
- *buf = append(*buf, ']')
- if b.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 !b.Colorize {
- pawMode = removeColor
- }
-
- baw := byteArrayWriter(*buf)
- (&protectedANSIWriter{
- w: &baw,
- mode: pawMode,
- }).Write([]byte(msg))
- *buf = baw
-
- if event.stacktrace != "" && b.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 (b *BaseLogger) LogEvent(event *Event) error {
- if b.Level > event.level {
- return nil
- }
-
- b.mu.Lock()
- defer b.mu.Unlock()
- if !b.Match(event) {
- return nil
- }
- var buf []byte
- b.createMsg(&buf, event)
- _, err := b.out.Write(buf)
- return err
- }
-
- // Match checks if the given event matches the logger's regexp expression
- func (b *BaseLogger) Match(event *Event) bool {
- if b.regexp == nil {
- return true
- }
- if b.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
- if b.regexp.Match(msg) {
- return true
- }
- return false
- }
-
- // Close the base logger
- func (b *BaseLogger) Close() {
- b.mu.Lock()
- defer b.mu.Unlock()
- if b.out != nil {
- b.out.Close()
- }
- }
-
- // GetName returns empty for these provider loggers
- func (b *BaseLogger) GetName() string {
- return ""
- }
|