253 lines · 6.0 KB · Go
RawDownload
// Copyright 2023 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package log import (	"bytes"	"fmt"	"strings"	"time") type Event struct {	Time time.Time 	GoroutinePid string	Caller       string	Filename     string	Line         int 	Level Level 	MsgSimpleText string 	msgFormat string // the format and args is only valid in the caller's goroutine	msgArgs   []any  // they are discarded before the event is passed to the writer's channel 	Stacktrace string} type EventFormatted struct {	Origin *Event	Msg    any // the message formatted by the writer's formatter, the writer knows its type} type EventFormatter func(mode *WriterMode, event *Event, msgFormat string, msgArgs ...any) []byte type logStringFormatter struct {	v LogStringer} var _ fmt.Formatter = logStringFormatter{} func (l logStringFormatter) Format(f fmt.State, verb rune) {	if f.Flag('#') && verb == 'v' {		_, _ = fmt.Fprintf(f, "%#v", l.v)		return	}	_, _ = f.Write([]byte(l.v.LogString()))} // Copy of cheap integer to fixed-width decimal to ascii from logger.// TODO: legacy bugs: doesn't support negative number, overflow if wid it too large.func itoa(buf []byte, i, wid int) []byte {	var s [20]byte	bp := len(s) - 1	for i >= 10 || wid > 1 {		wid--		q := i / 10		s[bp] = byte('0' + i - q*10)		bp--		i = q	}	// i < 10	s[bp] = byte('0' + i)	return append(buf, s[bp:]...)} func colorSprintf(colorize bool, format string, args ...any) string {	hasColorValue := false	for _, v := range args {		if _, hasColorValue = v.(*ColoredValue); hasColorValue {			break		}	}	if colorize || !hasColorValue {		return fmt.Sprintf(format, args...)	} 	noColors := make([]any, len(args))	copy(noColors, args)	for i, v := range args {		if cv, ok := v.(*ColoredValue); ok {			noColors[i] = cv.v		}	}	return fmt.Sprintf(format, noColors...)} // EventFormatTextMessage makes the log message for a writer with its mode. This function is a copy of the original packagefunc EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, msgArgs ...any) []byte {	buf := make([]byte, 0, 1024)	t := event.Time	flags := mode.Flags.Bits() 	// if log level prefixes are enabled, the message must begin with the prefix, see sd_daemon(3)	// "A line that is not prefixed will be logged at the default log level SD_INFO"	if flags&Llevelprefix != 0 {		prefix := event.Level.JournalPrefix()		buf = append(buf, prefix...)	} 	buf = append(buf, mode.Prefix...)	if flags&(Ldate|Ltime|Lmicroseconds) != 0 {		if mode.Colorize {			buf = append(buf, fgCyanBytes...)		}		if flags&LUTC != 0 {			t = t.UTC()		}		if flags&Ldate != 0 {			year, month, day := t.Date()			buf = itoa(buf, year, 4)			buf = append(buf, '/')			buf = itoa(buf, int(month), 2)			buf = append(buf, '/')			buf = itoa(buf, day, 2)			buf = append(buf, ' ')		}		if flags&(Ltime|Lmicroseconds) != 0 {			hour, min, sec := t.Clock()			buf = itoa(buf, hour, 2)			buf = append(buf, ':')			buf = itoa(buf, min, 2)			buf = append(buf, ':')			buf = itoa(buf, sec, 2)			if flags&Lmicroseconds != 0 {				buf = append(buf, '.')				buf = itoa(buf, t.Nanosecond()/1e3, 6)			}			buf = append(buf, ' ')		}		if mode.Colorize {			buf = append(buf, resetBytes...)		}	}	if flags&(Lshortfile|Llongfile) != 0 {		if mode.Colorize {			buf = append(buf, fgGreenBytes...)		}		file := event.Filename		if flags&Lmedfile == Lmedfile {			startIndex := len(file) - 20			if startIndex > 0 {				file = "..." + file[startIndex:]			}		} else if flags&Lshortfile != 0 {			startIndex := strings.LastIndexByte(file, '/')			if startIndex > 0 && startIndex < len(file) {				file = file[startIndex+1:]			}		}		buf = append(buf, file...)		buf = append(buf, ':')		buf = itoa(buf, event.Line, -1)		if flags&(Lfuncname|Lshortfuncname) != 0 {			buf = append(buf, ':')		} else {			if mode.Colorize {				buf = append(buf, resetBytes...)			}			buf = append(buf, ' ')		}	}	if flags&(Lfuncname|Lshortfuncname) != 0 {		if mode.Colorize {			buf = append(buf, fgGreenBytes...)		}		funcname := event.Caller		if flags&Lshortfuncname != 0 {			lastIndex := strings.LastIndexByte(funcname, '.')			if lastIndex > 0 && len(funcname) > lastIndex+1 {				funcname = funcname[lastIndex+1:]			}		}		buf = append(buf, funcname...)		if mode.Colorize {			buf = append(buf, resetBytes...)		}		buf = append(buf, ' ')	} 	if flags&(Llevel|Llevelinitial) != 0 {		level := strings.ToUpper(event.Level.String())		if mode.Colorize {			buf = append(buf, ColorBytes(levelToColor[event.Level]...)...)		}		buf = append(buf, '[')		if flags&Llevelinitial != 0 {			buf = append(buf, level[0])		} else {			buf = append(buf, level...)		}		buf = append(buf, ']')		if mode.Colorize {			buf = append(buf, resetBytes...)		}		buf = append(buf, ' ')	} 	var msg []byte 	// if the log needs colorizing, do it	if mode.Colorize && len(msgArgs) > 0 {		hasColorValue := false		for _, v := range msgArgs {			if _, hasColorValue = v.(*ColoredValue); hasColorValue {				break			}		}		if hasColorValue {			msg = []byte(fmt.Sprintf(msgFormat, msgArgs...))		}	}	// try to reuse the pre-formatted simple text message	if len(msg) == 0 {		msg = []byte(event.MsgSimpleText)	}	// if still no message, do the normal Sprintf for the message	if len(msg) == 0 {		msg = []byte(colorSprintf(mode.Colorize, msgFormat, msgArgs...))	}	// remove at most one trailing new line	if len(msg) > 0 && msg[len(msg)-1] == '\n' {		msg = msg[:len(msg)-1]	} 	if flags&Lgopid == Lgopid {		if event.GoroutinePid != "" {			buf = append(buf, '[')			if mode.Colorize {				buf = append(buf, ColorBytes(FgHiYellow)...)			}			buf = append(buf, event.GoroutinePid...)			if mode.Colorize {				buf = append(buf, resetBytes...)			}			buf = append(buf, ']', ' ')		}	}	buf = append(buf, msg...) 	if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level {		lines := bytes.Split([]byte(event.Stacktrace), []byte("\n"))		for _, line := range lines {			buf = append(buf, "\n\t"...)			buf = append(buf, line...)		}		buf = append(buf, '\n')	}	buf = append(buf, '\n')	return buf}