Skip to content

Commit

Permalink
feat(tracing): support tracing like OpenTracing with options
Browse files Browse the repository at this point in the history
  • Loading branch information
SianLoong committed May 1, 2020
1 parent 255fab2 commit 498c5ee
Show file tree
Hide file tree
Showing 16 changed files with 437 additions and 134 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ on:
push:
branches:
- master
pull_request:
branches:
- master
# pull_request:
# branches:
# - master

jobs:
build:
Expand Down
32 changes: 19 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,32 +220,38 @@ func main() {
}
```

### Integrate with OpenTracing
### Integrate with [OpenTracing](https://github.com/opentracing/opentracing-go)

Tracing is become more and more common in practice. And you may integrate your `OpenTracing` as such :

```go
import (
"context"
"github.com/go-sql-driver/mysql"
"github.com/si3nloong/sqlike/plugin/opentracing"
"github.com/si3nloong/sqlike/sql/instrumented"
"github.com/si3nloong/sqlike/sql/instrumented"

"github.com/si3nloong/sqlike/sqlike"
"github.com/si3nloong/sqlike/sqlike"
)

func main() {
ctx := context.Background()
driver := "mysql"
cfg := mysql.NewConfig()
cfg.User = "root"
cfg.Passwd = "abcd1234"
cfg.ParseTime = true
conn, err := mysql.NewConnector(cfg)
if err != nil {
panic(err)
}

itpr := new(opentracing.OpenTracingInterceptor)
itpr.Driver = driver
cfg.User = "root"
cfg.Passwd = "abcd1234"
cfg.ParseTime = true
conn, err := mysql.NewConnector(cfg)
if err != nil {
panic(err)
}

itpr := opentracing.Interceptor(
opentracing.WithDBInstance("sqlike"),
opentracing.WithDBUser("root"),
opentracing.WithExec(true),
opentracing.WithQuery(true),
)
client, err := sqlike.ConnectDB(
ctx, driver,
instrumented.WrapConnector(conn, itpr),
Expand Down
9 changes: 6 additions & 3 deletions examples/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ func TestExamples(t *testing.T) {
panic(err)
}

itpr := new(opentracing.OpenTracingInterceptor)
itpr.Database = ""
itpr.Driver = "mysql"
itpr := opentracing.Interceptor(
opentracing.WithDBInstance("sqlike"),
opentracing.WithDBUser("root"),
opentracing.WithExec(true),
opentracing.WithQuery(true),
)
client, err := sqlike.ConnectDB(ctx, "mysql", instrumented.WrapConnector(conn, itpr))
require.NoError(t, err)
defer client.Close()
Expand Down
106 changes: 64 additions & 42 deletions plugin/opentracing/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,62 +8,84 @@ import (
)

// ConnPing :
func (ot *OpenTracingInterceptor) ConnPing(ctx context.Context, conn driver.Pinger) error {
span := ot.StartSpan(ctx, "conn_ping")
defer span.Finish()
if err := conn.Ping(ctx); err != nil {
ext.LogError(span, err)
return err
func (ot *OpenTracingInterceptor) ConnPing(ctx context.Context, conn driver.Pinger) (err error) {
if ot.opts.Ping {
span := ot.StartSpan(ctx, "ping")
defer func() {
if err != nil {
ext.LogError(span, err)
}
span.Finish()
}()
}
return nil
err = conn.Ping(ctx)
return
}

// ConnPing :
func (ot *OpenTracingInterceptor) ConnBeginTx(ctx context.Context, conn driver.ConnBeginTx, opts driver.TxOptions) (driver.Tx, error) {
span := ot.StartSpan(ctx, "conn_begin_transaction")
defer span.Finish()
tx, err := conn.BeginTx(ctx, opts)
if err != nil {
ext.LogError(span, err)
return nil, err
func (ot *OpenTracingInterceptor) ConnBeginTx(ctx context.Context, conn driver.ConnBeginTx, opts driver.TxOptions) (tx driver.Tx, err error) {
if ot.opts.BeginTx {
span := ot.StartSpan(ctx, "begin_tx")
defer func() {
if err != nil {
ext.LogError(span, err)
}
span.Finish()
}()
}
return tx, nil
tx, err = conn.BeginTx(ctx, opts)
return
}

// ConnPrepareContext :
func (ot *OpenTracingInterceptor) ConnPrepareContext(ctx context.Context, conn driver.ConnPrepareContext, query string) (driver.Stmt, error) {
span := ot.StartSpan(ctx, "conn_prepare")
defer span.Finish()
ext.DBStatement.Set(span, query)
stmt, err := conn.PrepareContext(ctx, query)
if err != nil {
ext.LogError(span, err)
return nil, err
func (ot *OpenTracingInterceptor) ConnPrepareContext(ctx context.Context, conn driver.ConnPrepareContext, query string) (stmt driver.Stmt, err error) {
if ot.opts.Prepare {
span := ot.StartSpan(ctx, "prepare")
ext.DBStatement.Set(span, query)
defer func() {
if err != nil {
ext.LogError(span, err)
}
span.Finish()
}()
}
return stmt, nil
stmt, err = conn.PrepareContext(ctx, query)
return
}

// ConnExecContext :
func (ot *OpenTracingInterceptor) ConnExecContext(ctx context.Context, conn driver.ExecerContext, query string, args []driver.NamedValue) (driver.Result, error) {
span := ot.StartSpan(ctx, "conn_exec")
defer span.Finish()
ext.DBStatement.Set(span, query)
result, err := conn.ExecContext(ctx, query, args)
if err != nil {
ext.LogError(span, err)
return nil, err
func (ot *OpenTracingInterceptor) ConnExecContext(ctx context.Context, conn driver.ExecerContext, query string, args []driver.NamedValue) (result driver.Result, err error) {
if ot.opts.Exec {
span := ot.StartSpan(ctx, "exec")
ext.DBStatement.Set(span, query)
if ot.opts.Args {
logArgs(span, args)
}
defer func() {
if err != nil {
ext.LogError(span, err)
}
span.Finish()
}()
}
return result, nil
result, err = conn.ExecContext(ctx, query, args)
return
}

func (ot *OpenTracingInterceptor) ConnQueryContext(ctx context.Context, conn driver.QueryerContext, query string, args []driver.NamedValue) (driver.Rows, error) {
span := ot.StartSpan(ctx, "conn_query")
defer span.Finish()
ext.DBStatement.Set(span, query)
rows, err := conn.QueryContext(ctx, query, args)
if err != nil {
ext.LogError(span, err)
return nil, err
func (ot *OpenTracingInterceptor) ConnQueryContext(ctx context.Context, conn driver.QueryerContext, query string, args []driver.NamedValue) (rows driver.Rows, err error) {
if ot.opts.Query {
span := ot.StartSpan(ctx, "query")
ext.DBStatement.Set(span, query)
if ot.opts.Args {
logArgs(span, args)
}
defer func() {
if err != nil {
ext.LogError(span, err)
}
span.Finish()
}()
}
return rows, nil
rows, err = conn.QueryContext(ctx, query, args)
return
}
40 changes: 40 additions & 0 deletions plugin/opentracing/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package opentracing

import (
"database/sql"
"database/sql/driver"
"fmt"
"time"

"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
)

func logArgs(span opentracing.Span, args []driver.NamedValue) {
fields := make([]log.Field, len(args))
for i, arg := range args {
switch v := arg.Value.(type) {
case string:
fields[i] = log.String(arg.Name, v)
case int64:
fields[i] = log.Int64(arg.Name, v)
case uint64:
fields[i] = log.Uint64(arg.Name, v)
case float64:
fields[i] = log.Float64(arg.Name, v)
case bool:
fields[i] = log.Bool(arg.Name, v)
case time.Time:
fields[i] = log.String(arg.Name, v.Format(time.RFC3339))
case []byte:
fields[i] = log.String(arg.Name, string(v))
case sql.RawBytes:
fields[i] = log.String(arg.Name, string(v))
case nil:
fields[i] = log.String(arg.Name, "null")
default:
fields[i] = log.String(arg.Name, fmt.Sprintf("%v", v))
}
}
span.LogFields(fields...)
}
67 changes: 59 additions & 8 deletions plugin/opentracing/opentracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,76 @@ import (
"github.com/si3nloong/sqlike/sql/instrumented"
)

type TraceOptions struct {
// Component is a component name in opentracing
// component: value
Component string

// DBInstance is a db instance name in opentracing
// db.instance: value
DBInstance string

// DBType is a db type in opentracing
// db.type: value
DBType string

// DBUser is a db user in opentracing
// db.user: value
DBUser string

// Ping is a flag to log the ping
Ping bool

// Prepare is a flag to log the prepare stmt
Prepare bool

// RowsNext is a flag to log the rows next
RowsNext bool
RowsClose bool
RowsAffected bool
LastInsertID bool

// when Query is true, it will log all the query statement
Query bool

// when Exec is true, it will log all the exec statement
Exec bool
BeginTx bool
TxCommit bool
TxRollback bool

// when Args is true, it will log all the arguments of the query
Args bool
}

// OpenTracingInterceptor :
type OpenTracingInterceptor struct {
Driver string
Database string
censor bool
opts TraceOptions
instrumented.NullInterceptor
}

type Option interface{}
// TraceOption :
type TraceOption func(*TraceOptions)

var _ instrumented.Interceptor = (*OpenTracingInterceptor)(nil)

func New(opts ...Option) instrumented.Interceptor {
return &OpenTracingInterceptor{}
// Interceptor :
func Interceptor(opts ...TraceOption) instrumented.Interceptor {
it := new(OpenTracingInterceptor)
it.opts.Component = "database/sql"
it.opts.DBType = "sql"
for _, opt := range opts {
opt(&it.opts)
}
return it
}

// StartSpan :
func (ot *OpenTracingInterceptor) StartSpan(ctx context.Context, operationName string) opentracing.Span {
span, _ := opentracing.StartSpanFromContext(ctx, operationName)
ext.DBInstance.Set(span, ot.Database)
ext.DBType.Set(span, ot.Driver)
ext.DBInstance.Set(span, ot.opts.DBInstance)
ext.DBType.Set(span, ot.opts.DBType)
ext.DBUser.Set(span, ot.opts.DBUser)
ext.Component.Set(span, ot.opts.Component)
return span
}
Loading

0 comments on commit 498c5ee

Please # to comment.