@@ -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
1216var (
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.
5296func Wrap (d driver.Driver , options ... TraceOption ) driver.Driver {
5397 o := TraceOptions {}
@@ -94,10 +138,19 @@ func (c ocConn) Ping(ctx context.Context) (err error) {
94138func (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
136199func (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
179262func (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
203292func (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
340433func (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
363466func (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) {
378491func (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
392513func (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+
471647func setSpanStatus (span * trace.Span , err error ) {
472648 var status trace.Status
473649 switch err {
0 commit comments