forked from DataDog/dd-trace-go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
mongo.go
117 lines (105 loc) · 3.01 KB
/
mongo.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
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-2020 Datadog, Inc.
// Package mongo provides functions to trace the mongodb/mongo-go-driver package (https://github.com/mongodb/mongo-go-driver).
// It support v0.2.0 of github.com/mongodb/mongo-go-driver
//
// `NewMonitor` will return an event.CommandMonitor which is used to trace requests.
package mongo
import (
"context"
"fmt"
"math"
"strings"
"sync"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/event"
)
type spanKey struct {
ConnectionID string
RequestID int64
}
type monitor struct {
sync.Mutex
spans map[spanKey]ddtrace.Span
cfg *config
}
func (m *monitor) Started(ctx context.Context, evt *event.CommandStartedEvent) {
hostname, port := peerInfo(evt)
b, _ := bson.MarshalExtJSON(evt.Command, false, false)
opts := []ddtrace.StartSpanOption{
tracer.ServiceName(m.cfg.serviceName),
tracer.ResourceName("mongo." + evt.CommandName),
tracer.Tag(ext.DBInstance, evt.DatabaseName),
tracer.Tag(ext.DBStatement, string(b)),
tracer.Tag(ext.DBType, "mongo"),
tracer.Tag(ext.PeerHostname, hostname),
tracer.Tag(ext.PeerPort, port),
}
if !math.IsNaN(m.cfg.analyticsRate) {
opts = append(opts, tracer.Tag(ext.EventSampleRate, m.cfg.analyticsRate))
}
span, _ := tracer.StartSpanFromContext(ctx, "mongodb.query", opts...)
key := spanKey{
ConnectionID: evt.ConnectionID,
RequestID: evt.RequestID,
}
m.Lock()
m.spans[key] = span
m.Unlock()
}
func (m *monitor) Succeeded(ctx context.Context, evt *event.CommandSucceededEvent) {
m.Finished(&evt.CommandFinishedEvent, nil)
}
func (m *monitor) Failed(ctx context.Context, evt *event.CommandFailedEvent) {
m.Finished(&evt.CommandFinishedEvent, fmt.Errorf("%s", evt.Failure))
}
func (m *monitor) Finished(evt *event.CommandFinishedEvent, err error) {
key := spanKey{
ConnectionID: evt.ConnectionID,
RequestID: evt.RequestID,
}
m.Lock()
span, ok := m.spans[key]
if ok {
delete(m.spans, key)
}
m.Unlock()
if !ok {
return
}
span.Finish(tracer.WithError(err))
}
// NewMonitor creates a new mongodb event CommandMonitor.
func NewMonitor(opts ...Option) *event.CommandMonitor {
cfg := new(config)
defaults(cfg)
for _, opt := range opts {
opt(cfg)
}
m := &monitor{
spans: make(map[spanKey]ddtrace.Span),
cfg: cfg,
}
return &event.CommandMonitor{
Started: m.Started,
Succeeded: m.Succeeded,
Failed: m.Failed,
}
}
func peerInfo(evt *event.CommandStartedEvent) (hostname, port string) {
hostname = evt.ConnectionID
port = "27017"
if idx := strings.IndexByte(hostname, '['); idx >= 0 {
hostname = hostname[:idx]
}
if idx := strings.IndexByte(hostname, ':'); idx >= 0 {
port = hostname[idx+1:]
hostname = hostname[:idx]
}
return hostname, port
}