aboutsummaryrefslogtreecommitdiffstats
path: root/modules/log
diff options
context:
space:
mode:
Diffstat (limited to 'modules/log')
-rw-r--r--modules/log/event_format.go36
-rw-r--r--modules/log/event_writer_base.go2
-rw-r--r--modules/log/event_writer_conn_test.go3
-rw-r--r--modules/log/flags.go2
-rw-r--r--modules/log/flags_test.go14
-rw-r--r--modules/log/level_test.go6
-rw-r--r--modules/log/logger.go4
-rw-r--r--modules/log/logger_global.go2
-rw-r--r--modules/log/logger_impl.go56
-rw-r--r--modules/log/logger_test.go83
-rw-r--r--modules/log/manager_test.go2
-rw-r--r--modules/log/misc.go16
12 files changed, 142 insertions, 84 deletions
diff --git a/modules/log/event_format.go b/modules/log/event_format.go
index 8fda0a4980..4cf471d223 100644
--- a/modules/log/event_format.go
+++ b/modules/log/event_format.go
@@ -125,15 +125,19 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
buf = append(buf, resetBytes...)
}
}
- if flags&(Lshortfile|Llongfile) != 0 {
+ if flags&(Lshortfile|Llongfile) != 0 && event.Filename != "" {
if mode.Colorize {
buf = append(buf, fgGreenBytes...)
}
file := event.Filename
if flags&Lmedfile == Lmedfile {
- startIndex := len(file) - 20
- if startIndex > 0 {
- file = "..." + file[startIndex:]
+ fileLen := len(file)
+ const softLimit = 20
+ if fileLen > softLimit {
+ slashIndex := strings.LastIndexByte(file[:fileLen-softLimit], '/')
+ if slashIndex != -1 {
+ file = ".../" + file[slashIndex+1:]
+ }
}
} else if flags&Lshortfile != 0 {
startIndex := strings.LastIndexByte(file, '/')
@@ -157,14 +161,22 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
if mode.Colorize {
buf = append(buf, fgGreenBytes...)
}
- funcname := event.Caller
+ funcName := event.Caller
+ shortFuncName := funcName
if flags&Lshortfuncname != 0 {
- lastIndex := strings.LastIndexByte(funcname, '.')
- if lastIndex > 0 && len(funcname) > lastIndex+1 {
- funcname = funcname[lastIndex+1:]
+ // funcName = "code.gitea.io/gitea/modules/foo/bar.MyFunc.func1.2()"
+ slashPos := strings.LastIndexByte(funcName, '/')
+ dotPos := strings.IndexByte(funcName[slashPos+1:], '.')
+ if dotPos > 0 {
+ // shortFuncName = "MyFunc.func1.2()"
+ shortFuncName = funcName[slashPos+1+dotPos+1:]
+ if strings.Contains(shortFuncName, ".") {
+ shortFuncName = strings.ReplaceAll(shortFuncName, ".func", ".")
+ }
}
+ funcName = shortFuncName
}
- buf = append(buf, funcname...)
+ buf = append(buf, funcName...)
if mode.Colorize {
buf = append(buf, resetBytes...)
}
@@ -200,7 +212,7 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
}
}
if hasColorValue {
- msg = []byte(fmt.Sprintf(msgFormat, msgArgs...))
+ msg = fmt.Appendf(nil, msgFormat, msgArgs...)
}
}
// try to re-use the pre-formatted simple text message
@@ -231,8 +243,8 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
buf = append(buf, msg...)
if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level {
- lines := bytes.Split([]byte(event.Stacktrace), []byte("\n"))
- for _, line := range lines {
+ lines := bytes.SplitSeq([]byte(event.Stacktrace), []byte("\n"))
+ for line := range lines {
buf = append(buf, "\n\t"...)
buf = append(buf, line...)
}
diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go
index c327c48ca2..9189ca4e90 100644
--- a/modules/log/event_writer_base.go
+++ b/modules/log/event_writer_base.go
@@ -105,7 +105,7 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) {
case io.WriterTo:
_, err = msg.WriteTo(b.OutputWriteCloser)
default:
- _, err = b.OutputWriteCloser.Write([]byte(fmt.Sprint(msg)))
+ _, err = fmt.Fprint(b.OutputWriteCloser, msg)
}
if err != nil {
FallbackErrorf("unable to write log message of %q (%v): %v", b.Name, err, event.Msg)
diff --git a/modules/log/event_writer_conn_test.go b/modules/log/event_writer_conn_test.go
index 69e87aa8c4..2aff37812d 100644
--- a/modules/log/event_writer_conn_test.go
+++ b/modules/log/event_writer_conn_test.go
@@ -4,7 +4,6 @@
package log
import (
- "context"
"fmt"
"io"
"net"
@@ -40,7 +39,7 @@ func TestConnLogger(t *testing.T) {
level := INFO
flags := LstdFlags | LUTC | Lfuncname
- logger := NewLoggerWithWriters(context.Background(), "test", NewEventWriterConn("test-conn", WriterMode{
+ logger := NewLoggerWithWriters(t.Context(), "test", NewEventWriterConn("test-conn", WriterMode{
Level: level,
Prefix: prefix,
Flags: FlagsFromBits(flags),
diff --git a/modules/log/flags.go b/modules/log/flags.go
index 8064c91745..f409261150 100644
--- a/modules/log/flags.go
+++ b/modules/log/flags.go
@@ -123,7 +123,7 @@ func FlagsFromString(from string, def ...uint32) Flags {
return Flags{defined: true, flags: def[0]}
}
flags := uint32(0)
- for _, flag := range strings.Split(strings.ToLower(from), ",") {
+ for flag := range strings.SplitSeq(strings.ToLower(from), ",") {
flags |= flagFromString[strings.TrimSpace(flag)]
}
return Flags{defined: true, flags: flags}
diff --git a/modules/log/flags_test.go b/modules/log/flags_test.go
index 03972a9fb0..6eb65d8114 100644
--- a/modules/log/flags_test.go
+++ b/modules/log/flags_test.go
@@ -12,19 +12,19 @@ import (
)
func TestFlags(t *testing.T) {
- assert.EqualValues(t, Ldefault, Flags{}.Bits())
+ assert.Equal(t, Ldefault, Flags{}.Bits())
assert.EqualValues(t, 0, FlagsFromString("").Bits())
- assert.EqualValues(t, Lgopid, FlagsFromString("", Lgopid).Bits())
+ assert.Equal(t, Lgopid, FlagsFromString("", Lgopid).Bits())
assert.EqualValues(t, 0, FlagsFromString("none", Lgopid).Bits())
- assert.EqualValues(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits())
+ assert.Equal(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits())
- assert.EqualValues(t, "stdflags", FlagsFromString("stdflags").String())
- assert.EqualValues(t, "medfile", FlagsFromString("medfile").String())
+ assert.Equal(t, "stdflags", FlagsFromString("stdflags").String())
+ assert.Equal(t, "medfile", FlagsFromString("medfile").String())
bs, err := json.Marshal(FlagsFromString("utc,level"))
assert.NoError(t, err)
- assert.EqualValues(t, `"level,utc"`, string(bs))
+ assert.Equal(t, `"level,utc"`, string(bs))
var flags Flags
assert.NoError(t, json.Unmarshal(bs, &flags))
- assert.EqualValues(t, LUTC|Llevel, flags.Bits())
+ assert.Equal(t, LUTC|Llevel, flags.Bits())
}
diff --git a/modules/log/level_test.go b/modules/log/level_test.go
index cd18a807d8..0e59af6cb7 100644
--- a/modules/log/level_test.go
+++ b/modules/log/level_test.go
@@ -32,11 +32,11 @@ func TestLevelMarshalUnmarshalJSON(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, INFO, testLevel.Level)
- err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 2)), &testLevel)
+ err = json.Unmarshal(fmt.Appendf(nil, `{"level":%d}`, 2), &testLevel)
assert.NoError(t, err)
assert.Equal(t, INFO, testLevel.Level)
- err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 10012)), &testLevel)
+ err = json.Unmarshal(fmt.Appendf(nil, `{"level":%d}`, 10012), &testLevel)
assert.NoError(t, err)
assert.Equal(t, INFO, testLevel.Level)
@@ -51,5 +51,5 @@ func TestLevelMarshalUnmarshalJSON(t *testing.T) {
}
func makeTestLevelBytes(level string) []byte {
- return []byte(fmt.Sprintf(`{"level":"%s"}`, level))
+ return fmt.Appendf(nil, `{"level":"%s"}`, level)
}
diff --git a/modules/log/logger.go b/modules/log/logger.go
index a833b6ef0f..8b89e0eb5a 100644
--- a/modules/log/logger.go
+++ b/modules/log/logger.go
@@ -24,7 +24,7 @@ package log
// BaseLogger provides the basic logging functions
type BaseLogger interface {
- Log(skip int, level Level, format string, v ...any)
+ Log(skip int, event *Event, format string, v ...any)
GetLevel() Level
}
@@ -45,6 +45,6 @@ type Logger interface {
LevelLogger
}
-type LogStringer interface { //nolint:revive
+type LogStringer interface { //nolint:revive // export stutter
LogString() string
}
diff --git a/modules/log/logger_global.go b/modules/log/logger_global.go
index 6ce8b70fed..07c25cd62f 100644
--- a/modules/log/logger_global.go
+++ b/modules/log/logger_global.go
@@ -18,7 +18,7 @@ func GetLevel() Level {
}
func Log(skip int, level Level, format string, v ...any) {
- GetLogger(DEFAULT).Log(skip+1, level, format, v...)
+ GetLogger(DEFAULT).Log(skip+1, &Event{Level: level}, format, v...)
}
func Trace(format string, v ...any) {
diff --git a/modules/log/logger_impl.go b/modules/log/logger_impl.go
index 76dd5f43fb..551c1454aa 100644
--- a/modules/log/logger_impl.go
+++ b/modules/log/logger_impl.go
@@ -5,6 +5,7 @@ package log
import (
"context"
+ "reflect"
"runtime"
"strings"
"sync"
@@ -175,29 +176,42 @@ func (l *LoggerImpl) IsEnabled() bool {
return l.level.Load() < int32(FATAL) && len(l.eventWriters) > 0
}
+func asLogStringer(v any) LogStringer {
+ if s, ok := v.(LogStringer); ok {
+ return s
+ } else if a := reflect.ValueOf(v); a.Kind() == reflect.Struct {
+ // in case the receiver is a pointer, but the value is a struct
+ vp := reflect.New(a.Type())
+ vp.Elem().Set(a)
+ if s, ok := vp.Interface().(LogStringer); ok {
+ return s
+ }
+ }
+ return nil
+}
+
// Log prepares the log event, if the level matches, the event will be sent to the writers
-func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
- if Level(l.level.Load()) > level {
+func (l *LoggerImpl) Log(skip int, event *Event, format string, logArgs ...any) {
+ if Level(l.level.Load()) > event.Level {
return
}
- event := &Event{
- Time: time.Now(),
- Level: level,
- Caller: "?()",
+ if event.Time.IsZero() {
+ event.Time = time.Now()
}
-
- pc, filename, line, ok := runtime.Caller(skip + 1)
- if ok {
- fn := runtime.FuncForPC(pc)
- if fn != nil {
- event.Caller = fn.Name() + "()"
+ if event.Caller == "" {
+ pc, filename, line, ok := runtime.Caller(skip + 1)
+ if ok {
+ fn := runtime.FuncForPC(pc)
+ if fn != nil {
+ fnName := fn.Name()
+ event.Caller = strings.ReplaceAll(fnName, "[...]", "") + "()" // generic function names are "foo[...]"
+ }
+ }
+ event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line
+ if l.stacktraceLevel.Load() <= int32(event.Level) {
+ event.Stacktrace = Stack(skip + 1)
}
- }
- event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line
-
- if l.stacktraceLevel.Load() <= int32(level) {
- event.Stacktrace = Stack(skip + 1)
}
// get a simple text message without color
@@ -207,11 +221,11 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
// handle LogStringer values
for i, v := range msgArgs {
if cv, ok := v.(*ColoredValue); ok {
- if s, ok := cv.v.(LogStringer); ok {
- cv.v = logStringFormatter{v: s}
+ if ls := asLogStringer(cv.v); ls != nil {
+ cv.v = logStringFormatter{v: ls}
}
- } else if s, ok := v.(LogStringer); ok {
- msgArgs[i] = logStringFormatter{v: s}
+ } else if ls := asLogStringer(v); ls != nil {
+ msgArgs[i] = logStringFormatter{v: ls}
}
}
diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go
index 0de14eb411..a74139dc51 100644
--- a/modules/log/logger_test.go
+++ b/modules/log/logger_test.go
@@ -4,7 +4,7 @@
package log
import (
- "context"
+ "regexp"
"sync"
"testing"
"time"
@@ -35,11 +35,11 @@ func (d *dummyWriter) Close() error {
return nil
}
-func (d *dummyWriter) GetLogs() []string {
+func (d *dummyWriter) FetchLogs() []string {
d.mu.Lock()
defer d.mu.Unlock()
- logs := make([]string, len(d.logs))
- copy(logs, d.logs)
+ logs := d.logs
+ d.logs = nil
return logs
}
@@ -53,20 +53,20 @@ func newDummyWriter(name string, level Level, delay time.Duration) *dummyWriter
}
func TestLogger(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+ logger := NewLoggerWithWriters(t.Context(), "test")
dump := logger.DumpWriters()
assert.Empty(t, dump)
- assert.EqualValues(t, NONE, logger.GetLevel())
+ assert.Equal(t, NONE, logger.GetLevel())
assert.False(t, logger.IsEnabled())
w1 := newDummyWriter("dummy-1", DEBUG, 0)
logger.AddWriters(w1)
- assert.EqualValues(t, DEBUG, logger.GetLevel())
+ assert.Equal(t, DEBUG, logger.GetLevel())
w2 := newDummyWriter("dummy-2", WARN, 200*time.Millisecond)
logger.AddWriters(w2)
- assert.EqualValues(t, DEBUG, logger.GetLevel())
+ assert.Equal(t, DEBUG, logger.GetLevel())
dump = logger.DumpWriters()
assert.Len(t, dump, 2)
@@ -77,18 +77,18 @@ func TestLogger(t *testing.T) {
// w2 is slow, so only w1 has logs
time.Sleep(100 * time.Millisecond)
- assert.Equal(t, []string{"debug-level\n", "error-level\n"}, w1.GetLogs())
- assert.Equal(t, []string{}, w2.GetLogs())
+ assert.Equal(t, []string{"debug-level\n", "error-level\n"}, w1.FetchLogs())
+ assert.Empty(t, w2.FetchLogs())
logger.Close()
// after Close, all logs are flushed
- assert.Equal(t, []string{"debug-level\n", "error-level\n"}, w1.GetLogs())
- assert.Equal(t, []string{"error-level\n"}, w2.GetLogs())
+ assert.Empty(t, w1.FetchLogs())
+ assert.Equal(t, []string{"error-level\n"}, w2.FetchLogs())
}
func TestLoggerPause(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+ logger := NewLoggerWithWriters(t.Context(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
logger.AddWriters(w1)
@@ -98,12 +98,12 @@ func TestLoggerPause(t *testing.T) {
logger.Info("info-level")
time.Sleep(100 * time.Millisecond)
- assert.Equal(t, []string{}, w1.GetLogs())
+ assert.Empty(t, w1.FetchLogs())
GetManager().ResumeAll()
time.Sleep(100 * time.Millisecond)
- assert.Equal(t, []string{"info-level\n"}, w1.GetLogs())
+ assert.Equal(t, []string{"info-level\n"}, w1.FetchLogs())
logger.Close()
}
@@ -116,21 +116,54 @@ func (t testLogString) LogString() string {
return "log-string"
}
-func TestLoggerLogString(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+type testLogStringPtrReceiver struct {
+ Field string
+}
- w1 := newDummyWriter("dummy-1", DEBUG, 0)
- w1.Mode.Colorize = true
- logger.AddWriters(w1)
+func (t *testLogStringPtrReceiver) LogString() string {
+ return "log-string-ptr-receiver"
+}
- logger.Info("%s %s %#v %v", testLogString{}, &testLogString{}, testLogString{Field: "detail"}, NewColoredValue(testLogString{}, FgRed))
- logger.Close()
+func genericFunc[T any](logger Logger, v T) {
+ logger.Info("from genericFunc: %v", v)
+}
- assert.Equal(t, []string{"log-string log-string log.testLogString{Field:\"detail\"} \x1b[31mlog-string\x1b[0m\n"}, w1.GetLogs())
+func TestLoggerOutput(t *testing.T) {
+ t.Run("LogString", func(t *testing.T) {
+ logger := NewLoggerWithWriters(t.Context(), "test")
+ w1 := newDummyWriter("dummy-1", DEBUG, 0)
+ w1.Mode.Colorize = true
+ logger.AddWriters(w1)
+ logger.Info("%s %s %#v %v", testLogString{}, &testLogString{}, testLogString{Field: "detail"}, NewColoredValue(testLogString{}, FgRed))
+ logger.Info("%s %s %#v %v", testLogStringPtrReceiver{}, &testLogStringPtrReceiver{}, testLogStringPtrReceiver{Field: "detail"}, NewColoredValue(testLogStringPtrReceiver{}, FgRed))
+ logger.Close()
+
+ assert.Equal(t, []string{
+ "log-string log-string log.testLogString{Field:\"detail\"} \x1b[31mlog-string\x1b[0m\n",
+ "log-string-ptr-receiver log-string-ptr-receiver &log.testLogStringPtrReceiver{Field:\"detail\"} \x1b[31mlog-string-ptr-receiver\x1b[0m\n",
+ }, w1.FetchLogs())
+ })
+
+ t.Run("Caller", func(t *testing.T) {
+ logger := NewLoggerWithWriters(t.Context(), "test")
+ w1 := newDummyWriter("dummy-1", DEBUG, 0)
+ w1.EventWriterBaseImpl.Mode.Flags.flags = Lmedfile | Lshortfuncname
+ logger.AddWriters(w1)
+ anonymousFunc := func(logger Logger) {
+ logger.Info("from anonymousFunc")
+ }
+ genericFunc(logger, "123")
+ anonymousFunc(logger)
+ logger.Close()
+ logs := w1.FetchLogs()
+ assert.Len(t, logs, 2)
+ assert.Regexp(t, `modules/log/logger_test.go:\w+:`+regexp.QuoteMeta(`genericFunc() from genericFunc: 123`), logs[0])
+ assert.Regexp(t, `modules/log/logger_test.go:\w+:`+regexp.QuoteMeta(`TestLoggerOutput.2.1() from anonymousFunc`), logs[1])
+ })
}
func TestLoggerExpressionFilter(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+ logger := NewLoggerWithWriters(t.Context(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
w1.Mode.Expression = "foo.*"
@@ -142,5 +175,5 @@ func TestLoggerExpressionFilter(t *testing.T) {
logger.SendLogEvent(&Event{Level: INFO, Filename: "foo.go", MsgSimpleText: "by filename"})
logger.Close()
- assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.GetLogs())
+ assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.FetchLogs())
}
diff --git a/modules/log/manager_test.go b/modules/log/manager_test.go
index b8fbf84613..beddbccb73 100644
--- a/modules/log/manager_test.go
+++ b/modules/log/manager_test.go
@@ -37,6 +37,6 @@ func TestSharedWorker(t *testing.T) {
m.Close()
- logs := w.(*dummyWriter).GetLogs()
+ logs := w.(*dummyWriter).FetchLogs()
assert.Equal(t, []string{"msg-1\n", "msg-2\n", "msg-3\n"}, logs)
}
diff --git a/modules/log/misc.go b/modules/log/misc.go
index ae4ce04cf3..c9d230e4ac 100644
--- a/modules/log/misc.go
+++ b/modules/log/misc.go
@@ -19,8 +19,8 @@ func BaseLoggerToGeneralLogger(b BaseLogger) Logger {
var _ Logger = (*baseToLogger)(nil)
-func (s *baseToLogger) Log(skip int, level Level, format string, v ...any) {
- s.base.Log(skip+1, level, format, v...)
+func (s *baseToLogger) Log(skip int, event *Event, format string, v ...any) {
+ s.base.Log(skip+1, event, format, v...)
}
func (s *baseToLogger) GetLevel() Level {
@@ -32,27 +32,27 @@ func (s *baseToLogger) LevelEnabled(level Level) bool {
}
func (s *baseToLogger) Trace(format string, v ...any) {
- s.base.Log(1, TRACE, format, v...)
+ s.base.Log(1, &Event{Level: TRACE}, format, v...)
}
func (s *baseToLogger) Debug(format string, v ...any) {
- s.base.Log(1, DEBUG, format, v...)
+ s.base.Log(1, &Event{Level: DEBUG}, format, v...)
}
func (s *baseToLogger) Info(format string, v ...any) {
- s.base.Log(1, INFO, format, v...)
+ s.base.Log(1, &Event{Level: INFO}, format, v...)
}
func (s *baseToLogger) Warn(format string, v ...any) {
- s.base.Log(1, WARN, format, v...)
+ s.base.Log(1, &Event{Level: WARN}, format, v...)
}
func (s *baseToLogger) Error(format string, v ...any) {
- s.base.Log(1, ERROR, format, v...)
+ s.base.Log(1, &Event{Level: ERROR}, format, v...)
}
func (s *baseToLogger) Critical(format string, v ...any) {
- s.base.Log(1, CRITICAL, format, v...)
+ s.base.Log(1, &Event{Level: CRITICAL}, format, v...)
}
type PrintfLogger struct {