mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-08-28 13:13:48 +00:00
- It was possible for Forgejo adminstrators to configure the logger to log the goroutine PID that made the `Log` call.
- The need for the goroutine PID to be logged is that it might give insight which request or otherwise process made the log call by correlating the PID with other logs. However even for a single request I cannot make correlations between the goroutine PIDs and it seems that this particular need cannot be fulfilled by the current implementation.
- The gathering of this PID is discouraged, https://go.dev/doc/faq#no_goroutine_id, and the current implementation is a hack and can break in future Go releases; it has broken before in 61e21d7ded
.
- If the need arise, we instead should make the logger implementation context aware and use a PID that's associated with the context, which is guarantees to be consistent (this is also the more idiomatic way to achieve this functionality).
<!--start release-notes-assistant-->
## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Other changes without a feature or bug label
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8851): <!--number 8851 --><!--line 0 --><!--description Y2hvcmU6IHJlbW92ZSBnb3JvdXRpbmUgUElEIGxvZ2dpbmc=-->chore: remove goroutine PID logging<!--description-->
<!--end release-notes-assistant-->
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8851
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
239 lines
5.7 KiB
Go
239 lines
5.7 KiB
Go
// 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
|
|
|
|
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]
|
|
}
|
|
|
|
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
|
|
}
|