Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
6.2 KiB
270 lines
6.2 KiB
// 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, 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 "" |
|
}
|
|
|