Skip to content

Commit 71e206e

Browse files
committed
refactor: refactor logging system for improved stack trace handling
- Remove unused imports `path/filepath` and `runtime` from `logger.go` - Remove `Infof` method from `defaultLogger` and re-add it at the end of the file - Replace `runtime.Caller` and `filepath.Base` with `stack` function for logging stack traces - Simplify `Errorf` and `Error` methods to use `Printf` and `Println` directly - Remove calls to `os.Exit(1)` in `Fatalf` and `Fatal` methods - Add a new file `recovery.go` with utility functions for stack trace handling and a buffer pool for performance optimization Signed-off-by: Bo-Yi Wu <[email protected]>
1 parent 1850259 commit 71e206e

File tree

2 files changed

+114
-24
lines changed

2 files changed

+114
-24
lines changed

logger.go

+10-24
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"fmt"
55
"log"
66
"os"
7-
"path/filepath"
8-
"runtime"
97
)
108

119
// Logger interface is used throughout gorush
@@ -33,50 +31,38 @@ type defaultLogger struct {
3331
fatalLogger *log.Logger
3432
}
3533

36-
func (l defaultLogger) Infof(format string, args ...interface{}) {
37-
l.infoLogger.Printf(format, args...)
38-
}
39-
4034
func (l defaultLogger) logWithCallerf(logger *log.Logger, format string, args ...interface{}) {
41-
_, file, line, ok := runtime.Caller(2)
42-
if ok {
43-
shortFile := filepath.Base(file)
44-
logger.Printf("%s:%d: %s", shortFile, line, fmt.Sprintf(format, args...))
45-
return
46-
}
47-
logger.Printf(format, args...)
35+
stack := stack(3)
36+
logger.Printf("%s\n%s", stack, fmt.Sprintf(format, args...))
4837
}
4938

5039
func (l defaultLogger) logWithCaller(logger *log.Logger, args ...interface{}) {
51-
_, file, line, ok := runtime.Caller(2)
52-
if ok {
53-
shortFile := filepath.Base(file)
54-
logger.Printf("%s:%d: %s", shortFile, line, fmt.Sprint(args...))
55-
return
56-
}
57-
logger.Println(fmt.Sprint(args...))
40+
stack := stack(3)
41+
logger.Println(stack, fmt.Sprint(args...))
42+
}
43+
44+
func (l defaultLogger) Infof(format string, args ...interface{}) {
45+
l.infoLogger.Printf(format, args...)
5846
}
5947

6048
func (l defaultLogger) Errorf(format string, args ...interface{}) {
61-
l.logWithCallerf(l.errorLogger, format, args...)
49+
l.errorLogger.Printf(format, args...)
6250
}
6351

6452
func (l defaultLogger) Fatalf(format string, args ...interface{}) {
6553
l.logWithCallerf(l.fatalLogger, format, args...)
66-
os.Exit(1)
6754
}
6855

6956
func (l defaultLogger) Info(args ...interface{}) {
7057
l.infoLogger.Println(fmt.Sprint(args...))
7158
}
7259

7360
func (l defaultLogger) Error(args ...interface{}) {
74-
l.logWithCaller(l.errorLogger, args...)
61+
l.errorLogger.Println(fmt.Sprint(args...))
7562
}
7663

7764
func (l defaultLogger) Fatal(args ...interface{}) {
7865
l.logWithCaller(l.fatalLogger, args...)
79-
os.Exit(1)
8066
}
8167

8268
// NewEmptyLogger for simple logger.

recovery.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package queue
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"runtime"
8+
"strings"
9+
"sync"
10+
)
11+
12+
const (
13+
dunno = "???"
14+
centerDot = "·"
15+
dot = "."
16+
slash = "/"
17+
)
18+
19+
// bufferPool is a pool of byte buffers that can be reused to reduce the number
20+
// of allocations and improve performance. It uses sync.Pool to manage a pool
21+
// of reusable *bytes.Buffer objects. When a buffer is requested from the pool,
22+
// if one is available, it is returned; otherwise, a new buffer is created.
23+
// When a buffer is no longer needed, it should be put back into the pool to be
24+
// reused.
25+
var bufferPool = sync.Pool{
26+
New: func() interface{} {
27+
return new(bytes.Buffer)
28+
},
29+
}
30+
31+
// stack captures and returns the current stack trace, skipping the specified number of frames.
32+
// It retrieves the stack trace information, including the file name, line number, and function name,
33+
// and formats it into a byte slice. The function uses a buffer pool to manage memory efficiently.
34+
//
35+
// Parameters:
36+
// - skip: The number of stack frames to skip before recording the trace.
37+
//
38+
// Returns:
39+
// - A byte slice containing the formatted stack trace.
40+
func stack(skip int) []byte {
41+
buf := bufferPool.Get().(*bytes.Buffer)
42+
defer bufferPool.Put(buf)
43+
buf.Reset()
44+
45+
var lines [][]byte
46+
var lastFile string
47+
for i := skip; ; i++ {
48+
pc, file, line, ok := runtime.Caller(i)
49+
if !ok {
50+
break
51+
}
52+
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
53+
if file != lastFile {
54+
data, err := os.ReadFile(file)
55+
if err != nil {
56+
continue
57+
}
58+
lines = bytes.Split(data, []byte{'\n'})
59+
lastFile = file
60+
}
61+
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
62+
}
63+
return buf.Bytes()
64+
}
65+
66+
// source retrieves the n-th line from the provided slice of byte slices,
67+
// trims any leading and trailing whitespace, and returns it as a byte slice.
68+
// If n is out of range, it returns a default "dunno" byte slice.
69+
//
70+
// Parameters:
71+
//
72+
// lines - a slice of byte slices representing lines of text
73+
// n - the 1-based index of the line to retrieve
74+
//
75+
// Returns:
76+
//
77+
// A byte slice containing the trimmed n-th line, or a default "dunno" byte slice if n is out of range.
78+
func source(lines [][]byte, n int) []byte {
79+
n--
80+
if n < 0 || n >= len(lines) {
81+
return []byte(dunno)
82+
}
83+
return bytes.TrimSpace(lines[n])
84+
}
85+
86+
// function takes a program counter (pc) value and returns the name of the function
87+
// corresponding to that program counter as a byte slice. It uses runtime.FuncForPC
88+
// to retrieve the function information and processes the function name to remove
89+
// any path and package information, returning only the base function name.
90+
func function(pc uintptr) []byte {
91+
fn := runtime.FuncForPC(pc)
92+
if fn == nil {
93+
return []byte(dunno)
94+
}
95+
name := fn.Name()
96+
if lastSlash := strings.LastIndex(name, slash); lastSlash >= 0 {
97+
name = name[lastSlash+1:]
98+
}
99+
if period := strings.Index(name, dot); period >= 0 {
100+
name = name[period+1:]
101+
}
102+
name = strings.ReplaceAll(name, centerDot, dot)
103+
return []byte(name)
104+
}

0 commit comments

Comments
 (0)