* Rename BaseLogger to WriterLogger to help the creation of other providers * Don't export ColorBytes and ResetBytes from ColoredValues * Make integration tests only print logs if they fail * check can color before coloring * I always forget about MSSQL * Oh and use LEVEL in sqlite.ini * Make the test logger log at info - as it means you see the router * Remove empty expected changes * Make the migrations quieter too * Don't display SQL on error - it can be looked at in the file logs if necessary * Fix skip when using onGiteaRunfor-closed-social
@ -0,0 +1,104 @@ | |||||
// 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 integrations | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"os" | |||||
"runtime" | |||||
"strings" | |||||
"testing" | |||||
"code.gitea.io/gitea/modules/log" | |||||
) | |||||
var prefix string | |||||
// TestLogger is a logger which will write to the testing log | |||||
type TestLogger struct { | |||||
log.WriterLogger | |||||
} | |||||
var writerCloser = &testLoggerWriterCloser{} | |||||
type testLoggerWriterCloser struct { | |||||
t testing.TB | |||||
} | |||||
func (w *testLoggerWriterCloser) Write(p []byte) (int, error) { | |||||
if w.t != nil { | |||||
if len(p) > 0 && p[len(p)-1] == '\n' { | |||||
p = p[:len(p)-1] | |||||
} | |||||
w.t.Log(string(p)) | |||||
return len(p), nil | |||||
} | |||||
return len(p), nil | |||||
} | |||||
func (w *testLoggerWriterCloser) Close() error { | |||||
return nil | |||||
} | |||||
// PrintCurrentTest prints the current test to os.Stdout | |||||
func PrintCurrentTest(t testing.TB, skip ...int) { | |||||
actualSkip := 1 | |||||
if len(skip) > 0 { | |||||
actualSkip = skip[0] | |||||
} | |||||
_, filename, line, _ := runtime.Caller(actualSkip) | |||||
if log.CanColorStdout { | |||||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line) | |||||
} else { | |||||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", t.Name(), strings.TrimPrefix(filename, prefix), line) | |||||
} | |||||
writerCloser.t = t | |||||
} | |||||
// Printf takes a format and args and prints the string to os.Stdout | |||||
func Printf(format string, args ...interface{}) { | |||||
if log.CanColorStdout { | |||||
for i := 0; i < len(args); i++ { | |||||
args[i] = log.NewColoredValue(args[i]) | |||||
} | |||||
} | |||||
fmt.Fprintf(os.Stdout, "\t"+format, args...) | |||||
} | |||||
// NewTestLogger creates a TestLogger as a log.LoggerProvider | |||||
func NewTestLogger() log.LoggerProvider { | |||||
logger := &TestLogger{} | |||||
logger.Colorize = log.CanColorStdout | |||||
logger.Level = log.TRACE | |||||
return logger | |||||
} | |||||
// Init inits connection writer with json config. | |||||
// json config only need key "level". | |||||
func (log *TestLogger) Init(config string) error { | |||||
err := json.Unmarshal([]byte(config), log) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
log.NewWriterLogger(writerCloser) | |||||
return nil | |||||
} | |||||
// Flush when log should be flushed | |||||
func (log *TestLogger) Flush() { | |||||
} | |||||
// GetName returns the default name for this implementation | |||||
func (log *TestLogger) GetName() string { | |||||
return "test" | |||||
} | |||||
func init() { | |||||
log.Register("test", NewTestLogger) | |||||
_, filename, _, _ := runtime.Caller(0) | |||||
prefix = strings.TrimSuffix(filename, "integrations/testlogger.go") | |||||
} |
@ -1,328 +0,0 @@ | |||||
// 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 "" | |||||
} |
@ -0,0 +1,64 @@ | |||||
// 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 "strings" | |||||
// 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/logger/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/logger/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 | |||||
} |
@ -0,0 +1,273 @@ | |||||
// 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([]byte(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 | |||||
if logger.regexp.Match(msg) { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
// 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 "" | |||||
} |