tracer and logger improvements #312
@ -71,76 +71,106 @@ type Logger interface {
|
||||
type Field interface{}
|
||||
|
||||
// Info writes msg to default logger on info level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Info(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Info(ctx, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Info(ctx, args...)
|
||||
}
|
||||
|
||||
// Error writes msg to default logger on error level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Error(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Error(ctx, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Error(ctx, args...)
|
||||
}
|
||||
|
||||
// Debug writes msg to default logger on debug level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Debug(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Debug(ctx, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Debug(ctx, args...)
|
||||
}
|
||||
|
||||
// Warn writes msg to default logger on warn level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Warn(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Warn(ctx, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Warn(ctx, args...)
|
||||
}
|
||||
|
||||
// Trace writes msg to default logger on trace level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Trace(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Trace(ctx, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Trace(ctx, args...)
|
||||
}
|
||||
|
||||
// Fatal writes msg to default logger on fatal level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Fatal(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Fatal(ctx, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Fatal(ctx, args...)
|
||||
}
|
||||
|
||||
// Infof writes formatted msg to default logger on info level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||
DefaultLogger.Infof(ctx, msg, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Infof(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Errorf writes formatted msg to default logger on error level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||
DefaultLogger.Errorf(ctx, msg, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Errorf(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Debugf writes formatted msg to default logger on debug level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||
DefaultLogger.Debugf(ctx, msg, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Debugf(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Warnf writes formatted msg to default logger on warn level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||
DefaultLogger.Warnf(ctx, msg, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Warnf(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Tracef writes formatted msg to default logger on trace level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||
DefaultLogger.Tracef(ctx, msg, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Tracef(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Fatalf writes formatted msg to default logger on fatal level
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||
DefaultLogger.Fatalf(ctx, msg, args...)
|
||||
DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Fatalf(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// V returns true if passed level enabled in default logger
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func V(level Level) bool {
|
||||
return DefaultLogger.V(level)
|
||||
}
|
||||
|
||||
// Init initialize logger
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Init(opts ...Option) error {
|
||||
return DefaultLogger.Init(opts...)
|
||||
}
|
||||
|
||||
// Fields create logger with specific fields
|
||||
//
|
||||
// Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations
|
||||
func Fields(fields ...interface{}) Logger {
|
||||
return DefaultLogger.Fields(fields...)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ type Options struct {
|
||||
Name string
|
||||
// Fields holds additional metadata
|
||||
Fields []interface{}
|
||||
|
||||
// CallerSkipCount number of frmaes to skip
|
||||
CallerSkipCount int
|
||||
// ContextAttrFuncs contains funcs that executed before log func on context
|
||||
@ -28,14 +29,18 @@ type Options struct {
|
||||
TimeKey string
|
||||
// LevelKey is the key used for the level of the log call
|
||||
LevelKey string
|
||||
// ErroreKey is the key used for the error of the log call
|
||||
ErrorKey string
|
||||
// MessageKey is the key used for the message of the log call
|
||||
MessageKey string
|
||||
// SourceKey is the key used for the source file and line of the log call
|
||||
SourceKey string
|
||||
// StacktraceKey is the key used for the stacktrace
|
||||
StacktraceKey string
|
||||
// Stacktrace controls writing of stacktaces on error
|
||||
Stacktrace bool
|
||||
// AddStacktrace controls writing of stacktaces on error
|
||||
AddStacktrace bool
|
||||
// AddSource enabled writing source file and position in log
|
||||
AddSource bool
|
||||
// The logging level the logger should log
|
||||
Level Level
|
||||
}
|
||||
@ -49,6 +54,7 @@ func NewOptions(opts ...Option) Options {
|
||||
CallerSkipCount: DefaultCallerSkipCount,
|
||||
Context: context.Background(),
|
||||
ContextAttrFuncs: DefaultContextAttrFuncs,
|
||||
AddSource: true,
|
||||
}
|
||||
|
||||
WithMicroKeys()(&options)
|
||||
@ -56,6 +62,7 @@ func NewOptions(opts ...Option) Options {
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
@ -87,10 +94,17 @@ func WithOutput(out io.Writer) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithStacktrace controls writing stacktrace on error
|
||||
func WithStacktrace(v bool) Option {
|
||||
// WitAddStacktrace controls writing stacktrace on error
|
||||
func WithAddStacktrace(v bool) Option {
|
||||
return func(o *Options) {
|
||||
o.Stacktrace = v
|
||||
o.AddStacktrace = v
|
||||
}
|
||||
}
|
||||
|
||||
// WitAddSource controls writing source file and pos in log
|
||||
func WithAddSource(v bool) Option {
|
||||
return func(o *Options) {
|
||||
o.AddSource = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,6 +136,7 @@ func WithZapKeys() Option {
|
||||
o.MessageKey = "msg"
|
||||
o.SourceKey = "caller"
|
||||
o.StacktraceKey = "stacktrace"
|
||||
o.ErrorKey = "error"
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +147,7 @@ func WithZerologKeys() Option {
|
||||
o.MessageKey = "message"
|
||||
o.SourceKey = "caller"
|
||||
o.StacktraceKey = "stacktrace"
|
||||
o.ErrorKey = "error"
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,6 +158,7 @@ func WithSlogKeys() Option {
|
||||
o.MessageKey = slog.MessageKey
|
||||
o.SourceKey = slog.SourceKey
|
||||
o.StacktraceKey = "stacktrace"
|
||||
o.ErrorKey = "error"
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,5 +169,6 @@ func WithMicroKeys() Option {
|
||||
o.MessageKey = "msg"
|
||||
o.SourceKey = "caller"
|
||||
o.StacktraceKey = "stacktrace"
|
||||
o.ErrorKey = "error"
|
||||
}
|
||||
}
|
||||
|
@ -82,9 +82,9 @@ func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger {
|
||||
|
||||
l.leveler = new(slog.LevelVar)
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: s.renameAttr,
|
||||
ReplaceAttr: l.renameAttr,
|
||||
Level: l.leveler,
|
||||
AddSource: true,
|
||||
AddSource: l.opts.AddSource,
|
||||
}
|
||||
l.leveler.Set(loggerToSlogLevel(l.opts.Level))
|
||||
handler := slog.NewJSONHandler(options.Out, handleOpt)
|
||||
@ -109,22 +109,22 @@ func (s *slogLogger) Options() logger.Options {
|
||||
|
||||
func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger {
|
||||
s.mu.RLock()
|
||||
nl := &slogLogger{opts: s.opts}
|
||||
nl.leveler = new(slog.LevelVar)
|
||||
nl.leveler.Set(s.leveler.Level())
|
||||
l := &slogLogger{opts: s.opts}
|
||||
l.leveler = new(slog.LevelVar)
|
||||
l.leveler.Set(s.leveler.Level())
|
||||
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: nl.renameAttr,
|
||||
Level: nl.leveler,
|
||||
AddSource: true,
|
||||
ReplaceAttr: l.renameAttr,
|
||||
Level: l.leveler,
|
||||
AddSource: l.opts.AddSource,
|
||||
}
|
||||
|
||||
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||
nl.slog = slog.New(handler).With(attrs...)
|
||||
l.slog = slog.New(handler).With(attrs...)
|
||||
|
||||
s.mu.RUnlock()
|
||||
|
||||
return nl
|
||||
return l
|
||||
}
|
||||
|
||||
func (s *slogLogger) Init(opts ...logger.Option) error {
|
||||
@ -142,7 +142,7 @@ func (s *slogLogger) Init(opts ...logger.Option) error {
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: s.renameAttr,
|
||||
Level: s.leveler,
|
||||
AddSource: true,
|
||||
AddSource: s.opts.AddSource,
|
||||
}
|
||||
s.leveler.Set(loggerToSlogLevel(s.opts.Level))
|
||||
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||
@ -160,16 +160,35 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, attrs ...interfa
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
if s.opts.Stacktrace && lvl == logger.ErrorLevel {
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.opts.AddStacktrace && lvl == logger.ErrorLevel {
|
||||
stackInfo := make([]byte, 1024*1024)
|
||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
||||
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
||||
if len(traceLines) != 0 {
|
||||
r.AddAttrs(slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))
|
||||
attrs = append(attrs, slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
// r.Add(attrs[1:]...)
|
||||
r.Add(attrs[1:]...)
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
if a.Key == s.opts.ErrorKey {
|
||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -179,17 +198,36 @@ func (s *slogLogger) Logf(ctx context.Context, lvl logger.Level, msg string, att
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
if s.opts.Stacktrace && lvl == logger.ErrorLevel {
|
||||
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.opts.AddStacktrace && lvl == logger.ErrorLevel {
|
||||
stackInfo := make([]byte, 1024*1024)
|
||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
||||
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
||||
if len(traceLines) != 0 {
|
||||
r.AddAttrs(slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))
|
||||
attrs = append(attrs, (slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1])))
|
||||
}
|
||||
}
|
||||
}
|
||||
// r.Add(attrs...)
|
||||
r.Add(attrs[1:]...)
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
if a.Key == s.opts.ErrorKey {
|
||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -200,7 +238,17 @@ func (s *slogLogger) Info(ctx context.Context, attrs ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -210,8 +258,18 @@ func (s *slogLogger) Infof(ctx context.Context, msg string, attrs ...interface{}
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
r := slog.NewRecord(time.Now(), slog.LevelInfo, msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -222,7 +280,17 @@ func (s *slogLogger) Debug(ctx context.Context, attrs ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -232,8 +300,18 @@ func (s *slogLogger) Debugf(ctx context.Context, msg string, attrs ...interface{
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug, msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -244,7 +322,17 @@ func (s *slogLogger) Trace(ctx context.Context, attrs ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug-1, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -254,8 +342,18 @@ func (s *slogLogger) Tracef(ctx context.Context, msg string, attrs ...interface{
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug-1, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug-1, msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -266,18 +364,28 @@ func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
if s.opts.Stacktrace {
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.opts.AddStacktrace {
|
||||
stackInfo := make([]byte, 1024*1024)
|
||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
||||
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
||||
if len(traceLines) != 0 {
|
||||
r.AddAttrs(slog.String("stacktrace", traceLines[len(traceLines)-1]))
|
||||
attrs = append(attrs, slog.String("stacktrace", traceLines[len(traceLines)-1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
if a.Key == "error" {
|
||||
if a.Key == s.opts.ErrorKey {
|
||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||
return false
|
||||
@ -294,19 +402,29 @@ func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
if s.opts.Stacktrace {
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError, msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.opts.AddStacktrace {
|
||||
stackInfo := make([]byte, 1024*1024)
|
||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
||||
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
||||
if len(traceLines) != 0 {
|
||||
r.AddAttrs(slog.String("stacktrace", traceLines[len(traceLines)-1]))
|
||||
attrs = append(attrs, slog.String("stacktrace", traceLines[len(traceLines)-1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
r.Add(attrs...)
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
if a.Key == "error" {
|
||||
if a.Key == s.opts.ErrorKey {
|
||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||
return false
|
||||
@ -324,7 +442,17 @@ func (s *slogLogger) Fatal(ctx context.Context, attrs ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError+1, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
os.Exit(1)
|
||||
}
|
||||
@ -335,8 +463,18 @@ func (s *slogLogger) Fatalf(ctx context.Context, msg string, attrs ...interface{
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError+1, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError+1, msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
os.Exit(1)
|
||||
}
|
||||
@ -348,7 +486,17 @@ func (s *slogLogger) Warn(ctx context.Context, attrs ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
@ -358,8 +506,18 @@ func (s *slogLogger) Warnf(ctx context.Context, msg string, attrs ...interface{}
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
r := slog.NewRecord(time.Now(), slog.LevelWarn, msg, pcs[0])
|
||||
for _, fn := range s.opts.ContextAttrFuncs {
|
||||
attrs = append(attrs, fn(ctx)...)
|
||||
}
|
||||
|
||||
for idx, attr := range attrs {
|
||||
if ve, ok := attr.(error); ok && ve != nil {
|
||||
attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package slog
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
@ -17,10 +18,30 @@ func TestError(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
l.Error(ctx, "message")
|
||||
l.Error(ctx, "message", fmt.Errorf("error message"))
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) {
|
||||
t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes())
|
||||
}
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"error":"`)) {
|
||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorf(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf), logger.WithStacktrace(true))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
l.Errorf(ctx, "message", fmt.Errorf("error message"))
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) {
|
||||
t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes())
|
||||
}
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"error":"`)) {
|
||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext(t *testing.T) {
|
||||
|
139
tracer/memory/memory.go
Normal file
139
tracer/memory/memory.go
Normal file
@ -0,0 +1,139 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v3/tracer"
|
||||
"go.unistack.org/micro/v3/util/id"
|
||||
)
|
||||
|
||||
var _ tracer.Tracer = (*Tracer)(nil)
|
||||
|
||||
type Tracer struct {
|
||||
opts tracer.Options
|
||||
spans []tracer.Span
|
||||
}
|
||||
|
||||
func (t *Tracer) Spans() []tracer.Span {
|
||||
return t.spans
|
||||
}
|
||||
|
||||
func (t *Tracer) Start(ctx context.Context, name string, opts ...tracer.SpanOption) (context.Context, tracer.Span) {
|
||||
options := tracer.NewSpanOptions(opts...)
|
||||
span := &Span{
|
||||
name: name,
|
||||
ctx: ctx,
|
||||
tracer: t,
|
||||
kind: options.Kind,
|
||||
startTime: time.Now(),
|
||||
}
|
||||
span.spanID.s, _ = id.New()
|
||||
span.traceID.s, _ = id.New()
|
||||
if span.ctx == nil {
|
||||
span.ctx = context.Background()
|
||||
}
|
||||
t.spans = append(t.spans, span)
|
||||
return tracer.NewSpanContext(ctx, span), span
|
||||
}
|
||||
|
||||
func (t *Tracer) Flush(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tracer) Init(opts ...tracer.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&t.opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tracer) Name() string {
|
||||
return t.opts.Name
|
||||
}
|
||||
|
||||
type noopStringer struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (s noopStringer) String() string {
|
||||
return s.s
|
||||
}
|
||||
|
||||
type Span struct {
|
||||
ctx context.Context
|
||||
tracer tracer.Tracer
|
||||
name string
|
||||
statusMsg string
|
||||
startTime time.Time
|
||||
finishTime time.Time
|
||||
traceID noopStringer
|
||||
spanID noopStringer
|
||||
events []*Event
|
||||
labels []interface{}
|
||||
logs []interface{}
|
||||
kind tracer.SpanKind
|
||||
status tracer.SpanStatus
|
||||
}
|
||||
|
||||
func (s *Span) Finish(_ ...tracer.SpanOption) {
|
||||
s.finishTime = time.Now()
|
||||
}
|
||||
|
||||
func (s *Span) Context() context.Context {
|
||||
return s.ctx
|
||||
}
|
||||
|
||||
func (s *Span) Tracer() tracer.Tracer {
|
||||
return s.tracer
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
name string
|
||||
labels []interface{}
|
||||
}
|
||||
|
||||
func (s *Span) AddEvent(name string, opts ...tracer.EventOption) {
|
||||
options := tracer.NewEventOptions(opts...)
|
||||
s.events = append(s.events, &Event{name: name, labels: options.Labels})
|
||||
}
|
||||
|
||||
func (s *Span) SetName(name string) {
|
||||
s.name = name
|
||||
}
|
||||
|
||||
func (s *Span) AddLogs(kv ...interface{}) {
|
||||
s.logs = append(s.logs, kv...)
|
||||
}
|
||||
|
||||
func (s *Span) AddLabels(kv ...interface{}) {
|
||||
s.labels = append(s.labels, kv...)
|
||||
}
|
||||
|
||||
func (s *Span) Kind() tracer.SpanKind {
|
||||
return s.kind
|
||||
}
|
||||
|
||||
func (s *Span) TraceID() string {
|
||||
return s.traceID.String()
|
||||
}
|
||||
|
||||
func (s *Span) SpanID() string {
|
||||
return s.spanID.String()
|
||||
}
|
||||
|
||||
func (s *Span) Status() (tracer.SpanStatus, string) {
|
||||
return s.status, s.statusMsg
|
||||
}
|
||||
|
||||
func (s *Span) SetStatus(st tracer.SpanStatus, msg string) {
|
||||
s.status = st
|
||||
s.statusMsg = msg
|
||||
}
|
||||
|
||||
// NewTracer returns new memory tracer
|
||||
func NewTracer(opts ...tracer.Option) *Tracer {
|
||||
return &Tracer{
|
||||
opts: tracer.NewOptions(opts...),
|
||||
}
|
||||
}
|
38
tracer/memory/memory_test.go
Normal file
38
tracer/memory/memory_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.unistack.org/micro/v3/logger"
|
||||
"go.unistack.org/micro/v3/logger/slog"
|
||||
"go.unistack.org/micro/v3/tracer"
|
||||
)
|
||||
|
||||
func TestLoggerWithTracer(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
logger.DefaultLogger = slog.NewLogger(logger.WithOutput(buf))
|
||||
|
||||
if err := logger.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var span tracer.Span
|
||||
tr := NewTracer()
|
||||
ctx, span = tr.Start(ctx, "test1")
|
||||
|
||||
logger.Error(ctx, "my test error", fmt.Errorf("error"))
|
||||
|
||||
if !strings.Contains(buf.String(), span.TraceID()) {
|
||||
t.Fatalf("log does not contains trace id: %s", buf.Bytes())
|
||||
}
|
||||
|
||||
_, _ = tr.Start(ctx, "test2")
|
||||
|
||||
for _, s := range tr.Spans() {
|
||||
_ = s
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ package tracer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.unistack.org/micro/v3/util/id"
|
||||
)
|
||||
|
||||
var _ Tracer = (*noopTracer)(nil)
|
||||
@ -24,6 +26,8 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption)
|
||||
labels: options.Labels,
|
||||
kind: options.Kind,
|
||||
}
|
||||
span.spanID.s, _ = id.New()
|
||||
span.traceID.s, _ = id.New()
|
||||
if span.ctx == nil {
|
||||
span.ctx = context.Background()
|
||||
}
|
||||
@ -31,6 +35,14 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption)
|
||||
return NewSpanContext(ctx, span), span
|
||||
}
|
||||
|
||||
type noopStringer struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (s noopStringer) String() string {
|
||||
return s.s
|
||||
}
|
||||
|
||||
func (t *noopTracer) Flush(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
@ -56,6 +68,8 @@ type noopSpan struct {
|
||||
tracer Tracer
|
||||
name string
|
||||
statusMsg string
|
||||
traceID noopStringer
|
||||
spanID noopStringer
|
||||
events []*noopEvent
|
||||
labels []interface{}
|
||||
logs []interface{}
|
||||
@ -63,7 +77,15 @@ type noopSpan struct {
|
||||
status SpanStatus
|
||||
}
|
||||
|
||||
func (s *noopSpan) Finish(opts ...SpanOption) {
|
||||
func (s *noopSpan) TraceID() string {
|
||||
return s.traceID.String()
|
||||
}
|
||||
|
||||
func (s *noopSpan) SpanID() string {
|
||||
return s.spanID.String()
|
||||
}
|
||||
|
||||
func (s *noopSpan) Finish(_ ...SpanOption) {
|
||||
}
|
||||
|
||||
func (s *noopSpan) Context() context.Context {
|
||||
|
@ -5,11 +5,33 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"go.unistack.org/micro/v3/logger"
|
||||
)
|
||||
|
||||
// DefaultTracer is the global default tracer
|
||||
var DefaultTracer = NewTracer()
|
||||
|
||||
var (
|
||||
// TraceIDKey is the key used for the trace id in the log call
|
||||
TraceIDKey = "trace-id"
|
||||
// SpanIDKey is the key used for the span id in the log call
|
||||
SpanIDKey = "span-id"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger.DefaultContextAttrFuncs = append(logger.DefaultContextAttrFuncs,
|
||||
func(ctx context.Context) []interface{} {
|
||||
if span, ok := SpanFromContext(ctx); ok {
|
||||
return []interface{}{
|
||||
TraceIDKey, span.TraceID(),
|
||||
SpanIDKey, span.SpanID(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Tracer is an interface for distributed tracing
|
||||
type Tracer interface {
|
||||
// Name return tracer name
|
||||
@ -43,6 +65,10 @@ type Span interface {
|
||||
AddLogs(kv ...interface{})
|
||||
// Kind returns span kind
|
||||
Kind() SpanKind
|
||||
// TraceID returns trace id
|
||||
TraceID() string
|
||||
// SpanID returns span id
|
||||
SpanID() string
|
||||
}
|
||||
|
||||
// sort labels alphabeticaly by label name
|
||||
|
Loading…
Reference in New Issue
Block a user