Go

Introduction

We have a Go “database/sql” package/wrapper that is trace instrumented with OpenCensus!

Installing it

To install the “database/sql” plugin, please run

go get -u -v contrib.go.opencensus.io/integrations/ocsql

Using it

Given this simple initialization of a database/sql instance in Go:

packagemainimport("database/sql""log")funcmain(){varordinaryDriverNamestring// For example "mysql", "sqlite3" etc.
db,err:=sql.Open(ordinaryDriverName,"resource.db")iferr!=nil{log.Fatalf("Failed to open the SQL database: %v",err)}deferdb.Close()}

We can use the OpenCensus trace-instrumented SQL driver wrapper in one of these two ways:

By registration

This mimicks the idiomatic recommendation to use the “database/sql” package in Go where
we pass an implicitly registered driver to sql.Open which returns a *sql.DB handle

packagemainimport("database/sql""log""contrib.go.opencensus.io/integrations/ocsql")funcmain(){varordinaryDriverNamestring// For example "mysql", "sqlite3" etc.
// First step is to register the driver and
// then reuse that driver name while invoking sql.Open
driverName,err:=ocsql.Register(ordinaryDriverName,ocsql.WithAllTraceOptions())iferr!=nil{log.Fatalf("Failed to register the ocsql driver: %v",err)}db,err:=sql.Open(driverName,"resource.db")iferr!=nil{log.Fatalf("Failed to open the SQL database: %v",err)}deferdb.Close()}

By explicitly wrapping your driver

This option is useful if you’d like to be more explicit and if your database package exports
its driver implementation.

Please place the code below inside a go-gettable directory and a file main.go, so

mkdir -p ocsql-e2e && touch main.go

packagemainimport("context""database/sql""log""time""contrib.go.opencensus.io/integrations/ocsql""go.opencensus.io/exporter/jaeger""go.opencensus.io/trace"_"github.com/mattn/go-sqlite3")funcmain(){iferr:=enableOpenCensusTracingAndExporting();err!=nil{log.Fatalf("Failed to enable OpenCensus tracing and exporting: %v",err)}driverName,err:=ocsql.Register("sqlite3",ocsql.WithAllTraceOptions())iferr!=nil{log.Fatalf("Failed to register the ocsql driver: %v",err)}db,err:=sql.Open(driverName,"resource.db")iferr!=nil{log.Fatalf("Failed to open the SQL database: %v",err)}deferfunc(){db.Close()// Wait to 4 seconds so that the traces can be exported
waitTime:=4*time.Secondlog.Printf("Waiting for %s seconds to ensure all traces are exported before exiting",waitTime)<-time.After(waitTime)}()ctx,span:=trace.StartSpan(context.Background(),"NamesRegistryApp")deferspan.End()cCtx,cSpan:=trace.StartSpan(ctx,"CreateTable")_,err=db.ExecContext(cCtx,`CREATE TABLE names(
id INTEGER PRIMARY KEY AUTOINCREMENT,
first VARCHAR(256),
last VARCHAR(256)
)`)cSpan.End()iferr!=nil{span.SetStatus(trace.Status{Code:trace.StatusCodeInternal,Message:err.Error()})log.Fatalf("Failed to create table: %v",err)}deferfunc(){// And for the cleanup
_,err=db.ExecContext(ctx,`DROP TABLE names`)iferr!=nil{log.Fatalf("Failed to delete the row: %v",err)}}()iCtx,iSpan:=trace.StartSpan(ctx,"InsertNames")rs,err:=db.ExecContext(iCtx,`INSERT INTO names(first, last) VALUES (?, ?)`,"JANE","SMITH")iSpan.End()iferr!=nil{log.Fatalf("Failed to insert values into tables: %v",err)}id,err:=rs.LastInsertId()iferr!=nil{log.Fatalf("Failed to retrieve lastInserted ID: %v",err)}fCtx,fSpan:=trace.StartSpan(ctx,"Find")row:=db.QueryRowContext(fCtx,`SELECT * from names where id=?`,id)fSpan.End()typenamestruct{IdintFirst,Laststring}n1:=new(name)iferr:=row.Scan(&n1.Id,&n1.First,&n1.Last);err!=nil{log.Fatalf("Failed to fetch row: %v",err)}log.Printf("Got back: %+v\n",n1)}funcenableOpenCensusTracingAndExporting()error{// For demo purposes, we'll always trace
trace.ApplyConfig(trace.Config{DefaultSampler:trace.AlwaysSample()})je,err:=jaeger.NewExporter(jaeger.Options{AgentEndpoint:"localhost:6831",Endpoint:"http://localhost:14268",ServiceName:"ocsql-demo",})iferr==nil{// On success, register it as a trace exporter
trace.RegisterExporter(je)}returnerr}