|
- // Copyright 2017, OpenCensus Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- // Package zipkin contains an trace exporter for Zipkin.
- package zipkin // import "go.opencensus.io/exporter/zipkin"
-
- import (
- "encoding/binary"
- "strconv"
-
- "github.com/openzipkin/zipkin-go/model"
- "github.com/openzipkin/zipkin-go/reporter"
- "go.opencensus.io/trace"
- )
-
- // Exporter is an implementation of trace.Exporter that uploads spans to a
- // Zipkin server.
- type Exporter struct {
- reporter reporter.Reporter
- localEndpoint *model.Endpoint
- }
-
- // NewExporter returns an implementation of trace.Exporter that uploads spans
- // to a Zipkin server.
- //
- // reporter is a Zipkin Reporter which will be used to send the spans. These
- // can be created with the openzipkin library, using one of the packages under
- // github.com/openzipkin/zipkin-go/reporter.
- //
- // localEndpoint sets the local endpoint of exported spans. It can be
- // constructed with github.com/openzipkin/zipkin-go.NewEndpoint, e.g.:
- // localEndpoint, err := NewEndpoint("my server", listener.Addr().String())
- // localEndpoint can be nil.
- func NewExporter(reporter reporter.Reporter, localEndpoint *model.Endpoint) *Exporter {
- return &Exporter{
- reporter: reporter,
- localEndpoint: localEndpoint,
- }
- }
-
- // ExportSpan exports a span to a Zipkin server.
- func (e *Exporter) ExportSpan(s *trace.SpanData) {
- e.reporter.Send(zipkinSpan(s, e.localEndpoint))
- }
-
- const (
- statusCodeTagKey = "error"
- statusDescriptionTagKey = "opencensus.status_description"
- )
-
- var (
- sampledTrue = true
- canonicalCodes = [...]string{
- "OK",
- "CANCELLED",
- "UNKNOWN",
- "INVALID_ARGUMENT",
- "DEADLINE_EXCEEDED",
- "NOT_FOUND",
- "ALREADY_EXISTS",
- "PERMISSION_DENIED",
- "RESOURCE_EXHAUSTED",
- "FAILED_PRECONDITION",
- "ABORTED",
- "OUT_OF_RANGE",
- "UNIMPLEMENTED",
- "INTERNAL",
- "UNAVAILABLE",
- "DATA_LOSS",
- "UNAUTHENTICATED",
- }
- )
-
- func canonicalCodeString(code int32) string {
- if code < 0 || int(code) >= len(canonicalCodes) {
- return "error code " + strconv.FormatInt(int64(code), 10)
- }
- return canonicalCodes[code]
- }
-
- func convertTraceID(t trace.TraceID) model.TraceID {
- return model.TraceID{
- High: binary.BigEndian.Uint64(t[:8]),
- Low: binary.BigEndian.Uint64(t[8:]),
- }
- }
-
- func convertSpanID(s trace.SpanID) model.ID {
- return model.ID(binary.BigEndian.Uint64(s[:]))
- }
-
- func spanKind(s *trace.SpanData) model.Kind {
- switch s.SpanKind {
- case trace.SpanKindClient:
- return model.Client
- case trace.SpanKindServer:
- return model.Server
- }
- return model.Undetermined
- }
-
- func zipkinSpan(s *trace.SpanData, localEndpoint *model.Endpoint) model.SpanModel {
- sc := s.SpanContext
- z := model.SpanModel{
- SpanContext: model.SpanContext{
- TraceID: convertTraceID(sc.TraceID),
- ID: convertSpanID(sc.SpanID),
- Sampled: &sampledTrue,
- },
- Kind: spanKind(s),
- Name: s.Name,
- Timestamp: s.StartTime,
- Shared: false,
- LocalEndpoint: localEndpoint,
- }
-
- if s.ParentSpanID != (trace.SpanID{}) {
- id := convertSpanID(s.ParentSpanID)
- z.ParentID = &id
- }
-
- if s, e := s.StartTime, s.EndTime; !s.IsZero() && !e.IsZero() {
- z.Duration = e.Sub(s)
- }
-
- // construct Tags from s.Attributes and s.Status.
- if len(s.Attributes) != 0 {
- m := make(map[string]string, len(s.Attributes)+2)
- for key, value := range s.Attributes {
- switch v := value.(type) {
- case string:
- m[key] = v
- case bool:
- if v {
- m[key] = "true"
- } else {
- m[key] = "false"
- }
- case int64:
- m[key] = strconv.FormatInt(v, 10)
- case float64:
- m[key] = strconv.FormatFloat(v, 'f', -1, 64)
- }
- }
- z.Tags = m
- }
- if s.Status.Code != 0 || s.Status.Message != "" {
- if z.Tags == nil {
- z.Tags = make(map[string]string, 2)
- }
- if s.Status.Code != 0 {
- z.Tags[statusCodeTagKey] = canonicalCodeString(s.Status.Code)
- }
- if s.Status.Message != "" {
- z.Tags[statusDescriptionTagKey] = s.Status.Message
- }
- }
-
- // construct Annotations from s.Annotations and s.MessageEvents.
- if len(s.Annotations) != 0 || len(s.MessageEvents) != 0 {
- z.Annotations = make([]model.Annotation, 0, len(s.Annotations)+len(s.MessageEvents))
- for _, a := range s.Annotations {
- z.Annotations = append(z.Annotations, model.Annotation{
- Timestamp: a.Time,
- Value: a.Message,
- })
- }
- for _, m := range s.MessageEvents {
- a := model.Annotation{
- Timestamp: m.Time,
- }
- switch m.EventType {
- case trace.MessageEventTypeSent:
- a.Value = "SENT"
- case trace.MessageEventTypeRecv:
- a.Value = "RECV"
- default:
- a.Value = "<?>"
- }
- z.Annotations = append(z.Annotations, a)
- }
- }
-
- return z
- }
|