253 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
package jsonV1
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/influxdata/telegraf/plugins/inputs/zipkin/codec"
 | 
						|
	"github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore"
 | 
						|
)
 | 
						|
 | 
						|
// JSON decodes spans from  bodies `POST`ed to the spans endpoint
 | 
						|
type JSON struct{}
 | 
						|
 | 
						|
// Decode unmarshals and validates the JSON body
 | 
						|
func (j *JSON) Decode(octets []byte) ([]codec.Span, error) {
 | 
						|
	var spans []span
 | 
						|
	err := json.Unmarshal(octets, &spans)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	res := make([]codec.Span, len(spans))
 | 
						|
	for i := range spans {
 | 
						|
		if err := spans[i].Validate(); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		res[i] = &spans[i]
 | 
						|
	}
 | 
						|
	return res, nil
 | 
						|
}
 | 
						|
 | 
						|
type span struct {
 | 
						|
	TraceID  string             `json:"traceId"`
 | 
						|
	SpanName string             `json:"name"`
 | 
						|
	ParentID string             `json:"parentId,omitempty"`
 | 
						|
	ID       string             `json:"id"`
 | 
						|
	Time     *int64             `json:"timestamp,omitempty"`
 | 
						|
	Dur      *int64             `json:"duration,omitempty"`
 | 
						|
	Debug    bool               `json:"debug,omitempty"`
 | 
						|
	Anno     []annotation       `json:"annotations"`
 | 
						|
	BAnno    []binaryAnnotation `json:"binaryAnnotations"`
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Validate() error {
 | 
						|
	var err error
 | 
						|
	check := func(f func() (string, error)) {
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		_, err = f()
 | 
						|
	}
 | 
						|
 | 
						|
	check(s.Trace)
 | 
						|
	check(s.SpanID)
 | 
						|
	check(s.Parent)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = s.BinaryAnnotations()
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Trace() (string, error) {
 | 
						|
	if s.TraceID == "" {
 | 
						|
		return "", fmt.Errorf("Trace ID cannot be null")
 | 
						|
	}
 | 
						|
	return TraceIDFromString(s.TraceID)
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) SpanID() (string, error) {
 | 
						|
	if s.ID == "" {
 | 
						|
		return "", fmt.Errorf("Span ID cannot be null")
 | 
						|
	}
 | 
						|
	return IDFromString(s.ID)
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Parent() (string, error) {
 | 
						|
	if s.ParentID == "" {
 | 
						|
		return "", nil
 | 
						|
	}
 | 
						|
	return IDFromString(s.ParentID)
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Name() string {
 | 
						|
	return s.SpanName
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Annotations() []codec.Annotation {
 | 
						|
	res := make([]codec.Annotation, len(s.Anno))
 | 
						|
	for i := range s.Anno {
 | 
						|
		res[i] = &s.Anno[i]
 | 
						|
	}
 | 
						|
	return res
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) BinaryAnnotations() ([]codec.BinaryAnnotation, error) {
 | 
						|
	res := make([]codec.BinaryAnnotation, len(s.BAnno))
 | 
						|
	for i, a := range s.BAnno {
 | 
						|
		if a.Key() != "" && a.Value() == "" {
 | 
						|
			return nil, fmt.Errorf("No value for key %s at binaryAnnotations[%d]", a.K, i)
 | 
						|
		}
 | 
						|
		if a.Value() != "" && a.Key() == "" {
 | 
						|
			return nil, fmt.Errorf("No key at binaryAnnotations[%d]", i)
 | 
						|
		}
 | 
						|
		res[i] = &s.BAnno[i]
 | 
						|
	}
 | 
						|
	return res, nil
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Timestamp() time.Time {
 | 
						|
	if s.Time == nil {
 | 
						|
		return time.Time{}
 | 
						|
	}
 | 
						|
	return codec.MicroToTime(*s.Time)
 | 
						|
}
 | 
						|
 | 
						|
func (s *span) Duration() time.Duration {
 | 
						|
	if s.Dur == nil {
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	return time.Duration(*s.Dur) * time.Microsecond
 | 
						|
}
 | 
						|
 | 
						|
type annotation struct {
 | 
						|
	Endpoint *endpoint `json:"endpoint,omitempty"`
 | 
						|
	Time     int64     `json:"timestamp"`
 | 
						|
	Val      string    `json:"value,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
func (a *annotation) Timestamp() time.Time {
 | 
						|
	return codec.MicroToTime(a.Time)
 | 
						|
}
 | 
						|
 | 
						|
func (a *annotation) Value() string {
 | 
						|
	return a.Val
 | 
						|
}
 | 
						|
 | 
						|
func (a *annotation) Host() codec.Endpoint {
 | 
						|
	return a.Endpoint
 | 
						|
}
 | 
						|
 | 
						|
type binaryAnnotation struct {
 | 
						|
	K        string          `json:"key"`
 | 
						|
	V        json.RawMessage `json:"value"`
 | 
						|
	Type     string          `json:"type"`
 | 
						|
	Endpoint *endpoint       `json:"endpoint,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
func (b *binaryAnnotation) Key() string {
 | 
						|
	return b.K
 | 
						|
}
 | 
						|
 | 
						|
func (b *binaryAnnotation) Value() string {
 | 
						|
	t, err := zipkincore.AnnotationTypeFromString(b.Type)
 | 
						|
	// Assume this is a string if we cannot tell the type
 | 
						|
	if err != nil {
 | 
						|
		t = zipkincore.AnnotationType_STRING
 | 
						|
	}
 | 
						|
 | 
						|
	switch t {
 | 
						|
	case zipkincore.AnnotationType_BOOL:
 | 
						|
		var v bool
 | 
						|
		err := json.Unmarshal(b.V, &v)
 | 
						|
		if err == nil {
 | 
						|
			return strconv.FormatBool(v)
 | 
						|
		}
 | 
						|
	case zipkincore.AnnotationType_BYTES:
 | 
						|
		return string(b.V)
 | 
						|
	case zipkincore.AnnotationType_I16, zipkincore.AnnotationType_I32, zipkincore.AnnotationType_I64:
 | 
						|
		var v int64
 | 
						|
		err := json.Unmarshal(b.V, &v)
 | 
						|
		if err == nil {
 | 
						|
			return strconv.FormatInt(v, 10)
 | 
						|
		}
 | 
						|
	case zipkincore.AnnotationType_DOUBLE:
 | 
						|
		var v float64
 | 
						|
		err := json.Unmarshal(b.V, &v)
 | 
						|
		if err == nil {
 | 
						|
			return strconv.FormatFloat(v, 'f', -1, 64)
 | 
						|
		}
 | 
						|
	case zipkincore.AnnotationType_STRING:
 | 
						|
		var v string
 | 
						|
		err := json.Unmarshal(b.V, &v)
 | 
						|
		if err == nil {
 | 
						|
			return v
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func (b *binaryAnnotation) Host() codec.Endpoint {
 | 
						|
	return b.Endpoint
 | 
						|
}
 | 
						|
 | 
						|
type endpoint struct {
 | 
						|
	ServiceName string `json:"serviceName"`
 | 
						|
	Ipv4        string `json:"ipv4"`
 | 
						|
	Ipv6        string `json:"ipv6,omitempty"`
 | 
						|
	Port        int    `json:"port"`
 | 
						|
}
 | 
						|
 | 
						|
func (e *endpoint) Host() string {
 | 
						|
	if e.Port != 0 {
 | 
						|
		return fmt.Sprintf("%s:%d", e.Ipv4, e.Port)
 | 
						|
	}
 | 
						|
	return e.Ipv4
 | 
						|
}
 | 
						|
 | 
						|
func (e *endpoint) Name() string {
 | 
						|
	return e.ServiceName
 | 
						|
}
 | 
						|
 | 
						|
// TraceIDFromString creates a TraceID from a hexadecimal string
 | 
						|
func TraceIDFromString(s string) (string, error) {
 | 
						|
	var hi, lo uint64
 | 
						|
	var err error
 | 
						|
	if len(s) > 32 {
 | 
						|
		return "", fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
 | 
						|
	} else if len(s) > 16 {
 | 
						|
		hiLen := len(s) - 16
 | 
						|
		if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if hi == 0 {
 | 
						|
		return fmt.Sprintf("%x", lo), nil
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("%x%016x", hi, lo), nil
 | 
						|
}
 | 
						|
 | 
						|
// IDFromString validates the ID and returns it in hexadecimal format.
 | 
						|
func IDFromString(s string) (string, error) {
 | 
						|
	if len(s) > 16 {
 | 
						|
		return "", fmt.Errorf("ID cannot be longer than 16 hex characters: %s", s)
 | 
						|
	}
 | 
						|
	id, err := strconv.ParseUint(s, 16, 64)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	return strconv.FormatUint(id, 16), nil
 | 
						|
}
 |