2019-04-07 03:25:14 +03:00
// 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 ,
2019-07-23 21:50:39 +03:00
} ) . Write ( msg )
2019-04-07 03:25:14 +03:00
* 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
2019-06-12 22:41:28 +03:00
return logger . regexp . Match ( msg )
2019-04-07 03:25:14 +03:00
}
// 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 ""
}