aboutsummaryrefslogtreecommitdiffstats
path: root/modules/log/writer.go
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2019-04-07 01:25:14 +0100
committerLauris BH <lauris@nix.lv>2019-04-07 03:25:14 +0300
commit5422f23ed8174661b6e658250e4007b7fdf0d603 (patch)
tree87d975854be55c51e0a51c49cf93c33e310d30da /modules/log/writer.go
parent7ed65a98e80a341885b8cddce971012b7fcdae4e (diff)
downloadgitea-5422f23ed8174661b6e658250e4007b7fdf0d603.tar.gz
gitea-5422f23ed8174661b6e658250e4007b7fdf0d603.zip
Quieter Integration Tests (#6513)
* 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 onGiteaRun
Diffstat (limited to 'modules/log/writer.go')
-rw-r--r--modules/log/writer.go273
1 files changed, 273 insertions, 0 deletions
diff --git a/modules/log/writer.go b/modules/log/writer.go
new file mode 100644
index 0000000000..22ef0b9047
--- /dev/null
+++ b/modules/log/writer.go
@@ -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 ""
+}