Commit 36986c7a authored by Janoš Guljaš's avatar Janoš Guljaš Committed by GitHub

add traceid log field if tracing context if available (#35)

parent d2f1199b
...@@ -22,31 +22,31 @@ func (s *server) pingpongHandler(w http.ResponseWriter, r *http.Request) { ...@@ -22,31 +22,31 @@ func (s *server) pingpongHandler(w http.ResponseWriter, r *http.Request) {
peerID := mux.Vars(r)["peer-id"] peerID := mux.Vars(r)["peer-id"]
ctx := r.Context() ctx := r.Context()
span, ctx := s.Tracer.StartSpanFromContext(ctx, "pingpong-api") span, logger, ctx := s.Tracer.StartSpanFromContext(ctx, "pingpong-api", s.Logger)
defer span.Finish() defer span.Finish()
address, err := swarm.ParseHexAddress(peerID) address, err := swarm.ParseHexAddress(peerID)
if err != nil { if err != nil {
s.Logger.Debugf("pingpong: parse peer address %s: %v", peerID, err) logger.Debugf("pingpong: parse peer address %s: %v", peerID, err)
jsonhttp.BadRequest(w, "invalid peer address") jsonhttp.BadRequest(w, "invalid peer address")
return return
} }
rtt, err := s.Pingpong.Ping(ctx, address, "hey", "there", ",", "how are", "you", "?") rtt, err := s.Pingpong.Ping(ctx, address, "hey", "there", ",", "how are", "you", "?")
if err != nil { if err != nil {
s.Logger.Debugf("pingpong: ping %s: %v", peerID, err) logger.Debugf("pingpong: ping %s: %v", peerID, err)
if errors.Is(err, p2p.ErrPeerNotFound) { if errors.Is(err, p2p.ErrPeerNotFound) {
jsonhttp.NotFound(w, "peer not found") jsonhttp.NotFound(w, "peer not found")
return return
} }
s.Logger.Errorf("pingpong failed to peer %s", peerID) logger.Errorf("pingpong failed to peer %s", peerID)
jsonhttp.InternalServerError(w, nil) jsonhttp.InternalServerError(w, nil)
return return
} }
s.metrics.PingRequestCount.Inc() s.metrics.PingRequestCount.Inc()
s.Logger.Infof("pingpong succeeded to peer %s", peerID) logger.Infof("pingpong succeeded to peer %s", peerID)
jsonhttp.OK(w, pingpongResponse{ jsonhttp.OK(w, pingpongResponse{
RTT: rtt.String(), RTT: rtt.String(),
}) })
......
...@@ -24,6 +24,7 @@ type Logger interface { ...@@ -24,6 +24,7 @@ type Logger interface {
WithField(key string, value interface{}) *logrus.Entry WithField(key string, value interface{}) *logrus.Entry
WithFields(fields logrus.Fields) *logrus.Entry WithFields(fields logrus.Fields) *logrus.Entry
WriterLevel(logrus.Level) *io.PipeWriter WriterLevel(logrus.Level) *io.PipeWriter
NewEntry() *logrus.Entry
} }
type logger struct { type logger struct {
...@@ -45,3 +46,7 @@ func New(w io.Writer, level logrus.Level) Logger { ...@@ -45,3 +46,7 @@ func New(w io.Writer, level logrus.Level) Logger {
metrics: metrics, metrics: metrics,
} }
} }
func (l *logger) NewEntry() *logrus.Entry {
return logrus.NewEntry(l.Logger)
}
...@@ -248,6 +248,10 @@ func (s *Service) AddProtocol(p p2p.ProtocolSpec) (err error) { ...@@ -248,6 +248,10 @@ func (s *Service) AddProtocol(p p2p.ProtocolSpec) (err error) {
return return
} }
logger := tracing.NewLoggerWithTraceID(ctx, s.logger)
logger.Tracef("handle protocol %s/%s: stream %s: peer %s", p.Name, p.Version, ss.Name, overlay)
s.metrics.HandledStreamCount.Inc() s.metrics.HandledStreamCount.Inc()
if err := ss.Handler(ctx, p2p.Peer{Address: overlay}, stream); err != nil { if err := ss.Handler(ctx, p2p.Peer{Address: overlay}, stream); err != nil {
var e *p2p.DisconnectError var e *p2p.DisconnectError
...@@ -256,7 +260,8 @@ func (s *Service) AddProtocol(p p2p.ProtocolSpec) (err error) { ...@@ -256,7 +260,8 @@ func (s *Service) AddProtocol(p p2p.ProtocolSpec) (err error) {
_ = s.Disconnect(overlay) _ = s.Disconnect(overlay)
} }
s.logger.Debugf("handle protocol %s/%s: stream %s: peer %s: %v", p.Name, p.Version, ss.Name, overlay, err) logger.Debugf("handle protocol %s/%s: stream %s: peer %s: %v", p.Name, p.Version, ss.Name, overlay, err)
return
} }
}) })
} }
......
...@@ -44,7 +44,7 @@ func TestTracing(t *testing.T) { ...@@ -44,7 +44,7 @@ func TestTracing(t *testing.T) {
handled := make(chan struct{}) handled := make(chan struct{})
if err := s1.AddProtocol(newTestProtocol(func(ctx context.Context, _ p2p.Peer, _ p2p.Stream) error { if err := s1.AddProtocol(newTestProtocol(func(ctx context.Context, _ p2p.Peer, _ p2p.Stream) error {
span, _ := tracer1.StartSpanFromContext(ctx, "test-p2p-handler") span, _, _ := tracer1.StartSpanFromContext(ctx, "test-p2p-handler", nil)
defer span.Finish() defer span.Finish()
handledTracingSpan = fmt.Sprint(span.Context()) handledTracingSpan = fmt.Sprint(span.Context())
...@@ -66,7 +66,7 @@ func TestTracing(t *testing.T) { ...@@ -66,7 +66,7 @@ func TestTracing(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
span, ctx := tracer2.StartSpanFromContext(ctx, "test-p2p-client") span, _, ctx := tracer2.StartSpanFromContext(ctx, "test-p2p-client", nil)
defer span.Finish() defer span.Finish()
if fmt.Sprint(span.Context()) == "" { if fmt.Sprint(span.Context()) == "" {
......
...@@ -64,7 +64,7 @@ func (s *Service) Protocol() p2p.ProtocolSpec { ...@@ -64,7 +64,7 @@ func (s *Service) Protocol() p2p.ProtocolSpec {
} }
func (s *Service) Ping(ctx context.Context, address swarm.Address, msgs ...string) (rtt time.Duration, err error) { func (s *Service) Ping(ctx context.Context, address swarm.Address, msgs ...string) (rtt time.Duration, err error) {
span, ctx := s.tracer.StartSpanFromContext(ctx, "pingpong-p2p-ping") span, logger, ctx := s.tracer.StartSpanFromContext(ctx, "pingpong-p2p-ping", s.logger)
defer span.Finish() defer span.Finish()
start := time.Now() start := time.Now()
...@@ -92,7 +92,7 @@ func (s *Service) Ping(ctx context.Context, address swarm.Address, msgs ...strin ...@@ -92,7 +92,7 @@ func (s *Service) Ping(ctx context.Context, address swarm.Address, msgs ...strin
return 0, fmt.Errorf("read message: %w", err) return 0, fmt.Errorf("read message: %w", err)
} }
s.logger.Tracef("got pong: %q", pong.Response) logger.Tracef("got pong: %q", pong.Response)
s.metrics.PongReceivedCount.Inc() s.metrics.PongReceivedCount.Inc()
} }
return time.Since(start), nil return time.Since(start), nil
...@@ -102,7 +102,7 @@ func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) er ...@@ -102,7 +102,7 @@ func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) er
w, r := protobuf.NewWriterAndReader(stream) w, r := protobuf.NewWriterAndReader(stream)
defer stream.Close() defer stream.Close()
span, ctx := s.tracer.StartSpanFromContext(ctx, "pingpong-p2p-handler") span, logger, ctx := s.tracer.StartSpanFromContext(ctx, "pingpong-p2p-handler", s.logger)
defer span.Finish() defer span.Finish()
var ping pb.Ping var ping pb.Ping
...@@ -113,7 +113,7 @@ func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) er ...@@ -113,7 +113,7 @@ func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) er
} }
return fmt.Errorf("read message: %w", err) return fmt.Errorf("read message: %w", err)
} }
s.logger.Tracef("got ping: %q", ping.Greeting) logger.Tracef("got ping: %q", ping.Greeting)
s.metrics.PingReceivedCount.Inc() s.metrics.PingReceivedCount.Inc()
if err := w.WriteMsgWithContext(ctx, &pb.Pong{ if err := w.WriteMsgWithContext(ctx, &pb.Pong{
......
...@@ -12,8 +12,10 @@ import ( ...@@ -12,8 +12,10 @@ import (
"io" "io"
"time" "time"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p" "github.com/ethersphere/bee/pkg/p2p"
"github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/uber/jaeger-client-go" "github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config" "github.com/uber/jaeger-client-go/config"
) )
...@@ -29,6 +31,9 @@ var ( ...@@ -29,6 +31,9 @@ var (
noopTracer = &Tracer{tracer: new(opentracing.NoopTracer)} noopTracer = &Tracer{tracer: new(opentracing.NoopTracer)}
) )
// LogField is the key in log message field that holds tracing id value.
const LogField = "traceid"
// Tracer connect to a tracing server and handles tracing spans and contexts // Tracer connect to a tracing server and handles tracing spans and contexts
// by using opentracing Tracer. // by using opentracing Tracer.
type Tracer struct { type Tracer struct {
...@@ -71,8 +76,9 @@ func NewTracer(o *Options) (*Tracer, io.Closer, error) { ...@@ -71,8 +76,9 @@ func NewTracer(o *Options) (*Tracer, io.Closer, error) {
} }
// StartSpanFromContext starts a new tracing span that is either a root one or a // StartSpanFromContext starts a new tracing span that is either a root one or a
// child of existing one from the provided Context. // child of existing one from the provided Context. If logger is provided, a new
func (t *Tracer) StartSpanFromContext(ctx context.Context, operationName string, opts ...opentracing.StartSpanOption) (opentracing.Span, context.Context) { // log Entry will be returned with "traceid" log field.
func (t *Tracer) StartSpanFromContext(ctx context.Context, operationName string, l logging.Logger, opts ...opentracing.StartSpanOption) (opentracing.Span, *logrus.Entry, context.Context) {
if t == nil { if t == nil {
t = noopTracer t = noopTracer
} }
...@@ -84,7 +90,8 @@ func (t *Tracer) StartSpanFromContext(ctx context.Context, operationName string, ...@@ -84,7 +90,8 @@ func (t *Tracer) StartSpanFromContext(ctx context.Context, operationName string,
} else { } else {
span = t.tracer.StartSpan(operationName, opts...) span = t.tracer.StartSpan(operationName, opts...)
} }
return span, WithContext(ctx, span.Context()) sc := span.Context()
return span, loggerWithTraceID(sc, l), WithContext(ctx, sc)
} }
// AddContextHeader adds a tracing span context to provided p2p Headers from // AddContextHeader adds a tracing span context to provided p2p Headers from
...@@ -165,3 +172,24 @@ func FromContext(ctx context.Context) opentracing.SpanContext { ...@@ -165,3 +172,24 @@ func FromContext(ctx context.Context) opentracing.SpanContext {
} }
return c return c
} }
// NewLoggerWithTraceID creates a new log Entry with "traceid" field added if it
// exists in tracing span context stored from go context.
func NewLoggerWithTraceID(ctx context.Context, l logging.Logger) *logrus.Entry {
return loggerWithTraceID(FromContext(ctx), l)
}
func loggerWithTraceID(sc opentracing.SpanContext, l logging.Logger) *logrus.Entry {
if l == nil {
return nil
}
jsc, ok := sc.(jaeger.SpanContext)
if !ok {
return l.NewEntry()
}
traceID := jsc.TraceID()
if !traceID.IsValid() {
return l.NewEntry()
}
return l.WithField(LogField, traceID.String())
}
...@@ -8,17 +8,20 @@ import ( ...@@ -8,17 +8,20 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"os"
"testing" "testing"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p" "github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/tracing" "github.com/ethersphere/bee/pkg/tracing"
"github.com/uber/jaeger-client-go"
) )
func TestSpanFromHeaders(t *testing.T) { func TestSpanFromHeaders(t *testing.T) {
tracer, closer := newTracer(t) tracer, closer := newTracer(t)
defer closer.Close() defer closer.Close()
span, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation") span, _, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish() defer span.Finish()
headers := make(p2p.Headers) headers := make(p2p.Headers)
...@@ -49,7 +52,7 @@ func TestSpanWithContextFromHeaders(t *testing.T) { ...@@ -49,7 +52,7 @@ func TestSpanWithContextFromHeaders(t *testing.T) {
tracer, closer := newTracer(t) tracer, closer := newTracer(t)
defer closer.Close() defer closer.Close()
span, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation") span, _, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish() defer span.Finish()
headers := make(p2p.Headers) headers := make(p2p.Headers)
...@@ -81,7 +84,7 @@ func TestFromContext(t *testing.T) { ...@@ -81,7 +84,7 @@ func TestFromContext(t *testing.T) {
tracer, closer := newTracer(t) tracer, closer := newTracer(t)
defer closer.Close() defer closer.Close()
span, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation") span, _, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish() defer span.Finish()
wantSpanContext := span.Context() wantSpanContext := span.Context()
...@@ -103,7 +106,7 @@ func TestWithContext(t *testing.T) { ...@@ -103,7 +106,7 @@ func TestWithContext(t *testing.T) {
tracer, closer := newTracer(t) tracer, closer := newTracer(t)
defer closer.Close() defer closer.Close()
span, _ := tracer.StartSpanFromContext(context.Background(), "some-operation") span, _, _ := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish() defer span.Finish()
wantSpanContext := span.Context() wantSpanContext := span.Context()
...@@ -123,6 +126,82 @@ func TestWithContext(t *testing.T) { ...@@ -123,6 +126,82 @@ func TestWithContext(t *testing.T) {
} }
} }
func TestStartSpanFromContext_logger(t *testing.T) {
tracer, closer := newTracer(t)
defer closer.Close()
span, logger, _ := tracer.StartSpanFromContext(context.Background(), "some-operation", logging.New(os.Stdout, 5))
defer span.Finish()
wantTraceID := span.Context().(jaeger.SpanContext).TraceID()
v, ok := logger.Data[tracing.LogField]
if !ok {
t.Fatalf("log field %q not found", tracing.LogField)
}
gotTraceID, ok := v.(string)
if !ok {
t.Fatalf("log field %q is not string", tracing.LogField)
}
if gotTraceID != wantTraceID.String() {
t.Errorf("got trace id %q, want %q", gotTraceID, wantTraceID.String())
}
}
func TestStartSpanFromContext_nilLogger(t *testing.T) {
tracer, closer := newTracer(t)
defer closer.Close()
span, logger, _ := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish()
if logger != nil {
t.Error("logger is not nil")
}
}
func TestNewLoggerWithTraceID(t *testing.T) {
tracer, closer := newTracer(t)
defer closer.Close()
span, _, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish()
logger := tracing.NewLoggerWithTraceID(ctx, logging.New(os.Stdout, 5))
wantTraceID := span.Context().(jaeger.SpanContext).TraceID()
v, ok := logger.Data[tracing.LogField]
if !ok {
t.Fatalf("log field %q not found", tracing.LogField)
}
gotTraceID, ok := v.(string)
if !ok {
t.Fatalf("log field %q is not string", tracing.LogField)
}
if gotTraceID != wantTraceID.String() {
t.Errorf("got trace id %q, want %q", gotTraceID, wantTraceID.String())
}
}
func TestNewLoggerWithTraceID_nilLogger(t *testing.T) {
tracer, closer := newTracer(t)
defer closer.Close()
span, _, ctx := tracer.StartSpanFromContext(context.Background(), "some-operation", nil)
defer span.Finish()
logger := tracing.NewLoggerWithTraceID(ctx, nil)
if logger != nil {
t.Error("logger is not nil")
}
}
func newTracer(t *testing.T) (*tracing.Tracer, io.Closer) { func newTracer(t *testing.T) (*tracing.Tracer, io.Closer) {
t.Helper() t.Helper()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment