forked from nhatthm/otelsql
-
Notifications
You must be signed in to change notification settings - Fork 0
/
query.go
136 lines (107 loc) · 3.74 KB
/
query.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
package otelsql
import (
"context"
"database/sql/driver"
"go.opentelemetry.io/otel/attribute"
)
const (
metricMethodQuery = "go.sql.query"
traceMethodQuery = "query"
)
type queryContextFuncMiddleware = middleware[queryContextFunc]
type queryContextFunc func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error)
// nopQueryContext queries nothing.
func nopQueryContext(_ context.Context, _ string, _ []driver.NamedValue) (driver.Rows, error) {
return nil, nil
}
// skippedQueryContext always returns driver.ErrSkip.
func skippedQueryContext(_ context.Context, _ string, _ []driver.NamedValue) (driver.Rows, error) {
return nil, driver.ErrSkip
}
type attributeCtxKey struct{}
func getAttributes(ctx context.Context) []attribute.KeyValue {
attrs, ok := ctx.Value(attributeCtxKey{}).([]attribute.KeyValue)
if !ok || attrs == nil {
attrs = make([]attribute.KeyValue, 0)
}
return attrs
}
// WithCutomAttributes adds additional OTel attributes to the context.
// These attributes are added to metrics and spans generated for the query.
func WithCustomAttributes(ctx context.Context, labels ...attribute.KeyValue) context.Context {
addedLabels := getAttributes(ctx)
new := append(addedLabels, labels...)
return context.WithValue(ctx, attributeCtxKey{}, new)
}
// queryStats records metrics for query.
func queryStats(r methodRecorder, method string) queryContextFuncMiddleware {
return func(next queryContextFunc) queryContextFunc {
return func(ctx context.Context, query string, args []driver.NamedValue) (result driver.Rows, err error) {
attrs := getAttributes(ctx)
end := r.Record(ctx, method, attrs...)
defer func() {
end(err)
}()
result, err = next(ctx, query, args)
return
}
}
}
// queryTrace creates a span for query.
func queryTrace(t methodTracer, traceQuery queryTracer, method string) queryContextFuncMiddleware {
return func(next queryContextFunc) queryContextFunc {
return func(ctx context.Context, query string, args []driver.NamedValue) (result driver.Rows, err error) {
ctx = ContextWithQuery(ctx, query)
ctx, end := t.Trace(ctx, method)
defer func() {
labels := append(
getAttributes(ctx),
traceQuery(ctx, query, args)...,
)
end(err, labels...)
}()
result, err = next(ctx, query, args)
return
}
}
}
func queryWrapRows(t methodTracer, traceLastInsertID bool, traceRowsAffected bool) queryContextFuncMiddleware {
return func(next queryContextFunc) queryContextFunc {
return func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
result, err := next(ctx, query, args)
if err != nil {
return nil, err
}
shouldTrace, _ := t.ShouldTrace(ctx)
return wrapRows(ctx, result, t, shouldTrace && traceLastInsertID, shouldTrace && traceRowsAffected), nil
}
}
}
func makeQueryerContextMiddlewares(r methodRecorder, t methodTracer, cfg queryConfig) []queryContextFuncMiddleware {
middlewares := make([]queryContextFuncMiddleware, 0, 3)
middlewares = append(middlewares, queryStats(r, cfg.metricMethod))
if t == nil {
return middlewares
}
middlewares = append(middlewares, queryTrace(t, cfg.traceQuery, cfg.traceMethod))
if cfg.traceRowsNext || cfg.traceRowsClose {
middlewares = append(middlewares, queryWrapRows(t, cfg.traceRowsNext, cfg.traceRowsClose))
}
return middlewares
}
type queryConfig struct {
metricMethod string
traceMethod string
traceQuery queryTracer
traceRowsNext bool
traceRowsClose bool
}
func newQueryConfig(opts driverOptions, metricMethod, traceMethod string) queryConfig {
return queryConfig{
metricMethod: metricMethod,
traceMethod: traceMethod,
traceQuery: opts.trace.queryTracer,
traceRowsNext: opts.trace.RowsNext,
traceRowsClose: opts.trace.RowsClose,
}
}