Moved to google.golang.org/genproto/googleapis/api/annotations

Fixes #52
This commit is contained in:
Valerio Gheri
2017-03-31 18:01:58 +02:00
parent 024c5a4e4e
commit c40779224f
2037 changed files with 831329 additions and 1854 deletions

60
vendor/github.com/go-kit/kit/tracing/README.md generated vendored Normal file
View File

@@ -0,0 +1,60 @@
# package tracing
`package tracing` provides [Dapper][]-style request tracing to services.
## Rationale
Request tracing is a fundamental building block for large distributed
applications. It's instrumental in understanding request flows, identifying
hot spots, and diagnosing errors. All microservice infrastructures will
benefit from request tracing; sufficiently large infrastructures will require
it.
## OpenTracing
Go kit builds on top of the [OpenTracing] API and uses the [opentracing-go]
package to provide tracing middlewares for its servers and clients. Currently
`kit/transport/http` and `kit/transport/grpc` transports are supported.
Since [OpenTracing] is an upcoming standard API, Go kit should support a
multitude of tracing backends. If a Tracer implementation in Go for your
back-end exists, it should work out of the box. The following tracing back-ends
are known to work with Go kit through the OpenTracing interface and are
highlighted in the [addsvc] example.
### LightStep
[LightStep] support is available through their standard Go package
[lightstep-tracer-go].
### AppDash
[Appdash] support is available straight from their system repository in the
[appdash/opentracing] directory.
### Zipkin
[Zipkin] support is now available from the [zipkin-go-opentracing] package which
can be found at the [Open Zipkin GitHub] page. This means our old custom
`tracing/zipkin` package is now deprecated. In the `kit/tracing/zipkin`
directory you can still find the `docker-compose` script to bootstrap a Zipkin
development environment and a [README] detailing how to transition from the
old package to the new.
[Dapper]: http://research.google.com/pubs/pub36356.html
[addsvc]:https://github.com/go-kit/kit/tree/master/examples/addsvc
[README]: https://github.com/go-kit/kit/blob/master/tracing/zipkin/README.md
[OpenTracing]: http://opentracing.io
[opentracing-go]: https://github.com/opentracing/opentracing-go
[Zipkin]: http://zipkin.io/
[Open Zipkin GitHub]: https://github.com/openzipkin
[zipkin-go-opentracing]: https://github.com/openzipkin/zipkin-go-opentracing
[Appdash]: https://github.com/sourcegraph/appdash
[appdash/opentracing]: https://github.com/sourcegraph/appdash/tree/master/opentracing
[LightStep]: http://lightstep.com/
[lightstep-tracer-go]: https://github.com/lightstep/lightstep-tracer-go

8
vendor/github.com/go-kit/kit/tracing/doc.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// Package tracing provides helpers and bindings for distributed tracing.
//
// As your infrastructure grows, it becomes important to be able to trace a
// request, as it travels through multiple services and back to the user.
// Package tracing provides endpoints and transport helpers and middlewares to
// capture and emit request-scoped information. We use the excellent OpenTracing
// project to bind to concrete tracing systems.
package tracing

View File

@@ -0,0 +1,4 @@
// Package opentracing provides Go kit integration to the OpenTracing project.
// OpenTracing implements a general purpose interface that microservices can
// program against, and which adapts to all major distributed tracing systems.
package opentracing

View File

@@ -0,0 +1,55 @@
package opentracing
import (
"context"
"github.com/opentracing/opentracing-go"
otext "github.com/opentracing/opentracing-go/ext"
"github.com/go-kit/kit/endpoint"
)
// TraceServer returns a Middleware that wraps the `next` Endpoint in an
// OpenTracing Span called `operationName`.
//
// If `ctx` already has a Span, it is re-used and the operation name is
// overwritten. If `ctx` does not yet have a Span, one is created here.
func TraceServer(tracer opentracing.Tracer, operationName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
serverSpan := opentracing.SpanFromContext(ctx)
if serverSpan == nil {
// All we can do is create a new root span.
serverSpan = tracer.StartSpan(operationName)
} else {
serverSpan.SetOperationName(operationName)
}
defer serverSpan.Finish()
otext.SpanKindRPCServer.Set(serverSpan)
ctx = opentracing.ContextWithSpan(ctx, serverSpan)
return next(ctx, request)
}
}
}
// TraceClient returns a Middleware that wraps the `next` Endpoint in an
// OpenTracing Span called `operationName`.
func TraceClient(tracer opentracing.Tracer, operationName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var clientSpan opentracing.Span
if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
clientSpan = tracer.StartSpan(
operationName,
opentracing.ChildOf(parentSpan.Context()),
)
} else {
clientSpan = tracer.StartSpan(operationName)
}
defer clientSpan.Finish()
otext.SpanKindRPCClient.Set(clientSpan)
ctx = opentracing.ContextWithSpan(ctx, clientSpan)
return next(ctx, request)
}
}
}

View File

@@ -0,0 +1,117 @@
package opentracing_test
import (
"context"
"testing"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/mocktracer"
"github.com/go-kit/kit/endpoint"
kitot "github.com/go-kit/kit/tracing/opentracing"
)
func TestTraceServer(t *testing.T) {
tracer := mocktracer.New()
// Initialize the ctx with a nameless Span.
contextSpan := tracer.StartSpan("").(*mocktracer.MockSpan)
ctx := opentracing.ContextWithSpan(context.Background(), contextSpan)
tracedEndpoint := kitot.TraceServer(tracer, "testOp")(endpoint.Nop)
if _, err := tracedEndpoint(ctx, struct{}{}); err != nil {
t.Fatal(err)
}
finishedSpans := tracer.FinishedSpans()
if want, have := 1, len(finishedSpans); want != have {
t.Fatalf("Want %v span(s), found %v", want, have)
}
// Test that the op name is updated
endpointSpan := finishedSpans[0]
if want, have := "testOp", endpointSpan.OperationName; want != have {
t.Fatalf("Want %q, have %q", want, have)
}
contextContext := contextSpan.Context().(mocktracer.MockSpanContext)
endpointContext := endpointSpan.Context().(mocktracer.MockSpanContext)
// ...and that the ID is unmodified.
if want, have := contextContext.SpanID, endpointContext.SpanID; want != have {
t.Errorf("Want SpanID %q, have %q", want, have)
}
}
func TestTraceServerNoContextSpan(t *testing.T) {
tracer := mocktracer.New()
// Empty/background context.
tracedEndpoint := kitot.TraceServer(tracer, "testOp")(endpoint.Nop)
if _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {
t.Fatal(err)
}
// tracedEndpoint created a new Span.
finishedSpans := tracer.FinishedSpans()
if want, have := 1, len(finishedSpans); want != have {
t.Fatalf("Want %v span(s), found %v", want, have)
}
endpointSpan := finishedSpans[0]
if want, have := "testOp", endpointSpan.OperationName; want != have {
t.Fatalf("Want %q, have %q", want, have)
}
}
func TestTraceClient(t *testing.T) {
tracer := mocktracer.New()
// Initialize the ctx with a parent Span.
parentSpan := tracer.StartSpan("parent").(*mocktracer.MockSpan)
defer parentSpan.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), parentSpan)
tracedEndpoint := kitot.TraceClient(tracer, "testOp")(endpoint.Nop)
if _, err := tracedEndpoint(ctx, struct{}{}); err != nil {
t.Fatal(err)
}
// tracedEndpoint created a new Span.
finishedSpans := tracer.FinishedSpans()
if want, have := 1, len(finishedSpans); want != have {
t.Fatalf("Want %v span(s), found %v", want, have)
}
endpointSpan := finishedSpans[0]
if want, have := "testOp", endpointSpan.OperationName; want != have {
t.Fatalf("Want %q, have %q", want, have)
}
parentContext := parentSpan.Context().(mocktracer.MockSpanContext)
endpointContext := parentSpan.Context().(mocktracer.MockSpanContext)
// ... and that the parent ID is set appropriately.
if want, have := parentContext.SpanID, endpointContext.SpanID; want != have {
t.Errorf("Want ParentID %q, have %q", want, have)
}
}
func TestTraceClientNoContextSpan(t *testing.T) {
tracer := mocktracer.New()
// Empty/background context.
tracedEndpoint := kitot.TraceClient(tracer, "testOp")(endpoint.Nop)
if _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {
t.Fatal(err)
}
// tracedEndpoint created a new Span.
finishedSpans := tracer.FinishedSpans()
if want, have := 1, len(finishedSpans); want != have {
t.Fatalf("Want %v span(s), found %v", want, have)
}
endpointSpan := finishedSpans[0]
if want, have := "testOp", endpointSpan.OperationName; want != have {
t.Fatalf("Want %q, have %q", want, have)
}
}

View File

@@ -0,0 +1,70 @@
package opentracing
import (
"context"
"encoding/base64"
"strings"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"google.golang.org/grpc/metadata"
"github.com/go-kit/kit/log"
)
// ToGRPCRequest returns a grpc RequestFunc that injects an OpenTracing Span
// found in `ctx` into the grpc Metadata. If no such Span can be found, the
// RequestFunc is a noop.
func ToGRPCRequest(tracer opentracing.Tracer, logger log.Logger) func(ctx context.Context, md *metadata.MD) context.Context {
return func(ctx context.Context, md *metadata.MD) context.Context {
if span := opentracing.SpanFromContext(ctx); span != nil {
// There's nothing we can do with an error here.
if err := tracer.Inject(span.Context(), opentracing.TextMap, metadataReaderWriter{md}); err != nil {
logger.Log("err", err)
}
}
return ctx
}
}
// FromGRPCRequest returns a grpc RequestFunc that tries to join with an
// OpenTracing trace found in `req` and starts a new Span called
// `operationName` accordingly. If no trace could be found in `req`, the Span
// will be a trace root. The Span is incorporated in the returned Context and
// can be retrieved with opentracing.SpanFromContext(ctx).
func FromGRPCRequest(tracer opentracing.Tracer, operationName string, logger log.Logger) func(ctx context.Context, md metadata.MD) context.Context {
return func(ctx context.Context, md metadata.MD) context.Context {
var span opentracing.Span
wireContext, err := tracer.Extract(opentracing.TextMap, metadataReaderWriter{&md})
if err != nil && err != opentracing.ErrSpanContextNotFound {
logger.Log("err", err)
}
span = tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))
return opentracing.ContextWithSpan(ctx, span)
}
}
// A type that conforms to opentracing.TextMapReader and
// opentracing.TextMapWriter.
type metadataReaderWriter struct {
*metadata.MD
}
func (w metadataReaderWriter) Set(key, val string) {
key = strings.ToLower(key)
if strings.HasSuffix(key, "-bin") {
val = string(base64.StdEncoding.EncodeToString([]byte(val)))
}
(*w.MD)[key] = append((*w.MD)[key], val)
}
func (w metadataReaderWriter) ForeachKey(handler func(key, val string) error) error {
for k, vals := range *w.MD {
for _, v := range vals {
if err := handler(k, v); err != nil {
return err
}
}
}
return nil
}

View File

@@ -0,0 +1,64 @@
package opentracing_test
import (
"context"
"testing"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/mocktracer"
"google.golang.org/grpc/metadata"
"github.com/go-kit/kit/log"
kitot "github.com/go-kit/kit/tracing/opentracing"
)
func TestTraceGRPCRequestRoundtrip(t *testing.T) {
logger := log.NewNopLogger()
tracer := mocktracer.New()
// Initialize the ctx with a Span to inject.
beforeSpan := tracer.StartSpan("to_inject").(*mocktracer.MockSpan)
defer beforeSpan.Finish()
beforeSpan.SetBaggageItem("baggage", "check")
beforeCtx := opentracing.ContextWithSpan(context.Background(), beforeSpan)
toGRPCFunc := kitot.ToGRPCRequest(tracer, logger)
md := metadata.Pairs()
// Call the RequestFunc.
afterCtx := toGRPCFunc(beforeCtx, &md)
// The Span should not have changed.
afterSpan := opentracing.SpanFromContext(afterCtx)
if beforeSpan != afterSpan {
t.Error("Should not swap in a new span")
}
// No spans should have finished yet.
finishedSpans := tracer.FinishedSpans()
if want, have := 0, len(finishedSpans); want != have {
t.Errorf("Want %v span(s), found %v", want, have)
}
// Use FromGRPCRequest to verify that we can join with the trace given MD.
fromGRPCFunc := kitot.FromGRPCRequest(tracer, "joined", logger)
joinCtx := fromGRPCFunc(afterCtx, md)
joinedSpan := opentracing.SpanFromContext(joinCtx).(*mocktracer.MockSpan)
joinedContext := joinedSpan.Context().(mocktracer.MockSpanContext)
beforeContext := beforeSpan.Context().(mocktracer.MockSpanContext)
if joinedContext.SpanID == beforeContext.SpanID {
t.Error("SpanID should have changed", joinedContext.SpanID, beforeContext.SpanID)
}
// Check that the parent/child relationship is as expected for the joined span.
if want, have := beforeContext.SpanID, joinedSpan.ParentID; want != have {
t.Errorf("Want ParentID %q, have %q", want, have)
}
if want, have := "joined", joinedSpan.OperationName; want != have {
t.Errorf("Want %q, have %q", want, have)
}
if want, have := "check", joinedSpan.BaggageItem("baggage"); want != have {
t.Errorf("Want %q, have %q", want, have)
}
}

View File

@@ -0,0 +1,71 @@
package opentracing
import (
"context"
"net"
"net/http"
"strconv"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/go-kit/kit/log"
kithttp "github.com/go-kit/kit/transport/http"
)
// ToHTTPRequest returns an http RequestFunc that injects an OpenTracing Span
// found in `ctx` into the http headers. If no such Span can be found, the
// RequestFunc is a noop.
func ToHTTPRequest(tracer opentracing.Tracer, logger log.Logger) kithttp.RequestFunc {
return func(ctx context.Context, req *http.Request) context.Context {
// Try to find a Span in the Context.
if span := opentracing.SpanFromContext(ctx); span != nil {
// Add standard OpenTracing tags.
ext.HTTPMethod.Set(span, req.Method)
ext.HTTPUrl.Set(span, req.URL.String())
host, portString, err := net.SplitHostPort(req.URL.Host)
if err == nil {
ext.PeerHostname.Set(span, host)
if port, err := strconv.Atoi(portString); err != nil {
ext.PeerPort.Set(span, uint16(port))
}
} else {
ext.PeerHostname.Set(span, req.URL.Host)
}
// There's nothing we can do with any errors here.
if err = tracer.Inject(
span.Context(),
opentracing.TextMap,
opentracing.HTTPHeadersCarrier(req.Header),
); err != nil {
logger.Log("err", err)
}
}
return ctx
}
}
// FromHTTPRequest returns an http RequestFunc that tries to join with an
// OpenTracing trace found in `req` and starts a new Span called
// `operationName` accordingly. If no trace could be found in `req`, the Span
// will be a trace root. The Span is incorporated in the returned Context and
// can be retrieved with opentracing.SpanFromContext(ctx).
func FromHTTPRequest(tracer opentracing.Tracer, operationName string, logger log.Logger) kithttp.RequestFunc {
return func(ctx context.Context, req *http.Request) context.Context {
// Try to join to a trace propagated in `req`.
var span opentracing.Span
wireContext, err := tracer.Extract(
opentracing.TextMap,
opentracing.HTTPHeadersCarrier(req.Header),
)
if err != nil && err != opentracing.ErrSpanContextNotFound {
logger.Log("err", err)
}
span = tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))
ext.HTTPMethod.Set(span, req.Method)
ext.HTTPUrl.Set(span, req.URL.String())
return opentracing.ContextWithSpan(ctx, span)
}
}

View File

@@ -0,0 +1,109 @@
package opentracing_test
import (
"context"
"net/http"
"reflect"
"testing"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/opentracing/opentracing-go/mocktracer"
"github.com/go-kit/kit/log"
kitot "github.com/go-kit/kit/tracing/opentracing"
)
func TestTraceHTTPRequestRoundtrip(t *testing.T) {
logger := log.NewNopLogger()
tracer := mocktracer.New()
// Initialize the ctx with a Span to inject.
beforeSpan := tracer.StartSpan("to_inject").(*mocktracer.MockSpan)
defer beforeSpan.Finish()
beforeSpan.SetBaggageItem("baggage", "check")
beforeCtx := opentracing.ContextWithSpan(context.Background(), beforeSpan)
toHTTPFunc := kitot.ToHTTPRequest(tracer, logger)
req, _ := http.NewRequest("GET", "http://test.biz/path", nil)
// Call the RequestFunc.
afterCtx := toHTTPFunc(beforeCtx, req)
// The Span should not have changed.
afterSpan := opentracing.SpanFromContext(afterCtx)
if beforeSpan != afterSpan {
t.Error("Should not swap in a new span")
}
// No spans should have finished yet.
finishedSpans := tracer.FinishedSpans()
if want, have := 0, len(finishedSpans); want != have {
t.Errorf("Want %v span(s), found %v", want, have)
}
// Use FromHTTPRequest to verify that we can join with the trace given a req.
fromHTTPFunc := kitot.FromHTTPRequest(tracer, "joined", logger)
joinCtx := fromHTTPFunc(afterCtx, req)
joinedSpan := opentracing.SpanFromContext(joinCtx).(*mocktracer.MockSpan)
joinedContext := joinedSpan.Context().(mocktracer.MockSpanContext)
beforeContext := beforeSpan.Context().(mocktracer.MockSpanContext)
if joinedContext.SpanID == beforeContext.SpanID {
t.Error("SpanID should have changed", joinedContext.SpanID, beforeContext.SpanID)
}
// Check that the parent/child relationship is as expected for the joined span.
if want, have := beforeContext.SpanID, joinedSpan.ParentID; want != have {
t.Errorf("Want ParentID %q, have %q", want, have)
}
if want, have := "joined", joinedSpan.OperationName; want != have {
t.Errorf("Want %q, have %q", want, have)
}
if want, have := "check", joinedSpan.BaggageItem("baggage"); want != have {
t.Errorf("Want %q, have %q", want, have)
}
}
func TestToHTTPRequestTags(t *testing.T) {
tracer := mocktracer.New()
span := tracer.StartSpan("to_inject").(*mocktracer.MockSpan)
defer span.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), span)
req, _ := http.NewRequest("GET", "http://test.biz/path", nil)
kitot.ToHTTPRequest(tracer, log.NewNopLogger())(ctx, req)
expectedTags := map[string]interface{}{
string(ext.HTTPMethod): "GET",
string(ext.HTTPUrl): "http://test.biz/path",
string(ext.PeerHostname): "test.biz",
}
if !reflect.DeepEqual(expectedTags, span.Tags()) {
t.Errorf("Want %q, have %q", expectedTags, span.Tags())
}
}
func TestFromHTTPRequestTags(t *testing.T) {
tracer := mocktracer.New()
parentSpan := tracer.StartSpan("to_extract").(*mocktracer.MockSpan)
defer parentSpan.Finish()
req, _ := http.NewRequest("GET", "http://test.biz/path", nil)
tracer.Inject(parentSpan.Context(), opentracing.TextMap, opentracing.HTTPHeadersCarrier(req.Header))
ctx := kitot.FromHTTPRequest(tracer, "op", log.NewNopLogger())(context.Background(), req)
opentracing.SpanFromContext(ctx).Finish()
childSpan := tracer.FinishedSpans()[0]
expectedTags := map[string]interface{}{
string(ext.HTTPMethod): "GET",
string(ext.HTTPUrl): "http://test.biz/path",
string(ext.SpanKind): ext.SpanKindRPCServerEnum,
}
if !reflect.DeepEqual(expectedTags, childSpan.Tags()) {
t.Errorf("Want %q, have %q", expectedTags, childSpan.Tags())
}
if want, have := "op", childSpan.OperationName; want != have {
t.Errorf("Want %q, have %q", want, have)
}
}

178
vendor/github.com/go-kit/kit/tracing/zipkin/README.md generated vendored Normal file
View File

@@ -0,0 +1,178 @@
# Zipkin
## Development and Testing Set-up
Great efforts have been made to make [Zipkin] easier to test, develop and
experiment against. [Zipkin] can now be run from a single Docker container or by
running its self-contained executable jar without extensive configuration. In
its default configuration you will run Zipkin with a HTTP collector, In memory
Span storage backend and web UI on port 9411.
Example:
```
docker run -d -p 9411:9411 openzipkin/zipkin
```
[zipkin]: http://zipkin.io
Instrumenting your services with Zipkin distributed tracing using the default
configuration is now possible with the latest release of [zipkin-go-opentracing]
as it includes an HTTP transport for sending spans to the [Zipkin] HTTP
Collector.
## Middleware Usage
Follow the [addsvc] example to check out how to wire the Zipkin Middleware. The
changes should be relatively minor.
The [zipkin-go-opentracing] package has support for HTTP, Kafka and Scribe
collectors as well as using Go Kit's [Log] package for logging.
### Configuring for the Zipkin HTTP Collector
To select the transport for the HTTP Collector, you configure the `Recorder`
with the appropriate collector like this:
```go
var (
debugMode = false
serviceName = "MyService"
serviceHostPort = "localhost:8000"
zipkinHTTPEndpoint = "localhost:9411"
)
collector, err = zipkin.NewHTTPCollector(zipkinHTTPEndpoint)
if err != nil {
// handle error
}
tracer, err = zipkin.NewTracer(
zipkin.NewRecorder(collector, debugMode, serviceHostPort, serviceName),
...
)
```
### Span per Node vs. Span per RPC
By default Zipkin V1 considers either side of an RPC to have the same identity
and differs in that respect from many other tracing systems which consider the
caller to be the parent and the receiver to be the child. The OpenTracing
specification does not dictate one model over the other, but the Zipkin team is
looking into these [single-host-spans] to potentially bring Zipkin more in-line
with the other tracing systems.
[single-host-spans]: https://github.com/openzipkin/zipkin/issues/963
In case of a `span per node` the receiver will create a child span from the
propagated parent span like this:
```
Span per Node propagation and identities
CALLER: RECEIVER:
---------------------------------
traceId -> traceId
spanId (new)
spanId -> parentSpanId
parentSpanId
```
**Note:** most tracing implementations supporting the `span per node` model
therefore do not propagate their `parentSpanID` as its not needed.
A typical Zipkin implementation will use the `span per RPC` model and recreate
the span identity from the caller on the receiver's end and then annotates its
values on top of it. Propagation will happen like this:
```
Span per RPC propagation and identities
CALLER: RECEIVER:
---------------------------------
traceId -> traceId
spanId -> spanId
parentSpanId -> parentSpanId
```
The [zipkin-go-opentracing] implementation allows you to choose which model you
wish to use. Make sure you select the same model consistently for all your
services that are required to communicate with each other or you will have trace
propagation issues. If using non OpenTracing / legacy instrumentation, it's
probably best to use the `span per RPC call` model.
To adhere to the more common tracing philosophy of `span per node`, the Tracer
defaults to `span per node`. To set the `span per RPC call` mode start your
tracer like this:
```go
tracer, err = zipkin.NewTracer(
zipkin.NewRecorder(...),
zipkin.ClientServerSameSpan(true),
)
```
[zipkin-go-opentracing]: https://github.com/openzipkin/zipkin-go-opentracing
[addsvc]:https://github.com/go-kit/kit/tree/master/examples/addsvc
[Log]: https://github.com/go-kit/kit/tree/master/log
### Tracing Resources
In our legacy implementation we had the `NewChildSpan` method to allow
annotation of resources such as databases, caches and other services that do not
have server side tracing support. Since OpenTracing has no specific method of
dealing with these items explicitely that is compatible with Zipkin's `SA`
annotation, the [zipkin-go-opentracing] has implemented support using the
OpenTracing Tags system. Here is an example of how one would be able to record
a resource span compatible with standard OpenTracing and triggering an `SA`
annotation in [zipkin-go-opentracing]:
```go
// you need to import the ext package for the Tag helper functions
import (
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
func (svc *Service) GetMeSomeExamples(ctx context.Context, ...) ([]Examples, error) {
// Example of annotating a database query:
var (
serviceName = "MySQL"
serviceHost = "mysql.example.com"
servicePort = uint16(3306)
queryLabel = "GetExamplesByParam"
query = "select * from example where param = 'value'"
)
// retrieve the parent span, if not found create a new trace
parentSpan := opentracing.SpanFromContext(ctx)
if parentSpan == nil {
parentSpan = opentracing.StartSpan(queryLabel)
defer parentSpan.Finish()
}
// create a new span to record the resource interaction
span := opentracing.StartChildSpan(parentSpan, queryLabel)
// span.kind "resource" triggers SA annotation
ext.SpanKind.Set(span, "resource")
// this will label the span's service & hostPort (called Endpoint in Zipkin)
ext.PeerService.Set(span, serviceName)
ext.PeerHostname.Set(span, serviceHost)
ext.PeerPort.Set(span, servicePort)
// a Tag is the equivalent of a Zipkin Binary Annotation (key:value pair)
span.SetTag("query", query)
// a LogEvent is the equivalent of a Zipkin Annotation (timestamped)
span.LogEvent("query:start")
// do the actual query...
// let's annotate the end...
span.LogEvent("query:end")
// we're done with this span.
span.Finish()
// do other stuff
...
}
```