in exporter/trace/trace_proto.go [128:225]
func (e *traceExporter) protoFromReadOnlySpan(s sdktrace.ReadOnlySpan) (*tracepb.Span, string) {
if s == nil {
return nil, ""
}
traceIDString := s.SpanContext().TraceID().String()
spanIDString := s.SpanContext().SpanID().String()
projectID := e.projectID
// override project ID with gcp.project.id, if present
attrs := s.Resource().Attributes()
for _, attr := range attrs {
if attr.Key == resourcemapping.ProjectIDAttributeKey {
projectID = attr.Value.AsString()
break
}
}
sp := &tracepb.Span{
Name: "projects/" + projectID + "/traces/" + traceIDString + "/spans/" + spanIDString,
SpanId: spanIDString,
DisplayName: trunc(s.Name(), 128),
StartTime: timestampProto(s.StartTime()),
EndTime: timestampProto(s.EndTime()),
SameProcessAsParentSpan: &wrapperspb.BoolValue{Value: !s.Parent().IsRemote()},
SpanKind: convertSpanKind(s.SpanKind()),
}
if s.Parent().SpanID() != s.SpanContext().SpanID() && s.Parent().SpanID().IsValid() {
sp.ParentSpanId = s.Parent().SpanID().String()
}
switch s.Status().Code {
case codes.Ok:
sp.Status = &statuspb.Status{Code: int32(codepb.Code_OK)}
case codes.Unset:
// Don't set status code.
case codes.Error:
sp.Status = &statuspb.Status{Code: int32(codepb.Code_UNKNOWN), Message: s.Status().Description}
default:
sp.Status = &statuspb.Status{Code: int32(codepb.Code_UNKNOWN)}
}
attributes := attributeWithLabelsFromResources(s)
e.copyAttributes(&sp.Attributes, attributes)
// NOTE(ymotongpoo): omitting copyMonitoringReesourceAttributes()
var annotations, droppedAnnotationsCount int
es := s.Events()
for i, ev := range es {
if annotations >= maxAnnotationEventsPerSpan {
droppedAnnotationsCount = len(es) - i
break
}
annotation := &tracepb.Span_TimeEvent_Annotation{Description: trunc(ev.Name, maxAttributeStringValue)}
e.copyAttributes(&annotation.Attributes, ev.Attributes)
event := &tracepb.Span_TimeEvent{
Time: timestampProto(ev.Time),
Value: &tracepb.Span_TimeEvent_Annotation_{Annotation: annotation},
}
annotations++
if sp.TimeEvents == nil {
sp.TimeEvents = &tracepb.Span_TimeEvents{}
}
sp.TimeEvents.TimeEvent = append(sp.TimeEvents.TimeEvent, event)
}
if sp.Attributes == nil {
sp.Attributes = &tracepb.Span_Attributes{
AttributeMap: make(map[string]*tracepb.AttributeValue),
}
}
// Only set the agent label if it is not already set. That enables the
// OpenTelemery service/collector to set the agent label based on the library that
// sent the span to the service.
// TODO(jsuereth): This scenario is highly unlikely. This would require vanilla OTLP
// sources of tracess to be setting "g.co/agent" labels on spans. We should confirm
// and remove/update this code.
if _, hasAgent := sp.Attributes.AttributeMap[agentLabel]; !hasAgent {
sp.Attributes.AttributeMap[agentLabel] = &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_StringValue{
StringValue: trunc(userAgent, maxAttributeStringValue),
},
}
}
// TODO(ymotongpoo): add implementations for Span_TimeEvent_MessageEvent_
// once OTel finish implementations for gRPC.
if droppedAnnotationsCount != 0 {
if sp.TimeEvents == nil {
sp.TimeEvents = &tracepb.Span_TimeEvents{}
}
sp.TimeEvents.DroppedAnnotationsCount = clip32(droppedAnnotationsCount)
}
sp.Links = e.linksProtoFromLinks(s.Links())
return sp, projectID
}