blob: 45bae6f62a3154cd081cc4834cd90ee6afa4d550 [file] [log] [blame]
// Package loggingtracer provides a trace.Tracer that wraps trace.DefaultTracer and logs span
// starts/ends and durations. It is intended for local debugging only, as usage in production might
// cause noisy and/or verbose logs.
//
// To use this logger, call Initialize(). To customize the logger function, override Logf.
package loggingtracer
import (
"context"
"fmt"
"runtime"
"time"
"go.opencensus.io/trace"
"go.skia.org/infra/go/now"
"go.skia.org/infra/go/sklog"
)
// Logf is the logging function used by this package.
var Logf func(format string, v ...interface{}) = sklog.Infof
// Initialize wraps trace.DefaultTracer with a trace.Tracer implementation that logs span
// starts/ends using the Logf function defined in this package.
//
// This is intended for local debugging only. Usage in production might cuase noisy/verbose logs.
func Initialize() {
trace.DefaultTracer = &loggingTracer{actualTracer: trace.DefaultTracer}
}
// getCaller returns the calling file:line via runtime.Caller.
func getCaller(skip int) string {
_, file, line, ok := runtime.Caller(skip)
if ok {
return fmt.Sprintf("%s:%d", file, line)
}
return "unknown caller"
}
// loggingTracer is a trace.Tracer that wraps another trace.Tracer and logs span stars/ends.
type loggingTracer struct {
actualTracer trace.Tracer
}
// StartSpan implements the trace.Tracer interface.
func (t *loggingTracer) StartSpan(ctx context.Context, name string, o ...trace.StartOption) (context.Context, *trace.Span) {
Logf("Starting span: %s [%s]", name, getCaller(3))
ctx, actualSpan := t.actualTracer.StartSpan(ctx, name, o...)
return ctx, trace.NewSpan(&loggingSpan{actualSpan: actualSpan, ctx: ctx, name: name, startTime: now.Now(ctx)})
}
// StartSpanWithRemoteParent implements the trace.Tracer interface.
func (t *loggingTracer) StartSpanWithRemoteParent(ctx context.Context, name string, parent trace.SpanContext, o ...trace.StartOption) (context.Context, *trace.Span) {
Logf("Starting span with remote parent: %s [%s]", name, getCaller(3))
ctx, actualSpan := t.actualTracer.StartSpanWithRemoteParent(ctx, name, parent, o...)
return ctx, trace.NewSpan(&loggingSpan{actualSpan: actualSpan, ctx: ctx, name: name, startTime: now.Now(ctx)})
}
// FromContext implements the trace.Tracer interface.
func (t *loggingTracer) FromContext(ctx context.Context) *trace.Span {
return t.actualTracer.FromContext(ctx)
}
// NewContext implements the trace.Tracer interface.
func (t *loggingTracer) NewContext(parent context.Context, s *trace.Span) context.Context {
return t.actualTracer.NewContext(parent, s)
}
var _ trace.Tracer = (*loggingTracer)(nil)
// loggingSpan is a trace.SpanInterface that wraps a trace.Span and logs the span end. It also
// implements the spanCtxGetterSetter interface so we can get and set its context from tests, which
// is necessary for time traveling.
type loggingSpan struct {
actualSpan *trace.Span
ctx context.Context
name string
startTime time.Time
}
// spanCtxGetterSetter allows manipulating the context of a span from tests, which is necessary for
// time traveling.
type spanCtxGetterSetter interface {
GetCtx() context.Context
SetCtx(context.Context)
}
// GetCtx implements the spanCtxGetterSetter interface.
func (s *loggingSpan) GetCtx() context.Context { return s.ctx }
// SetCtx implements the spanCtxGetterSetter interface.
func (s *loggingSpan) SetCtx(ctx context.Context) { s.ctx = ctx }
var _ spanCtxGetterSetter = (*loggingSpan)(nil)
// IsRecordingEvents implements the trace.SpanInterface interface.
func (s *loggingSpan) IsRecordingEvents() bool { return s.actualSpan.IsRecordingEvents() }
// End implements the trace.SpanInterface interface.
func (s *loggingSpan) End() {
Logf("Ending span: %s [%s] (%s)", s.name, getCaller(3), now.Now(s.ctx).Sub(s.startTime))
s.actualSpan.End()
}
// SpanContext implements the trace.SpanInterface interface.
func (s *loggingSpan) SpanContext() trace.SpanContext { return s.actualSpan.SpanContext() }
// SetName implements the trace.SpanInterface interface.
func (s *loggingSpan) SetName(name string) { s.actualSpan.SetName(name) }
// SetStatus implements the trace.SpanInterface interface.
func (s *loggingSpan) SetStatus(status trace.Status) { s.actualSpan.SetStatus(status) }
// AddAttributes implements the trace.SpanInterface interface.
func (s *loggingSpan) AddAttributes(attributes ...trace.Attribute) {
s.actualSpan.AddAttributes(attributes...)
}
// Annotate implements the trace.SpanInterface interface.
func (s *loggingSpan) Annotate(attributes []trace.Attribute, str string) {
s.actualSpan.Annotate(attributes, str)
}
// Annotatef implements the trace.SpanInterface interface.
func (s *loggingSpan) Annotatef(attributes []trace.Attribute, format string, a ...interface{}) {
s.actualSpan.Annotatef(attributes, format, a...)
}
// AddMessageSendEvent implements the trace.SpanInterface interface.
func (s *loggingSpan) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
s.actualSpan.AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize)
}
// AddMessageReceiveEvent implements the trace.SpanInterface interface.
func (s *loggingSpan) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
s.actualSpan.AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize)
}
// AddLink implements the trace.SpanInterface interface.
func (s *loggingSpan) AddLink(l trace.Link) { s.actualSpan.AddLink(l) }
// String implements the trace.SpanInterface interface.
func (s *loggingSpan) String() string { return s.actualSpan.String() }
var _ trace.SpanInterface = (*loggingSpan)(nil)