forked from DataDog/dd-trace-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gintrace.go
143 lines (121 loc) · 3.5 KB
/
gintrace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Package gintrace provides tracing middleware for the Gin web framework.
package gintrace
import (
"fmt"
"strconv"
"github.com/DataDog/dd-trace-go/tracer"
"github.com/DataDog/dd-trace-go/tracer/ext"
"github.com/gin-gonic/gin"
)
// key is the string that we'll use to store spans in the tracer.
var key = "datadog_trace_span"
// Middleware returns middleware that will trace requests with the default
// tracer.
func Middleware(service string) gin.HandlerFunc {
return MiddlewareTracer(service, tracer.DefaultTracer)
}
// MiddlewareTracer returns middleware that will trace requests with the given
// tracer.
func MiddlewareTracer(service string, t *tracer.Tracer) gin.HandlerFunc {
t.SetServiceInfo(service, "gin-gonic", ext.AppTypeWeb)
mw := newMiddleware(service, t)
return mw.Handle
}
// middleware implements gin middleware.
type middleware struct {
service string
trc *tracer.Tracer
}
func newMiddleware(service string, trc *tracer.Tracer) *middleware {
return &middleware{
service: service,
trc: trc,
}
}
// Handle is a gin HandlerFunc that will add tracing to the given request.
func (m *middleware) Handle(c *gin.Context) {
// bail if not enabled
if !m.trc.Enabled() {
c.Next()
return
}
// FIXME[matt] the handler name is a bit unwieldy and uses reflection
// under the hood. might be better to tackle this task and do it right
// so we can end up with "user/:user/whatever" instead of
// "github.com/foobar/blah"
//
// See here: https://github.com/gin-gonic/gin/issues/649
resource := c.HandlerName()
// Create our span and patch it to the context for downstream.
span := m.trc.NewRootSpan("gin.request", m.service, resource)
c.Set(key, span)
// Pass along the request.
c.Next()
// Set http tags.
span.SetMeta(ext.HTTPCode, strconv.Itoa(c.Writer.Status()))
span.SetMeta(ext.HTTPMethod, c.Request.Method)
span.SetMeta(ext.HTTPURL, c.Request.URL.Path)
// Set any error information.
var err error
if len(c.Errors) > 0 {
span.SetMeta("gin.errors", c.Errors.String()) // set all errors
err = c.Errors[0] // but use the first for standard fields
}
span.FinishWithErr(err)
}
// Span returns the Span stored in the given Context and true. If it doesn't exist,
// it will returns (nil, false)
func Span(c *gin.Context) (*tracer.Span, bool) {
if c == nil {
return nil, false
}
s, ok := c.Get(key)
if !ok {
return nil, false
}
switch span := s.(type) {
case *tracer.Span:
return span, true
}
return nil, false
}
// SpanDefault returns the span stored in the given Context. If none exists,
// it will return an empty span.
func SpanDefault(c *gin.Context) *tracer.Span {
span, ok := Span(c)
if !ok {
return &tracer.Span{}
}
return span
}
// NewChildSpan will create a span that is the child of the span stored in
// the context.
func NewChildSpan(name string, c *gin.Context) *tracer.Span {
span, ok := Span(c)
if !ok {
return &tracer.Span{}
}
return span.Tracer().NewChildSpan(name, span)
}
// HTML will trace the rendering of the template as a child of the span in the
// given context.
func HTML(c *gin.Context, code int, name string, obj interface{}) {
span, _ := Span(c)
if span == nil {
c.HTML(code, name, obj)
return
}
child := span.Tracer().NewChildSpan("gin.render.html", span)
child.SetMeta("go.template", name)
defer func() {
if r := recover(); r != nil {
err := fmt.Errorf("error rendering tmpl:%s: %s", name, r)
child.FinishWithErr(err)
panic(r)
} else {
child.Finish()
}
}()
// render
c.HTML(code, name, obj)
}