优化一些问题&修复一些BUG
This commit is contained in:
@@ -7,84 +7,142 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (l *Logger) logger(ctx context.Context, event string, v ...any) {
|
||||
pc, file, line, _ := runtime.Caller(2)
|
||||
// fmt.Println("runtime.Caller", pc, file, line, ok)
|
||||
// 调用方可能是 log 包(Logger.Write -> logger),所以这里取第 2 层
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
|
||||
basePath, _ := filepath.Abs("./")
|
||||
basePath = strings.ReplaceAll(basePath, "\\", "/")
|
||||
// fmt.Println("basePath", basePath)
|
||||
|
||||
file = strings.TrimPrefix(file, basePath)
|
||||
|
||||
funcName := runtime.FuncForPC(pc).Name()
|
||||
funcName = filepath.Ext(funcName)
|
||||
funcName = strings.TrimPrefix(funcName, ".")
|
||||
|
||||
// by, _ := json.Marshal(v)
|
||||
var funcName string
|
||||
if ok && pc != 0 {
|
||||
if fn := runtime.FuncForPC(pc); fn != nil {
|
||||
funcName = strings.TrimPrefix(filepath.Ext(fn.Name()), ".")
|
||||
}
|
||||
if basePath, err := filepath.Abs("./"); err == nil {
|
||||
basePath = strings.ReplaceAll(basePath, "\\", "/")
|
||||
file = strings.TrimPrefix(strings.ReplaceAll(file, "\\", "/"), basePath)
|
||||
}
|
||||
}
|
||||
|
||||
nowTime := time.Now().In(l.option.timeZone).Format("2006-01-02 15:04:05.000000")
|
||||
|
||||
traceId, _ := ctx.Value(l.option.traceField).(string)
|
||||
|
||||
// writeStr := "[" + event + "]" + nowTime + " " + file + ":" + fmt.Sprintf("%d", line) + " " + funcName + " gid:" + getGID() + " " + traceId + " @data@: " + string(by) + "\n\n"
|
||||
// ctx 允许为 nil,不能直接调 Value
|
||||
var traceId string
|
||||
if ctx != nil {
|
||||
traceId, _ = ctx.Value(l.option.traceField).(string)
|
||||
}
|
||||
|
||||
// error 转成带堆栈的字符串(%+v 会带上 pkg/errors 的调用栈)
|
||||
for idx, val := range v {
|
||||
if _, ok := val.(error); ok {
|
||||
if _, isErr := val.(error); isErr {
|
||||
v[idx] = fmt.Sprintf("%+v", val)
|
||||
}
|
||||
}
|
||||
|
||||
var gid string
|
||||
if l.option.isGid {
|
||||
gid = getGID()
|
||||
}
|
||||
|
||||
fd := FormatData{
|
||||
Time: nowTime,
|
||||
File: file + ":" + fmt.Sprintf("%d", line),
|
||||
File: file + ":" + strconv.Itoa(line),
|
||||
Func: funcName,
|
||||
Gid: getGID(),
|
||||
Gid: gid,
|
||||
Content: v,
|
||||
TraceId: traceId,
|
||||
Expand: l.option.expandData,
|
||||
Expand: l.option.expandData,
|
||||
}
|
||||
|
||||
if event == "error" {
|
||||
// fd.Stack = string(debug.Stack())
|
||||
}
|
||||
fdb := marshalLog(fd, nowTime, file, line, l.option.escapeHTML, gid, traceId)
|
||||
|
||||
var fdb []byte
|
||||
if l.option.escapeHTML {
|
||||
fdb, _ = json.Marshal(fd)
|
||||
fdb = append([]byte("\n["+event+"]"), fdb...)
|
||||
|
||||
_, _ = l.write(event, fdb)
|
||||
|
||||
if l.option.errorToInfo && event == "error" {
|
||||
_, _ = l.write("info", fdb)
|
||||
}
|
||||
}
|
||||
|
||||
// marshalLog 序列化日志内容
|
||||
// json 序列化失败时(chan/func 等不可序列化类型,或循环引用)不能静默丢弃,
|
||||
// 否则只会留下一行没有内容的 "["+event+"]",需要降级成可读的文本
|
||||
func marshalLog(fd FormatData, nowTime, file string, line int, escapeHTML bool, gid, traceId string) []byte {
|
||||
var (
|
||||
b []byte
|
||||
err error
|
||||
)
|
||||
if escapeHTML {
|
||||
b, err = json.Marshal(fd)
|
||||
} else {
|
||||
// 非转义
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.Encode(fd)
|
||||
fdb = buf.Bytes()
|
||||
err = encoder.Encode(fd)
|
||||
b = bytes.TrimRight(buf.Bytes(), "\n")
|
||||
}
|
||||
fdb = bytes.TrimRight(fdb, "\n")
|
||||
|
||||
ff := []byte("\n[" + event + "]")
|
||||
fdb = append(ff, fdb...)
|
||||
|
||||
l.write(event, fdb)
|
||||
|
||||
if l.option.errorToInfo && event == "error" {
|
||||
l.write("info", fdb)
|
||||
if err == nil {
|
||||
return b
|
||||
}
|
||||
|
||||
// log.Println("" + string(by))
|
||||
// 降级:逐字段尝试,失败的内容用 %+v 兜底
|
||||
fallback := FormatData{
|
||||
Time: nowTime,
|
||||
File: file + ":" + strconv.Itoa(line),
|
||||
Func: fd.Func,
|
||||
Gid: gid,
|
||||
TraceId: traceId,
|
||||
Content: stringifyValues(fd.Content),
|
||||
Expand: fd.Expand,
|
||||
}
|
||||
if fb, ferr := json.Marshal(fallback); ferr == nil {
|
||||
return fb
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf(`{"time":%q,"content":%q,"marshal_error":%q}`,
|
||||
nowTime, fmt.Sprintf("%+v", fd.Content), err.Error()))
|
||||
}
|
||||
|
||||
// stringifyValues 把不可序列化的值转成字符串
|
||||
func stringifyValues(v any) []string {
|
||||
switch val := v.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case []any:
|
||||
out := make([]string, 0, len(val))
|
||||
for _, item := range val {
|
||||
out = append(out, fmt.Sprintf("%+v", item))
|
||||
}
|
||||
return out
|
||||
default:
|
||||
return []string{fmt.Sprintf("%+v", val)}
|
||||
}
|
||||
}
|
||||
|
||||
// getGID 获取当前 goroutine 的 ID
|
||||
func getGID() string {
|
||||
b := make([]byte, 64)
|
||||
b = b[:runtime.Stack(b, false)]
|
||||
b = bytes.TrimPrefix(b, []byte("goroutine "))
|
||||
idx := bytes.IndexByte(b, ' ')
|
||||
if idx < 0 {
|
||||
// 解析失败时不要 panic
|
||||
return "0"
|
||||
}
|
||||
return string(b[:idx])
|
||||
}
|
||||
|
||||
type FormatData struct {
|
||||
Time string `json:"time,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
Func string `json:"func,omitempty"`
|
||||
Gid string `json:"gid,omitempty"`
|
||||
Content interface{} `json:"content,omitempty"`
|
||||
TraceId string `json:"traceId,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
Expand map[string]string `json:"expand,omitempty"`
|
||||
Time string `json:"time,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
Func string `json:"func,omitempty"`
|
||||
Gid string `json:"gid,omitempty"`
|
||||
Content interface{} `json:"content,omitempty"`
|
||||
TraceId string `json:"traceId,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
Expand map[string]string `json:"expand,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user