Add support for tracing via OpenCencus
This adds a few flags for configuring the tracer. Includes support for jaeger tracing (built into OC).
This commit is contained in:
89
vendor/go.opencensus.io/exporter/jaeger/agent.go
generated
vendored
Normal file
89
vendor/go.opencensus.io/exporter/jaeger/agent.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2018, 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 jaeger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"git.apache.org/thrift.git/lib/go/thrift"
|
||||
gen "go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger"
|
||||
)
|
||||
|
||||
// udpPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent
|
||||
const udpPacketMaxLength = 65000
|
||||
|
||||
// agentClientUDP is a UDP client to Jaeger agent that implements gen.Agent interface.
|
||||
type agentClientUDP struct {
|
||||
gen.Agent
|
||||
io.Closer
|
||||
|
||||
connUDP *net.UDPConn
|
||||
client *gen.AgentClient
|
||||
maxPacketSize int // max size of datagram in bytes
|
||||
thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span
|
||||
}
|
||||
|
||||
// newAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP.
|
||||
func newAgentClientUDP(hostPort string, maxPacketSize int) (*agentClientUDP, error) {
|
||||
if maxPacketSize == 0 {
|
||||
maxPacketSize = udpPacketMaxLength
|
||||
}
|
||||
|
||||
thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize)
|
||||
protocolFactory := thrift.NewTCompactProtocolFactory()
|
||||
client := gen.NewAgentClientFactory(thriftBuffer, protocolFactory)
|
||||
|
||||
destAddr, err := net.ResolveUDPAddr("udp", hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := connUDP.SetWriteBuffer(maxPacketSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientUDP := &agentClientUDP{
|
||||
connUDP: connUDP,
|
||||
client: client,
|
||||
maxPacketSize: maxPacketSize,
|
||||
thriftBuffer: thriftBuffer}
|
||||
return clientUDP, nil
|
||||
}
|
||||
|
||||
// EmitBatch implements EmitBatch() of Agent interface
|
||||
func (a *agentClientUDP) EmitBatch(batch *gen.Batch) error {
|
||||
a.thriftBuffer.Reset()
|
||||
a.client.SeqId = 0 // we have no need for distinct SeqIds for our one-way UDP messages
|
||||
if err := a.client.EmitBatch(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.thriftBuffer.Len() > a.maxPacketSize {
|
||||
return fmt.Errorf("Data does not fit within one UDP packet; size %d, max %d, spans %d",
|
||||
a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans))
|
||||
}
|
||||
_, err := a.connUDP.Write(a.thriftBuffer.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
// Close implements Close() of io.Closer and closes the underlying UDP connection.
|
||||
func (a *agentClientUDP) Close() error {
|
||||
return a.connUDP.Close()
|
||||
}
|
||||
6
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/GoUnusedProtection__.go
generated
vendored
Normal file
6
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/GoUnusedProtection__.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Autogenerated by Thrift Compiler (0.11.0)
|
||||
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
|
||||
package jaeger
|
||||
|
||||
var GoUnusedProtection__ int
|
||||
244
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/agent.go
generated
vendored
Normal file
244
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/agent.go
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
// Autogenerated by Thrift Compiler (0.9.3)
|
||||
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.apache.org/thrift.git/lib/go/thrift"
|
||||
)
|
||||
|
||||
// (needed to ensure safety because of naive import list construction.)
|
||||
var _ = thrift.ZERO
|
||||
var _ = fmt.Printf
|
||||
var _ = bytes.Equal
|
||||
|
||||
type Agent interface {
|
||||
// Parameters:
|
||||
// - Batch
|
||||
EmitBatch(batch *Batch) (err error)
|
||||
}
|
||||
|
||||
type AgentClient struct {
|
||||
Transport thrift.TTransport
|
||||
ProtocolFactory thrift.TProtocolFactory
|
||||
InputProtocol thrift.TProtocol
|
||||
OutputProtocol thrift.TProtocol
|
||||
SeqId int32
|
||||
}
|
||||
|
||||
func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient {
|
||||
return &AgentClient{Transport: t,
|
||||
ProtocolFactory: f,
|
||||
InputProtocol: f.GetProtocol(t),
|
||||
OutputProtocol: f.GetProtocol(t),
|
||||
SeqId: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient {
|
||||
return &AgentClient{Transport: t,
|
||||
ProtocolFactory: nil,
|
||||
InputProtocol: iprot,
|
||||
OutputProtocol: oprot,
|
||||
SeqId: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters:
|
||||
// - Batch
|
||||
func (p *AgentClient) EmitBatch(batch *Batch) (err error) {
|
||||
if err = p.sendEmitBatch(batch); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *AgentClient) sendEmitBatch(batch *Batch) (err error) {
|
||||
oprot := p.OutputProtocol
|
||||
if oprot == nil {
|
||||
oprot = p.ProtocolFactory.GetProtocol(p.Transport)
|
||||
p.OutputProtocol = oprot
|
||||
}
|
||||
p.SeqId++
|
||||
if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil {
|
||||
return
|
||||
}
|
||||
args := AgentEmitBatchArgs{
|
||||
Batch: batch,
|
||||
}
|
||||
if err = args.Write(oprot); err != nil {
|
||||
return
|
||||
}
|
||||
if err = oprot.WriteMessageEnd(); err != nil {
|
||||
return
|
||||
}
|
||||
return oprot.Flush(context.Background())
|
||||
}
|
||||
|
||||
type AgentProcessor struct {
|
||||
processorMap map[string]thrift.TProcessorFunction
|
||||
handler Agent
|
||||
}
|
||||
|
||||
func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {
|
||||
p.processorMap[key] = processor
|
||||
}
|
||||
|
||||
func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {
|
||||
processor, ok = p.processorMap[key]
|
||||
return processor, ok
|
||||
}
|
||||
|
||||
func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {
|
||||
return p.processorMap
|
||||
}
|
||||
|
||||
func NewAgentProcessor(handler Agent) *AgentProcessor {
|
||||
|
||||
self0 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}
|
||||
self0.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler}
|
||||
return self0
|
||||
}
|
||||
|
||||
func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
|
||||
ctx := context.Background()
|
||||
name, _, seqId, err := iprot.ReadMessageBegin()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if processor, ok := p.GetProcessorFunction(name); ok {
|
||||
return processor.Process(ctx, seqId, iprot, oprot)
|
||||
}
|
||||
iprot.Skip(thrift.STRUCT)
|
||||
iprot.ReadMessageEnd()
|
||||
x1 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name)
|
||||
oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)
|
||||
x1.Write(oprot)
|
||||
oprot.WriteMessageEnd()
|
||||
oprot.Flush(ctx)
|
||||
return false, x1
|
||||
}
|
||||
|
||||
type agentProcessorEmitBatch struct {
|
||||
handler Agent
|
||||
}
|
||||
|
||||
func (p *agentProcessorEmitBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
|
||||
args := AgentEmitBatchArgs{}
|
||||
if err = args.Read(iprot); err != nil {
|
||||
iprot.ReadMessageEnd()
|
||||
return false, err
|
||||
}
|
||||
|
||||
iprot.ReadMessageEnd()
|
||||
var err2 error
|
||||
if err2 = p.handler.EmitBatch(args.Batch); err2 != nil {
|
||||
return true, err2
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// HELPER FUNCTIONS AND STRUCTURES
|
||||
|
||||
// Attributes:
|
||||
// - Batch
|
||||
type AgentEmitBatchArgs struct {
|
||||
Batch *Batch `thrift:"batch,1" json:"batch"`
|
||||
}
|
||||
|
||||
func NewAgentEmitBatchArgs() *AgentEmitBatchArgs {
|
||||
return &AgentEmitBatchArgs{}
|
||||
}
|
||||
|
||||
var AgentEmitBatchArgs_Batch_DEFAULT *Batch
|
||||
|
||||
func (p *AgentEmitBatchArgs) GetBatch() *Batch {
|
||||
if !p.IsSetBatch() {
|
||||
return AgentEmitBatchArgs_Batch_DEFAULT
|
||||
}
|
||||
return p.Batch
|
||||
}
|
||||
func (p *AgentEmitBatchArgs) IsSetBatch() bool {
|
||||
return p.Batch != nil
|
||||
}
|
||||
|
||||
func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error {
|
||||
if _, err := iprot.ReadStructBegin(); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()
|
||||
if err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
|
||||
}
|
||||
if fieldTypeId == thrift.STOP {
|
||||
break
|
||||
}
|
||||
switch fieldId {
|
||||
case 1:
|
||||
if err := p.readField1(iprot); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := iprot.Skip(fieldTypeId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := iprot.ReadFieldEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := iprot.ReadStructEnd(); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error {
|
||||
p.Batch = &Batch{}
|
||||
if err := p.Batch.Read(iprot); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error {
|
||||
if err := oprot.WriteStructBegin("emitBatch_args"); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
|
||||
}
|
||||
if err := p.writeField1(oprot); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := oprot.WriteFieldStop(); err != nil {
|
||||
return thrift.PrependError("write field stop error: ", err)
|
||||
}
|
||||
if err := oprot.WriteStructEnd(); err != nil {
|
||||
return thrift.PrependError("write struct stop error: ", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) {
|
||||
if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err)
|
||||
}
|
||||
if err := p.Batch.Write(oprot); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err)
|
||||
}
|
||||
if err := oprot.WriteFieldEnd(); err != nil {
|
||||
return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *AgentEmitBatchArgs) String() string {
|
||||
if p == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p)
|
||||
}
|
||||
23
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/jaeger-consts.go
generated
vendored
Normal file
23
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/jaeger-consts.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Autogenerated by Thrift Compiler (0.11.0)
|
||||
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"git.apache.org/thrift.git/lib/go/thrift"
|
||||
)
|
||||
|
||||
// (needed to ensure safety because of naive import list construction.)
|
||||
var _ = thrift.ZERO
|
||||
var _ = fmt.Printf
|
||||
var _ = context.Background
|
||||
var _ = reflect.DeepEqual
|
||||
var _ = bytes.Equal
|
||||
|
||||
func init() {
|
||||
}
|
||||
2443
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/jaeger.go
generated
vendored
Normal file
2443
vendor/go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger/jaeger.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
331
vendor/go.opencensus.io/exporter/jaeger/jaeger.go
generated
vendored
Normal file
331
vendor/go.opencensus.io/exporter/jaeger/jaeger.go
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
// Copyright 2018, 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 jaeger contains an OpenCensus tracing exporter for Jaeger.
|
||||
package jaeger // import "go.opencensus.io/exporter/jaeger"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.apache.org/thrift.git/lib/go/thrift"
|
||||
gen "go.opencensus.io/exporter/jaeger/internal/gen-go/jaeger"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/api/support/bundler"
|
||||
)
|
||||
|
||||
const defaultServiceName = "OpenCensus"
|
||||
|
||||
// Options are the options to be used when initializing a Jaeger exporter.
|
||||
type Options struct {
|
||||
// Endpoint is the Jaeger HTTP Thrift endpoint.
|
||||
// For example, http://localhost:14268.
|
||||
Endpoint string
|
||||
|
||||
// AgentEndpoint instructs exporter to send spans to jaeger-agent at this address.
|
||||
// For example, localhost:6831.
|
||||
AgentEndpoint string
|
||||
|
||||
// OnError is the hook to be called when there is
|
||||
// an error occurred when uploading the stats data.
|
||||
// If no custom hook is set, errors are logged.
|
||||
// Optional.
|
||||
OnError func(err error)
|
||||
|
||||
// Username to be used if basic auth is required.
|
||||
// Optional.
|
||||
Username string
|
||||
|
||||
// Password to be used if basic auth is required.
|
||||
// Optional.
|
||||
Password string
|
||||
|
||||
// ServiceName is the Jaeger service name.
|
||||
// Deprecated: Specify Process instead.
|
||||
ServiceName string
|
||||
|
||||
// Process contains the information about the exporting process.
|
||||
Process Process
|
||||
}
|
||||
|
||||
// NewExporter returns a trace.Exporter implementation that exports
|
||||
// the collected spans to Jaeger.
|
||||
func NewExporter(o Options) (*Exporter, error) {
|
||||
endpoint := o.Endpoint
|
||||
if endpoint == "" && o.AgentEndpoint == "" {
|
||||
return nil, errors.New("missing endpoint for Jaeger exporter")
|
||||
}
|
||||
|
||||
var client *agentClientUDP
|
||||
var err error
|
||||
if endpoint != "" {
|
||||
endpoint = endpoint + "/api/traces?format=jaeger.thrift"
|
||||
} else {
|
||||
client, err = newAgentClientUDP(o.AgentEndpoint, udpPacketMaxLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
onError := func(err error) {
|
||||
if o.OnError != nil {
|
||||
o.OnError(err)
|
||||
return
|
||||
}
|
||||
log.Printf("Error when uploading spans to Jaeger: %v", err)
|
||||
}
|
||||
service := o.Process.ServiceName
|
||||
if service == "" && o.ServiceName != "" {
|
||||
// fallback to old service name if specified
|
||||
service = o.ServiceName
|
||||
} else if service == "" {
|
||||
service = defaultServiceName
|
||||
}
|
||||
tags := make([]*gen.Tag, len(o.Process.Tags))
|
||||
for i, tag := range o.Process.Tags {
|
||||
tags[i] = attributeToTag(tag.key, tag.value)
|
||||
}
|
||||
e := &Exporter{
|
||||
endpoint: endpoint,
|
||||
agentEndpoint: o.AgentEndpoint,
|
||||
client: client,
|
||||
username: o.Username,
|
||||
password: o.Password,
|
||||
process: &gen.Process{
|
||||
ServiceName: service,
|
||||
Tags: tags,
|
||||
},
|
||||
}
|
||||
bundler := bundler.NewBundler((*gen.Span)(nil), func(bundle interface{}) {
|
||||
if err := e.upload(bundle.([]*gen.Span)); err != nil {
|
||||
onError(err)
|
||||
}
|
||||
})
|
||||
e.bundler = bundler
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// Process contains the information exported to jaeger about the source
|
||||
// of the trace data.
|
||||
type Process struct {
|
||||
// ServiceName is the Jaeger service name.
|
||||
ServiceName string
|
||||
|
||||
// Tags are added to Jaeger Process exports
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
// Tag defines a key-value pair
|
||||
// It is limited to the possible conversions to *jaeger.Tag by attributeToTag
|
||||
type Tag struct {
|
||||
key string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// BoolTag creates a new tag of type bool, exported as jaeger.TagType_BOOL
|
||||
func BoolTag(key string, value bool) Tag {
|
||||
return Tag{key, value}
|
||||
}
|
||||
|
||||
// StringTag creates a new tag of type string, exported as jaeger.TagType_STRING
|
||||
func StringTag(key string, value string) Tag {
|
||||
return Tag{key, value}
|
||||
}
|
||||
|
||||
// Int64Tag creates a new tag of type int64, exported as jaeger.TagType_LONG
|
||||
func Int64Tag(key string, value int64) Tag {
|
||||
return Tag{key, value}
|
||||
}
|
||||
|
||||
// Exporter is an implementation of trace.Exporter that uploads spans to Jaeger.
|
||||
type Exporter struct {
|
||||
endpoint string
|
||||
agentEndpoint string
|
||||
process *gen.Process
|
||||
bundler *bundler.Bundler
|
||||
client *agentClientUDP
|
||||
|
||||
username, password string
|
||||
}
|
||||
|
||||
var _ trace.Exporter = (*Exporter)(nil)
|
||||
|
||||
// ExportSpan exports a SpanData to Jaeger.
|
||||
func (e *Exporter) ExportSpan(data *trace.SpanData) {
|
||||
e.bundler.Add(spanDataToThrift(data), 1)
|
||||
// TODO(jbd): Handle oversized bundlers.
|
||||
}
|
||||
|
||||
func spanDataToThrift(data *trace.SpanData) *gen.Span {
|
||||
tags := make([]*gen.Tag, 0, len(data.Attributes))
|
||||
for k, v := range data.Attributes {
|
||||
tag := attributeToTag(k, v)
|
||||
if tag != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
|
||||
tags = append(tags,
|
||||
attributeToTag("status.code", data.Status.Code),
|
||||
attributeToTag("status.message", data.Status.Message),
|
||||
)
|
||||
|
||||
var logs []*gen.Log
|
||||
for _, a := range data.Annotations {
|
||||
fields := make([]*gen.Tag, 0, len(a.Attributes))
|
||||
for k, v := range a.Attributes {
|
||||
tag := attributeToTag(k, v)
|
||||
if tag != nil {
|
||||
fields = append(fields, tag)
|
||||
}
|
||||
}
|
||||
fields = append(fields, attributeToTag("message", a.Message))
|
||||
logs = append(logs, &gen.Log{
|
||||
Timestamp: a.Time.UnixNano() / 1000,
|
||||
Fields: fields,
|
||||
})
|
||||
}
|
||||
var refs []*gen.SpanRef
|
||||
for _, link := range data.Links {
|
||||
refs = append(refs, &gen.SpanRef{
|
||||
TraceIdHigh: bytesToInt64(link.TraceID[0:8]),
|
||||
TraceIdLow: bytesToInt64(link.TraceID[8:16]),
|
||||
SpanId: bytesToInt64(link.SpanID[:]),
|
||||
})
|
||||
}
|
||||
return &gen.Span{
|
||||
TraceIdHigh: bytesToInt64(data.TraceID[0:8]),
|
||||
TraceIdLow: bytesToInt64(data.TraceID[8:16]),
|
||||
SpanId: bytesToInt64(data.SpanID[:]),
|
||||
ParentSpanId: bytesToInt64(data.ParentSpanID[:]),
|
||||
OperationName: name(data),
|
||||
Flags: int32(data.TraceOptions),
|
||||
StartTime: data.StartTime.UnixNano() / 1000,
|
||||
Duration: data.EndTime.Sub(data.StartTime).Nanoseconds() / 1000,
|
||||
Tags: tags,
|
||||
Logs: logs,
|
||||
References: refs,
|
||||
}
|
||||
}
|
||||
|
||||
func name(sd *trace.SpanData) string {
|
||||
n := sd.Name
|
||||
switch sd.SpanKind {
|
||||
case trace.SpanKindClient:
|
||||
n = "Sent." + n
|
||||
case trace.SpanKindServer:
|
||||
n = "Recv." + n
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func attributeToTag(key string, a interface{}) *gen.Tag {
|
||||
var tag *gen.Tag
|
||||
switch value := a.(type) {
|
||||
case bool:
|
||||
tag = &gen.Tag{
|
||||
Key: key,
|
||||
VBool: &value,
|
||||
VType: gen.TagType_BOOL,
|
||||
}
|
||||
case string:
|
||||
tag = &gen.Tag{
|
||||
Key: key,
|
||||
VStr: &value,
|
||||
VType: gen.TagType_STRING,
|
||||
}
|
||||
case int64:
|
||||
tag = &gen.Tag{
|
||||
Key: key,
|
||||
VLong: &value,
|
||||
VType: gen.TagType_LONG,
|
||||
}
|
||||
case int32:
|
||||
v := int64(value)
|
||||
tag = &gen.Tag{
|
||||
Key: key,
|
||||
VLong: &v,
|
||||
VType: gen.TagType_LONG,
|
||||
}
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
// Flush waits for exported trace spans to be uploaded.
|
||||
//
|
||||
// This is useful if your program is ending and you do not want to lose recent spans.
|
||||
func (e *Exporter) Flush() {
|
||||
e.bundler.Flush()
|
||||
}
|
||||
|
||||
func (e *Exporter) upload(spans []*gen.Span) error {
|
||||
batch := &gen.Batch{
|
||||
Spans: spans,
|
||||
Process: e.process,
|
||||
}
|
||||
if e.endpoint != "" {
|
||||
return e.uploadCollector(batch)
|
||||
}
|
||||
return e.uploadAgent(batch)
|
||||
}
|
||||
|
||||
func (e *Exporter) uploadAgent(batch *gen.Batch) error {
|
||||
return e.client.EmitBatch(batch)
|
||||
}
|
||||
|
||||
func (e *Exporter) uploadCollector(batch *gen.Batch) error {
|
||||
body, err := serialize(batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest("POST", e.endpoint, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.username != "" && e.password != "" {
|
||||
req.SetBasicAuth(e.username, e.password)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-thrift")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("failed to upload traces; HTTP status code: %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func serialize(obj thrift.TStruct) (*bytes.Buffer, error) {
|
||||
buf := thrift.NewTMemoryBuffer()
|
||||
if err := obj.Write(thrift.NewTBinaryProtocolTransport(buf)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Buffer, nil
|
||||
}
|
||||
|
||||
func bytesToInt64(buf []byte) int64 {
|
||||
u := binary.BigEndian.Uint64(buf)
|
||||
return int64(u)
|
||||
}
|
||||
Reference in New Issue
Block a user