summaryrefslogtreecommitdiffstats
path: root/modules/log/event_format.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/log/event_format.go253
1 files changed, 253 insertions, 0 deletions
diff --git a/modules/log/event_format.go b/modules/log/event_format.go
new file mode 100644
index 0000000..df6b083
--- /dev/null
+++ b/modules/log/event_format.go
@@ -0,0 +1,253 @@
+// 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)
+ 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
+}