Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contrib/database/sql: Add WithErrorCheck options #1315

Merged
merged 14 commits into from
Aug 29, 2022
5 changes: 4 additions & 1 deletion contrib/database/sql/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,5 +203,8 @@ func (tp *traceParams) tryTrace(ctx context.Context, qtype queryType, query stri
span.SetTag(k, v)
}
}
span.Finish(tracer.WithError(err))
defer span.Finish()
if err != nil && tp.cfg.errCheck != nil && tp.cfg.errCheck(err) {
span.SetTag(ext.Error, err)
}
soh335 marked this conversation as resolved.
Show resolved Hide resolved
}
34 changes: 34 additions & 0 deletions contrib/database/sql/conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
"context"
"database/sql/driver"
"log"
"strings"
"testing"

"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"

"github.com/go-sql-driver/mysql"
Expand Down Expand Up @@ -168,3 +170,35 @@ func TestWithChildSpansOnly(t *testing.T) {
})
}
}

func TestWithErrorCheck(t *testing.T) {
testOpts := func(errExist bool, opts ...Option) func(t *testing.T) {
return func(t *testing.T) {
mt := mocktracer.Start()
defer mt.Stop()

Register("mysql", &mysql.MySQLDriver{})
defer unregister("mysql")

db, err := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test", opts...)
if err != nil {
log.Fatal(err)
}
defer db.Close()

db.QueryContext(context.Background(), "SELECT a FROM "+tableName)

spans := mt.FinishedSpans()
assert.True(t, len(spans) > 0)

s := spans[len(spans)-1]
assert.Equal(t, errExist, s.Tag(ext.Error) != nil)
}
}

t.Run("defaults", testOpts(true))
soh335 marked this conversation as resolved.
Show resolved Hide resolved
t.Run("errcheck", testOpts(false, WithErrorCheck(func(err error) bool {
return !strings.Contains(err.Error(), `Error 1054: Unknown column 'a' in 'field list'`)
})))

}
10 changes: 10 additions & 0 deletions contrib/database/sql/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type config struct {
analyticsRate float64
dsn string
childSpansOnly bool
errCheck func(err error) bool
}

// Option represents an option that can be passed to Register, Open or OpenDB.
Expand Down Expand Up @@ -83,3 +84,12 @@ func WithChildSpansOnly() Option {
cfg.childSpansOnly = true
}
}

// WithErrorCheck specifies a function fn which determines whether the passed
// error should be marked as an error. The fn is called whenever a database/sql operation
// finishes with an error
func WithErrorCheck(fn func(err error) bool) Option {
gbbr marked this conversation as resolved.
Show resolved Hide resolved
return func(cfg *config) {
cfg.errCheck = fn
}
}
3 changes: 3 additions & 0 deletions contrib/database/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ func OpenDB(c driver.Connector, opts ...Option) *sql.DB {
if math.IsNaN(cfg.analyticsRate) {
cfg.analyticsRate = rc.analyticsRate
}
if cfg.errCheck == nil {
cfg.errCheck = rc.errCheck
}
cfg.childSpansOnly = rc.childSpansOnly
tc := &tracedConnector{
connector: c,
Expand Down