Skip to content

Commit 5728769

Browse files
committed
more attributes and Register function for easy wrapping
1 parent f50250f commit 5728769

File tree

2 files changed

+205
-17
lines changed

2 files changed

+205
-17
lines changed

driver.go

Lines changed: 193 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ import (
44
"context"
55
"database/sql"
66
"database/sql/driver"
7+
"errors"
8+
"fmt"
79
"io"
10+
"strconv"
11+
"sync"
812

913
"go.opencensus.io/trace"
1014
)
1115

1216
var (
17+
regMu sync.Mutex
1318
attrMissingContext = trace.StringAttribute("ocsql.warning", "missing upstream context")
1419
attrDeprecated = trace.StringAttribute("ocsql.warning", "database driver uses deprecated features")
1520
)
@@ -48,6 +53,45 @@ type ocRows struct {
4853
options TraceOptions
4954
}
5055

56+
// Register initializes and registers our ocsql wrapped database driver
57+
// identified by its driverName and using provided TraceOptions. On success it
58+
// returns the generated driverName to use when calling sql.Open.
59+
func Register(driverName string, options ...TraceOption) (string, error) {
60+
// retrieve the driver implementation we need to wrap with instrumentation
61+
db, err := sql.Open(driverName, "")
62+
if err != nil {
63+
return "", err
64+
}
65+
dri := db.Driver()
66+
if err = db.Close(); err != nil {
67+
return "", err
68+
}
69+
70+
regMu.Lock()
71+
defer regMu.Unlock()
72+
73+
// Since we might want to register multiple ocsql drivers to have different
74+
// TraceOptions, but potentially the same underlying database driver, we
75+
// cycle through available driver names.
76+
driverName = "ocsql-" + driverName
77+
for i := int64(0); i < 100; i++ {
78+
var (
79+
found = false
80+
regName = driverName + strconv.FormatInt(i, 10)
81+
)
82+
for _, name := range sql.Drivers() {
83+
if name == regName {
84+
found = true
85+
}
86+
}
87+
if !found {
88+
sql.Register(driverName, Wrap(dri, options...))
89+
return driverName, nil
90+
}
91+
}
92+
return "", errors.New("unable to register driver, all slots have been taken")
93+
}
94+
5195
// Wrap takes a SQL driver and wraps it with OpenCensus instrumentation.
5296
func Wrap(d driver.Driver, options ...TraceOption) driver.Driver {
5397
o := TraceOptions{}
@@ -94,10 +138,19 @@ func (c ocConn) Ping(ctx context.Context) (err error) {
94138
func (c ocConn) Exec(query string, args []driver.Value) (res driver.Result, err error) {
95139
if exec, ok := c.parent.(driver.Execer); ok {
96140
ctx, span := trace.StartSpan(context.Background(), "sql:exec")
97-
span.AddAttributes(attrDeprecated)
98-
span.AddAttributes(trace.StringAttribute(
99-
"ocsql.deprecated", "driver does not support ExecerContext",
100-
))
141+
attrs := []trace.Attribute{
142+
attrDeprecated,
143+
trace.StringAttribute(
144+
"ocsql.deprecated", "driver does not support ExecerContext",
145+
),
146+
}
147+
if c.options.Query {
148+
attrs = append(attrs, trace.StringAttribute("sql.query", query))
149+
if c.options.QueryParams {
150+
attrs = append(attrs, paramsAttr(args)...)
151+
}
152+
}
153+
span.AddAttributes(attrs...)
101154

102155
defer func() {
103156
setSpanStatus(span, err)
@@ -118,6 +171,16 @@ func (c ocConn) ExecContext(ctx context.Context, query string, args []driver.Nam
118171
if execCtx, ok := c.parent.(driver.ExecerContext); ok {
119172
var span *trace.Span
120173
ctx, span = trace.StartSpan(ctx, "sql:exec")
174+
if c.options.Query {
175+
attrs := []trace.Attribute{
176+
trace.StringAttribute("sql.query", query),
177+
}
178+
if c.options.QueryParams {
179+
attrs = append(attrs, namedParamsAttr(args)...)
180+
}
181+
span.AddAttributes(attrs...)
182+
}
183+
121184
defer func() {
122185
setSpanStatus(span, err)
123186
span.End()
@@ -136,10 +199,20 @@ func (c ocConn) ExecContext(ctx context.Context, query string, args []driver.Nam
136199
func (c ocConn) Query(query string, args []driver.Value) (rows driver.Rows, err error) {
137200
if queryer, ok := c.parent.(driver.Queryer); ok {
138201
ctx, span := trace.StartSpan(context.Background(), "sql:query")
139-
span.AddAttributes(attrDeprecated)
140-
span.AddAttributes(trace.StringAttribute(
141-
"ocsql.deprecated", "driver does not support QueryerContext",
142-
))
202+
attrs := []trace.Attribute{
203+
attrDeprecated,
204+
trace.StringAttribute(
205+
"ocsql.deprecated", "driver does not support QueryerContext",
206+
),
207+
}
208+
if c.options.Query {
209+
attrs = append(attrs, trace.StringAttribute("sql.query", query))
210+
if c.options.QueryParams {
211+
attrs = append(attrs, paramsAttr(args)...)
212+
}
213+
}
214+
span.AddAttributes(attrs...)
215+
143216
defer func() {
144217
setSpanStatus(span, err)
145218
span.End()
@@ -160,6 +233,16 @@ func (c ocConn) QueryContext(ctx context.Context, query string, args []driver.Na
160233
if queryerCtx, ok := c.parent.(driver.QueryerContext); ok {
161234
var span *trace.Span
162235
ctx, span = trace.StartSpan(ctx, "sql:query")
236+
if c.options.Query {
237+
attrs := []trace.Attribute{
238+
trace.StringAttribute("sql.query", query),
239+
}
240+
if c.options.QueryParams {
241+
attrs = append(attrs, namedParamsAttr(args)...)
242+
}
243+
span.AddAttributes(attrs...)
244+
}
245+
163246
defer func() {
164247
setSpanStatus(span, err)
165248
span.End()
@@ -178,6 +261,12 @@ func (c ocConn) QueryContext(ctx context.Context, query string, args []driver.Na
178261

179262
func (c ocConn) Prepare(query string) (stmt driver.Stmt, err error) {
180263
_, span := trace.StartSpan(context.Background(), "sql:prepare")
264+
attrs := []trace.Attribute{attrMissingContext}
265+
if c.options.Query {
266+
attrs = append(attrs, trace.StringAttribute("sql.query", query))
267+
}
268+
span.AddAttributes(attrs...)
269+
181270
defer func() {
182271
setSpanStatus(span, err)
183272
span.End()
@@ -201,7 +290,11 @@ func (c *ocConn) Begin() (driver.Tx, error) {
201290
}
202291

203292
func (c *ocConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) {
204-
_, span := trace.StartSpan(ctx, "sql:prepare")
293+
var span *trace.Span
294+
ctx, span = trace.StartSpan(ctx, "sql:prepare")
295+
if c.options.Query {
296+
span.AddAttributes(trace.StringAttribute("sql.query", query))
297+
}
205298
defer func() {
206299
setSpanStatus(span, err)
207300
span.End()
@@ -339,10 +432,20 @@ func (r ocResult) RowsAffected() (cnt int64, err error) {
339432

340433
func (s ocStmt) Exec(args []driver.Value) (res driver.Result, err error) {
341434
_, span := trace.StartSpan(context.Background(), "sql:exec")
342-
span.AddAttributes(attrDeprecated)
343-
span.AddAttributes(trace.StringAttribute(
344-
"ocsql.deprecated", "driver does not support StmtExecContext",
345-
))
435+
attrs := []trace.Attribute{
436+
attrDeprecated,
437+
trace.StringAttribute(
438+
"ocsql.deprecated", "driver does not support StmtExecContext",
439+
),
440+
}
441+
if s.options.Query {
442+
attrs = append(attrs, trace.StringAttribute("sql.query", s.query))
443+
if s.options.QueryParams {
444+
attrs = append(attrs, paramsAttr(args)...)
445+
}
446+
}
447+
span.AddAttributes(attrs...)
448+
346449
defer func() {
347450
setSpanStatus(span, err)
348451
span.End()
@@ -362,10 +465,20 @@ func (s ocStmt) NumInput() int {
362465

363466
func (s ocStmt) Query(args []driver.Value) (rows driver.Rows, err error) {
364467
_, span := trace.StartSpan(context.Background(), "sql:query")
365-
span.AddAttributes(attrDeprecated)
366-
span.AddAttributes(trace.StringAttribute(
367-
"ocsql.deprecated", "driver does not support StmtQueryContext",
368-
))
468+
attrs := []trace.Attribute{
469+
attrDeprecated,
470+
trace.StringAttribute(
471+
"ocsql.deprecated", "driver does not support StmtQueryContext",
472+
),
473+
}
474+
if s.options.Query {
475+
attrs = append(attrs, trace.StringAttribute("sql.query", s.query))
476+
if s.options.QueryParams {
477+
attrs = append(attrs, paramsAttr(args)...)
478+
}
479+
}
480+
span.AddAttributes(attrs...)
481+
369482
defer func() {
370483
setSpanStatus(span, err)
371484
span.End()
@@ -378,6 +491,14 @@ func (s ocStmt) Query(args []driver.Value) (rows driver.Rows, err error) {
378491
func (s ocStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res driver.Result, err error) {
379492
var span *trace.Span
380493
ctx, span = trace.StartSpan(ctx, "sql:exec")
494+
if s.options.Query {
495+
attrs := []trace.Attribute{trace.StringAttribute("sql.query", s.query)}
496+
if s.options.QueryParams {
497+
attrs = append(attrs, namedParamsAttr(args)...)
498+
}
499+
span.AddAttributes(attrs...)
500+
}
501+
381502
defer func() {
382503
setSpanStatus(span, err)
383504
span.End()
@@ -392,6 +513,14 @@ func (s ocStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res
392513
func (s ocStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) {
393514
var span *trace.Span
394515
ctx, span = trace.StartSpan(ctx, "sql:query")
516+
if s.options.Query {
517+
attrs := []trace.Attribute{trace.StringAttribute("sql.query", s.query)}
518+
if s.options.QueryParams {
519+
attrs = append(attrs, namedParamsAttr(args)...)
520+
}
521+
span.AddAttributes(attrs...)
522+
}
523+
395524
defer func() {
396525
setSpanStatus(span, err)
397526
span.End()
@@ -468,6 +597,53 @@ func wrapStmt(stmt driver.Stmt, query string, options TraceOptions) driver.Stmt
468597
panic("unreachable")
469598
}
470599

600+
func paramsAttr(args []driver.Value) []trace.Attribute {
601+
attrs := make([]trace.Attribute, 0, len(args))
602+
for i, arg := range args {
603+
key := "sql.arg" + strconv.Itoa(i)
604+
attrs[i] = argToAttr(key, arg)
605+
}
606+
return attrs
607+
}
608+
609+
func namedParamsAttr(args []driver.NamedValue) []trace.Attribute {
610+
attrs := make([]trace.Attribute, 0, len(args))
611+
for i, arg := range args {
612+
var key string
613+
if arg.Name != "" {
614+
key = arg.Name
615+
} else {
616+
key = "sql.arg." + strconv.Itoa(arg.Ordinal)
617+
}
618+
attrs[i] = argToAttr(key, arg.Value)
619+
}
620+
return attrs
621+
}
622+
623+
func argToAttr(key string, val interface{}) trace.Attribute {
624+
switch v := val.(type) {
625+
case nil:
626+
return trace.StringAttribute(key, "")
627+
case int64:
628+
return trace.Int64Attribute(key, v)
629+
case float64:
630+
return trace.StringAttribute(key, fmt.Sprintf("%f", v))
631+
case bool:
632+
return trace.BoolAttribute(key, v)
633+
case []byte:
634+
if len(v) > 256 {
635+
v = v[0:256]
636+
}
637+
return trace.StringAttribute(key, fmt.Sprintf("%s", v))
638+
default:
639+
s := fmt.Sprintf("%v", v)
640+
if len(s) > 256 {
641+
s = s[0:256]
642+
}
643+
return trace.StringAttribute(key, s)
644+
}
645+
}
646+
471647
func setSpanStatus(span *trace.Span, err error) {
472648
var status trace.Status
473649
switch err {

options.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ type TraceOptions struct {
1515
QueryParams bool
1616
}
1717

18+
// TraceAll has all tracing options enabled.
19+
var TraceAll = TraceOptions{
20+
Transaction: true,
21+
Ping: true,
22+
RowsNext: true,
23+
RowsClose: true,
24+
RowsAffected: true,
25+
LastInsertID: true,
26+
Query: true,
27+
QueryParams: true,
28+
}
29+
1830
// WithOptions sets our ocsql tracing middleware options.
1931
func WithOptions(options TraceOptions) TraceOption {
2032
return func(o *TraceOptions) {

0 commit comments

Comments
 (0)