A SQL wrapper including Zipkin instrumentation
import (
_ "github.com/go-sql-driver/mysql"
zipkinsql "github.com/openzipkin-contrib/zipkin-go-sql"
zipkin "github.com/openzipkin/zipkin-go"
)
var (
driverName string
err error
db *sql.DB
tracer *zipkin.Tracer
)
// Register our zipkinsql wrapper for the provided MySQL driver.
driverName, err = zipkinsql.Register("mysql", tracer, zipkinsql.WithAllTraceOptions())
if err != nil {
log.Fatalf("unable to register zipkin driver: %v\n", err)
}
// Connect to a MySQL database using the zipkinsql driver wrapper.
db, err = sql.Open(driverName, "mysql://user:pass@127.0.0.1:3306/db")
You can also wrap your own driver with zipkin instrumentation as follows:
import (
mysql "github.com/go-sql-driver/mysql"
zipkinsql "github.com/openzipkin-contrib/zipkin-go-sql"
zipkinmodel "github.com/openzipkin/zipkin-go/model"
)
var (
driver driver.Driver
err error
db *sql.DB
tracer *zipkin.Tracer
)
// Explicitly wrap the MySQL driver with zipkinsql
driver = zipkinsql.Wrap(
&mysql.MySQLDriver{},
tracer,
zipkinsql.WithRemoteEndpoint(zipkinmodel.Endpoint{
ServiceName: "resultsdb",
Port: 5432
}),
)
// Register our zipkinsql wrapper as a database driver
sql.Register("zipkinsql-mysql", driver)
// Connect to a MySQL database using the zipkinsql driver wrapper
db, err = sql.Open("zipkinsql-mysql", "mysql://user:pass@127.0.0.1:3306/db")
Projects providing their own abstractions on top of database/sql/driver can also wrap an existing driver.Conn interface directly with zipkinsql.
import zipkinsql "github.com/openzipkin-contrib/zipkin-go-sql"
func initializeConn(...) driver.Conn {
// create custom driver.Conn
conn := Connect(...)
// wrap with zipkinsql
return zipkinsql.WrapConn(conn, tracer, zipkinsql.WithAllTraceOptions())
}
Go 1.10+ provides a new driver.Connector interface that can be wrapped directly by zipkinsql without the need for zipkinsql to register a driver.Driver.
Example:
import(
zipkinsql "github.com/openzipkin-contrib/zipkin-go-sql"
"github.com/lib/pq"
)
var (
connector driver.Connector
err error
db *sql.DB
tracer *zipkin.Tracer
)
connector, err = pq.NewConnector("postgres://user:pass@host:5432/db")
if err != nil {
log.Fatalf("unable to create postgres connector: %v\n", err)
}
// Wrap the driver.Connector with zipkinsql.
connector = zipkinsql.WrapConnector(connector, tracer, zipkinsql.WithAllTraceOptions())
// Use the wrapped driver.Connector.
db = sql.OpenDB(connector)
If using the sqlx
library with named queries you will need to use the
sqlx.NewDb
function to wrap an existing *sql.DB
connection. sqlx.Open
and sqlx.Connect
methods won't work.
First create a *sql.DB
connection and then create a *sqlx.DB
connection by wrapping the former and keeping the same driver name e.g.:
driverName, err := zipkinsql.Register("postgres", zipkinsql.WithAllTraceOptions())
if err != nil { ... }
db, err := sql.Open(driverName, "postgres://localhost:5432/my_database")
if err != nil { ... }
// Keep the driver name!
dbx := sqlx.NewDb(db, "postgres")
Instrumentation is possible if the context is being passed downstream in methods.
This is not only for instrumentation purposes but also a good practice in go programming. database/sql
package exposes already a set of methods that receive the context as first paramenter:
*DB.Begin
->*DB.BeginTx
*DB.Exec
->*DB.ExecContext
*DB.Ping
->*DB.PingContext
*DB.Prepare
->*DB.PrepareContext
*DB.Query
->*DB.QueryContext
*DB.QueryRow
->*DB.QueryRowContext
*Stmt.Exec
->*Stmt.ExecContext
*Stmt.Query
->*Stmt.QueryContext
*Stmt.QueryRow
->*Stmt.QueryRowContext
*Tx.Exec
->*Tx.ExecContext
*Tx.Prepare
->*Tx.PrepareContext
*Tx.Query
->*Tx.QueryContext
*Tx.QueryRow
->*Tx.QueryRowContext