code.gitea.io/gitea@v1.22.3/modules/log/event_format.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package log
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  )
    12  
    13  type Event struct {
    14  	Time time.Time
    15  
    16  	GoroutinePid string
    17  	Caller       string
    18  	Filename     string
    19  	Line         int
    20  
    21  	Level Level
    22  
    23  	MsgSimpleText string
    24  
    25  	msgFormat string // the format and args is only valid in the caller's goroutine
    26  	msgArgs   []any  // they are discarded before the event is passed to the writer's channel
    27  
    28  	Stacktrace string
    29  }
    30  
    31  type EventFormatted struct {
    32  	Origin *Event
    33  	Msg    any // the message formatted by the writer's formatter, the writer knows its type
    34  }
    35  
    36  type EventFormatter func(mode *WriterMode, event *Event, msgFormat string, msgArgs ...any) []byte
    37  
    38  type logStringFormatter struct {
    39  	v LogStringer
    40  }
    41  
    42  var _ fmt.Formatter = logStringFormatter{}
    43  
    44  func (l logStringFormatter) Format(f fmt.State, verb rune) {
    45  	if f.Flag('#') && verb == 'v' {
    46  		_, _ = fmt.Fprintf(f, "%#v", l.v)
    47  		return
    48  	}
    49  	_, _ = f.Write([]byte(l.v.LogString()))
    50  }
    51  
    52  // Copy of cheap integer to fixed-width decimal to ascii from logger.
    53  // TODO: legacy bugs: doesn't support negative number, overflow if wid it too large.
    54  func itoa(buf []byte, i, wid int) []byte {
    55  	var s [20]byte
    56  	bp := len(s) - 1
    57  	for i >= 10 || wid > 1 {
    58  		wid--
    59  		q := i / 10
    60  		s[bp] = byte('0' + i - q*10)
    61  		bp--
    62  		i = q
    63  	}
    64  	// i < 10
    65  	s[bp] = byte('0' + i)
    66  	return append(buf, s[bp:]...)
    67  }
    68  
    69  func colorSprintf(colorize bool, format string, args ...any) string {
    70  	hasColorValue := false
    71  	for _, v := range args {
    72  		if _, hasColorValue = v.(*ColoredValue); hasColorValue {
    73  			break
    74  		}
    75  	}
    76  	if colorize || !hasColorValue {
    77  		return fmt.Sprintf(format, args...)
    78  	}
    79  
    80  	noColors := make([]any, len(args))
    81  	copy(noColors, args)
    82  	for i, v := range args {
    83  		if cv, ok := v.(*ColoredValue); ok {
    84  			noColors[i] = cv.v
    85  		}
    86  	}
    87  	return fmt.Sprintf(format, noColors...)
    88  }
    89  
    90  // EventFormatTextMessage makes the log message for a writer with its mode. This function is a copy of the original package
    91  func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, msgArgs ...any) []byte {
    92  	buf := make([]byte, 0, 1024)
    93  	buf = append(buf, mode.Prefix...)
    94  	t := event.Time
    95  	flags := mode.Flags.Bits()
    96  	if flags&(Ldate|Ltime|Lmicroseconds) != 0 {
    97  		if mode.Colorize {
    98  			buf = append(buf, fgCyanBytes...)
    99  		}
   100  		if flags&LUTC != 0 {
   101  			t = t.UTC()
   102  		}
   103  		if flags&Ldate != 0 {
   104  			year, month, day := t.Date()
   105  			buf = itoa(buf, year, 4)
   106  			buf = append(buf, '/')
   107  			buf = itoa(buf, int(month), 2)
   108  			buf = append(buf, '/')
   109  			buf = itoa(buf, day, 2)
   110  			buf = append(buf, ' ')
   111  		}
   112  		if flags&(Ltime|Lmicroseconds) != 0 {
   113  			hour, min, sec := t.Clock()
   114  			buf = itoa(buf, hour, 2)
   115  			buf = append(buf, ':')
   116  			buf = itoa(buf, min, 2)
   117  			buf = append(buf, ':')
   118  			buf = itoa(buf, sec, 2)
   119  			if flags&Lmicroseconds != 0 {
   120  				buf = append(buf, '.')
   121  				buf = itoa(buf, t.Nanosecond()/1e3, 6)
   122  			}
   123  			buf = append(buf, ' ')
   124  		}
   125  		if mode.Colorize {
   126  			buf = append(buf, resetBytes...)
   127  		}
   128  	}
   129  	if flags&(Lshortfile|Llongfile) != 0 {
   130  		if mode.Colorize {
   131  			buf = append(buf, fgGreenBytes...)
   132  		}
   133  		file := event.Filename
   134  		if flags&Lmedfile == Lmedfile {
   135  			startIndex := len(file) - 20
   136  			if startIndex > 0 {
   137  				file = "..." + file[startIndex:]
   138  			}
   139  		} else if flags&Lshortfile != 0 {
   140  			startIndex := strings.LastIndexByte(file, '/')
   141  			if startIndex > 0 && startIndex < len(file) {
   142  				file = file[startIndex+1:]
   143  			}
   144  		}
   145  		buf = append(buf, file...)
   146  		buf = append(buf, ':')
   147  		buf = itoa(buf, event.Line, -1)
   148  		if flags&(Lfuncname|Lshortfuncname) != 0 {
   149  			buf = append(buf, ':')
   150  		} else {
   151  			if mode.Colorize {
   152  				buf = append(buf, resetBytes...)
   153  			}
   154  			buf = append(buf, ' ')
   155  		}
   156  	}
   157  	if flags&(Lfuncname|Lshortfuncname) != 0 {
   158  		if mode.Colorize {
   159  			buf = append(buf, fgGreenBytes...)
   160  		}
   161  		funcname := event.Caller
   162  		if flags&Lshortfuncname != 0 {
   163  			lastIndex := strings.LastIndexByte(funcname, '.')
   164  			if lastIndex > 0 && len(funcname) > lastIndex+1 {
   165  				funcname = funcname[lastIndex+1:]
   166  			}
   167  		}
   168  		buf = append(buf, funcname...)
   169  		if mode.Colorize {
   170  			buf = append(buf, resetBytes...)
   171  		}
   172  		buf = append(buf, ' ')
   173  	}
   174  
   175  	if flags&(Llevel|Llevelinitial) != 0 {
   176  		level := strings.ToUpper(event.Level.String())
   177  		if mode.Colorize {
   178  			buf = append(buf, ColorBytes(levelToColor[event.Level]...)...)
   179  		}
   180  		buf = append(buf, '[')
   181  		if flags&Llevelinitial != 0 {
   182  			buf = append(buf, level[0])
   183  		} else {
   184  			buf = append(buf, level...)
   185  		}
   186  		buf = append(buf, ']')
   187  		if mode.Colorize {
   188  			buf = append(buf, resetBytes...)
   189  		}
   190  		buf = append(buf, ' ')
   191  	}
   192  
   193  	var msg []byte
   194  
   195  	// if the log needs colorizing, do it
   196  	if mode.Colorize && len(msgArgs) > 0 {
   197  		hasColorValue := false
   198  		for _, v := range msgArgs {
   199  			if _, hasColorValue = v.(*ColoredValue); hasColorValue {
   200  				break
   201  			}
   202  		}
   203  		if hasColorValue {
   204  			msg = []byte(fmt.Sprintf(msgFormat, msgArgs...))
   205  		}
   206  	}
   207  	// try to re-use the pre-formatted simple text message
   208  	if len(msg) == 0 {
   209  		msg = []byte(event.MsgSimpleText)
   210  	}
   211  	// if still no message, do the normal Sprintf for the message
   212  	if len(msg) == 0 {
   213  		msg = []byte(colorSprintf(mode.Colorize, msgFormat, msgArgs...))
   214  	}
   215  	// remove at most one trailing new line
   216  	if len(msg) > 0 && msg[len(msg)-1] == '\n' {
   217  		msg = msg[:len(msg)-1]
   218  	}
   219  
   220  	if flags&Lgopid == Lgopid {
   221  		if event.GoroutinePid != "" {
   222  			buf = append(buf, '[')
   223  			if mode.Colorize {
   224  				buf = append(buf, ColorBytes(FgHiYellow)...)
   225  			}
   226  			buf = append(buf, event.GoroutinePid...)
   227  			if mode.Colorize {
   228  				buf = append(buf, resetBytes...)
   229  			}
   230  			buf = append(buf, ']', ' ')
   231  		}
   232  	}
   233  	buf = append(buf, msg...)
   234  
   235  	if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level {
   236  		lines := bytes.Split([]byte(event.Stacktrace), []byte("\n"))
   237  		for _, line := range lines {
   238  			buf = append(buf, "\n\t"...)
   239  			buf = append(buf, line...)
   240  		}
   241  		buf = append(buf, '\n')
   242  	}
   243  	buf = append(buf, '\n')
   244  	return buf
   245  }