You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
1.9 KiB
94 lines
1.9 KiB
package log
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"gitoa.ru/go-4devs/log/entry"
|
|
"gitoa.ru/go-4devs/log/field"
|
|
)
|
|
|
|
func WithSource(items int) Middleware {
|
|
const (
|
|
skip = 4
|
|
funcPrefix = "gitoa.ru/go-4devs/log.Logger"
|
|
skipHelper = "gitoa.ru/go-4devs/log."
|
|
)
|
|
|
|
items += skip
|
|
|
|
return func(ctx context.Context, data *entry.Entry, handler Logger) (int, error) {
|
|
pc := make([]uintptr, items)
|
|
n := runtime.Callers(skip, pc)
|
|
|
|
if n == 0 {
|
|
return handler(ctx, data.Add(errSourceField(skip, items)))
|
|
}
|
|
|
|
pc = pc[:n] // pass only valid pcs to runtime.CallersFrames
|
|
frames := runtime.CallersFrames(pc)
|
|
prew := false
|
|
|
|
for {
|
|
frame, more := frames.Next()
|
|
|
|
has := strings.HasPrefix(frame.Function, funcPrefix)
|
|
if !has && prew {
|
|
if strings.HasPrefix(frame.Function, skipHelper) {
|
|
continue
|
|
}
|
|
|
|
return handler(ctx, data.AddAny(KeySource, Source{
|
|
Func: filepath.Base(frame.Function),
|
|
Line: frame.Line,
|
|
File: trimPath(frame.File),
|
|
}))
|
|
}
|
|
|
|
prew = has
|
|
|
|
if !more {
|
|
break
|
|
}
|
|
}
|
|
|
|
return handler(ctx, data.Add(errSourceField(skip, items)))
|
|
}
|
|
}
|
|
|
|
func trimPath(file string) string {
|
|
idx := strings.LastIndexByte(file, '/')
|
|
if idx == -1 {
|
|
return filepath.Base(file)
|
|
}
|
|
|
|
// Find the penultimate separator.
|
|
idx = strings.LastIndexByte(file[:idx], '/')
|
|
if idx == -1 {
|
|
return filepath.Base(file)
|
|
}
|
|
|
|
return file[idx+1:]
|
|
}
|
|
|
|
// Source describes the location of a line of source code.
|
|
type Source struct {
|
|
Func string `json:"func"`
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
}
|
|
|
|
func (l Source) MarshalText() ([]byte, error) {
|
|
return []byte(fmt.Sprintf("%s:%d", l.File, l.Line)), nil
|
|
}
|
|
|
|
func (l Source) MarshalJSON() ([]byte, error) {
|
|
return fmt.Appendf([]byte{}, `{"file":"%s","line":%d,"func":"%s"}`, l.File, l.Line, l.Func), nil
|
|
}
|
|
|
|
func errSourceField(skip, max int) field.Field {
|
|
return field.String(KeySource, fmt.Sprintf("source not found by frames[%d:%d]", skip, max))
|
|
}
|
|
|