1// Unless explicitly stated otherwise all files in this repository are licensed
2// under the Apache License Version 2.0.
3// This product includes software developed at Datadog (https://www.datadoghq.com/).
4// Copyright 2016 Datadog, Inc.
5
6// Package restful provides functions to trace the emicklei/go-restful package (https://github.com/emicklei/go-restful).
7package restful
8
9import (
10	"math"
11	"strconv"
12
13	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
14	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
15	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
16	"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
17
18	"github.com/emicklei/go-restful"
19)
20
21// FilterFunc returns a restful.FilterFunction which will automatically trace incoming request.
22func FilterFunc(configOpts ...Option) restful.FilterFunction {
23	cfg := newConfig()
24	for _, opt := range configOpts {
25		opt(cfg)
26	}
27	log.Debug("contrib/emicklei/go-restful: Creating tracing filter: %#v", cfg)
28	return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
29		opts := []ddtrace.StartSpanOption{
30			tracer.ServiceName(cfg.serviceName),
31			tracer.ResourceName(req.SelectedRoutePath()),
32			tracer.SpanType(ext.SpanTypeWeb),
33			tracer.Tag(ext.HTTPMethod, req.Request.Method),
34			tracer.Tag(ext.HTTPURL, req.Request.URL.Path),
35		}
36		if !math.IsNaN(cfg.analyticsRate) {
37			opts = append(opts, tracer.Tag(ext.EventSampleRate, cfg.analyticsRate))
38		}
39		if spanctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(req.Request.Header)); err == nil {
40			opts = append(opts, tracer.ChildOf(spanctx))
41		}
42		span, ctx := tracer.StartSpanFromContext(req.Request.Context(), "http.request", opts...)
43		defer span.Finish()
44
45		// pass the span through the request context
46		req.Request = req.Request.WithContext(ctx)
47
48		chain.ProcessFilter(req, resp)
49
50		span.SetTag(ext.HTTPCode, strconv.Itoa(resp.StatusCode()))
51		span.SetTag(ext.Error, resp.Error())
52	}
53}
54
55// Filter is deprecated. Please use FilterFunc.
56func Filter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
57	opts := []ddtrace.StartSpanOption{
58		tracer.ResourceName(req.SelectedRoutePath()),
59		tracer.SpanType(ext.SpanTypeWeb),
60		tracer.Tag(ext.HTTPMethod, req.Request.Method),
61		tracer.Tag(ext.HTTPURL, req.Request.URL.Path),
62	}
63	if spanctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(req.Request.Header)); err == nil {
64		opts = append(opts, tracer.ChildOf(spanctx))
65	}
66	span, ctx := tracer.StartSpanFromContext(req.Request.Context(), "http.request", opts...)
67	defer span.Finish()
68
69	// pass the span through the request context
70	req.Request = req.Request.WithContext(ctx)
71
72	chain.ProcessFilter(req, resp)
73
74	span.SetTag(ext.HTTPCode, strconv.Itoa(resp.StatusCode()))
75	span.SetTag(ext.Error, resp.Error())
76}
77