2020-03-24 07:41:10 +02:00
|
|
|
// Copyright The OpenTelemetry Authors
|
2019-09-09 23:59:39 +02:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2020-11-04 19:10:58 +02:00
|
|
|
package jaeger // import "go.opentelemetry.io/otel/exporters/trace/jaeger"
|
2019-09-09 23:59:39 +02:00
|
|
|
|
|
|
|
import (
|
2019-10-08 20:56:58 +02:00
|
|
|
"context"
|
2019-10-23 08:01:33 +02:00
|
|
|
"encoding/binary"
|
2021-03-08 19:32:02 +02:00
|
|
|
"encoding/json"
|
2020-09-09 19:19:03 +02:00
|
|
|
"fmt"
|
2019-09-09 23:59:39 +02:00
|
|
|
|
|
|
|
"google.golang.org/api/support/bundler"
|
|
|
|
|
2020-11-16 19:30:54 +02:00
|
|
|
"go.opentelemetry.io/otel"
|
2021-02-18 19:59:37 +02:00
|
|
|
"go.opentelemetry.io/otel/attribute"
|
2020-10-05 20:36:03 +02:00
|
|
|
"go.opentelemetry.io/otel/codes"
|
2020-03-02 23:54:57 +02:00
|
|
|
gen "go.opentelemetry.io/otel/exporters/trace/jaeger/internal/gen-go/jaeger"
|
2021-03-16 18:04:46 +02:00
|
|
|
"go.opentelemetry.io/otel/sdk/resource"
|
2020-03-10 22:58:45 +02:00
|
|
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
2021-03-16 18:04:46 +02:00
|
|
|
"go.opentelemetry.io/otel/semconv"
|
2020-11-07 00:13:31 +02:00
|
|
|
"go.opentelemetry.io/otel/trace"
|
2019-09-09 23:59:39 +02:00
|
|
|
)
|
|
|
|
|
2020-09-03 16:22:05 +02:00
|
|
|
const (
|
2021-03-08 19:32:02 +02:00
|
|
|
keyInstrumentationLibraryName = "otel.library.name"
|
|
|
|
keyInstrumentationLibraryVersion = "otel.library.version"
|
2021-04-01 18:27:37 +02:00
|
|
|
keyError = "error"
|
|
|
|
keySpanKind = "span.kind"
|
|
|
|
keyStatusCode = "otel.status_code"
|
|
|
|
keyStatusMessage = "otel.status_description"
|
2021-04-06 16:51:15 +02:00
|
|
|
keyDroppedAttributeCount = "otel.event.dropped_attributes_count"
|
2021-04-02 16:15:20 +02:00
|
|
|
keyEventName = "event"
|
2020-09-03 16:22:05 +02:00
|
|
|
)
|
2019-09-09 23:59:39 +02:00
|
|
|
|
2019-10-04 21:07:42 +02:00
|
|
|
type Option func(*options)
|
2019-09-09 23:59:39 +02:00
|
|
|
|
2019-10-08 20:56:58 +02:00
|
|
|
// options are the options to be used when initializing a Jaeger export.
|
2019-10-04 21:07:42 +02:00
|
|
|
type options struct {
|
2019-09-09 23:59:39 +02:00
|
|
|
|
2020-06-09 02:05:16 +02:00
|
|
|
// BufferMaxCount defines the total number of traces that can be buffered in memory
|
2019-09-09 23:59:39 +02:00
|
|
|
BufferMaxCount int
|
2020-03-10 22:58:45 +02:00
|
|
|
|
2020-07-10 16:28:45 +02:00
|
|
|
// BatchMaxCount defines the maximum number of spans sent in one batch
|
|
|
|
BatchMaxCount int
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:05:16 +02:00
|
|
|
// WithBufferMaxCount defines the total number of traces that can be buffered in memory
|
2020-04-22 05:30:57 +02:00
|
|
|
func WithBufferMaxCount(bufferMaxCount int) Option {
|
2019-10-04 21:07:42 +02:00
|
|
|
return func(o *options) {
|
|
|
|
o.BufferMaxCount = bufferMaxCount
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 16:28:45 +02:00
|
|
|
// WithBatchMaxCount defines the maximum number of spans in one batch
|
|
|
|
func WithBatchMaxCount(batchMaxCount int) Option {
|
|
|
|
return func(o *options) {
|
|
|
|
o.BatchMaxCount = batchMaxCount
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 04:58:56 +02:00
|
|
|
// NewRawExporter returns an OTel Exporter implementation that exports the
|
|
|
|
// collected spans to Jaeger.
|
2020-03-10 22:58:45 +02:00
|
|
|
func NewRawExporter(endpointOption EndpointOption, opts ...Option) (*Exporter, error) {
|
2020-06-09 02:05:16 +02:00
|
|
|
uploader, err := endpointOption()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-10-04 21:07:42 +02:00
|
|
|
o := options{}
|
|
|
|
for _, opt := range opts {
|
|
|
|
opt(&o)
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
2019-10-04 21:07:42 +02:00
|
|
|
|
2021-03-16 18:04:46 +02:00
|
|
|
// Fetch default service.name from default resource for backup
|
|
|
|
var defaultServiceName string
|
|
|
|
defaultResource := resource.Default()
|
|
|
|
if value, exists := defaultResource.Set().Value(semconv.ServiceNameKey); exists {
|
|
|
|
defaultServiceName = value.AsString()
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
2021-03-16 18:04:46 +02:00
|
|
|
if defaultServiceName == "" {
|
|
|
|
return nil, fmt.Errorf("failed to get service name from default resource")
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
2021-03-16 18:04:46 +02:00
|
|
|
|
2021-04-17 21:24:11 +02:00
|
|
|
stopCh := make(chan struct{})
|
2019-09-09 23:59:39 +02:00
|
|
|
e := &Exporter{
|
2021-04-07 00:03:32 +02:00
|
|
|
uploader: uploader,
|
2021-04-17 21:24:11 +02:00
|
|
|
stopCh: stopCh,
|
2021-04-07 00:03:32 +02:00
|
|
|
defaultServiceName: defaultServiceName,
|
2021-03-16 18:04:46 +02:00
|
|
|
}
|
2021-04-07 17:03:43 +02:00
|
|
|
bundler := bundler.NewBundler((*sdktrace.SpanSnapshot)(nil), func(bundle interface{}) {
|
|
|
|
if err := e.upload(bundle.([]*sdktrace.SpanSnapshot)); err != nil {
|
2020-11-16 19:30:54 +02:00
|
|
|
otel.Handle(err)
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// Set BufferedByteLimit with the total number of spans that are permissible to be held in memory.
|
|
|
|
// This needs to be done since the size of messages is always set to 1. Failing to set this would allow
|
|
|
|
// 1G messages to be held in memory since that is the default value of BufferedByteLimit.
|
|
|
|
if o.BufferMaxCount != 0 {
|
|
|
|
bundler.BufferedByteLimit = o.BufferMaxCount
|
|
|
|
}
|
|
|
|
|
2020-07-10 16:28:45 +02:00
|
|
|
// The default value bundler uses is 10, increase to send larger batches
|
|
|
|
if o.BatchMaxCount != 0 {
|
|
|
|
bundler.BundleCountThreshold = o.BatchMaxCount
|
|
|
|
}
|
|
|
|
|
2019-09-09 23:59:39 +02:00
|
|
|
e.bundler = bundler
|
|
|
|
return e, nil
|
|
|
|
}
|
|
|
|
|
2020-03-10 22:58:45 +02:00
|
|
|
// NewExportPipeline sets up a complete export pipeline
|
|
|
|
// with the recommended setup for trace provider
|
2021-04-20 18:31:59 +02:00
|
|
|
func NewExportPipeline(endpointOption EndpointOption, opts ...Option) (*sdktrace.TracerProvider, error) {
|
2020-03-10 22:58:45 +02:00
|
|
|
exporter, err := NewRawExporter(endpointOption, opts...)
|
|
|
|
if err != nil {
|
2021-04-20 18:31:59 +02:00
|
|
|
return nil, err
|
2020-03-10 22:58:45 +02:00
|
|
|
}
|
2020-09-09 19:19:03 +02:00
|
|
|
|
2021-04-21 01:48:34 +02:00
|
|
|
// TODO (MrAlias): The recommended default setup needs to register the
|
|
|
|
// exporter as a batcher, not a syncer. This will conflict with batching
|
|
|
|
// of the bundler currently, but when the bundler is removed it needs to
|
|
|
|
// be updated.
|
|
|
|
// https://github.com/open-telemetry/opentelemetry-go/issues/1799
|
|
|
|
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter))
|
2021-04-20 18:31:59 +02:00
|
|
|
return tp, nil
|
2020-03-10 22:58:45 +02:00
|
|
|
}
|
|
|
|
|
2020-07-22 20:57:48 +02:00
|
|
|
// InstallNewPipeline instantiates a NewExportPipeline with the
|
|
|
|
// recommended configuration and registers it globally.
|
2021-04-20 18:31:59 +02:00
|
|
|
func InstallNewPipeline(endpointOption EndpointOption, opts ...Option) (*sdktrace.TracerProvider, error) {
|
|
|
|
tp, err := NewExportPipeline(endpointOption, opts...)
|
2020-07-22 20:57:48 +02:00
|
|
|
if err != nil {
|
2021-04-20 18:31:59 +02:00
|
|
|
return tp, err
|
2020-07-22 20:57:48 +02:00
|
|
|
}
|
|
|
|
|
2020-11-16 19:30:54 +02:00
|
|
|
otel.SetTracerProvider(tp)
|
2021-04-20 18:31:59 +02:00
|
|
|
return tp, nil
|
2020-07-22 20:57:48 +02:00
|
|
|
}
|
|
|
|
|
2020-10-09 04:58:56 +02:00
|
|
|
// Exporter is an implementation of an OTel SpanSyncer that uploads spans to
|
|
|
|
// Jaeger.
|
2019-09-09 23:59:39 +02:00
|
|
|
type Exporter struct {
|
2019-10-04 21:07:42 +02:00
|
|
|
bundler *bundler.Bundler
|
|
|
|
uploader batchUploader
|
2020-09-09 19:19:03 +02:00
|
|
|
|
2021-04-17 21:24:11 +02:00
|
|
|
stopCh chan struct{}
|
2021-03-16 18:04:46 +02:00
|
|
|
|
2021-04-07 00:03:32 +02:00
|
|
|
defaultServiceName string
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
2021-04-07 17:03:43 +02:00
|
|
|
var _ sdktrace.SpanExporter = (*Exporter)(nil)
|
2020-09-09 19:19:03 +02:00
|
|
|
|
2020-12-11 07:15:44 +02:00
|
|
|
// ExportSpans exports SpanSnapshots to Jaeger.
|
2021-04-07 17:03:43 +02:00
|
|
|
func (e *Exporter) ExportSpans(ctx context.Context, ss []*sdktrace.SpanSnapshot) error {
|
2021-04-17 21:24:11 +02:00
|
|
|
// Return fast if context is already canceled or Exporter shutdown.
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case <-e.stopCh:
|
2020-09-09 19:19:03 +02:00
|
|
|
return nil
|
2021-04-17 21:24:11 +02:00
|
|
|
default:
|
2020-09-09 19:19:03 +02:00
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
|
2021-04-17 21:24:11 +02:00
|
|
|
// Cancel export if Exporter is shutdown.
|
|
|
|
var cancel context.CancelFunc
|
|
|
|
ctx, cancel = context.WithCancel(ctx)
|
|
|
|
defer cancel()
|
|
|
|
go func(ctx context.Context, cancel context.CancelFunc) {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
case <-e.stopCh:
|
|
|
|
cancel()
|
|
|
|
}
|
|
|
|
}(ctx, cancel)
|
|
|
|
|
2020-12-11 07:15:44 +02:00
|
|
|
for _, span := range ss {
|
2020-09-09 19:19:03 +02:00
|
|
|
// TODO(jbd): Handle oversized bundlers.
|
2021-04-05 22:33:16 +02:00
|
|
|
err := e.bundler.AddWait(ctx, span, 1)
|
2020-09-09 19:19:03 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to bundle %q: %w", span.Name, err)
|
|
|
|
}
|
2021-04-05 22:33:16 +02:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
default:
|
|
|
|
}
|
2020-09-09 19:19:03 +02:00
|
|
|
}
|
2021-04-05 22:33:16 +02:00
|
|
|
|
2020-09-09 19:19:03 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// flush is used to wrap the bundler's Flush method for testing.
|
|
|
|
var flush = func(e *Exporter) {
|
|
|
|
e.bundler.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown stops the exporter flushing any pending exports.
|
|
|
|
func (e *Exporter) Shutdown(ctx context.Context) error {
|
2021-04-17 21:24:11 +02:00
|
|
|
// Stop any active and subsequent exports.
|
|
|
|
close(e.stopCh)
|
2020-09-09 19:19:03 +02:00
|
|
|
|
|
|
|
done := make(chan struct{}, 1)
|
|
|
|
// Shadow so if the goroutine is leaked in testing it doesn't cause a race
|
|
|
|
// condition when the file level var is reset.
|
|
|
|
go func(FlushFunc func(*Exporter)) {
|
|
|
|
// The OpenTelemetry specification is explicit in not having this
|
|
|
|
// method block so the preference here is to orphan this goroutine if
|
|
|
|
// the context is canceled or times out while this flushing is
|
|
|
|
// occurring. This is a consequence of the bundler Flush method not
|
|
|
|
// supporting a context.
|
|
|
|
FlushFunc(e)
|
|
|
|
done <- struct{}{}
|
|
|
|
}(flush)
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case <-done:
|
|
|
|
}
|
|
|
|
return nil
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
2021-04-07 17:03:43 +02:00
|
|
|
func spanSnapshotToThrift(ss *sdktrace.SpanSnapshot) *gen.Span {
|
2020-12-11 07:15:44 +02:00
|
|
|
tags := make([]*gen.Tag, 0, len(ss.Attributes))
|
|
|
|
for _, kv := range ss.Attributes {
|
2019-10-17 18:59:30 +02:00
|
|
|
tag := keyValueToTag(kv)
|
|
|
|
if tag != nil {
|
|
|
|
tags = append(tags, tag)
|
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
2020-12-11 07:15:44 +02:00
|
|
|
if il := ss.InstrumentationLibrary; il.Name != "" {
|
2020-09-03 16:22:05 +02:00
|
|
|
tags = append(tags, getStringTag(keyInstrumentationLibraryName, il.Name))
|
2020-06-10 07:15:53 +02:00
|
|
|
if il.Version != "" {
|
2020-09-03 16:22:05 +02:00
|
|
|
tags = append(tags, getStringTag(keyInstrumentationLibraryVersion, il.Version))
|
2020-06-10 07:15:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 19:32:02 +02:00
|
|
|
if ss.SpanKind != trace.SpanKindInternal {
|
|
|
|
tags = append(tags,
|
2021-04-01 18:27:37 +02:00
|
|
|
getStringTag(keySpanKind, ss.SpanKind.String()),
|
2021-03-08 19:32:02 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if ss.StatusCode != codes.Unset {
|
2021-04-01 18:27:37 +02:00
|
|
|
tags = append(tags, getInt64Tag(keyStatusCode, int64(ss.StatusCode)))
|
|
|
|
if ss.StatusMessage != "" {
|
|
|
|
tags = append(tags, getStringTag(keyStatusMessage, ss.StatusMessage))
|
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
|
2021-03-08 19:32:02 +02:00
|
|
|
if ss.StatusCode == codes.Error {
|
2021-04-01 18:27:37 +02:00
|
|
|
tags = append(tags, getBoolTag(keyError, true))
|
2021-03-08 19:32:02 +02:00
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var logs []*gen.Log
|
2020-12-11 07:15:44 +02:00
|
|
|
for _, a := range ss.MessageEvents {
|
2021-04-02 16:15:20 +02:00
|
|
|
nTags := len(a.Attributes)
|
|
|
|
if a.Name != "" {
|
|
|
|
nTags++
|
|
|
|
}
|
2021-04-06 16:51:15 +02:00
|
|
|
if a.DroppedAttributeCount != 0 {
|
|
|
|
nTags++
|
|
|
|
}
|
2021-04-02 16:15:20 +02:00
|
|
|
fields := make([]*gen.Tag, 0, nTags)
|
|
|
|
if a.Name != "" {
|
|
|
|
// If an event contains an attribute with the same key, it needs
|
|
|
|
// to be given precedence and overwrite this.
|
|
|
|
fields = append(fields, getStringTag(keyEventName, a.Name))
|
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
for _, kv := range a.Attributes {
|
2019-10-17 18:59:30 +02:00
|
|
|
tag := keyValueToTag(kv)
|
2019-09-09 23:59:39 +02:00
|
|
|
if tag != nil {
|
|
|
|
fields = append(fields, tag)
|
|
|
|
}
|
|
|
|
}
|
2021-04-06 16:51:15 +02:00
|
|
|
if a.DroppedAttributeCount != 0 {
|
|
|
|
fields = append(fields, getInt64Tag(keyDroppedAttributeCount, int64(a.DroppedAttributeCount)))
|
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
logs = append(logs, &gen.Log{
|
2019-09-25 22:22:33 +02:00
|
|
|
Timestamp: a.Time.UnixNano() / 1000,
|
2019-09-09 23:59:39 +02:00
|
|
|
Fields: fields,
|
|
|
|
})
|
|
|
|
}
|
2019-10-07 18:40:36 +02:00
|
|
|
|
|
|
|
var refs []*gen.SpanRef
|
2020-12-11 07:15:44 +02:00
|
|
|
for _, link := range ss.Links {
|
2021-03-09 18:17:29 +02:00
|
|
|
tid := link.TraceID()
|
|
|
|
sid := link.SpanID()
|
2019-10-07 18:40:36 +02:00
|
|
|
refs = append(refs, &gen.SpanRef{
|
2021-03-09 18:17:29 +02:00
|
|
|
TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])),
|
|
|
|
TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])),
|
|
|
|
SpanId: int64(binary.BigEndian.Uint64(sid[:])),
|
2021-03-08 19:32:02 +02:00
|
|
|
RefType: gen.SpanRefType_FOLLOWS_FROM,
|
2019-10-07 18:40:36 +02:00
|
|
|
})
|
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
|
2021-03-09 18:17:29 +02:00
|
|
|
tid := ss.SpanContext.TraceID()
|
|
|
|
sid := ss.SpanContext.SpanID()
|
2021-03-30 17:59:54 +02:00
|
|
|
psid := ss.Parent.SpanID()
|
2019-09-09 23:59:39 +02:00
|
|
|
return &gen.Span{
|
2021-03-09 18:17:29 +02:00
|
|
|
TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])),
|
|
|
|
TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])),
|
|
|
|
SpanId: int64(binary.BigEndian.Uint64(sid[:])),
|
2021-03-30 17:59:54 +02:00
|
|
|
ParentSpanId: int64(binary.BigEndian.Uint64(psid[:])),
|
2020-12-11 07:15:44 +02:00
|
|
|
OperationName: ss.Name, // TODO: if span kind is added then add prefix "Sent"/"Recv"
|
2021-03-09 18:17:29 +02:00
|
|
|
Flags: int32(ss.SpanContext.TraceFlags()),
|
2020-12-11 07:15:44 +02:00
|
|
|
StartTime: ss.StartTime.UnixNano() / 1000,
|
|
|
|
Duration: ss.EndTime.Sub(ss.StartTime).Nanoseconds() / 1000,
|
2019-09-09 23:59:39 +02:00
|
|
|
Tags: tags,
|
|
|
|
Logs: logs,
|
2019-10-07 18:40:36 +02:00
|
|
|
References: refs,
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 19:59:37 +02:00
|
|
|
func keyValueToTag(keyValue attribute.KeyValue) *gen.Tag {
|
2019-09-25 22:22:33 +02:00
|
|
|
var tag *gen.Tag
|
2020-05-14 01:06:03 +02:00
|
|
|
switch keyValue.Value.Type() {
|
2021-02-18 19:59:37 +02:00
|
|
|
case attribute.STRING:
|
2020-05-14 01:06:03 +02:00
|
|
|
s := keyValue.Value.AsString()
|
2019-09-25 22:22:33 +02:00
|
|
|
tag = &gen.Tag{
|
2020-05-14 01:06:03 +02:00
|
|
|
Key: string(keyValue.Key),
|
2019-10-31 00:01:19 +02:00
|
|
|
VStr: &s,
|
2019-09-25 22:22:33 +02:00
|
|
|
VType: gen.TagType_STRING,
|
|
|
|
}
|
2021-02-18 19:59:37 +02:00
|
|
|
case attribute.BOOL:
|
2020-05-14 01:06:03 +02:00
|
|
|
b := keyValue.Value.AsBool()
|
2019-09-25 22:22:33 +02:00
|
|
|
tag = &gen.Tag{
|
2020-05-14 01:06:03 +02:00
|
|
|
Key: string(keyValue.Key),
|
2019-10-31 00:01:19 +02:00
|
|
|
VBool: &b,
|
2019-09-25 22:22:33 +02:00
|
|
|
VType: gen.TagType_BOOL,
|
|
|
|
}
|
2021-02-18 19:59:37 +02:00
|
|
|
case attribute.INT64:
|
2020-05-14 01:06:03 +02:00
|
|
|
i := keyValue.Value.AsInt64()
|
2019-10-31 00:01:19 +02:00
|
|
|
tag = &gen.Tag{
|
2020-05-14 01:06:03 +02:00
|
|
|
Key: string(keyValue.Key),
|
2019-10-31 00:01:19 +02:00
|
|
|
VLong: &i,
|
|
|
|
VType: gen.TagType_LONG,
|
|
|
|
}
|
2021-02-18 19:59:37 +02:00
|
|
|
case attribute.FLOAT64:
|
2020-05-14 01:06:03 +02:00
|
|
|
f := keyValue.Value.AsFloat64()
|
2019-09-25 22:22:33 +02:00
|
|
|
tag = &gen.Tag{
|
2020-05-14 01:06:03 +02:00
|
|
|
Key: string(keyValue.Key),
|
2019-10-31 00:01:19 +02:00
|
|
|
VDouble: &f,
|
2019-09-25 22:22:33 +02:00
|
|
|
VType: gen.TagType_DOUBLE,
|
|
|
|
}
|
2021-03-08 19:32:02 +02:00
|
|
|
case attribute.ARRAY:
|
|
|
|
json, _ := json.Marshal(keyValue.Value.AsArray())
|
|
|
|
a := (string)(json)
|
|
|
|
tag = &gen.Tag{
|
|
|
|
Key: string(keyValue.Key),
|
|
|
|
VStr: &a,
|
|
|
|
VType: gen.TagType_STRING,
|
|
|
|
}
|
2019-09-25 22:22:33 +02:00
|
|
|
}
|
|
|
|
return tag
|
|
|
|
}
|
|
|
|
|
|
|
|
func getInt64Tag(k string, i int64) *gen.Tag {
|
|
|
|
return &gen.Tag{
|
|
|
|
Key: k,
|
|
|
|
VLong: &i,
|
|
|
|
VType: gen.TagType_LONG,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getStringTag(k, s string) *gen.Tag {
|
|
|
|
return &gen.Tag{
|
|
|
|
Key: k,
|
|
|
|
VStr: &s,
|
|
|
|
VType: gen.TagType_STRING,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBoolTag(k string, b bool) *gen.Tag {
|
|
|
|
return &gen.Tag{
|
|
|
|
Key: k,
|
|
|
|
VBool: &b,
|
|
|
|
VType: gen.TagType_BOOL,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-09 23:59:39 +02:00
|
|
|
// 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() {
|
2021-04-17 21:24:11 +02:00
|
|
|
// Return fast if Exporter shutdown.
|
|
|
|
select {
|
|
|
|
case <-e.stopCh:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
2020-09-09 19:19:03 +02:00
|
|
|
flush(e)
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
2021-04-07 17:03:43 +02:00
|
|
|
func (e *Exporter) upload(spans []*sdktrace.SpanSnapshot) error {
|
2021-04-07 00:03:32 +02:00
|
|
|
batchList := jaegerBatchList(spans, e.defaultServiceName)
|
2021-03-16 18:04:46 +02:00
|
|
|
for _, batch := range batchList {
|
2021-04-19 16:27:22 +02:00
|
|
|
// TODO (MrAlias): pass an appropriate context (#1799, #1803).
|
|
|
|
err := e.uploader.upload(context.TODO(), batch)
|
2021-03-16 18:04:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// jaegerBatchList transforms a slice of SpanSnapshot into a slice of jaeger
|
|
|
|
// Batch.
|
2021-04-07 17:03:43 +02:00
|
|
|
func jaegerBatchList(ssl []*sdktrace.SpanSnapshot, defaultServiceName string) []*gen.Batch {
|
2021-03-16 18:04:46 +02:00
|
|
|
if len(ssl) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
batchDict := make(map[attribute.Distinct]*gen.Batch)
|
|
|
|
|
|
|
|
for _, ss := range ssl {
|
|
|
|
if ss == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-04-07 00:03:32 +02:00
|
|
|
resourceKey := ss.Resource.Equivalent()
|
2021-03-16 18:04:46 +02:00
|
|
|
batch, bOK := batchDict[resourceKey]
|
|
|
|
if !bOK {
|
|
|
|
batch = &gen.Batch{
|
2021-04-07 00:03:32 +02:00
|
|
|
Process: process(ss.Resource, defaultServiceName),
|
2021-03-16 18:04:46 +02:00
|
|
|
Spans: []*gen.Span{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
batch.Spans = append(batch.Spans, spanSnapshotToThrift(ss))
|
|
|
|
batchDict[resourceKey] = batch
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transform the categorized map into a slice
|
|
|
|
batchList := make([]*gen.Batch, 0, len(batchDict))
|
|
|
|
for _, batch := range batchDict {
|
|
|
|
batchList = append(batchList, batch)
|
|
|
|
}
|
|
|
|
return batchList
|
|
|
|
}
|
|
|
|
|
|
|
|
// process transforms an OTel Resource into a jaeger Process.
|
|
|
|
func process(res *resource.Resource, defaultServiceName string) *gen.Process {
|
|
|
|
var process gen.Process
|
|
|
|
|
|
|
|
var serviceName attribute.KeyValue
|
|
|
|
if res != nil {
|
|
|
|
for iter := res.Iter(); iter.Next(); {
|
|
|
|
if iter.Attribute().Key == semconv.ServiceNameKey {
|
|
|
|
serviceName = iter.Attribute()
|
|
|
|
// Don't convert service.name into tag.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if tag := keyValueToTag(iter.Attribute()); tag != nil {
|
|
|
|
process.Tags = append(process.Tags, tag)
|
|
|
|
}
|
|
|
|
}
|
2019-09-09 23:59:39 +02:00
|
|
|
}
|
|
|
|
|
2021-03-16 18:04:46 +02:00
|
|
|
// If no service.name is contained in a Span's Resource,
|
|
|
|
// that field MUST be populated from the default Resource.
|
|
|
|
if serviceName.Value.AsString() == "" {
|
2021-04-12 17:49:55 +02:00
|
|
|
serviceName = semconv.ServiceNameKey.String(defaultServiceName)
|
2021-03-16 18:04:46 +02:00
|
|
|
}
|
|
|
|
process.ServiceName = serviceName.Value.AsString()
|
|
|
|
|
|
|
|
return &process
|
|
|
|
}
|