249 lines
6.8 KiB
Go
249 lines
6.8 KiB
Go
// 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 ocagent
|
|
|
|
import (
|
|
"math"
|
|
"time"
|
|
|
|
"go.opencensus.io/trace"
|
|
"go.opencensus.io/trace/tracestate"
|
|
|
|
tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
|
|
"github.com/golang/protobuf/ptypes/timestamp"
|
|
)
|
|
|
|
const (
|
|
maxAnnotationEventsPerSpan = 32
|
|
maxMessageEventsPerSpan = 128
|
|
)
|
|
|
|
func ocSpanToProtoSpan(sd *trace.SpanData) *tracepb.Span {
|
|
if sd == nil {
|
|
return nil
|
|
}
|
|
var namePtr *tracepb.TruncatableString
|
|
if sd.Name != "" {
|
|
namePtr = &tracepb.TruncatableString{Value: sd.Name}
|
|
}
|
|
return &tracepb.Span{
|
|
TraceId: sd.TraceID[:],
|
|
SpanId: sd.SpanID[:],
|
|
ParentSpanId: sd.ParentSpanID[:],
|
|
Status: ocStatusToProtoStatus(sd.Status),
|
|
StartTime: timeToTimestamp(sd.StartTime),
|
|
EndTime: timeToTimestamp(sd.EndTime),
|
|
Links: ocLinksToProtoLinks(sd.Links),
|
|
Kind: ocSpanKindToProtoSpanKind(sd.SpanKind),
|
|
Name: namePtr,
|
|
Attributes: ocAttributesToProtoAttributes(sd.Attributes),
|
|
TimeEvents: ocTimeEventsToProtoTimeEvents(sd.Annotations, sd.MessageEvents),
|
|
Tracestate: ocTracestateToProtoTracestate(sd.Tracestate),
|
|
}
|
|
}
|
|
|
|
var blankStatus trace.Status
|
|
|
|
func ocStatusToProtoStatus(status trace.Status) *tracepb.Status {
|
|
if status == blankStatus {
|
|
return nil
|
|
}
|
|
return &tracepb.Status{
|
|
Code: status.Code,
|
|
Message: status.Message,
|
|
}
|
|
}
|
|
|
|
func ocLinksToProtoLinks(links []trace.Link) *tracepb.Span_Links {
|
|
if len(links) == 0 {
|
|
return nil
|
|
}
|
|
|
|
sl := make([]*tracepb.Span_Link, 0, len(links))
|
|
for _, ocLink := range links {
|
|
// This redefinition is necessary to prevent ocLink.*ID[:] copies
|
|
// being reused -- in short we need a new ocLink per iteration.
|
|
ocLink := ocLink
|
|
|
|
sl = append(sl, &tracepb.Span_Link{
|
|
TraceId: ocLink.TraceID[:],
|
|
SpanId: ocLink.SpanID[:],
|
|
Type: ocLinkTypeToProtoLinkType(ocLink.Type),
|
|
})
|
|
}
|
|
|
|
return &tracepb.Span_Links{
|
|
Link: sl,
|
|
}
|
|
}
|
|
|
|
func ocLinkTypeToProtoLinkType(oct trace.LinkType) tracepb.Span_Link_Type {
|
|
switch oct {
|
|
case trace.LinkTypeChild:
|
|
return tracepb.Span_Link_CHILD_LINKED_SPAN
|
|
case trace.LinkTypeParent:
|
|
return tracepb.Span_Link_PARENT_LINKED_SPAN
|
|
default:
|
|
return tracepb.Span_Link_TYPE_UNSPECIFIED
|
|
}
|
|
}
|
|
|
|
func ocAttributesToProtoAttributes(attrs map[string]interface{}) *tracepb.Span_Attributes {
|
|
if len(attrs) == 0 {
|
|
return nil
|
|
}
|
|
outMap := make(map[string]*tracepb.AttributeValue)
|
|
for k, v := range attrs {
|
|
switch v := v.(type) {
|
|
case bool:
|
|
outMap[k] = &tracepb.AttributeValue{Value: &tracepb.AttributeValue_BoolValue{BoolValue: v}}
|
|
|
|
case int:
|
|
outMap[k] = &tracepb.AttributeValue{Value: &tracepb.AttributeValue_IntValue{IntValue: int64(v)}}
|
|
|
|
case int64:
|
|
outMap[k] = &tracepb.AttributeValue{Value: &tracepb.AttributeValue_IntValue{IntValue: v}}
|
|
|
|
case string:
|
|
outMap[k] = &tracepb.AttributeValue{
|
|
Value: &tracepb.AttributeValue_StringValue{
|
|
StringValue: &tracepb.TruncatableString{Value: v},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
return &tracepb.Span_Attributes{
|
|
AttributeMap: outMap,
|
|
}
|
|
}
|
|
|
|
// This code is mostly copied from
|
|
// https://github.com/census-ecosystem/opencensus-go-exporter-stackdriver/blob/master/trace_proto.go#L46
|
|
func ocTimeEventsToProtoTimeEvents(as []trace.Annotation, es []trace.MessageEvent) *tracepb.Span_TimeEvents {
|
|
if len(as) == 0 && len(es) == 0 {
|
|
return nil
|
|
}
|
|
|
|
timeEvents := &tracepb.Span_TimeEvents{}
|
|
var annotations, droppedAnnotationsCount int
|
|
var messageEvents, droppedMessageEventsCount int
|
|
|
|
// Transform annotations
|
|
for i, a := range as {
|
|
if annotations >= maxAnnotationEventsPerSpan {
|
|
droppedAnnotationsCount = len(as) - i
|
|
break
|
|
}
|
|
annotations++
|
|
timeEvents.TimeEvent = append(timeEvents.TimeEvent,
|
|
&tracepb.Span_TimeEvent{
|
|
Time: timeToTimestamp(a.Time),
|
|
Value: transformAnnotationToTimeEvent(&a),
|
|
},
|
|
)
|
|
}
|
|
|
|
// Transform message events
|
|
for i, e := range es {
|
|
if messageEvents >= maxMessageEventsPerSpan {
|
|
droppedMessageEventsCount = len(es) - i
|
|
break
|
|
}
|
|
messageEvents++
|
|
timeEvents.TimeEvent = append(timeEvents.TimeEvent,
|
|
&tracepb.Span_TimeEvent{
|
|
Time: timeToTimestamp(e.Time),
|
|
Value: transformMessageEventToTimeEvent(&e),
|
|
},
|
|
)
|
|
}
|
|
|
|
// Process dropped counter
|
|
timeEvents.DroppedAnnotationsCount = clip32(droppedAnnotationsCount)
|
|
timeEvents.DroppedMessageEventsCount = clip32(droppedMessageEventsCount)
|
|
|
|
return timeEvents
|
|
}
|
|
|
|
func transformAnnotationToTimeEvent(a *trace.Annotation) *tracepb.Span_TimeEvent_Annotation_ {
|
|
return &tracepb.Span_TimeEvent_Annotation_{
|
|
Annotation: &tracepb.Span_TimeEvent_Annotation{
|
|
Description: &tracepb.TruncatableString{Value: a.Message},
|
|
Attributes: ocAttributesToProtoAttributes(a.Attributes),
|
|
},
|
|
}
|
|
}
|
|
|
|
func transformMessageEventToTimeEvent(e *trace.MessageEvent) *tracepb.Span_TimeEvent_MessageEvent_ {
|
|
return &tracepb.Span_TimeEvent_MessageEvent_{
|
|
MessageEvent: &tracepb.Span_TimeEvent_MessageEvent{
|
|
Type: tracepb.Span_TimeEvent_MessageEvent_Type(e.EventType),
|
|
Id: uint64(e.MessageID),
|
|
UncompressedSize: uint64(e.UncompressedByteSize),
|
|
CompressedSize: uint64(e.CompressedByteSize),
|
|
},
|
|
}
|
|
}
|
|
|
|
// clip32 clips an int to the range of an int32.
|
|
func clip32(x int) int32 {
|
|
if x < math.MinInt32 {
|
|
return math.MinInt32
|
|
}
|
|
if x > math.MaxInt32 {
|
|
return math.MaxInt32
|
|
}
|
|
return int32(x)
|
|
}
|
|
|
|
func timeToTimestamp(t time.Time) *timestamp.Timestamp {
|
|
nanoTime := t.UnixNano()
|
|
return ×tamp.Timestamp{
|
|
Seconds: nanoTime / 1e9,
|
|
Nanos: int32(nanoTime % 1e9),
|
|
}
|
|
}
|
|
|
|
func ocSpanKindToProtoSpanKind(kind int) tracepb.Span_SpanKind {
|
|
switch kind {
|
|
case trace.SpanKindClient:
|
|
return tracepb.Span_CLIENT
|
|
case trace.SpanKindServer:
|
|
return tracepb.Span_SERVER
|
|
default:
|
|
return tracepb.Span_SPAN_KIND_UNSPECIFIED
|
|
}
|
|
}
|
|
|
|
func ocTracestateToProtoTracestate(ts *tracestate.Tracestate) *tracepb.Span_Tracestate {
|
|
if ts == nil {
|
|
return nil
|
|
}
|
|
return &tracepb.Span_Tracestate{
|
|
Entries: ocTracestateEntriesToProtoTracestateEntries(ts.Entries()),
|
|
}
|
|
}
|
|
|
|
func ocTracestateEntriesToProtoTracestateEntries(entries []tracestate.Entry) []*tracepb.Span_Tracestate_Entry {
|
|
protoEntries := make([]*tracepb.Span_Tracestate_Entry, 0, len(entries))
|
|
for _, entry := range entries {
|
|
protoEntries = append(protoEntries, &tracepb.Span_Tracestate_Entry{
|
|
Key: entry.Key,
|
|
Value: entry.Value,
|
|
})
|
|
}
|
|
return protoEntries
|
|
}
|