Go
- Introduction
- Background
- Packages to import
- Enable tracing
- Add the trace exporter
- End to end code sample
- Viewing your traces
Introduction
Cloud Datastore’s Go package was already instrumented for Tracing with OpenCensus.
This guide makes use of a couple of APIs
API | Guided codelab |
---|---|
Cloud Datastore | Enable Cloud Datastore |
Stackdriver | Please visit the Stackdriver codelab to set it up |
Background
Our application is a segment of a source code mirroring service. It saves git commits and blobs to Cloud Datastore. With OpenCensus, we’ll gain insights into how our distributed application is performing by instrumenting our application to examine traces.
Packages to import
For tracing on Datastore, we’ll import the following packages
Package Name | Package link |
---|---|
The Cloud Datastore Go package | cloud.google.com/go/datastore |
The OpenCensus trace package | go.opencensus.io/trace |
And when imported in code
import (
"cloud.google.com/go/datastore"
"go.opencensus.io/trace"
)
Enabling tracing
To enable tracing, we’ll follow the steps for tracing by:
- Starting and stopping spans
ctx, span := trace.StartSpan(ctx, "The span name")
// Ensure that the span is ended
defer span.End()
_ = ctx // Use the context below
Add the trace exporter
The last step is to enable trace exporting. For that we’ll use Stackdriver Exporter
import (
"log"
"contrib.go.opencensus.io/exporter/stackdriver"
"go.opencensus.io/trace"
)
func main() {
sd, err := stackdriver.NewExporter(stackdriver.Options{
ProjectID: projectID,
})
if err != nil {
log.Fatalf("Failed to create the Stackdrsiver exporter: %v", err)
}
defer sd.Flush()
trace.RegisterExporter(sd)
}
End to end code sample
With all the steps combined, we’ll finally have this code snippet
package main
import (
"context"
"log"
"cloud.google.com/go/datastore"
"contrib.go.opencensus.io/exporter/stackdriver"
"go.opencensus.io/trace"
)
type record struct {
Hash string `json:"hash"`
Message string `json:"message"`
Date string `json:"date"`
}
// record implements Load so that datastore can deserialize to it
func (r *record) Load(ps []datastore.Property) error {
return datastore.LoadStruct(r, ps)
}
func main() {
projectID := "census-demos"
client, err := datastore.NewClient(context.Background(), projectID)
if err != nil {
log.Fatalf("Failed to create datastore client: %v", err)
}
defer client.Close()
// Warm up datastore first to ensure the session is already setup. Usually in
// your application, you'd have hit this point by the time you are handling traffic.
_, _ = client.Count(context.Background(), datastore.NewQuery("Record"))
sd, err := stackdriver.NewExporter(stackdriver.Options{
ProjectID: projectID,
})
if err != nil {
log.Fatalf("Failed to create the Stackdrsiver exporter: %v", err)
}
defer sd.Flush()
// For demo purposes, we'll always sample traces
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
trace.RegisterExporter(sd)
// Now onto the application code
ctx, span := trace.StartSpan(context.Background(), "DatastoreOpenCensus-Tutorial")
defer span.End()
// Count the number of records
_, nrSpan := trace.StartSpan(ctx, "Count")
q := datastore.NewQuery("Record")
n, err := client.Count(ctx, q)
nrSpan.End()
if err != nil {
log.Fatalf("Failed to count the number of records: %v", err)
}
if n == 0 {
log.Printf("No records available yet unfortunately!")
} else {
log.Printf("There are %d records", n)
}
// Delete the bad record entities
badRecords := []*datastore.Key{
datastore.NameKey("Record", "6746d075-3b42-4a9b-80d6-4b43b91152ed", nil),
datastore.NameKey("Record", "af1ed566-0e76-4033-b9a4-bbb8dcf5b15b", nil),
}
_, dmSpan := trace.StartSpan(ctx, "DeleteBadRecords")
err = client.DeleteMulti(ctx, badRecords)
dmSpan.End()
if err != nil {
log.Fatalf("Failed to delete the bad record: %v", err)
}
// Insert the actual/good records
goodRecords := []*record{
{
Hash: "34b58f87c92fefa14ed717149a0502b29c090d1f",
Date: "Thu Oct 26 22:54:05 2017 -0700",
Message: "routing: now supporting multiple divergent route/proxies",
},
{
Hash: "8d2d3f365197aea09c9fb6d996453ca6618d17cd",
Date: "Wed Oct 25 21:34:45 2017 -0700",
Message: "ignore unmarshal errors from ping, avoid nil roundRobinAddress",
},
}
keys := []*datastore.Key{
datastore.NameKey("Record", "6746d075-3b42-4a9b-80d6-4b43b91152ed", nil),
datastore.NameKey("Record", "af1ed566-0e76-4033-b9a4-bbb8dcf5b15b", nil),
}
_, pmSpan := trace.StartSpan(ctx, "PutGoodRecords")
_, err = client.PutMulti(ctx, keys, goodRecords)
pmSpan.End()
if err != nil {
log.Fatalf("Failed to put good records: %v", err)
}
// Now fetch all the records
var allRecords []*record
_, gaSpan := trace.StartSpan(ctx, "FetchAllRecords")
allKeys, err := client.GetAll(ctx, q, &allRecords)
gaSpan.End()
if err != nil {
log.Fatalf("Failed to get back all records: %v", err)
}
for i, key := range allKeys {
log.Printf("#%d: Key(%T)=%q Record=%+v\n", i, key, key, allRecords[i])
}
// Then finally clear them all
_, dmaSpan := trace.StartSpan(ctx, "DeleteAllRecords")
err = client.DeleteMulti(ctx, allKeys)
dmaSpan.End()
if err != nil {
log.Fatalf("Failed to delete all records: %v", err)
}
}
Viewing your traces
Please visit https://console.cloud.google.com/traces/traces