123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- // 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 package
- func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, msgArgs ...any) []byte {
- buf := make([]byte, 0, 1024)
- buf = append(buf, mode.Prefix...)
- t := event.Time
- flags := mode.Flags.Bits()
- 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 re-use 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
- }
|