1// Copyright (c) 2017 Uber Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package jaeger
16
17import (
18	"time"
19
20	"github.com/opentracing/opentracing-go"
21
22	j "github.com/uber/jaeger-client-go/thrift-gen/jaeger"
23	"github.com/uber/jaeger-client-go/utils"
24)
25
26// BuildJaegerThrift builds jaeger span based on internal span.
27func BuildJaegerThrift(span *Span) *j.Span {
28	span.Lock()
29	defer span.Unlock()
30	startTime := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime)
31	duration := span.duration.Nanoseconds() / int64(time.Microsecond)
32	jaegerSpan := &j.Span{
33		TraceIdLow:    int64(span.context.traceID.Low),
34		TraceIdHigh:   int64(span.context.traceID.High),
35		SpanId:        int64(span.context.spanID),
36		ParentSpanId:  int64(span.context.parentID),
37		OperationName: span.operationName,
38		Flags:         int32(span.context.samplingState.flags()),
39		StartTime:     startTime,
40		Duration:      duration,
41		Tags:          buildTags(span.tags, span.tracer.options.maxTagValueLength),
42		Logs:          buildLogs(span.logs),
43		References:    buildReferences(span.references),
44	}
45	return jaegerSpan
46}
47
48// BuildJaegerProcessThrift creates a thrift Process type.
49func BuildJaegerProcessThrift(span *Span) *j.Process {
50	span.Lock()
51	defer span.Unlock()
52	return buildJaegerProcessThrift(span.tracer)
53}
54
55func buildJaegerProcessThrift(tracer *Tracer) *j.Process {
56	process := &j.Process{
57		ServiceName: tracer.serviceName,
58		Tags:        buildTags(tracer.tags, tracer.options.maxTagValueLength),
59	}
60	if tracer.process.UUID != "" {
61		process.Tags = append(process.Tags, &j.Tag{Key: TracerUUIDTagKey, VStr: &tracer.process.UUID, VType: j.TagType_STRING})
62	}
63	return process
64}
65
66func buildTags(tags []Tag, maxTagValueLength int) []*j.Tag {
67	jTags := make([]*j.Tag, 0, len(tags))
68	for _, tag := range tags {
69		jTag := buildTag(&tag, maxTagValueLength)
70		jTags = append(jTags, jTag)
71	}
72	return jTags
73}
74
75func buildLogs(logs []opentracing.LogRecord) []*j.Log {
76	jLogs := make([]*j.Log, 0, len(logs))
77	for _, log := range logs {
78		jLog := &j.Log{
79			Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp),
80			Fields:    ConvertLogsToJaegerTags(log.Fields),
81		}
82		jLogs = append(jLogs, jLog)
83	}
84	return jLogs
85}
86
87func buildTag(tag *Tag, maxTagValueLength int) *j.Tag {
88	jTag := &j.Tag{Key: tag.key}
89	switch value := tag.value.(type) {
90	case string:
91		vStr := truncateString(value, maxTagValueLength)
92		jTag.VStr = &vStr
93		jTag.VType = j.TagType_STRING
94	case []byte:
95		if len(value) > maxTagValueLength {
96			value = value[:maxTagValueLength]
97		}
98		jTag.VBinary = value
99		jTag.VType = j.TagType_BINARY
100	case int:
101		vLong := int64(value)
102		jTag.VLong = &vLong
103		jTag.VType = j.TagType_LONG
104	case uint:
105		vLong := int64(value)
106		jTag.VLong = &vLong
107		jTag.VType = j.TagType_LONG
108	case int8:
109		vLong := int64(value)
110		jTag.VLong = &vLong
111		jTag.VType = j.TagType_LONG
112	case uint8:
113		vLong := int64(value)
114		jTag.VLong = &vLong
115		jTag.VType = j.TagType_LONG
116	case int16:
117		vLong := int64(value)
118		jTag.VLong = &vLong
119		jTag.VType = j.TagType_LONG
120	case uint16:
121		vLong := int64(value)
122		jTag.VLong = &vLong
123		jTag.VType = j.TagType_LONG
124	case int32:
125		vLong := int64(value)
126		jTag.VLong = &vLong
127		jTag.VType = j.TagType_LONG
128	case uint32:
129		vLong := int64(value)
130		jTag.VLong = &vLong
131		jTag.VType = j.TagType_LONG
132	case int64:
133		vLong := int64(value)
134		jTag.VLong = &vLong
135		jTag.VType = j.TagType_LONG
136	case uint64:
137		vLong := int64(value)
138		jTag.VLong = &vLong
139		jTag.VType = j.TagType_LONG
140	case float32:
141		vDouble := float64(value)
142		jTag.VDouble = &vDouble
143		jTag.VType = j.TagType_DOUBLE
144	case float64:
145		vDouble := float64(value)
146		jTag.VDouble = &vDouble
147		jTag.VType = j.TagType_DOUBLE
148	case bool:
149		vBool := value
150		jTag.VBool = &vBool
151		jTag.VType = j.TagType_BOOL
152	default:
153		vStr := truncateString(stringify(value), maxTagValueLength)
154		jTag.VStr = &vStr
155		jTag.VType = j.TagType_STRING
156	}
157	return jTag
158}
159
160func buildReferences(references []Reference) []*j.SpanRef {
161	retMe := make([]*j.SpanRef, 0, len(references))
162	for _, ref := range references {
163		if ref.Type == opentracing.ChildOfRef {
164			retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_CHILD_OF))
165		} else if ref.Type == opentracing.FollowsFromRef {
166			retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_FOLLOWS_FROM))
167		}
168	}
169	return retMe
170}
171
172func spanRef(ctx SpanContext, refType j.SpanRefType) *j.SpanRef {
173	return &j.SpanRef{
174		RefType:     refType,
175		TraceIdLow:  int64(ctx.traceID.Low),
176		TraceIdHigh: int64(ctx.traceID.High),
177		SpanId:      int64(ctx.spanID),
178	}
179}
180