diff --git a/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data.json b/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data.json new file mode 100644 index 000000000000..e8c74b364396 --- /dev/null +++ b/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data.json @@ -0,0 +1,314 @@ +[ + [ + { + "Key": "host", + "Value": "xxx.et15" + }, + { + "Key": "service", + "Value": "signup_aggregator" + }, + { + "Key": "resource", + "Value": "{\"cloud.account.id\":\"999999998\",\"cloud.provider\":\"aws\",\"cloud.region\":\"us-west-2\",\"cloud.zone\":\"us-west-1b\",\"container.image.name\":\"otel/signupaggregator\",\"container.image.tag\":\"v1\",\"container.name\":\"signup_aggregator\"}" + }, + { + "Key": "otlp.name", + "Value": "golang-sls-exporter" + }, + { + "Key": "otlp.version", + "Value": "v0.1.0" + }, + { + "Key": "traceID", + "Value": "000000000000000052969a8955571a3f" + }, + { + "Key": "spanID", + "Value": "0000000000647d98" + }, + { + "Key": "parentSpanID", + "Value": "0000000000647d98" + }, + { + "Key": "kind", + "Value": "client" + }, + { + "Key": "name", + "Value": "/users/junit" + }, + { + "Key": "links", + "Value": "[{\"attribute\":{\"link\":\"true\"},\"spanID\":\"\",\"traceID\":\"\"}]" + }, + { + "Key": "logs", + "Value": "[{\"attribute\":{\"key\":\"value\"},\"name\":\"event\",\"time\":1024}]" + }, + { + "Key": "traceState", + "Value": "x:y" + }, + { + "Key": "start", + "Value": "12210123456" + }, + { + "Key": "end", + "Value": "12300123456" + }, + { + "Key": "duration", + "Value": "90000000" + }, + { + "Key": "attribute", + "Value": "{\"component\":\"http\",\"http.method\":\"GET\",\"http.status_code\":200,\"http.url\":\"https://api.example.com/users/junit\"}" + }, + { + "Key": "statusCode", + "Value": "OK" + }, + { + "Key": "statusMessage", + "Value": "OK" + } + ], + [ + { + "Key": "host", + "Value": "xxx.et15" + }, + { + "Key": "service", + "Value": "signup_aggregator" + }, + { + "Key": "resource", + "Value": "{\"cloud.account.id\":\"999999998\",\"cloud.provider\":\"aws\",\"cloud.region\":\"us-west-2\",\"cloud.zone\":\"us-west-1b\",\"container.image.name\":\"otel/signupaggregator\",\"container.image.tag\":\"v1\",\"container.name\":\"signup_aggregator\"}" + }, + { + "Key": "otlp.name", + "Value": "golang-sls-exporter" + }, + { + "Key": "otlp.version", + "Value": "v0.1.0" + }, + { + "Key": "traceID", + "Value": "000000000000000052969a8955571a3f" + }, + { + "Key": "spanID", + "Value": "0000000000647d98" + }, + { + "Key": "parentSpanID", + "Value": "0000000000647d98" + }, + { + "Key": "kind", + "Value": "server" + }, + { + "Key": "name", + "Value": "/users/junit" + }, + { + "Key": "links", + "Value": "[]" + }, + { + "Key": "logs", + "Value": "[]" + }, + { + "Key": "traceState", + "Value": "" + }, + { + "Key": "start", + "Value": "12210123456" + }, + { + "Key": "end", + "Value": "12300123456" + }, + { + "Key": "duration", + "Value": "90000000" + }, + { + "Key": "attribute", + "Value": "{\"component\":\"http\",\"http.client_ip\":\"192.168.15.32\",\"http.method\":\"GET\",\"http.status_code\":200,\"http.url\":\"https://api.example.com/users/junit\"}" + }, + { + "Key": "statusCode", + "Value": "ERROR" + }, + { + "Key": "statusMessage", + "Value": "something error" + } + ], + [ + { + "Key": "host", + "Value": "xxx.et15" + }, + { + "Key": "service", + "Value": "signup_aggregator" + }, + { + "Key": "resource", + "Value": "{\"cloud.account.id\":\"999999998\",\"cloud.provider\":\"aws\",\"cloud.region\":\"us-west-2\",\"cloud.zone\":\"us-west-1b\",\"container.image.name\":\"otel/signupaggregator\",\"container.image.tag\":\"v1\",\"container.name\":\"signup_aggregator\"}" + }, + { + "Key": "otlp.name", + "Value": "golang-sls-exporter" + }, + { + "Key": "otlp.version", + "Value": "v0.1.0" + }, + { + "Key": "traceID", + "Value": "" + }, + { + "Key": "spanID", + "Value": "" + }, + { + "Key": "parentSpanID", + "Value": "" + }, + { + "Key": "kind", + "Value": "" + }, + { + "Key": "name", + "Value": "" + }, + { + "Key": "links", + "Value": "[]" + }, + { + "Key": "logs", + "Value": "[]" + }, + { + "Key": "traceState", + "Value": "" + }, + { + "Key": "start", + "Value": "0" + }, + { + "Key": "end", + "Value": "0" + }, + { + "Key": "duration", + "Value": "0" + }, + { + "Key": "attribute", + "Value": "{}" + }, + { + "Key": "statusCode", + "Value": "UNSET" + }, + { + "Key": "statusMessage", + "Value": "status is nil" + } + ], + [ + { + "Key": "host", + "Value": "xxx.et15" + }, + { + "Key": "service", + "Value": "signup_aggregator" + }, + { + "Key": "resource", + "Value": "{\"cloud.account.id\":\"999999998\",\"cloud.provider\":\"aws\",\"cloud.region\":\"us-west-2\",\"cloud.zone\":\"us-west-1b\",\"container.image.name\":\"otel/signupaggregator\",\"container.image.tag\":\"v1\",\"container.name\":\"signup_aggregator\"}" + }, + { + "Key": "otlp.name", + "Value": "golang-sls-exporter" + }, + { + "Key": "otlp.version", + "Value": "v0.1.0" + }, + { + "Key": "traceID", + "Value": "" + }, + { + "Key": "spanID", + "Value": "" + }, + { + "Key": "parentSpanID", + "Value": "" + }, + { + "Key": "kind", + "Value": "" + }, + { + "Key": "name", + "Value": "" + }, + { + "Key": "links", + "Value": "[]" + }, + { + "Key": "logs", + "Value": "[]" + }, + { + "Key": "traceState", + "Value": "" + }, + { + "Key": "start", + "Value": "0" + }, + { + "Key": "end", + "Value": "0" + }, + { + "Key": "duration", + "Value": "0" + }, + { + "Key": "attribute", + "Value": "{}" + }, + { + "Key": "statusCode", + "Value": "UNSET" + }, + { + "Key": "statusMessage", + "Value": "status is nil" + } + ] +] \ No newline at end of file diff --git a/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data_no_binary_tags_01.json b/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data_no_binary_tags_01.json deleted file mode 100644 index c96c69de301a..000000000000 --- a/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data_no_binary_tags_01.json +++ /dev/null @@ -1,116 +0,0 @@ -[ - [ - { - "Key": "traceID", - "Value": "000000000000000052969a8955571a3f" - }, - { - "Key": "spanID", - "Value": "0000000000647d98" - }, - { - "Key": "parentSpanID", - "Value": "000000000068c4e3" - }, - { - "Key": "startTime", - "Value": "1485467191639875" - }, - { - "Key": "operationName", - "Value": "get" - }, - { - "Key": "duration", - "Value": "22938" - }, - { - "Key": "tags.http.url", - "Value": "http://localhost:15598/client_transactions" - }, - { - "Key": "tags.peer.ipv4", - "Value": "3224716605" - }, - { - "Key": "tags.peer.port", - "Value": "53931" - }, - { - "Key": "tags.peer.service", - "Value": "rtapi" - }, - { - "Key": "tags.someBool", - "Value": "true" - }, - { - "Key": "tags.someDouble", - "Value": "129.8" - }, - { - "Key": "tags.span.kind", - "Value": "client" - }, - { - "Key": "logs", - "Value": "[{\"TimeUs\":1485467191639874,\"Fields\":{\"message.compressed_size\":\"512\",\"message.id\":\"0\",\"message.type\":\"SENT\",\"message.uncompressed_size\":\"1024\"}},{\"TimeUs\":1485467191639875,\"Fields\":{\"key1\":\"value1\"}},{\"TimeUs\":1485467191639875,\"Fields\":{\"description\":\"annotation description\",\"event\":\"nothing\"}}]" - }, - { - "Key": "tags.status.code", - "Value": "0" - }, - { - "Key": "tags.status.message", - "Value": "" - }, - { - "Key": "process.tags.a.double", - "Value": "1234.56789" - }, - { - "Key": "process.tags.a.long", - "Value": "123456789" - }, - { - "Key": "process.tags.ip", - "Value": "10.53.69.61" - }, - { - "Key": "process.tags.a.binary", - "Value": "AQIDBAMCAQ==" - }, - { - "Key": "process.tags.a.bool", - "Value": "true" - }, - { - "Key": "process.tags.hostname", - "Value": "api246-sjc1" - }, - { - "Key": "process.tags.pid", - "Value": "13" - }, - { - "Key": "process.tags.start.time", - "Value": "2017-01-26T21:46:30.639875Z" - }, - { - "Key": "process.tags.exporter_version", - "Value": "someVersion" - }, - { - "Key": "process.tags.resource_type", - "Value": "k8s.io/container" - }, - { - "Key": "process.tags.resource_key1", - "Value": "resource_val1" - }, - { - "Key": "process.serviceName", - "Value": "api" - } - ] -] \ No newline at end of file diff --git a/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data_no_binary_tags_02.json b/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data_no_binary_tags_02.json deleted file mode 100644 index b3724ee290d2..000000000000 --- a/exporter/alibabacloudlogserviceexporter/testdata/logservice_trace_data_no_binary_tags_02.json +++ /dev/null @@ -1,136 +0,0 @@ -[ - [ - { - "Key": "traceID", - "Value": "000000000000000052969a8955571a3f" - }, - { - "Key": "spanID", - "Value": "0000000000647d98" - }, - { - "Key": "parentSpanID", - "Value": "0" - }, - { - "Key": "startTime", - "Value": "1485467191639875" - }, - { - "Key": "operationName", - "Value": "get" - }, - { - "Key": "duration", - "Value": "22938" - }, - { - "Key": "tags.peer.service", - "Value": "AAAAAAAAMDk=" - }, - { - "Key": "tags.span.kind", - "Value": "server" - }, - { - "Key": "tags.status.code", - "Value": "0" - }, - { - "Key": "tags.status.message", - "Value": "" - }, - { - "Key": "process.serviceName", - "Value": "api" - } - ], - [ - { - "Key": "traceID", - "Value": "000000000000000052969a8955571a3f" - }, - { - "Key": "spanID", - "Value": "0000000000647d99" - }, - { - "Key": "reference", - "Value": "[{\"TraceID\":\"000000000000000052969a8955571a3f\",\"SpanID\":\"0000000000647d98\",\"RefType\":\"PARENT_LINKED_SPAN\"},{\"TraceID\":\"000000000000000052969a8955571a3f\",\"SpanID\":\"000000000068c4e3\",\"RefType\":\"TYPE_UNSPECIFIED\"},{\"TraceID\":\"00000000000000000000000000000000\",\"SpanID\":\"0000000000000000\",\"RefType\":\"TYPE_UNSPECIFIED\"}]" - }, - { - "Key": "parentSpanID", - "Value": "0" - }, - { - "Key": "startTime", - "Value": "1485467191639875" - }, - { - "Key": "operationName", - "Value": "get" - }, - { - "Key": "duration", - "Value": "22938" - }, - { - "Key": "tags.span.kind", - "Value": "server" - }, - { - "Key": "tags.status.code", - "Value": "0" - }, - { - "Key": "tags.status.message", - "Value": "" - }, - { - "Key": "process.serviceName", - "Value": "api" - } - ], - [ - { - "Key": "traceID", - "Value": "000000000000000052969a8955571a3f" - }, - { - "Key": "spanID", - "Value": "0000000000647d98" - }, - { - "Key": "parentSpanID", - "Value": "0000000000000000" - }, - { - "Key": "startTime", - "Value": "1485467192639875" - }, - { - "Key": "operationName", - "Value": "get2" - }, - { - "Key": "duration", - "Value": "22938" - }, - { - "Key": "tags.span.kind", - "Value": "" - }, - { - "Key": "tags.status.code", - "Value": "0" - }, - { - "Key": "tags.status.message", - "Value": "" - }, - { - "Key": "process.serviceName", - "Value": "api" - } - ] -] \ No newline at end of file diff --git a/exporter/alibabacloudlogserviceexporter/trace_exporter.go b/exporter/alibabacloudlogserviceexporter/trace_exporter.go index 9baea4202d06..a3387100ae0f 100644 --- a/exporter/alibabacloudlogserviceexporter/trace_exporter.go +++ b/exporter/alibabacloudlogserviceexporter/trace_exporter.go @@ -18,11 +18,9 @@ import ( "context" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenterror" "go.opentelemetry.io/collector/config/configmodels" "go.opentelemetry.io/collector/consumer/pdata" "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.opentelemetry.io/collector/translator/internaldata" "go.uber.org/zap" ) @@ -53,16 +51,10 @@ func (s *logServiceTraceSender) pushTraceData( _ context.Context, td pdata.Traces, ) (int, error) { - octds := internaldata.TraceDataToOC(td) - var errs []error - for _, octd := range octds { - logs := traceDataToLogServiceData(octd) - if len(logs) > 0 { - if err := s.client.SendLogs(logs); err != nil { - errs = append(errs, err) - } - } + var err error + slsLogs, dropped := traceDataToLogServiceData(td) + if len(slsLogs) > 0 { + err = s.client.SendLogs(slsLogs) } - return 0, componenterror.CombineErrors(errs) - + return dropped, err } diff --git a/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice.go b/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice.go index 240868cf7aa4..eb21bd16269f 100644 --- a/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice.go +++ b/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice.go @@ -15,393 +15,226 @@ package alibabacloudlogserviceexporter import ( - "encoding/hex" "encoding/json" "strconv" "time" sls "github.com/aliyun/aliyun-log-go-sdk" - commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1" - resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1" - tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1" "github.com/gogo/protobuf/proto" - "go.opentelemetry.io/collector/consumer/consumerdata" + "go.opentelemetry.io/collector/consumer/pdata" tracetranslator "go.opentelemetry.io/collector/translator/trace" - "google.golang.org/protobuf/types/known/timestamppb" ) const ( traceIDField = "traceID" spanIDField = "spanID" parentSpanIDField = "parentSpanID" - operationNameField = "operationName" - referenceField = "reference" - startTimeField = "startTime" + nameField = "name" + kindField = "kind" + linksField = "links" + timeField = "time" + startTimeField = "start" + endTimeField = "end" + traceStateField = "traceState" durationField = "duration" - tagsPrefix = "tags." + attributeField = "attribute" + statusCodeField = "statusCode" + statusMessageField = "statusMessage" logsField = "logs" - serviceNameField = "process.serviceName" - processTagsPrefix = "process.tags." -) - -const ( - // Tags - opencensusLanguage = "language" - opencensusExporterVersion = "exporter_version" - opencensusCoreLibVersion = "core_lib_version" - opencensusResourceType = "resource_type" ) // traceDataToLogService translates trace data into the LogService format. -func traceDataToLogServiceData(td consumerdata.TraceData) []*sls.Log { - logs := spansToLogServiceData(td.Spans) - tagContents := nodeAndResourceToLogContent(td.Node, td.Resource) - - for _, log := range logs { - log.Contents = append(log.Contents, tagContents...) - } - return logs +func traceDataToLogServiceData(td pdata.Traces) ([]*sls.Log, int) { + var slsLogs []*sls.Log + resourceSpansSlice := td.ResourceSpans() + for i := 0; i < resourceSpansSlice.Len(); i++ { + resourceSpans := resourceSpansSlice.At(i) + if resourceSpans.IsNil() { + continue + } + logs := resourceSpansToLogServiceData(resourceSpans) + slsLogs = append(slsLogs, logs...) + } + return slsLogs, 0 } -func nodeAndResourceToLogContent(node *commonpb.Node, resource *resourcepb.Resource) []*sls.LogContent { - if node == nil { - return []*sls.LogContent{} - } - - var contents []*sls.LogContent - - for key, val := range node.Attributes { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + key), - Value: proto.String(val), - }) - } - if node.Identifier != nil { - if node.Identifier.HostName != "" { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + "hostname"), - Value: proto.String(node.Identifier.HostName), - }) - } - if node.Identifier.Pid != 0 { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + "pid"), - Value: proto.String(strconv.Itoa(int(node.Identifier.Pid))), - }) - } - if node.Identifier.StartTimestamp != nil && node.Identifier.StartTimestamp.Seconds != 0 { - startTimeStr := node.Identifier.StartTimestamp.AsTime().Format(time.RFC3339Nano) - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + "start.time"), - Value: proto.String(startTimeStr), - }) - } - } - - // Add OpenCensus library information as tags if available - ocLib := node.LibraryInfo - if ocLib != nil { - // Only add language if specified - if ocLib.Language != commonpb.LibraryInfo_LANGUAGE_UNSPECIFIED { - languageStr := ocLib.Language.String() - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + opencensusLanguage), - Value: proto.String(languageStr), - }) - } - if ocLib.ExporterVersion != "" { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + opencensusExporterVersion), - Value: proto.String(ocLib.ExporterVersion), - }) - } - if ocLib.CoreLibraryVersion != "" { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + opencensusCoreLibVersion), - Value: proto.String(ocLib.CoreLibraryVersion), - }) - } - } +func resourceSpansToLogServiceData(resourceSpans pdata.ResourceSpans) []*sls.Log { + resourceContents := resourceToLogContents(resourceSpans.Resource()) + insLibSpansSlice := resourceSpans.InstrumentationLibrarySpans() + var slsLogs []*sls.Log + for i := 0; i < insLibSpansSlice.Len(); i++ { + insLibSpans := insLibSpansSlice.At(i) + if insLibSpans.IsNil() { + continue + } + instrumentationLibraryContents := instrumentationLibraryToLogContents(insLibSpans.InstrumentationLibrary()) + spans := insLibSpans.Spans() + for j := 0; j < spans.Len(); j++ { + span := spans.At(j) + if span.IsNil() { + continue + } + if slsLog := spanToLogServiceData(span, resourceContents, instrumentationLibraryContents); slsLog != nil { + slsLogs = append(slsLogs, slsLog) + } + } + } + return slsLogs +} - var serviceName string - if node.ServiceInfo != nil && node.ServiceInfo.Name != "" { - serviceName = node.ServiceInfo.Name +func spanToLogServiceData(span pdata.Span, resourceContents, instrumentationLibraryContents []*sls.LogContent) *sls.Log { + timeNano := int64(span.EndTime()) + if timeNano == 0 { + timeNano = time.Now().UnixNano() } - - if resource != nil { - resourceType := resource.GetType() - if resourceType != "" { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + opencensusResourceType), - Value: proto.String(resourceType), - }) - } - for k, v := range resource.GetLabels() { - contents = append(contents, &sls.LogContent{ - Key: proto.String(processTagsPrefix + k), - Value: proto.String(v), - }) - } + slsLog := sls.Log{ + Time: proto.Uint32(uint32(timeNano / 1000 / 1000 / 1000)), } + // pre alloc, refine if logContent's len > 16 + preAllocCount := 16 + slsLog.Contents = make([]*sls.LogContent, 0, preAllocCount+len(resourceContents)+len(instrumentationLibraryContents)) + contentsBuffer := make([]sls.LogContent, 0, preAllocCount) - if serviceName == "" && len(contents) == 0 { - // No info to put in the process... - return nil - } + slsLog.Contents = append(slsLog.Contents, resourceContents...) + slsLog.Contents = append(slsLog.Contents, instrumentationLibraryContents...) - contents = append(contents, &sls.LogContent{ - Key: proto.String(serviceNameField), - Value: proto.String(serviceName), + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(traceIDField), + Value: proto.String(span.TraceID().HexString()), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(spanIDField), + Value: proto.String(span.SpanID().HexString()), + }) + // if ParentSpanID is not valid, the return "", it is compatible for log service + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(parentSpanIDField), + Value: proto.String(span.ParentSpanID().HexString()), }) - return contents -} - -func spansToLogServiceData(spans []*tracepb.Span) []*sls.Log { - if spans == nil { - return nil - } - - // Pre-allocate assuming that few, if any spans, are nil. - logs := make([]*sls.Log, 0, len(spans)) - for _, span := range spans { - contents := make([]*sls.LogContent, 0) - traceID := hex.EncodeToString(span.TraceId[:]) - spanID := hex.EncodeToString(span.SpanId[:]) - contents = append(contents, - &sls.LogContent{ - Key: proto.String(traceIDField), - Value: proto.String(traceID), - }) - contents = append(contents, - &sls.LogContent{ - Key: proto.String(spanIDField), - Value: proto.String(spanID), - }) - linksContent := linksToLogContents(span.Links) - if linksContent != nil { - contents = append(contents, linksContent) - } - if len(span.ParentSpanId) != 0 { - parentSpanID := hex.EncodeToString(span.ParentSpanId[:]) - contents = append(contents, - &sls.LogContent{ - Key: proto.String(parentSpanIDField), - Value: proto.String(parentSpanID), - }) - } else { - // set "0" if no ParentSpanId - contents = append(contents, - &sls.LogContent{ - Key: proto.String(parentSpanIDField), - Value: proto.String("0"), - }) - } - startTime := timestampToEpochMicroseconds(span.StartTime) - contents = append(contents, - &sls.LogContent{ - Key: proto.String(startTimeField), - Value: proto.String(strconv.FormatInt(startTime, 10)), - }) - - contents = append(contents, - &sls.LogContent{ - Key: proto.String(operationNameField), - Value: proto.String(truncableStringToStr(span.Name)), - }) - - contents = append(contents, - &sls.LogContent{ - Key: proto.String(durationField), - Value: proto.String(strconv.FormatInt(timestampToEpochMicroseconds(span.EndTime)-startTime, 10)), - }) - - for k, v := range span.GetAttributes().GetAttributeMap() { - contents = append(contents, &sls.LogContent{ - Key: proto.String(tagsPrefix + k), - Value: proto.String(attributeValueToString(v)), - }) - } - - if logContent := timeEventToLogContent(span.TimeEvents); logContent != nil { - contents = append(contents, logContent) - } - // Only add the "span.kind" tag if not set in the OC span attributes. - if !tracetranslator.OCAttributeKeyExist(span.Attributes, tracetranslator.TagSpanKind) { - contents = append(contents, - &sls.LogContent{ - Key: proto.String(tagsPrefix + tracetranslator.TagSpanKind), - Value: proto.String(spanKindToStr(span.Kind)), - }) - } - // Only add status tags if neither status.code and status.message are set in the OC span attributes. - if !tracetranslator.OCAttributeKeyExist(span.Attributes, tracetranslator.TagStatusCode) && - !tracetranslator.OCAttributeKeyExist(span.Attributes, tracetranslator.TagStatusMsg) { - contents = append(contents, - &sls.LogContent{ - Key: proto.String(tagsPrefix + tracetranslator.TagStatusCode), - Value: proto.String(strconv.Itoa(int(span.GetStatus().GetCode()))), - }) - contents = append(contents, - &sls.LogContent{ - Key: proto.String(tagsPrefix + tracetranslator.TagStatusMsg), - Value: proto.String(span.GetStatus().GetMessage()), - }) - } + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(kindField), + Value: proto.String(spanKindToShortString(span.Kind())), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(nameField), + Value: proto.String(span.Name()), + }) - logs = append(logs, &sls.Log{ - Time: proto.Uint32(uint32(span.GetEndTime().GetSeconds())), - Contents: contents, + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(linksField), + Value: proto.String(spanLinksToString(span.Links())), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(logsField), + Value: proto.String(eventsToString(span.Events())), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(traceStateField), + Value: proto.String(string(span.TraceState())), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(startTimeField), + Value: proto.String(strconv.FormatUint(uint64(span.StartTime()/1000), 10)), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(endTimeField), + Value: proto.String(strconv.FormatUint(uint64(span.EndTime()/1000), 10)), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(durationField), + Value: proto.String(strconv.FormatUint(uint64((span.EndTime()-span.StartTime())/1000), 10)), + }) + attributeMap := tracetranslator.AttributeMapToMap(span.Attributes()) + attributeJSONBytes, _ := json.Marshal(attributeMap) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(attributeField), + Value: proto.String(string(attributeJSONBytes)), + }) + if span.Status().IsNil() { + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(statusCodeField), + Value: proto.String("UNSET"), }) - } - - return logs -} - -func linksToLogContents(ocSpanLinks *tracepb.Span_Links) *sls.LogContent { - if ocSpanLinks == nil || ocSpanLinks.Link == nil { - return nil - } - - ocLinks := ocSpanLinks.Link - - type linkSpanRef struct { - TraceID string - SpanID string - RefType string - } - spanRefs := make([]linkSpanRef, 0, len(ocLinks)) - - for _, ocLink := range ocLinks { - spanRefs = append(spanRefs, linkSpanRef{ - TraceID: hex.EncodeToString(ocLink.TraceId[:]), - SpanID: hex.EncodeToString(ocLink.SpanId[:]), - RefType: ocLink.GetType().String(), + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(statusMessageField), + Value: proto.String("status is nil"), + }) + } else { + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(statusCodeField), + Value: proto.String(statusCodeToShortString(span.Status().Code())), + }) + contentsBuffer = append(contentsBuffer, sls.LogContent{ + Key: proto.String(statusMessageField), + Value: proto.String(span.Status().Message()), }) } - - spanRefsStr, _ := json.Marshal(spanRefs) - return &sls.LogContent{ - Key: proto.String(referenceField), - Value: proto.String(string(spanRefsStr)), - } -} - -func attributeValueToString(v *tracepb.AttributeValue) string { - switch attribValue := v.Value.(type) { - case *tracepb.AttributeValue_StringValue: - return truncableStringToStr(attribValue.StringValue) - case *tracepb.AttributeValue_IntValue: - return strconv.FormatInt(attribValue.IntValue, 10) - case *tracepb.AttributeValue_BoolValue: - if attribValue.BoolValue { - return "true" - } - return "false" - case *tracepb.AttributeValue_DoubleValue: - return strconv.FormatFloat(attribValue.DoubleValue, 'g', -1, 64) - default: + for i := range contentsBuffer { + slsLog.Contents = append(slsLog.Contents, &contentsBuffer[i]) } - return "" + return &slsLog } -func spanKindToStr(spanKind tracepb.Span_SpanKind) string { - - switch spanKind { - case tracepb.Span_CLIENT: +func spanKindToShortString(kind pdata.SpanKind) string { + switch kind { + case pdata.SpanKindINTERNAL: + return string(tracetranslator.OpenTracingSpanKindInternal) + case pdata.SpanKindCLIENT: return string(tracetranslator.OpenTracingSpanKindClient) - case tracepb.Span_SERVER: + case pdata.SpanKindSERVER: return string(tracetranslator.OpenTracingSpanKindServer) - } - return "" -} - -func timeEventToLogContent(ocSpanTimeEvents *tracepb.Span_TimeEvents) *sls.LogContent { - if ocSpanTimeEvents == nil || ocSpanTimeEvents.TimeEvent == nil { - return nil - } - - ocTimeEvents := ocSpanTimeEvents.TimeEvent - - type timeEvent struct { - TimeUs int64 - Fields map[string]string - } - - // Assume that in general no time events are going to produce nil Jaeger logs. - timeEvents := make([]timeEvent, 0, len(ocTimeEvents)) - for _, ocTimeEvent := range ocTimeEvents { - e := timeEvent{ - TimeUs: timestampToEpochMicroseconds(ocTimeEvent.Time), - } - switch teValue := ocTimeEvent.Value.(type) { - case *tracepb.Span_TimeEvent_Annotation_: - e.Fields = ocAnnotationToMap(teValue.Annotation) - case *tracepb.Span_TimeEvent_MessageEvent_: - e.Fields = ocMessageEventToMap(teValue.MessageEvent) - default: - msg := "An unknown OpenCensus TimeEvent type was detected when translating" - e.Fields = make(map[string]string) - e.Fields["error"] = msg - } - timeEvents = append(timeEvents, e) - } - - // ignore marshal error - timeEventsStr, _ := json.Marshal(timeEvents) - return &sls.LogContent{ - Key: proto.String(logsField), - Value: proto.String(string(timeEventsStr)), + case pdata.SpanKindPRODUCER: + return string(tracetranslator.OpenTracingSpanKindProducer) + case pdata.SpanKindCONSUMER: + return string(tracetranslator.OpenTracingSpanKindConsumer) + default: + return string(tracetranslator.OpenTracingSpanKindUnspecified) } } -func ocAnnotationToMap(annotation *tracepb.Span_TimeEvent_Annotation) map[string]string { - if annotation == nil { - return nil - } - - keyVals := make(map[string]string) - for k, v := range annotation.GetAttributes().GetAttributeMap() { - keyVals[k] = attributeValueToString(v) - } - - desc := truncableStringToStr(annotation.Description) - if desc != "" { - keyVals[tracetranslator.AnnotationDescriptionKey] = desc +func statusCodeToShortString(code pdata.StatusCode) string { + switch code { + case pdata.StatusCodeError: + return "ERROR" + case pdata.StatusCodeOk: + return "OK" + default: + return "UNSET" } - return keyVals } -func ocMessageEventToMap(msgEvent *tracepb.Span_TimeEvent_MessageEvent) map[string]string { - if msgEvent == nil { - return nil - } - - keyVals := make(map[string]string) - keyVals[tracetranslator.MessageEventIDKey] = strconv.FormatUint(msgEvent.Id, 10) - keyVals[tracetranslator.MessageEventTypeKey] = msgEvent.Type.String() - - // Some implementations always have these two fields as zeros. - if msgEvent.CompressedSize == 0 && msgEvent.UncompressedSize == 0 { - return keyVals +func eventsToString(events pdata.SpanEventSlice) string { + eventArray := make([]map[string]interface{}, 0, events.Len()) + for i := 0; i < events.Len(); i++ { + spanEvent := events.At(i) + if spanEvent.IsNil() { + continue + } + event := map[string]interface{}{} + event[nameField] = spanEvent.Name() + event[timeField] = spanEvent.Timestamp() + event[attributeField] = tracetranslator.AttributeMapToMap(spanEvent.Attributes()) + eventArray = append(eventArray, event) } + eventArrayBytes, _ := json.Marshal(&eventArray) + return string(eventArrayBytes) - keyVals[tracetranslator.MessageEventCompressedSizeKey] = strconv.FormatUint(msgEvent.CompressedSize, 10) - keyVals[tracetranslator.MessageEventUncompressedSizeKey] = strconv.FormatUint(msgEvent.UncompressedSize, 10) - - return keyVals -} - -func truncableStringToStr(ts *tracepb.TruncatableString) string { - if ts == nil { - return "" - } - return ts.Value } -func timestampToEpochMicroseconds(ts *timestamppb.Timestamp) int64 { - if ts == nil { - return 0 - } - return ts.GetSeconds()*1e6 + int64(ts.GetNanos()/1e3) +func spanLinksToString(spanLinkSlice pdata.SpanLinkSlice) string { + linkArray := make([]map[string]interface{}, 0, spanLinkSlice.Len()) + for i := 0; i < spanLinkSlice.Len(); i++ { + spanLink := spanLinkSlice.At(i) + if spanLink.IsNil() { + continue + } + link := map[string]interface{}{} + link[spanIDField] = spanLink.SpanID().HexString() + link[traceIDField] = spanLink.TraceID().HexString() + link[attributeField] = tracetranslator.AttributeMapToMap(spanLink.Attributes()) + linkArray = append(linkArray, link) + } + linkArrayBytes, _ := json.Marshal(&linkArray) + return string(linkArrayBytes) } diff --git a/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice_test.go b/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice_test.go index 6bdb864d3363..ddad2dacf6b6 100644 --- a/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice_test.go +++ b/exporter/alibabacloudlogserviceexporter/tracedata_to_logservice_test.go @@ -20,16 +20,12 @@ import ( "io/ioutil" "reflect" "sort" - "strings" "testing" + "time" - commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1" - resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1" - tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1" "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/consumer/consumerdata" - tracetranslator "go.opentelemetry.io/collector/translator/trace" - "google.golang.org/protobuf/types/known/timestamppb" + "go.opentelemetry.io/collector/consumer/pdata" + semconventions "go.opentelemetry.io/collector/translator/conventions" ) type logKeyValuePair struct { @@ -43,529 +39,205 @@ func (kv logKeyValuePairs) Len() int { return len(kv) } func (kv logKeyValuePairs) Swap(i, j int) { kv[i], kv[j] = kv[j], kv[i] } func (kv logKeyValuePairs) Less(i, j int) bool { return kv[i].Key < kv[j].Key } -func TestNilOCProtoNodeToLogServiceData(t *testing.T) { - nilNodeBatch := consumerdata.TraceData{ - Spans: []*tracepb.Span{ - { - TraceId: []byte("0123456789abcdef"), - SpanId: []byte("01234567"), - }, - }, +func TestTraceDataToLogService(t *testing.T) { + gotLogs, dropped := traceDataToLogServiceData(constructSpanData()) + assert.Equal(t, len(gotLogs), 4) + assert.Equal(t, dropped, 0) + + gotLogPairs := make([][]logKeyValuePair, 0, len(gotLogs)) + + for _, log := range gotLogs { + pairs := make([]logKeyValuePair, 0, len(log.Contents)) + for _, content := range log.Contents { + pairs = append(pairs, logKeyValuePair{ + Key: content.GetKey(), + Value: content.GetValue(), + }) + fmt.Printf("%s : %s\n", content.GetKey(), content.GetValue()) + } + gotLogPairs = append(gotLogPairs, pairs) + + fmt.Println("#################") } - got := traceDataToLogServiceData(nilNodeBatch) - if len(got) == 0 { - t.Fatalf("Logs count must > 0") + str, _ := json.Marshal(gotLogPairs) + fmt.Println(string(str)) + + wantLogs := make([][]logKeyValuePair, 0, len(gotLogs)) + resultLogFile := "./testdata/logservice_trace_data.json" + if err := loadFromJSON(resultLogFile, &wantLogs); err != nil { + t.Errorf("Failed load log key value pairs from %q: %v", resultLogFile, err) + return } - for _, log := range got { - if len(log.Contents) == 0 { - t.Fatalf("Log contents count must > 0") + for j := 0; j < len(gotLogs); j++ { + + sort.Sort(logKeyValuePairs(gotLogPairs[j])) + sort.Sort(logKeyValuePairs(wantLogs[j])) + if !reflect.DeepEqual(gotLogPairs[j], wantLogs[j]) { + t.Errorf("Unsuccessful conversion \nGot:\n\t%v\nWant:\n\t%v", gotLogPairs[j], wantLogs[j]) } } } -func TestOCProtoToLogServiceData(t *testing.T) { - const numOfFiles = 2 - for i := 0; i < numOfFiles; i++ { - td := tds[i] - - gotLogs := traceDataToLogServiceData(td) - - gotLogPairs := make([][]logKeyValuePair, 0, len(gotLogs)) - - for _, log := range gotLogs { - pairs := make([]logKeyValuePair, 0, len(log.Contents)) - for _, content := range log.Contents { - pairs = append(pairs, logKeyValuePair{ - Key: content.GetKey(), - Value: content.GetValue(), - }) - //fmt.Printf("%s : %s\n", content.GetKey(), content.GetValue()) - } - gotLogPairs = append(gotLogPairs, pairs) - - //fmt.Println("#################") - } - //str, _ := json.Marshal(gotLogPairs) - //fmt.Println(string(str)) - - wantSpanCount, gotSpanCount := len(td.Spans), len(gotLogs) - if wantSpanCount != gotSpanCount { - t.Errorf("Different number of spans in the batches on pass #%d (want %d, got %d)", i, wantSpanCount, gotSpanCount) - continue - } - - resultLogFile := fmt.Sprintf("./testdata/logservice_trace_data_no_binary_tags_%02d.json", i+1) - - wantLogs := make([][]logKeyValuePair, 0, wantSpanCount) - - if err := loadFromJSON(resultLogFile, &wantLogs); err != nil { - t.Errorf("Failed load log key value pairs from %q: %v", resultLogFile, err) - continue - } +func loadFromJSON(file string, obj interface{}) error { + blob, err := ioutil.ReadFile(file) + if err == nil { + err = json.Unmarshal(blob, obj) + } - for j := 0; j < wantSpanCount; j++ { + return err +} - sort.Sort(logKeyValuePairs(gotLogPairs[j])) - sort.Sort(logKeyValuePairs(wantLogs[j])) - if !reflect.DeepEqual(gotLogPairs[j], wantLogs[j]) { - t.Errorf("Unsuccessful conversion %d \nGot:\n\t%v\nWant:\n\t%v", i, gotLogPairs, wantLogs) - } - } - } +func constructSpanData() pdata.Traces { + resource := constructResource() + + traces := pdata.NewTraces() + traces.ResourceSpans().Resize(2) + rspans := traces.ResourceSpans().At(0) + traces.ResourceSpans().Append(pdata.NewResourceSpans()) + resource.CopyTo(rspans.Resource()) + rspans.InstrumentationLibrarySpans().Resize(2) + ispans := rspans.InstrumentationLibrarySpans().At(0) + rspans.InstrumentationLibrarySpans().Append(pdata.NewInstrumentationLibrarySpans()) + ispans.Spans().Resize(4) + constructHTTPClientSpan().CopyTo(ispans.Spans().At(0)) + constructHTTPServerSpan().CopyTo(ispans.Spans().At(1)) + ispans.Spans().At(2).InitEmpty() + ispans.InstrumentationLibrary().SetName("golang-sls-exporter") + ispans.InstrumentationLibrary().SetVersion("v0.1.0") + ispans.Spans().Append(pdata.NewSpan()) + return traces } -func TestOCStatusToJaegerThriftTags(t *testing.T) { +func constructResource() pdata.Resource { + resource := pdata.NewResource() + attrs := pdata.NewAttributeMap() + attrs.InsertString(semconventions.AttributeServiceName, "signup_aggregator") + attrs.InsertString(semconventions.AttributeHostName, "xxx.et15") + attrs.InsertString(semconventions.AttributeContainerName, "signup_aggregator") + attrs.InsertString(semconventions.AttributeContainerImage, "otel/signupaggregator") + attrs.InsertString(semconventions.AttributeContainerTag, "v1") + attrs.InsertString(semconventions.AttributeCloudProvider, semconventions.AttributeCloudProviderAWS) + attrs.InsertString(semconventions.AttributeCloudAccount, "999999998") + attrs.InsertString(semconventions.AttributeCloudRegion, "us-west-2") + attrs.InsertString(semconventions.AttributeCloudZone, "us-west-1b") + attrs.CopyTo(resource.Attributes()) + return resource +} - type test struct { - haveAttributes *tracepb.Span_Attributes - haveStatus *tracepb.Status - wantTags []logKeyValuePair - } +func constructHTTPClientSpan() pdata.Span { + attributes := make(map[string]interface{}) + attributes[semconventions.AttributeComponent] = semconventions.ComponentTypeHTTP + attributes[semconventions.AttributeHTTPMethod] = "GET" + attributes[semconventions.AttributeHTTPURL] = "https://api.example.com/users/junit" + attributes[semconventions.AttributeHTTPStatusCode] = 200 + endTime := time.Unix(12300, 123456789) + startTime := endTime.Add(-90 * time.Second) + spanAttributes := constructSpanAttributes(attributes) + + span := pdata.NewSpan() + span.InitEmpty() + span.SetTraceID(newTraceID()) + span.SetSpanID(newSegmentID()) + span.SetParentSpanID(newSegmentID()) + span.SetName("/users/junit") + span.SetKind(pdata.SpanKindCLIENT) + span.SetStartTime(pdata.TimestampUnixNano(startTime.UnixNano())) + span.SetEndTime(pdata.TimestampUnixNano(endTime.UnixNano())) + span.SetTraceState("x:y") + + event := pdata.NewSpanEvent() + event.InitEmpty() + event.SetName("event") + event.SetTimestamp(1024) + event.Attributes().InsertString("key", "value") + span.Events().Resize(1) + event.CopyTo(span.Events().At(0)) + span.Events().Append(pdata.NewSpanEvent()) + + link := pdata.NewSpanLink() + link.InitEmpty() + link.SetTraceState("link:state") + link.Attributes().InsertString("link", "true") + span.Links().Resize(1) + link.CopyTo(span.Links().At(0)) + span.Links().Append(pdata.NewSpanLink()) + + status := pdata.NewSpanStatus() + status.InitEmpty() + status.SetCode(1) + status.SetMessage("OK") + status.CopyTo(span.Status()) + + spanAttributes.CopyTo(span.Attributes()) + return span +} - cases := []test{ - // only status.code - { - haveAttributes: nil, - haveStatus: &tracepb.Status{ - Code: 10, - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagStatusCode, - Value: "10", - }, - { - Key: tagsPrefix + tracetranslator.TagStatusMsg, - Value: "", - }, - }, - }, - // only status.message - { - haveAttributes: nil, - haveStatus: &tracepb.Status{ - Message: "Message", - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagStatusCode, - Value: "0", - }, - { - Key: tagsPrefix + tracetranslator.TagStatusMsg, - Value: "Message", - }, - }, - }, - // both status.code and status.message - { - haveAttributes: nil, - haveStatus: &tracepb.Status{ - Code: 12, - Message: "Forbidden", - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagStatusCode, - Value: "12", - }, - { - Key: tagsPrefix + tracetranslator.TagStatusMsg, - Value: "Forbidden", - }, - }, - }, - - // status and existing tags - { - haveStatus: &tracepb.Status{ - Code: 404, - Message: "NotFound", - }, - haveAttributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "status.code": { - Value: &tracepb.AttributeValue_IntValue{ - IntValue: 13, - }, - }, - "status.message": { - Value: &tracepb.AttributeValue_StringValue{ - StringValue: &tracepb.TruncatableString{Value: "Error"}, - }, - }, - }, - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagStatusCode, - Value: "13", - }, - { - Key: tagsPrefix + tracetranslator.TagStatusMsg, - Value: "Error", - }, - }, - }, - - // partial existing tag - - { - haveStatus: &tracepb.Status{ - Code: 404, - Message: "NotFound", - }, - haveAttributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "status.code": { - Value: &tracepb.AttributeValue_IntValue{ - IntValue: 13, - }, - }, - }, - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagStatusCode, - Value: "13", - }, - }, - }, - - { - haveStatus: &tracepb.Status{ - Code: 404, - Message: "NotFound", - }, - haveAttributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "status.message": { - Value: &tracepb.AttributeValue_StringValue{ - StringValue: &tracepb.TruncatableString{Value: "Error"}, - }, - }, - }, - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagStatusMsg, - Value: "Error", - }, - }, - }, - // both status and tags - { - haveStatus: &tracepb.Status{ - Code: 13, - Message: "Forbidden", - }, - haveAttributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "http.status_code": { - Value: &tracepb.AttributeValue_IntValue{ - IntValue: 404, - }, - }, - "http.status_message": { - Value: &tracepb.AttributeValue_StringValue{ - StringValue: &tracepb.TruncatableString{Value: "NotFound"}, - }, - }, - }, - }, - wantTags: []logKeyValuePair{ - { - Key: tagsPrefix + tracetranslator.TagHTTPStatusCode, - Value: "404", - }, - { - Key: tagsPrefix + tracetranslator.TagHTTPStatusMsg, - Value: "NotFound", - }, - { - Key: tagsPrefix + tracetranslator.TagStatusCode, - Value: "13", - }, - { - Key: tagsPrefix + tracetranslator.TagStatusMsg, - Value: "Forbidden", - }, - }, - }, - } +func constructHTTPServerSpan() pdata.Span { + attributes := make(map[string]interface{}) + attributes[semconventions.AttributeComponent] = semconventions.ComponentTypeHTTP + attributes[semconventions.AttributeHTTPMethod] = "GET" + attributes[semconventions.AttributeHTTPURL] = "https://api.example.com/users/junit" + attributes[semconventions.AttributeHTTPClientIP] = "192.168.15.32" + attributes[semconventions.AttributeHTTPStatusCode] = 200 + endTime := time.Unix(12300, 123456789) + startTime := endTime.Add(-90 * time.Second) + spanAttributes := constructSpanAttributes(attributes) + + span := pdata.NewSpan() + span.InitEmpty() + span.SetTraceID(newTraceID()) + span.SetSpanID(newSegmentID()) + span.SetParentSpanID(newSegmentID()) + span.SetName("/users/junit") + span.SetKind(pdata.SpanKindSERVER) + span.SetStartTime(pdata.TimestampUnixNano(startTime.UnixNano())) + span.SetEndTime(pdata.TimestampUnixNano(endTime.UnixNano())) + + status := pdata.NewSpanStatus() + status.InitEmpty() + status.SetCode(2) + status.SetMessage("something error") + status.CopyTo(span.Status()) + + spanAttributes.CopyTo(span.Attributes()) + return span +} - fakeTraceID := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - fakeSpanID := []byte{0, 1, 2, 3, 4, 5, 6, 7} - for i, c := range cases { - gotLogs := traceDataToLogServiceData(consumerdata.TraceData{ - Spans: []*tracepb.Span{{ - TraceId: fakeTraceID, - SpanId: fakeSpanID, - Status: c.haveStatus, - Attributes: c.haveAttributes, - }}, - }) - gotLog := gotLogs[0] - - var gotPairs []logKeyValuePair - for _, content := range gotLog.Contents { - if strings.HasPrefix(content.GetKey(), tagsPrefix) && strings.Contains(content.GetKey(), "status") { - gotPairs = append(gotPairs, logKeyValuePair{ - Key: content.GetKey(), - Value: content.GetValue(), - }) - } - } - sort.Sort(logKeyValuePairs(gotPairs)) - sort.Sort(logKeyValuePairs(c.wantTags)) - if !reflect.DeepEqual(gotPairs, c.wantTags) { - t.Errorf("Unsuccessful conversion : %d \nGot:\n\t%v\nWant:\n\t%v", i, gotPairs, c.wantTags) +func constructSpanAttributes(attributes map[string]interface{}) pdata.AttributeMap { + attrs := pdata.NewAttributeMap() + for key, value := range attributes { + if cast, ok := value.(int); ok { + attrs.InsertInt(key, int64(cast)) + } else if cast, ok := value.(int64); ok { + attrs.InsertInt(key, cast) + } else { + attrs.InsertString(key, fmt.Sprintf("%v", value)) } } + return attrs } -// tds has the TraceData proto used in the test. They are hard coded because -// structs like tracepb.AttributeMap cannot be ready from JSON. -var tds = []consumerdata.TraceData{ - { - Node: &commonpb.Node{ - Identifier: &commonpb.ProcessIdentifier{ - HostName: "api246-sjc1", - Pid: 13, - StartTimestamp: ×tamppb.Timestamp{Seconds: 1485467190, Nanos: 639875000}, - }, - LibraryInfo: &commonpb.LibraryInfo{ExporterVersion: "someVersion"}, - ServiceInfo: &commonpb.ServiceInfo{Name: "api"}, - Attributes: map[string]string{ - "a.binary": "AQIDBAMCAQ==", - "a.bool": "true", - "a.double": "1234.56789", - "a.long": "123456789", - "ip": "10.53.69.61", - }, - }, - Resource: &resourcepb.Resource{ - Type: "k8s.io/container", - Labels: map[string]string{"resource_key1": "resource_val1"}, - }, - Spans: []*tracepb.Span{ - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x98}, - ParentSpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xC4, 0xE3}, - Name: &tracepb.TruncatableString{Value: "get"}, - Kind: tracepb.Span_CLIENT, - StartTime: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 639875000}, - EndTime: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 662813000}, - Attributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "http.url": { - Value: &tracepb.AttributeValue_StringValue{StringValue: &tracepb.TruncatableString{Value: "http://localhost:15598/client_transactions"}}, - }, - "peer.ipv4": { - Value: &tracepb.AttributeValue_IntValue{IntValue: 3224716605}, - }, - "peer.port": { - Value: &tracepb.AttributeValue_IntValue{IntValue: 53931}, - }, - "peer.service": { - Value: &tracepb.AttributeValue_StringValue{StringValue: &tracepb.TruncatableString{Value: "rtapi"}}, - }, - "someBool": { - Value: &tracepb.AttributeValue_BoolValue{BoolValue: true}, - }, - "someDouble": { - Value: &tracepb.AttributeValue_DoubleValue{DoubleValue: 129.8}, - }, - "span.kind": { - Value: &tracepb.AttributeValue_StringValue{StringValue: &tracepb.TruncatableString{Value: "client"}}, - }, - }, - }, - TimeEvents: &tracepb.Span_TimeEvents{ - TimeEvent: []*tracepb.Span_TimeEvent{ - { - Time: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 639874000}, - Value: &tracepb.Span_TimeEvent_MessageEvent_{ - MessageEvent: &tracepb.Span_TimeEvent_MessageEvent{ - Type: tracepb.Span_TimeEvent_MessageEvent_SENT, UncompressedSize: 1024, CompressedSize: 512, - }, - }, - }, - { - Time: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 639875000}, - Value: &tracepb.Span_TimeEvent_Annotation_{ - Annotation: &tracepb.Span_TimeEvent_Annotation{ - Attributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "key1": { - Value: &tracepb.AttributeValue_StringValue{StringValue: &tracepb.TruncatableString{Value: "value1"}}, - }, - }, - }, - }, - }, - }, - { - Time: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 639875000}, - Value: &tracepb.Span_TimeEvent_Annotation_{ - Annotation: &tracepb.Span_TimeEvent_Annotation{ - Description: &tracepb.TruncatableString{Value: "annotation description"}, - Attributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "event": { - Value: &tracepb.AttributeValue_StringValue{StringValue: &tracepb.TruncatableString{Value: "nothing"}}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - Node: &commonpb.Node{ - ServiceInfo: &commonpb.ServiceInfo{Name: "api"}, - }, - Spans: []*tracepb.Span{ - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x98}, - ParentSpanId: nil, - Name: &tracepb.TruncatableString{Value: "get"}, - Kind: tracepb.Span_SERVER, - StartTime: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 639875000}, - EndTime: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 662813000}, - Attributes: &tracepb.Span_Attributes{ - AttributeMap: map[string]*tracepb.AttributeValue{ - "peer.service": { - Value: &tracepb.AttributeValue_StringValue{StringValue: &tracepb.TruncatableString{Value: "AAAAAAAAMDk="}}, - }, - }, - }, - }, - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x99}, - ParentSpanId: []byte{}, - Name: &tracepb.TruncatableString{Value: "get"}, - Kind: tracepb.Span_SERVER, - StartTime: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 639875000}, - EndTime: ×tamppb.Timestamp{Seconds: 1485467191, Nanos: 662813000}, - Links: &tracepb.Span_Links{ - Link: []*tracepb.Span_Link{ - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x98}, - Type: tracepb.Span_Link_PARENT_LINKED_SPAN, - }, - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xC4, 0xE3}, - }, - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - }, - }, - }, - }, - { - TraceId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F}, - SpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x98}, - ParentSpanId: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - Name: &tracepb.TruncatableString{Value: "get2"}, - StartTime: ×tamppb.Timestamp{Seconds: 1485467192, Nanos: 639875000}, - EndTime: ×tamppb.Timestamp{Seconds: 1485467192, Nanos: 662813000}, - }, - }, - }, +func newTraceID() pdata.TraceID { + r := [16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x96, 0x9A, 0x89, 0x55, 0x57, 0x1A, 0x3F} + return pdata.NewTraceID(r) } -func loadFromJSON(file string, obj interface{}) error { - blob, err := ioutil.ReadFile(file) - if err == nil { - err = json.Unmarshal(blob, obj) - } +func newSegmentID() pdata.SpanID { + r := [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x98} + return pdata.NewSpanID(r) +} - return err +func TestSpanKindToShortString(t *testing.T) { + assert.Equal(t, spanKindToShortString(pdata.SpanKindCONSUMER), "consumer") + assert.Equal(t, spanKindToShortString(pdata.SpanKindPRODUCER), "producer") + assert.Equal(t, spanKindToShortString(pdata.SpanKindCLIENT), "client") + assert.Equal(t, spanKindToShortString(pdata.SpanKindSERVER), "server") + assert.Equal(t, spanKindToShortString(pdata.SpanKindINTERNAL), "internal") + assert.Equal(t, spanKindToShortString(pdata.SpanKindUNSPECIFIED), "") } -func TestTraceCornerCases(t *testing.T) { - assert.Equal(t, truncableStringToStr(nil), "") - assert.Equal(t, timestampToEpochMicroseconds(nil), int64(0)) - assert.Nil(t, ocMessageEventToMap(nil)) - msgEvent := &tracepb.Span_TimeEvent_MessageEvent{} - assert.NotNil(t, ocMessageEventToMap(msgEvent)) - msgEvent.UncompressedSize = 1 - msgEvent.CompressedSize = 1 - assert.NotNil(t, ocMessageEventToMap(msgEvent)) - annotation := &tracepb.Span_TimeEvent_Annotation{} - assert.Nil(t, ocAnnotationToMap(nil)) - assert.NotNil(t, ocAnnotationToMap(annotation)) - annotation.Description = &tracepb.TruncatableString{ - Value: "testDesc", - } - assert.NotNil(t, ocAnnotationToMap(annotation)) - - assert.Nil(t, timeEventToLogContent(nil)) - - assert.NotNil(t, timeEventToLogContent(&tracepb.Span_TimeEvents{ - TimeEvent: []*tracepb.Span_TimeEvent{ - { - Value: nil, - }, - }, - })) - - assert.Equal(t, spanKindToStr(tracepb.Span_SpanKind(99)), "") - assert.Equal(t, spanKindToStr(tracepb.Span_CLIENT), string(tracetranslator.OpenTracingSpanKindClient)) - assert.Equal(t, spanKindToStr(tracepb.Span_SERVER), string(tracetranslator.OpenTracingSpanKindServer)) - assert.Equal(t, attributeValueToString(&tracepb.AttributeValue{}), "") - assert.Equal(t, attributeValueToString(&tracepb.AttributeValue{ - Value: &tracepb.AttributeValue_StringValue{ - StringValue: annotation.Description, - }, - }), "testDesc") - assert.Equal(t, attributeValueToString(&tracepb.AttributeValue{ - Value: &tracepb.AttributeValue_BoolValue{ - BoolValue: true, - }, - }), "true") - assert.Equal(t, attributeValueToString(&tracepb.AttributeValue{ - Value: &tracepb.AttributeValue_BoolValue{ - BoolValue: false, - }, - }), "false") - assert.Equal(t, attributeValueToString(&tracepb.AttributeValue{ - Value: &tracepb.AttributeValue_IntValue{ - IntValue: 1, - }, - }), "1") - assert.Equal(t, attributeValueToString(&tracepb.AttributeValue{ - Value: &tracepb.AttributeValue_DoubleValue{ - DoubleValue: 1, - }, - }), "1") - rst := linksToLogContents(nil) - assert.Nil(t, rst) - rstLogs := spansToLogServiceData(nil) - assert.Nil(t, rstLogs) - rstLogContents := nodeAndResourceToLogContent(nil, nil) - assert.Equal(t, len(rstLogContents), 0) - - rstLogContents = nodeAndResourceToLogContent(&commonpb.Node{}, &resourcepb.Resource{}) - assert.Equal(t, len(rstLogContents), 0) - - rstLogContents = nodeAndResourceToLogContent(&commonpb.Node{ - LibraryInfo: &commonpb.LibraryInfo{ - Language: commonpb.LibraryInfo_GO_LANG, - ExporterVersion: "v_1.0", - CoreLibraryVersion: "v0.1", - }, - }, &resourcepb.Resource{}) - assert.Equal(t, len(rstLogContents), 4) +func TestStatusCodeToShortString(t *testing.T) { + assert.Equal(t, statusCodeToShortString(pdata.StatusCodeOk), "OK") + assert.Equal(t, statusCodeToShortString(pdata.StatusCodeError), "ERROR") + assert.Equal(t, statusCodeToShortString(pdata.StatusCodeUnset), "UNSET") }