From 945e7b039e93665a2a15793366af262dc4575ae8 Mon Sep 17 00:00:00 2001 From: Kevin Brockhoff Date: Tue, 26 May 2020 11:28:53 -0500 Subject: [PATCH] Generate "Golden Data" trace spans (#967) 1. PICT tool input files for trace data and resulting output files from running pict from the command line. These are in `internal/goldendataset/testdata`. See: [Pairwise Independent Combinatorial Testing](https://github.com/microsoft/pict) 2. Generator for fully-populated OLTP ResourceSpans from PICT output files. This has all the intended functionality for this PR. It has no impact on other functionality so there should be no problem in merging to master. I will follow up with other PRs to complete full "Golden Dataset" testing functionality. **Link to tracking Issue:** #652 **Testing:** Unit tests exist for all functionality which has been coded so far **Documentation:** None yet. Will do so after golden dataset generation coding is complete. --- internal/goldendataset/generator_commons.go | 105 ++++ .../goldendataset/pict_tracing_input_defs.go | 184 +++++++ internal/goldendataset/resource_generator.go | 144 +++++ .../goldendataset/resource_generator_test.go | 49 ++ internal/goldendataset/span_generator.go | 514 ++++++++++++++++++ internal/goldendataset/span_generator_test.go | 78 +++ .../testdata/generated_pict_pairs_spans.txt | 307 +++++++++++ .../testdata/generated_pict_pairs_traces.txt | 29 + .../testdata/pict_input_spans.txt | 14 + .../testdata/pict_input_traces.txt | 3 + internal/goldendataset/traces_generator.go | 139 +++++ .../goldendataset/traces_generator_test.go | 31 ++ translator/conventions/opentelemetry.go | 58 +- translator/internaldata/metrics_to_oc_test.go | 4 +- translator/internaldata/oc_testdata_test.go | 20 +- translator/internaldata/oc_to_resource.go | 4 +- translator/internaldata/resource_to_oc.go | 4 +- 17 files changed, 1660 insertions(+), 27 deletions(-) create mode 100644 internal/goldendataset/generator_commons.go create mode 100644 internal/goldendataset/pict_tracing_input_defs.go create mode 100644 internal/goldendataset/resource_generator.go create mode 100644 internal/goldendataset/resource_generator_test.go create mode 100644 internal/goldendataset/span_generator.go create mode 100644 internal/goldendataset/span_generator_test.go create mode 100644 internal/goldendataset/testdata/generated_pict_pairs_spans.txt create mode 100644 internal/goldendataset/testdata/generated_pict_pairs_traces.txt create mode 100644 internal/goldendataset/testdata/pict_input_spans.txt create mode 100644 internal/goldendataset/testdata/pict_input_traces.txt create mode 100644 internal/goldendataset/traces_generator.go create mode 100644 internal/goldendataset/traces_generator_test.go diff --git a/internal/goldendataset/generator_commons.go b/internal/goldendataset/generator_commons.go new file mode 100644 index 00000000000..6a276cb17c9 --- /dev/null +++ b/internal/goldendataset/generator_commons.go @@ -0,0 +1,105 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + "encoding/csv" + "io" + "os" + "path/filepath" + + otlpcommon "github.com/open-telemetry/opentelemetry-proto/gen/go/common/v1" + "github.com/spf13/cast" +) + +func convertMapToAttributeKeyValues(attrsMap map[string]interface{}) []*otlpcommon.AttributeKeyValue { + if attrsMap == nil { + return nil + } + attrList := make([]*otlpcommon.AttributeKeyValue, len(attrsMap)) + index := 0 + for key, value := range attrsMap { + attrList[index] = constructAttributeKeyValue(key, value) + index++ + } + return attrList +} + +func constructAttributeKeyValue(key string, value interface{}) *otlpcommon.AttributeKeyValue { + var attr otlpcommon.AttributeKeyValue + switch val := value.(type) { + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + attr = otlpcommon.AttributeKeyValue{ + Key: key, + Type: otlpcommon.AttributeKeyValue_INT, + IntValue: cast.ToInt64(val), + } + case float32, float64: + attr = otlpcommon.AttributeKeyValue{ + Key: key, + Type: otlpcommon.AttributeKeyValue_DOUBLE, + DoubleValue: cast.ToFloat64(val), + } + case bool: + attr = otlpcommon.AttributeKeyValue{ + Key: key, + Type: otlpcommon.AttributeKeyValue_BOOL, + BoolValue: cast.ToBool(val), + } + default: + attr = otlpcommon.AttributeKeyValue{ + Key: key, + Type: otlpcommon.AttributeKeyValue_STRING, + StringValue: val.(string), + } + } + return &attr +} + +func loadPictOutputFile(fileName string) ([][]string, error) { + file, err := os.Open(filepath.Clean(fileName)) + if err != nil { + return nil, err + } + defer func() { + cerr := file.Close() + if err == nil { + err = cerr + } + }() + + reader := csv.NewReader(file) + reader.Comma = '\t' + + return reader.ReadAll() +} + +func generateTraceID(random io.Reader) []byte { + var r [16]byte + _, err := random.Read(r[:]) + if err != nil { + panic(err) + } + return r[:] +} + +func generateSpanID(random io.Reader) []byte { + var r [8]byte + _, err := random.Read(r[:]) + if err != nil { + panic(err) + } + return r[:] +} diff --git a/internal/goldendataset/pict_tracing_input_defs.go b/internal/goldendataset/pict_tracing_input_defs.go new file mode 100644 index 00000000000..c818cf0b0b3 --- /dev/null +++ b/internal/goldendataset/pict_tracing_input_defs.go @@ -0,0 +1,184 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +//// Start of PICT inputs for generating golden dataset ResourceSpans (pict_input_traces.txt) //// + +// Input columns in pict_input_traces.txt +const ( + TracesColumnResource = 0 + TracesColumnInstrumentationLibrary = 1 + TracesColumnSpans = 2 +) + +// Enumerates the supported types of resource instances that can be generated. +type PICTInputResource string + +const ( + ResourceNil PICTInputResource = "Nil" + ResourceEmpty PICTInputResource = "Empty" + ResourceVMOnPrem PICTInputResource = "VMOnPrem" + ResourceVMCloud PICTInputResource = "VMCloud" + ResourceK8sOnPrem PICTInputResource = "K8sOnPrem" + ResourceK8sCloud PICTInputResource = "K8sCloud" + ResourceFaas PICTInputResource = "Faas" +) + +// Enumerates the number and kind of instrumentation library instances that can be generated. +type PICTInputInstrumentationLibrary string + +const ( + LibraryNone PICTInputInstrumentationLibrary = "None" + LibraryOne PICTInputInstrumentationLibrary = "One" + LibraryTwo PICTInputInstrumentationLibrary = "Two" +) + +// Enumerates the relative sizes of tracing spans that can be attached to an instrumentation library span instance. +type PICTInputSpans string + +const ( + LibrarySpansNone PICTInputSpans = "None" + LibrarySpansOne PICTInputSpans = "One" + LibrarySpansSeveral PICTInputSpans = "Several" + LibrarySpansAll PICTInputSpans = "All" +) + +// PICTTracingInputs defines one pairwise combination of ResourceSpans variations +type PICTTracingInputs struct { + // Specifies the category of attributes to populate the Resource field with + Resource PICTInputResource + // Specifies the number and library categories to populte the InstrumentationLibrarySpans field with + InstrumentationLibrary PICTInputInstrumentationLibrary + // Specifies the relative number of spans to populate the InstrumentationLibrarySpans' Spans field with + Spans PICTInputSpans +} + +//// Start of PICT inputs for generating golden dataset Spans (pict_input_spans.txt) //// + +// Input columns in pict_input_spans.txt +const ( + SpansColumnParent = 0 + SpansColumnTracestate = 1 + SpansColumnKind = 2 + SpansColumnAttributes = 3 + SpansColumnEvents = 4 + SpansColumnLinks = 5 + SpansColumnStatus = 6 +) + +// Enumerates the parent/child types of spans that can be generated. +type PICTInputParent string + +const ( + SpanParentRoot PICTInputParent = "Root" + SpanParentChild PICTInputParent = "Child" +) + +// Enumerates the categories of tracestate values that can be generated for a span. +type PICTInputTracestate string + +const ( + TraceStateEmpty PICTInputTracestate = "Empty" + TraceStateOne PICTInputTracestate = "One" + TraceStateFour PICTInputTracestate = "Four" +) + +// Enumerates the span kind values that can be set for a span. +type PICTInputKind string + +const ( + SpanKindUnspecified PICTInputKind = "Unspecified" + SpanKindInternal PICTInputKind = "Internal" + SpanKindServer PICTInputKind = "Server" + SpanKindClient PICTInputKind = "Client" + SpanKindProducer PICTInputKind = "Producer" + SpanKindConsumer PICTInputKind = "Consumer" +) + +// Enumerates the categories of representative attributes a generated span can be populated with. +type PICTInputAttributes string + +const ( + SpanAttrNil PICTInputAttributes = "Nil" + SpanAttrEmpty PICTInputAttributes = "Empty" + SpanAttrDatabaseSQL PICTInputAttributes = "DatabaseSQL" + SpanAttrDatabaseNoSQL PICTInputAttributes = "DatabaseNoSQL" + SpanAttrFaaSDatasource PICTInputAttributes = "FaaSDatasource" + SpanAttrFaaSHTTP PICTInputAttributes = "FaaSHTTP" + SpanAttrFaaSPubSub PICTInputAttributes = "FaaSPubSub" + SpanAttrFaaSTimer PICTInputAttributes = "FaaSTimer" + SpanAttrFaaSOther PICTInputAttributes = "FaaSOther" + SpanAttrHTTPClient PICTInputAttributes = "HTTPClient" + SpanAttrHTTPServer PICTInputAttributes = "HTTPServer" + SpanAttrMessagingProducer PICTInputAttributes = "MessagingProducer" + SpanAttrMessagingConsumer PICTInputAttributes = "MessagingConsumer" + SpanAttrGRPCClient PICTInputAttributes = "gRPCClient" + SpanAttrGRPCServer PICTInputAttributes = "gRPCServer" + SpanAttrInternal PICTInputAttributes = "Internal" + SpanAttrMaxCount PICTInputAttributes = "MaxCount" +) + +// Enumerates the categories of events and/or links a generated span can be populated with. +type PICTInputSpanChild string + +const ( + SpanChildCountNil PICTInputSpanChild = "Nil" + SpanChildCountEmpty PICTInputSpanChild = "Empty" + SpanChildCountOne PICTInputSpanChild = "One" + SpanChildCountTwo PICTInputSpanChild = "Two" + SpanChildCountEight PICTInputSpanChild = "Eight" +) + +// Enumerates the status values a generated span can be populated with. +type PICTInputStatus string + +const ( + SpanStatusNil PICTInputStatus = "Nil" + SpanStatusOk PICTInputStatus = "Ok" + SpanStatusCancelled PICTInputStatus = "Cancelled" + SpanStatusUnknownError PICTInputStatus = "UnknownError" + SpanStatusInvalidArgument PICTInputStatus = "InvalidArgument" + SpanStatusDeadlineExceeded PICTInputStatus = "DeadlineExceeded" + SpanStatusNotFound PICTInputStatus = "NotFound" + SpanStatusAlreadyExists PICTInputStatus = "AlreadyExists" + SpanStatusPermissionDenied PICTInputStatus = "PermissionDenied" + SpanStatusResourceExhausted PICTInputStatus = "ResourceExhausted" + SpanStatusFailedPrecondition PICTInputStatus = "FailedPrecondition" + SpanStatusAborted PICTInputStatus = "Aborted" + SpanStatusOutOfRange PICTInputStatus = "OutOfRange" + SpanStatusUnimplemented PICTInputStatus = "Unimplemented" + SpanStatusInternalError PICTInputStatus = "InternalError" + SpanStatusUnavailable PICTInputStatus = "Unavailable" + SpanStatusDataLoss PICTInputStatus = "DataLoss" + SpanStatusUnauthenticated PICTInputStatus = "Unauthenticated" +) + +// PICTSpanInputs defines one pairwise combination of Span variations +type PICTSpanInputs struct { + // Specifies whether the ParentSpanId field should be populated or not + Parent PICTInputParent + // Specifies the category of contents to populate the TraceState field with + Tracestate PICTInputTracestate + // Specifies the value to populate the Kind field with + Kind PICTInputKind + // Specifies the category of values to populate the Attributes field with + Attributes PICTInputAttributes + // Specifies the category of contents to populate the Events field with + Events PICTInputSpanChild + // Specifies the category of contents to populate the Links field with + Links PICTInputSpanChild + // Specifies the value to populate the Status field with + Status PICTInputStatus +} diff --git a/internal/goldendataset/resource_generator.go b/internal/goldendataset/resource_generator.go new file mode 100644 index 00000000000..ef350190fbb --- /dev/null +++ b/internal/goldendataset/resource_generator.go @@ -0,0 +1,144 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + otlpresource "github.com/open-telemetry/opentelemetry-proto/gen/go/resource/v1" + + "go.opentelemetry.io/collector/translator/conventions" +) + +//GenerateResource generates a OTLP Resource object with representative attributes for the +//underlying resource type specified by the rscID input parameter. +func GenerateResource(rscID PICTInputResource) *otlpresource.Resource { + var attrs map[string]interface{} + switch rscID { + case ResourceNil: + attrs = generateNilAttributes() + case ResourceEmpty: + attrs = generateEmptyAttributes() + case ResourceVMOnPrem: + attrs = generateOnpremVMAttributes() + case ResourceVMCloud: + attrs = generateCloudVMAttributes() + case ResourceK8sOnPrem: + attrs = generateOnpremK8sAttributes() + case ResourceK8sCloud: + attrs = generateCloudK8sAttributes() + case ResourceFaas: + attrs = generateFassAttributes() + default: + attrs = generateEmptyAttributes() + } + var dropped uint32 + if len(attrs) < 10 { + dropped = 0 + } else { + dropped = uint32(len(attrs) % 4) + } + return &otlpresource.Resource{ + Attributes: convertMapToAttributeKeyValues(attrs), + DroppedAttributesCount: dropped, + } +} + +func generateNilAttributes() map[string]interface{} { + return nil +} + +func generateEmptyAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + return attrMap +} + +func generateOnpremVMAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeServiceName] = "customers" + attrMap[conventions.AttributeServiceNamespace] = "production" + attrMap[conventions.AttributeServiceVersion] = "semver:0.7.3" + attrMap[conventions.AttributeHostHostname] = "tc-prod9.internal.example.com" + attrMap[conventions.AttributeHostName] = "172.18.36.18" + attrMap[conventions.AttributeHostImageID] = "661ADFA6-E293-4870-9EFA-1AA052C49F18" + attrMap[conventions.AttributeTelemetrySDKLanguage] = "java" + attrMap[conventions.AttributeTelemetrySDKName] = "opentelemetry" + attrMap[conventions.AttributeTelemetrySDKVersion] = "0.3.0" + return attrMap +} + +func generateCloudVMAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeServiceName] = "shoppingcart" + attrMap[conventions.AttributeServiceName] = "customers" + attrMap[conventions.AttributeServiceNamespace] = "production" + attrMap[conventions.AttributeServiceVersion] = "semver:0.7.3" + attrMap[conventions.AttributeTelemetrySDKLanguage] = "java" + attrMap[conventions.AttributeTelemetrySDKName] = "opentelemetry" + attrMap[conventions.AttributeTelemetrySDKVersion] = "0.3.0" + attrMap[conventions.AttributeHostHostname] = "env-check" + attrMap[conventions.AttributeHostID] = "57e8add1f79a454bae9fb1f7756a009a" + attrMap[conventions.AttributeHostName] = "10.0.0.4" + attrMap[conventions.AttributeHostImageID] = "5.3.0-1020-azure" + attrMap[conventions.AttributeHostType] = "B1ms" + attrMap[conventions.AttributeCloudProvider] = "azure" + attrMap[conventions.AttributeCloudAccount] = "2f5b8278-4b80-4930-a6bb-d86fc63a2534" + attrMap[conventions.AttributeCloudRegion] = "South Central US" + return attrMap +} + +func generateOnpremK8sAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeContainerName] = "cert-manager" + attrMap[conventions.AttributeContainerImage] = "quay.io/jetstack/cert-manager-controller:v0.14.2" + attrMap[conventions.AttributeK8sCluster] = "docker-desktop" + attrMap[conventions.AttributeK8sNamespace] = "cert-manager" + attrMap[conventions.AttributeK8sDeployment] = "cm-1-cert-manager" + attrMap[conventions.AttributeK8sPod] = "cm-1-cert-manager-6448b4949b-t2jtd" + attrMap[conventions.AttributeHostHostname] = "docker-desktop" + attrMap[conventions.AttributeHostName] = "192.168.65.3" + return attrMap +} + +func generateCloudK8sAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeContainerName] = "otel-collector" + attrMap[conventions.AttributeContainerImage] = "otel/opentelemetry-collector-contrib" + attrMap[conventions.AttributeContainerTag] = "0.4.0" + attrMap[conventions.AttributeK8sCluster] = "erp-dev" + attrMap[conventions.AttributeK8sNamespace] = "monitoring" + attrMap[conventions.AttributeK8sDeployment] = "otel-collector" + attrMap[conventions.AttributeK8sPod] = "otel-collector-6484db5844-c6f9m" + attrMap[conventions.AttributeHostHostname] = "ip-10-99-118-157.ec2.internal" + attrMap[conventions.AttributeHostID] = "ec2e3fdaffa294348bdf355156b94cda" + attrMap[conventions.AttributeHostName] = "10.99.118.157" + attrMap[conventions.AttributeHostImageID] = "ami-011c865bf7da41a9d" + attrMap[conventions.AttributeHostType] = "m5.xlarge" + attrMap[conventions.AttributeCloudProvider] = "aws" + attrMap[conventions.AttributeCloudAccount] = "12345678901" + attrMap[conventions.AttributeCloudRegion] = "us-east-1" + attrMap[conventions.AttributeCloudZone] = "us-east-1c" + return attrMap +} + +func generateFassAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeFaasID] = "https://us-central1-dist-system-demo.cloudfunctions.net/env-vars-print" + attrMap[conventions.AttributeFaasName] = "env-vars-print" + attrMap[conventions.AttributeFaasVersion] = "semver:1.0.0" + attrMap[conventions.AttributeCloudProvider] = "gcp" + attrMap[conventions.AttributeCloudAccount] = "opentelemetry" + attrMap[conventions.AttributeCloudRegion] = "us-central1" + attrMap[conventions.AttributeCloudZone] = "us-central1-a" + return attrMap +} diff --git a/internal/goldendataset/resource_generator_test.go b/internal/goldendataset/resource_generator_test.go new file mode 100644 index 00000000000..a7a6ad20b31 --- /dev/null +++ b/internal/goldendataset/resource_generator_test.go @@ -0,0 +1,49 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + "testing" + + "github.com/golang/protobuf/proto" + otlpresource "github.com/open-telemetry/opentelemetry-proto/gen/go/resource/v1" + "github.com/stretchr/testify/assert" +) + +func TestGenerateResource(t *testing.T) { + resourceIds := []PICTInputResource{ResourceNil, ResourceEmpty, ResourceVMOnPrem, ResourceVMCloud, ResourceK8sOnPrem, + ResourceK8sCloud, ResourceFaas} + for _, rscID := range resourceIds { + rsc := GenerateResource(rscID) + if rscID == ResourceNil { + assert.Nil(t, rsc.Attributes) + } else { + assert.NotNil(t, rsc.Attributes) + } + // test marshal/unmarshal + bytes, err := proto.Marshal(rsc) + if err != nil { + assert.Fail(t, err.Error()) + } + if len(bytes) > 0 { + copy := &otlpresource.Resource{} + err = proto.Unmarshal(bytes, copy) + if err != nil { + assert.Fail(t, err.Error()) + } + assert.EqualValues(t, len(rsc.Attributes), len(copy.Attributes)) + } + } +} diff --git a/internal/goldendataset/span_generator.go b/internal/goldendataset/span_generator.go new file mode 100644 index 00000000000..134b9e6ee29 --- /dev/null +++ b/internal/goldendataset/span_generator.go @@ -0,0 +1,514 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + "fmt" + "io" + "time" + + otlpcommon "github.com/open-telemetry/opentelemetry-proto/gen/go/common/v1" + otlptrace "github.com/open-telemetry/opentelemetry-proto/gen/go/trace/v1" + + "go.opentelemetry.io/collector/translator/conventions" +) + +var statusCodeMap = map[PICTInputStatus]otlptrace.Status_StatusCode{ + SpanStatusOk: otlptrace.Status_Ok, + SpanStatusCancelled: otlptrace.Status_Cancelled, + SpanStatusUnknownError: otlptrace.Status_UnknownError, + SpanStatusInvalidArgument: otlptrace.Status_InvalidArgument, + SpanStatusDeadlineExceeded: otlptrace.Status_DeadlineExceeded, + SpanStatusNotFound: otlptrace.Status_NotFound, + SpanStatusAlreadyExists: otlptrace.Status_AlreadyExists, + SpanStatusPermissionDenied: otlptrace.Status_PermissionDenied, + SpanStatusResourceExhausted: otlptrace.Status_ResourceExhausted, + SpanStatusFailedPrecondition: otlptrace.Status_FailedPrecondition, + SpanStatusAborted: otlptrace.Status_Aborted, + SpanStatusOutOfRange: otlptrace.Status_OutOfRange, + SpanStatusUnimplemented: otlptrace.Status_Unimplemented, + SpanStatusInternalError: otlptrace.Status_InternalError, + SpanStatusUnavailable: otlptrace.Status_Unavailable, + SpanStatusDataLoss: otlptrace.Status_DataLoss, + SpanStatusUnauthenticated: otlptrace.Status_Unauthenticated, +} + +var statusMsgMap = map[PICTInputStatus]string{ + SpanStatusOk: "", + SpanStatusCancelled: "Cancellation received", + SpanStatusUnknownError: "", + SpanStatusInvalidArgument: "parameter is required", + SpanStatusDeadlineExceeded: "timed out after 30002 ms", + SpanStatusNotFound: "/dragons/RomanianLonghorn not found", + SpanStatusAlreadyExists: "/dragons/Drogon already exists", + SpanStatusPermissionDenied: "tlannister does not have write permission", + SpanStatusResourceExhausted: "ResourceExhausted", + SpanStatusFailedPrecondition: "33a64df551425fcc55e4d42a148795d9f25f89d4 has been edited", + SpanStatusAborted: "", + SpanStatusOutOfRange: "Range Not Satisfiable", + SpanStatusUnimplemented: "Unimplemented", + SpanStatusInternalError: "java.lang.NullPointerException", + SpanStatusUnavailable: "RecommendationService is currently unavailable", + SpanStatusDataLoss: "", + SpanStatusUnauthenticated: "nstark is unknown user", +} + +//GenerateSpans generates a slice of OTLP Span objects with the number of spans specified by the count input +//parameter. The startPos parameter specifies the line in the PICT tool-generated, test parameter +//combination records file specified by the pictFile parameter to start reading from. When the end record +//is reached it loops back to the first record. The random parameter injects the random number generator +//to use in generating IDs and other random values. Using a random number generator with the same seed value +//enables reproducible tests. +// +//The return values are the slice with the generated spans, the starting position for the next generation +//run and the error which caused the spans generation to fail. If err is not nil, the spans slice will +//have nil values. +func GenerateSpans(count int, startPos int, pictFile string, random io.Reader) ([]*otlptrace.Span, int, error) { + pairsData, err := loadPictOutputFile(pictFile) + if err != nil { + return nil, 0, err + } + pairsTotal := len(pairsData) + spanList := make([]*otlptrace.Span, count) + index := startPos + 1 + var inputs []string + var spanInputs *PICTSpanInputs + var traceID []byte + var parentID []byte + for i := 0; i < count; i++ { + if index >= pairsTotal { + index = 1 + } + inputs = pairsData[index] + spanInputs = &PICTSpanInputs{ + Parent: PICTInputParent(inputs[SpansColumnParent]), + Tracestate: PICTInputTracestate(inputs[SpansColumnTracestate]), + Kind: PICTInputKind(inputs[SpansColumnKind]), + Attributes: PICTInputAttributes(inputs[SpansColumnAttributes]), + Events: PICTInputSpanChild(inputs[SpansColumnEvents]), + Links: PICTInputSpanChild(inputs[SpansColumnLinks]), + Status: PICTInputStatus(inputs[SpansColumnStatus]), + } + switch spanInputs.Parent { + case SpanParentRoot: + traceID = generateTraceID(random) + parentID = nil + case SpanParentChild: + // use existing if available + if traceID == nil { + traceID = generateTraceID(random) + } + if parentID == nil { + parentID = generateSpanID(random) + } + } + spanName := generateSpanName(spanInputs) + spanList[i] = GenerateSpan(traceID, parentID, spanName, spanInputs, random) + parentID = spanList[i].SpanId + index++ + } + return spanList, index, nil +} + +func generateSpanName(spanInputs *PICTSpanInputs) string { + return fmt.Sprintf("/%s/%s/%s/%s/%s/%s/%s", spanInputs.Parent, spanInputs.Tracestate, spanInputs.Kind, + spanInputs.Attributes, spanInputs.Events, spanInputs.Links, spanInputs.Status) +} + +//GenerateSpan generates a single OTLP Span based on the input values provided. They are: +// traceID - the trace ID to use, should not be nil +// parentID - the parent span ID or nil if it is a root span +// spanName - the span name, should not be blank +// spanInputs - the pairwise combination of field value variations for this span +// random - the random number generator to use in generating ID values +// +//The generated span is returned. +func GenerateSpan(traceID []byte, parentID []byte, spanName string, spanInputs *PICTSpanInputs, + random io.Reader) *otlptrace.Span { + endTime := time.Now().Add(-50 * time.Microsecond) + return &otlptrace.Span{ + TraceId: traceID, + SpanId: generateSpanID(random), + TraceState: generateTraceState(spanInputs.Tracestate), + ParentSpanId: parentID, + Name: spanName, + Kind: lookupSpanKind(spanInputs.Kind), + StartTimeUnixNano: uint64(endTime.Add(-215 * time.Millisecond).UnixNano()), + EndTimeUnixNano: uint64(endTime.UnixNano()), + Attributes: generateSpanAttributes(spanInputs.Attributes), + DroppedAttributesCount: 0, + Events: generateSpanEvents(spanInputs.Events), + DroppedEventsCount: 0, + Links: generateSpanLinks(spanInputs.Links, random), + DroppedLinksCount: 0, + Status: generateStatus(spanInputs.Status), + } +} + +func generateTraceState(tracestate PICTInputTracestate) string { + switch tracestate { + case TraceStateOne: + return "lasterror=f39cd56cc44274fd5abd07ef1164246d10ce2955" + case TraceStateFour: + return "err@ck=80ee5638,rate@ck=1.62,rojo=00f067aa0ba902b7,congo=t61rcWkgMzE" + case TraceStateEmpty: + fallthrough + default: + return "" + } +} + +func lookupSpanKind(kind PICTInputKind) otlptrace.Span_SpanKind { + switch kind { + case SpanKindClient: + return otlptrace.Span_CLIENT + case SpanKindServer: + return otlptrace.Span_SERVER + case SpanKindProducer: + return otlptrace.Span_PRODUCER + case SpanKindConsumer: + return otlptrace.Span_CONSUMER + case SpanKindInternal: + return otlptrace.Span_INTERNAL + case SpanKindUnspecified: + fallthrough + default: + return otlptrace.Span_SPAN_KIND_UNSPECIFIED + } +} + +func generateSpanAttributes(spanTypeID PICTInputAttributes) []*otlpcommon.AttributeKeyValue { + var attrs map[string]interface{} + switch spanTypeID { + case SpanAttrNil: + attrs = nil + case SpanAttrEmpty: + attrs = make(map[string]interface{}) + case SpanAttrDatabaseSQL: + attrs = generateDatabaseSQLAttributes() + case SpanAttrDatabaseNoSQL: + attrs = generateDatabaseNoSQLAttributes() + case SpanAttrFaaSDatasource: + attrs = generateFaaSDatasourceAttributes() + case SpanAttrFaaSHTTP: + attrs = generateFaaSHTTPAttributes() + case SpanAttrFaaSPubSub: + attrs = generateFaaSPubSubAttributes() + case SpanAttrFaaSTimer: + attrs = generateFaaSTimerAttributes() + case SpanAttrFaaSOther: + attrs = generateFaaSOtherAttributes() + case SpanAttrHTTPClient: + attrs = generateHTTPClientAttributes() + case SpanAttrHTTPServer: + attrs = generateHTTPServerAttributes() + case SpanAttrMessagingProducer: + attrs = generateMessagingProducerAttributes() + case SpanAttrMessagingConsumer: + attrs = generateMessagingConsumerAttributes() + case SpanAttrGRPCClient: + attrs = generateGRPCClientAttributes() + case SpanAttrGRPCServer: + attrs = generateGRPCServerAttributes() + case SpanAttrInternal: + attrs = generateInternalAttributes() + case SpanAttrMaxCount: + attrs = generateMaxCountAttributes() + default: + attrs = generateGRPCClientAttributes() + } + return convertMapToAttributeKeyValues(attrs) +} + +func generateStatus(statusStr PICTInputStatus) *otlptrace.Status { + if SpanStatusNil == statusStr { + return nil + } + return &otlptrace.Status{ + Code: statusCodeMap[statusStr], + Message: statusMsgMap[statusStr], + } +} + +func generateDatabaseSQLAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeDBType] = "sql" + attrMap[conventions.AttributeDBInstance] = "inventory" + attrMap[conventions.AttributeDBStatement] = + "SELECT c.product_catg_id, c.catg_name, c.description, c.html_frag, c.image_url, p.name FROM product_catg c OUTER JOIN product p ON c.product_catg_id=p.product_catg_id WHERE c.product_catg_id = :catgId" + attrMap[conventions.AttributeDBUser] = "invsvc" + attrMap[conventions.AttributeDBURL] = "jdbc:postgresql://invdev.cdsr3wfqepqo.us-east-1.rds.amazonaws.com:5432/inventory" + attrMap[conventions.AttributeNetPeerIP] = "172.30.2.7" + attrMap[conventions.AttributeNetPeerPort] = int64(5432) + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateDatabaseNoSQLAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeDBType] = "cosmosdb" + attrMap[conventions.AttributeDBInstance] = "graphdb" + attrMap[conventions.AttributeDBStatement] = "g.V().hasLabel('postive').has('age', gt(65)).values('geocode')" + attrMap[conventions.AttributeDBURL] = "wss://contacttrace.gremlin.cosmos.azure.com:443/" + attrMap[conventions.AttributeNetPeerIP] = "10.118.17.63" + attrMap[conventions.AttributeNetPeerPort] = int64(443) + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateFaaSDatasourceAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeFaaSTrigger] = conventions.FaaSTriggerDataSource + attrMap[conventions.AttributeFaaSExecution] = "DB85AF51-5E13-473D-8454-1E2D59415EAB" + attrMap[conventions.AttributeFaaSDocumentCollection] = "faa-flight-delay-information-incoming" + attrMap[conventions.AttributeFaaSDocumentOperation] = "insert" + attrMap[conventions.AttributeFaaSDocumentTime] = "2020-05-09T19:50:06Z" + attrMap[conventions.AttributeFaaSDocumentName] = "delays-20200509-13.csv" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateFaaSHTTPAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeFaaSTrigger] = conventions.FaaSTriggerHTTP + attrMap[conventions.AttributeHTTPMethod] = "POST" + attrMap[conventions.AttributeHTTPScheme] = "https" + attrMap[conventions.AttributeHTTPHost] = "api.opentelemetry.io" + attrMap[conventions.AttributeHTTPTarget] = "/blog/posts" + attrMap[conventions.AttributeHTTPFlavor] = "2" + attrMap[conventions.AttributeHTTPStatusCode] = int64(201) + attrMap[conventions.AttributeHTTPUserAgent] = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateFaaSPubSubAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeFaaSTrigger] = conventions.FaaSTriggerPubSub + attrMap[conventions.AttributeMessagingSystem] = "sqs" + attrMap[conventions.AttributeMessagingDestination] = "video-views-au" + attrMap[conventions.AttributeMessagingOperation] = "process" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateFaaSTimerAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeFaaSTrigger] = conventions.FaaSTriggerTimer + attrMap[conventions.AttributeFaaSExecution] = "73103A4C-E22F-4493-BDE8-EAE5CAB37B50" + attrMap[conventions.AttributeFaaSTime] = "2020-05-09T20:00:08Z" + attrMap[conventions.AttributeFaaSCron] = "0/15 * * * *" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateFaaSOtherAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeFaaSTrigger] = conventions.FaaSTriggerOther + attrMap["processed.count"] = int64(256) + attrMap["processed.data"] = 14.46 + attrMap["processed.errors"] = false + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateHTTPClientAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeHTTPMethod] = "GET" + attrMap[conventions.AttributeHTTPURL] = "https://opentelemetry.io/registry/" + attrMap[conventions.AttributeHTTPStatusCode] = int64(200) + attrMap[conventions.AttributeHTTPStatusText] = "More Than OK" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateHTTPServerAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeHTTPMethod] = "POST" + attrMap[conventions.AttributeHTTPScheme] = "https" + attrMap[conventions.AttributeHTTPServerName] = "api22.opentelemetry.io" + attrMap[conventions.AttributeNetHostPort] = int64(443) + attrMap[conventions.AttributeHTTPTarget] = "/blog/posts" + attrMap[conventions.AttributeHTTPFlavor] = "2" + attrMap[conventions.AttributeHTTPStatusCode] = int64(201) + attrMap[conventions.AttributeHTTPUserAgent] = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" + attrMap[conventions.AttributeHTTPRoute] = "/blog/posts" + attrMap[conventions.AttributeHTTPClientIP] = "2001:506:71f0:16e::1" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateMessagingProducerAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeMessagingSystem] = "nats" + attrMap[conventions.AttributeMessagingDestination] = "time.us.east.atlanta" + attrMap[conventions.AttributeMessagingDestinationKind] = "topic" + attrMap[conventions.AttributeMessagingMessageID] = "AA7C5438-D93A-43C8-9961-55613204648F" + attrMap["messaging.sequence"] = int64(1) + attrMap[conventions.AttributeNetPeerIP] = "10.10.212.33" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateMessagingConsumerAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeMessagingSystem] = "kafka" + attrMap[conventions.AttributeMessagingDestination] = "infrastructure-events-zone1" + attrMap[conventions.AttributeMessagingOperation] = "receive" + attrMap[conventions.AttributeNetPeerIP] = "2600:1700:1f00:11c0:4de0:c223:a800:4e87" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateGRPCClientAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeRPCService] = "PullRequestsService" + attrMap[conventions.AttributeNetPeerIP] = "2600:1700:1f00:11c0:4de0:c223:a800:4e87" + attrMap[conventions.AttributeNetHostPort] = int64(8443) + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateGRPCServerAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeRPCService] = "PullRequestsService" + attrMap[conventions.AttributeNetPeerIP] = "192.168.1.70" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateInternalAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap["parameters"] = "account=7310,amount=1817.10" + attrMap[conventions.AttributeEnduserID] = "unittest" + return attrMap +} + +func generateMaxCountAttributes() map[string]interface{} { + attrMap := make(map[string]interface{}) + attrMap[conventions.AttributeHTTPMethod] = "POST" + attrMap[conventions.AttributeHTTPScheme] = "https" + attrMap[conventions.AttributeHTTPHost] = "api.opentelemetry.io" + attrMap[conventions.AttributeNetHostName] = "api22.opentelemetry.io" + attrMap[conventions.AttributeNetHostIP] = "2600:1700:1f00:11c0:1ced:afa5:fd88:9d48" + attrMap[conventions.AttributeNetHostPort] = int64(443) + attrMap[conventions.AttributeHTTPTarget] = "/blog/posts" + attrMap[conventions.AttributeHTTPFlavor] = "2" + attrMap[conventions.AttributeHTTPStatusCode] = int64(201) + attrMap[conventions.AttributeHTTPStatusText] = "Created" + attrMap[conventions.AttributeHTTPUserAgent] = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" + attrMap[conventions.AttributeHTTPRoute] = "/blog/posts" + attrMap[conventions.AttributeHTTPClientIP] = "2600:1700:1f00:11c0:1ced:afa5:fd77:9d01" + attrMap[conventions.AttributeNetPeerIP] = "2600:1700:1f00:11c0:1ced:afa5:fd77:9ddc" + attrMap[conventions.AttributeNetPeerPort] = int64(39111) + attrMap["ai-sampler.weight"] = float64(0.07) + attrMap["ai-sampler.absolute"] = false + attrMap["ai-sampler.maxhops"] = int64(6) + attrMap["application.create.location"] = "https://api.opentelemetry.io/blog/posts/806673B9-4F4D-4284-9635-3A3E3E3805BE" + attrMap["application.svcmap"] = "Blogosphere" + attrMap["application.abflags"] = "UIx=false,UI4=true,flow-alt3=false" + attrMap["application.thread"] = "proc-pool-14" + attrMap["application.session"] = "233CC260-63A8-4ACA-A1C1-8F97AB4A2C01" + attrMap["application.persist.size"] = int64(1172184) + attrMap["application.queue.size"] = int64(0) + attrMap["application.validation.results"] = "Success" + attrMap["application.job.id"] = "0E38800B-9C4C-484E-8F2B-C7864D854321" + attrMap["application.service.sla"] = float64(0.34) + attrMap["application.service.slo"] = float64(0.55) + attrMap[conventions.AttributeEnduserID] = "unittest" + attrMap[conventions.AttributeEnduserRole] = "poweruser" + attrMap[conventions.AttributeEnduserScope] = "email profile administrator" + return attrMap +} + +func generateSpanEvents(eventCnt PICTInputSpanChild) []*otlptrace.Span_Event { + if SpanChildCountNil == eventCnt { + return nil + } + listSize := calculateListSize(eventCnt) + eventList := make([]*otlptrace.Span_Event, listSize) + for i := 0; i < listSize; i++ { + eventList[i] = generateSpanEvent(i) + } + return eventList +} + +func generateSpanLinks(linkCnt PICTInputSpanChild, random io.Reader) []*otlptrace.Span_Link { + if SpanChildCountNil == linkCnt { + return nil + } + listSize := calculateListSize(linkCnt) + linkList := make([]*otlptrace.Span_Link, listSize) + for i := 0; i < listSize; i++ { + linkList[i] = generateSpanLink(i, random) + } + return linkList +} + +func calculateListSize(listCnt PICTInputSpanChild) int { + switch listCnt { + case SpanChildCountOne: + return 1 + case SpanChildCountTwo: + return 2 + case SpanChildCountEight: + return 8 + case SpanChildCountEmpty: + fallthrough + default: + return 0 + } +} + +func generateSpanEvent(index int) *otlptrace.Span_Event { + t := time.Now().Add(-75 * time.Microsecond) + return &otlptrace.Span_Event{ + TimeUnixNano: uint64(t.UnixNano()), + Name: "message", + Attributes: generateEventAttributes(index), + DroppedAttributesCount: 0, + } +} + +func generateEventAttributes(index int) []*otlpcommon.AttributeKeyValue { + attrMap := make(map[string]interface{}) + if index%2 == 0 { + attrMap[conventions.AttributeMessageType] = "SENT" + } else { + attrMap[conventions.AttributeMessageType] = "RECEIVED" + } + attrMap[conventions.AttributeMessageID] = int64(index) + attrMap[conventions.AttributeMessageCompressedSize] = int64(17 * index) + attrMap[conventions.AttributeMessageUncompressedSize] = int64(24 * index) + return convertMapToAttributeKeyValues(attrMap) +} + +func generateSpanLink(index int, random io.Reader) *otlptrace.Span_Link { + return &otlptrace.Span_Link{ + TraceId: generateTraceID(random), + SpanId: generateSpanID(random), + TraceState: "", + Attributes: generateLinkAttributes(index), + DroppedAttributesCount: 0, + } +} + +func generateLinkAttributes(index int) []*otlpcommon.AttributeKeyValue { + attrMap := generateMessagingConsumerAttributes() + return convertMapToAttributeKeyValues(attrMap) +} diff --git a/internal/goldendataset/span_generator_test.go b/internal/goldendataset/span_generator_test.go new file mode 100644 index 00000000000..c672400c2f9 --- /dev/null +++ b/internal/goldendataset/span_generator_test.go @@ -0,0 +1,78 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + "crypto/rand" + "testing" + + otlptrace "github.com/open-telemetry/opentelemetry-proto/gen/go/trace/v1" + "github.com/stretchr/testify/assert" +) + +func TestGenerateParentSpan(t *testing.T) { + random := rand.Reader + traceID := generateTraceID(random) + spanInputs := &PICTSpanInputs{ + Parent: SpanParentRoot, + Tracestate: TraceStateEmpty, + Kind: SpanKindServer, + Attributes: SpanAttrHTTPServer, + Events: SpanChildCountTwo, + Links: SpanChildCountOne, + Status: SpanStatusOk, + } + span := GenerateSpan(traceID, nil, "/gotest-parent", spanInputs, random) + assert.Equal(t, traceID, span.TraceId) + assert.Nil(t, span.ParentSpanId) + assert.Equal(t, 11, len(span.Attributes)) + assert.Equal(t, otlptrace.Status_Ok, span.Status.Code) +} + +func TestGenerateChildSpan(t *testing.T) { + random := rand.Reader + traceID := generateTraceID(random) + parentID := generateSpanID(random) + spanInputs := &PICTSpanInputs{ + Parent: SpanParentChild, + Tracestate: TraceStateEmpty, + Kind: SpanKindClient, + Attributes: SpanAttrDatabaseSQL, + Events: SpanChildCountEmpty, + Links: SpanChildCountNil, + Status: SpanStatusOk, + } + span := GenerateSpan(traceID, parentID, "get_test_info", spanInputs, random) + assert.Equal(t, traceID, span.TraceId) + assert.Equal(t, parentID, span.ParentSpanId) + assert.Equal(t, 8, len(span.Attributes)) + assert.Equal(t, otlptrace.Status_Ok, span.Status.Code) +} + +func TestGenerateSpans(t *testing.T) { + random := rand.Reader + count1 := 16 + spans, nextPos, err := GenerateSpans(count1, 0, "testdata/generated_pict_pairs_spans.txt", random) + assert.Nil(t, err) + assert.Equal(t, count1, len(spans)) + count2 := 256 + spans, nextPos, err = GenerateSpans(count2, nextPos, "testdata/generated_pict_pairs_spans.txt", random) + assert.Nil(t, err) + assert.Equal(t, count2, len(spans)) + count3 := 118 + spans, _, err = GenerateSpans(count3, nextPos, "testdata/generated_pict_pairs_spans.txt", random) + assert.Nil(t, err) + assert.Equal(t, count3, len(spans)) +} diff --git a/internal/goldendataset/testdata/generated_pict_pairs_spans.txt b/internal/goldendataset/testdata/generated_pict_pairs_spans.txt new file mode 100644 index 00000000000..61d1bae4452 --- /dev/null +++ b/internal/goldendataset/testdata/generated_pict_pairs_spans.txt @@ -0,0 +1,307 @@ +Parent Tracestate Kind Attributes Events Links Status +Child One Consumer FaaSDatasource Empty Nil AlreadyExists +Child Empty Unspecified gRPCClient Two One ResourceExhausted +Child Four Client gRPCClient Eight Eight DataLoss +Root Four Server FaaSHTTP One Empty ResourceExhausted +Child One Server FaaSOther Nil Two Unimplemented +Child One Unspecified HTTPClient Nil Eight InternalError +Root One Producer FaaSPubSub Two Empty Cancelled +Child One Client DatabaseSQL One One PermissionDenied +Child Four Unspecified FaaSTimer Empty Two FailedPrecondition +Child Empty Unspecified MessagingConsumer Eight Nil InvalidArgument +Root Empty Server FaaSTimer Two Eight AlreadyExists +Child One Internal Nil Eight Two ResourceExhausted +Child Empty Unspecified FaaSHTTP Nil Nil DeadlineExceeded +Child Empty Producer MessagingProducer Empty Empty Ok +Root Four Server HTTPServer Nil One Ok +Root Four Producer Empty One Nil OutOfRange +Child Empty Consumer FaaSDatasource One Two Unavailable +Child One Client gRPCClient Nil Empty OutOfRange +Child Empty Internal Internal One Eight FailedPrecondition +Root Empty Server FaaSTimer Eight One OutOfRange +Child Four Consumer FaaSDatasource Empty One Unauthenticated +Child Empty Client HTTPClient Two Nil DataLoss +Child Empty Unspecified FaaSPubSub Empty Two UnknownError +Child Four Client gRPCClient Two Two Ok +Child Four Unspecified HTTPClient Eight Empty Ok +Root One Server FaaSHTTP Empty Eight Aborted +Child One Client DatabaseNoSQL Nil One FailedPrecondition +Child Empty Client HTTPClient Empty One ResourceExhausted +Child Four Internal Nil Nil Empty AlreadyExists +Root Four Producer FaaSPubSub Eight One AlreadyExists +Child Four Client HTTPClient One Two InvalidArgument +Root One Server FaaSTimer Nil Nil UnknownError +Child Empty Unspecified HTTPServer One Two Cancelled +Child Four Server FaaSHTTP Eight One Cancelled +Child Empty Server FaaSTimer Nil Eight ResourceExhausted +Root One Server gRPCServer Nil Eight InvalidArgument +Child Four Unspecified gRPCServer Two Two Nil +Child One Consumer MessagingConsumer Nil Eight ResourceExhausted +Child One Unspecified MessagingProducer Two Nil FailedPrecondition +Child Four Consumer MessagingConsumer Two Empty Unavailable +Child One Producer FaaSPubSub One Nil Ok +Root Four Server MaxCount Empty Nil Cancelled +Root One Server HTTPServer Empty Eight DeadlineExceeded +Child One Consumer MessagingConsumer Empty Two FailedPrecondition +Child Empty Unspecified MaxCount Two One InvalidArgument +Child One Unspecified FaaSHTTP Two Two OutOfRange +Child Four Unspecified DatabaseSQL Eight Two Aborted +Child One Unspecified MaxCount Eight Eight UnknownError +Child Four Unspecified FaaSOther Eight Empty FailedPrecondition +Root One Server HTTPServer Eight Nil Unavailable +Root Empty Server MaxCount Nil Two Ok +Child Empty Consumer MessagingConsumer One One Aborted +Child One Client Empty Eight One Nil +Root Four Producer MessagingProducer Eight Eight PermissionDenied +Child Empty Internal Nil Empty Nil Nil +Child Empty Unspecified DatabaseNoSQL Two Two NotFound +Child Empty Client DatabaseSQL Nil Empty Nil +Child Four Producer FaaSPubSub Nil Eight ResourceExhausted +Child Empty Unspecified FaaSOther Two One DeadlineExceeded +Child Four Consumer FaaSDatasource Eight Empty InternalError +Root Empty Producer Empty Two Two ResourceExhausted +Root Four Server FaaSOther One Eight Nil +Child Four Internal Internal Two Nil PermissionDenied +Child One Client DatabaseSQL Empty Eight FailedPrecondition +Child Four Producer MessagingProducer One One InvalidArgument +Child Four Unspecified DatabaseNoSQL Empty Empty InvalidArgument +Child Four Unspecified DatabaseNoSQL One Nil ResourceExhausted +Child Empty Producer MessagingProducer Nil Nil Aborted +Child Empty Server gRPCServer Empty Empty Aborted +Child One Unspecified DatabaseNoSQL Eight One DataLoss +Root One Producer MessagingProducer Nil Two DataLoss +Root Four Producer FaaSPubSub Empty One FailedPrecondition +Child Four Client DatabaseNoSQL Empty Eight Unavailable +Child Four Consumer Nil One One NotFound +Root One Server Nil Two Eight DataLoss +Child Four Internal Internal Nil One UnknownError +Child One Producer FaaSPubSub Nil One Unavailable +Child Four Client DatabaseNoSQL Two Empty Unimplemented +Child One Unspecified FaaSOther Empty Empty UnknownError +Child One Client gRPCClient Empty Nil Nil +Child One Unspecified Internal Eight Two Nil +Child Four Unspecified FaaSDatasource Two Eight Ok +Child One Unspecified Empty Nil Empty Ok +Child One Consumer FaaSDatasource Empty Eight OutOfRange +Child Empty Consumer MessagingConsumer Eight One Unimplemented +Child Empty Unspecified Nil One Eight Unimplemented +Child Four Client gRPCClient One Nil Unimplemented +Child Empty Unspecified DatabaseSQL Two Nil Ok +Child One Client DatabaseNoSQL Nil Eight Unauthenticated +Child Four Internal Internal One Empty DeadlineExceeded +Child One Unspecified gRPCServer One Nil OutOfRange +Child Empty Unspecified MaxCount One Two AlreadyExists +Root Empty Server FaaSOther Nil Empty PermissionDenied +Child Four Internal Internal Empty Two InvalidArgument +Root Four Producer MessagingProducer Eight Two DeadlineExceeded +Root One Server FaaSOther Eight Nil NotFound +Child Empty Unspecified Nil Two One Unavailable +Child Four Internal Internal Nil Eight Ok +Child Four Producer Empty Empty Eight FailedPrecondition +Child One Server gRPCServer Eight One DeadlineExceeded +Child Four Consumer MessagingConsumer Two Nil Nil +Root Four Server gRPCServer Eight Two FailedPrecondition +Root Four Producer Empty Empty Nil Unavailable +Root Empty Server HTTPServer Two Empty Unauthenticated +Child Empty Unspecified FaaSHTTP One Empty DataLoss +Child Four Client DatabaseNoSQL One Nil DeadlineExceeded +Root One Producer FaaSPubSub Empty Nil Unimplemented +Root Empty Producer MessagingProducer One One InternalError +Child Empty Unspecified FaaSOther Two Empty AlreadyExists +Child Empty Unspecified DatabaseSQL Empty Nil ResourceExhausted +Child Four Unspecified gRPCClient Eight Nil Unauthenticated +Child Four Client HTTPClient Two Nil UnknownError +Child Four Unspecified HTTPServer Empty Two PermissionDenied +Root Four Producer MessagingProducer One Two AlreadyExists +Child One Unspecified HTTPClient Eight Two PermissionDenied +Child Four Consumer Nil Nil Two Ok +Child Empty Internal Internal Nil Empty NotFound +Child Four Unspecified FaaSDatasource Nil Two FailedPrecondition +Root One Server MaxCount Empty Empty InternalError +Child One Consumer Nil One Eight InvalidArgument +Child One Unspecified HTTPClient Empty Nil OutOfRange +Child Four Client HTTPClient Empty One DeadlineExceeded +Child Empty Client DatabaseSQL Nil Eight Cancelled +Child Four Internal Internal Nil Two Cancelled +Child Four Consumer MessagingConsumer Two Nil InternalError +Child Empty Consumer MessagingConsumer Eight Nil OutOfRange +Root Four Producer MessagingProducer Empty Two Unimplemented +Root One Server FaaSTimer One Empty InvalidArgument +Child Empty Client Empty Empty Eight NotFound +Child Four Unspecified FaaSOther Two Two InternalError +Child One Client DatabaseNoSQL One One Ok +Child One Unspecified MessagingConsumer One Empty Ok +Child Four Unspecified FaaSHTTP Two Empty NotFound +Root Empty Server FaaSTimer Empty Eight Unimplemented +Child One Unspecified FaaSPubSub Nil Nil PermissionDenied +Root Empty Server HTTPServer Eight Two InvalidArgument +Child Four Client HTTPClient One Two Unauthenticated +Child Empty Server gRPCServer One Nil InternalError +Root Empty Producer MessagingProducer Empty Eight OutOfRange +Child Four Producer MessagingProducer Eight Nil Nil +Child Empty Consumer FaaSDatasource Eight Empty Unimplemented +Child Empty Unspecified FaaSPubSub Empty Eight DataLoss +Child Four Unspecified MessagingConsumer Empty Empty AlreadyExists +Child Empty Producer FaaSPubSub One One NotFound +Child One Internal Internal Two Nil InternalError +Root Four Server FaaSTimer Nil One NotFound +Child Four Unspecified FaaSOther Nil One Unavailable +Child Empty Unspecified FaaSHTTP One Nil InternalError +Child Empty Unspecified gRPCServer Eight Nil AlreadyExists +Child One Client HTTPClient Nil One Unimplemented +Child One Client HTTPClient Empty Eight NotFound +Child Four Consumer FaaSDatasource One Eight UnknownError +Root Empty Producer MessagingProducer Two Two Unauthenticated +Child Empty Unspecified FaaSDatasource Two One Aborted +Child One Consumer MessagingConsumer Empty Nil DataLoss +Child One Consumer MessagingConsumer Eight One Cancelled +Child Empty Unspecified FaaSDatasource One Two DataLoss +Child Empty Client gRPCClient Empty Eight FailedPrecondition +Child Empty Unspecified Internal Eight Two ResourceExhausted +Child Empty Client gRPCClient One Nil InternalError +Child Empty Consumer Nil Two Nil PermissionDenied +Child Empty Producer FaaSPubSub One Eight OutOfRange +Child One Unspecified gRPCServer One Nil Ok +Child One Consumer FaaSDatasource One Empty DeadlineExceeded +Child One Unspecified FaaSDatasource Nil Eight NotFound +Child Empty Unspecified DatabaseNoSQL Empty Two PermissionDenied +Child One Unspecified FaaSHTTP Empty Empty UnknownError +Child Empty Server HTTPServer Empty One Aborted +Child Empty Unspecified HTTPClient Eight Eight Cancelled +Child Four Producer MessagingProducer One Empty Cancelled +Child Four Server MaxCount One Eight FailedPrecondition +Child Empty Internal Nil One Eight OutOfRange +Child One Unspecified gRPCServer Empty Two Cancelled +Child Four Server HTTPServer Nil Empty AlreadyExists +Child Four Unspecified Empty Two Two InvalidArgument +Root Empty Server HTTPServer Eight Two DataLoss +Child Empty Client gRPCClient Two Two Unavailable +Child Four Unspecified HTTPServer One One Nil +Child One Client gRPCClient Nil Eight DeadlineExceeded +Root One Server FaaSTimer Empty Eight Cancelled +Child Empty Consumer Nil Eight Eight Cancelled +Child Four Server FaaSTimer Eight Nil Ok +Root One Producer Empty Eight Empty UnknownError +Child One Client Empty Eight Nil AlreadyExists +Child Empty Internal Nil Eight Nil Unauthenticated +Child One Internal Nil Nil Eight DeadlineExceeded +Child One Producer Empty Two Two Cancelled +Child One Unspecified FaaSHTTP Eight Nil InvalidArgument +Child Empty Unspecified HTTPClient One One FailedPrecondition +Child One Unspecified HTTPServer Nil Empty ResourceExhausted +Child One Server Nil One Eight InternalError +Child Four Unspecified Empty Eight Nil Unauthenticated +Child Empty Unspecified MessagingConsumer Eight Two NotFound +Child Four Unspecified MaxCount Empty Eight NotFound +Child One Client gRPCClient One Two InvalidArgument +Child Four Unspecified DatabaseSQL Nil Empty InvalidArgument +Child Four Unspecified FaaSOther One Two OutOfRange +Child Empty Unspecified HTTPServer Two Nil FailedPrecondition +Child Empty Consumer FaaSDatasource Two Eight Nil +Child One Server FaaSTimer Nil One Aborted +Child Four Unspecified DatabaseNoSQL Two Empty UnknownError +Child Empty Server MaxCount Nil Nil OutOfRange +Child Four Unspecified FaaSTimer Nil Nil Unavailable +Child One Unspecified FaaSHTTP Eight Eight AlreadyExists +Child Empty Client DatabaseSQL Empty Eight UnknownError +Child One Producer Empty Eight Nil DeadlineExceeded +Child Empty Producer FaaSPubSub Empty One InternalError +Child Empty Unspecified gRPCClient Two One PermissionDenied +Child One Unspecified DatabaseSQL One Eight Unauthenticated +Child Four Client gRPCClient One Empty Cancelled +Child One Server MaxCount Empty Two Unimplemented +Child Empty Server Nil One Eight UnknownError +Root One Server gRPCServer Eight Eight DataLoss +Child Four Unspecified FaaSPubSub Two One Nil +Root One Server gRPCServer Nil Eight Unimplemented +Child One Server FaaSTimer Two Two Nil +Child Four Unspecified gRPCServer Two Eight Unauthenticated +Child Empty Server FaaSOther One Eight Unauthenticated +Child One Unspecified FaaSDatasource One Eight PermissionDenied +Child Empty Server Nil Two Two FailedPrecondition +Child One Unspecified Empty One Nil PermissionDenied +Child Four Internal Internal One Two Unimplemented +Child Empty Unspecified Empty Eight Two DataLoss +Child Empty Unspecified FaaSTimer Two Empty DeadlineExceeded +Child Empty Unspecified FaaSOther One Eight Aborted +Child One Unspecified FaaSOther One Nil ResourceExhausted +Child Empty Unspecified gRPCServer Two Nil PermissionDenied +Child Empty Unspecified MaxCount Eight Eight Aborted +Child One Consumer MessagingConsumer Two Nil Unauthenticated +Child Four Client Empty One One Unimplemented +Child Four Server MaxCount Two Eight PermissionDenied +Child One Unspecified FaaSDatasource Nil Nil ResourceExhausted +Child Empty Unspecified gRPCServer Eight Empty Unavailable +Child One Unspecified HTTPServer Nil One UnknownError +Child Four Internal Internal Nil Eight OutOfRange +Child One Unspecified FaaSOther One Nil Ok +Child Four Client DatabaseSQL Eight Two InternalError +Child Empty Unspecified DatabaseSQL One Eight NotFound +Child Empty Client DatabaseSQL One Nil OutOfRange +Child Four Server FaaSTimer Eight Empty Unauthenticated +Child Four Client DatabaseSQL One Nil AlreadyExists +Child Empty Unspecified HTTPServer Empty One InternalError +Root One Server MaxCount One One Nil +Child Four Unspecified MessagingProducer Two Nil ResourceExhausted +Child Four Client HTTPClient One Two Aborted +Child Empty Client DatabaseNoSQL Two Nil AlreadyExists +Child One Unspecified MaxCount Nil Empty DataLoss +Child One Internal Internal Empty Nil DataLoss +Child One Producer MessagingProducer One Two NotFound +Child One Unspecified FaaSTimer Two Two PermissionDenied +Root One Server FaaSOther Eight Empty Cancelled +Child Empty Client DatabaseSQL Empty One DeadlineExceeded +Child One Unspecified HTTPServer Two Eight Unimplemented +Child Four Client HTTPClient Nil Eight Nil +Root Empty Server MaxCount Nil Nil Unavailable +Child Four Internal Internal One One Aborted +Child One Unspecified FaaSHTTP Empty Nil PermissionDenied +Child One Unspecified FaaSHTTP Nil Two Unimplemented +Child One Unspecified MessagingConsumer Two Two PermissionDenied +Root One Server FaaSOther Nil Nil InvalidArgument +Child Empty Unspecified HTTPClient Empty Eight Unavailable +Child One Unspecified FaaSPubSub Eight Empty Unauthenticated +Child Empty Client gRPCClient Empty Empty AlreadyExists +Child One Unspecified DatabaseNoSQL One Empty InternalError +Root One Server FaaSHTTP One Empty Unauthenticated +Child Empty Server MaxCount Empty Empty ResourceExhausted +Child Four Client DatabaseSQL One Nil Unavailable +Root Four Server gRPCServer Nil Eight ResourceExhausted +Child Empty Internal Internal Nil Empty Unauthenticated +Child Four Unspecified HTTPServer Two Empty NotFound +Child Four Server MaxCount Two Eight Unauthenticated +Child Empty Unspecified MessagingConsumer Empty Two DeadlineExceeded +Child Four Client HTTPClient Two Two AlreadyExists +Child One Unspecified gRPCClient Nil Two NotFound +Child Empty Unspecified FaaSPubSub Nil Nil InvalidArgument +Child One Internal Internal Two Two AlreadyExists +Child Empty Consumer FaaSDatasource One Two InvalidArgument +Child Empty Server FaaSOther Nil Eight DataLoss +Child One Unspecified gRPCClient Nil Empty UnknownError +Child One Server Nil One Empty Aborted +Child Four Unspecified FaaSTimer One Two DataLoss +Child Empty Unspecified FaaSPubSub Empty One Aborted +Child One Unspecified FaaSHTTP Eight One Nil +Child One Client DatabaseSQL Eight Nil DataLoss +Child Empty Server HTTPServer Nil Eight OutOfRange +Child One Client gRPCClient Eight Two Aborted +Child One Unspecified DatabaseNoSQL Two Eight Nil +Child Four Client DatabaseNoSQL Eight Empty Aborted +Child Empty Internal Internal Eight One Unavailable +Child One Unspecified gRPCServer One Eight NotFound +Child Empty Unspecified FaaSHTTP One Two Ok +Child Four Unspecified gRPCServer One Empty UnknownError +Child Four Client DatabaseNoSQL One Nil Cancelled +Child Four Unspecified MessagingProducer Two Empty Unavailable +Child Empty Unspecified Empty Nil Eight Aborted +Child Four Server MaxCount Nil Nil DeadlineExceeded +Child Empty Client DatabaseSQL One Nil Unimplemented +Child Four Unspecified FaaSTimer Two Empty InternalError +Child Empty Unspecified DatabaseNoSQL One Eight OutOfRange +Root One Server FaaSHTTP Empty Empty Unavailable +Child One Unspecified FaaSDatasource Two Empty Cancelled +Child Empty Consumer MessagingConsumer Two One UnknownError +Child Empty Unspecified FaaSHTTP Two One FailedPrecondition +Child One Client Empty Two Nil InternalError +Root One Producer FaaSPubSub Eight Two DeadlineExceeded +Root One Producer MessagingProducer Empty Two UnknownError diff --git a/internal/goldendataset/testdata/generated_pict_pairs_traces.txt b/internal/goldendataset/testdata/generated_pict_pairs_traces.txt new file mode 100644 index 00000000000..59706ccec5e --- /dev/null +++ b/internal/goldendataset/testdata/generated_pict_pairs_traces.txt @@ -0,0 +1,29 @@ +Resource InstrumentationLibrary Spans +Nil None Several +Nil One One +Empty One None +K8sCloud None All +VMOnPrem Two All +Faas Two None +K8sOnPrem One Several +K8sOnPrem Two All +VMOnPrem None One +Faas One All +K8sCloud Two Several +Nil None None +K8sOnPrem None None +Faas None One +VMCloud One None +VMCloud Two All +Nil Two All +K8sCloud One None +K8sCloud Two One +VMCloud None One +Empty Two One +Faas One Several +VMOnPrem One None +K8sOnPrem One One +VMCloud None Several +Empty None All +Empty None Several +VMOnPrem Two Several diff --git a/internal/goldendataset/testdata/pict_input_spans.txt b/internal/goldendataset/testdata/pict_input_spans.txt new file mode 100644 index 00000000000..9430ce47e3e --- /dev/null +++ b/internal/goldendataset/testdata/pict_input_spans.txt @@ -0,0 +1,14 @@ +Parent: Root, Child +Tracestate: Empty, One, Four +Kind: Unspecified, Internal, Server, Client, Producer, Consumer +Attributes: Nil, Empty, DatabaseSQL, DatabaseNoSQL, FaaSDatasource, FaaSHTTP, FaaSPubSub, FaaSTimer, FaaSOther, HTTPClient, HTTPServer, MessagingProducer, MessagingConsumer, gRPCClient, gRPCServer, Internal, MaxCount +Events: Nil, Empty, One, Two, Eight +Links: Nil, Empty, One, Two, Eight +Status: Nil, Ok, Cancelled, UnknownError, InvalidArgument, DeadlineExceeded, NotFound, AlreadyExists, PermissionDenied, ResourceExhausted, FailedPrecondition, Aborted, OutOfRange, Unimplemented, InternalError, Unavailable, DataLoss, Unauthenticated + +IF [Parent] = "Root" THEN [Kind] in {"Server", "Producer"}; +IF [Kind] = "Internal" THEN [Attributes] in {"Nil", "Internal"}; +IF [Kind] = "Server" THEN [Attributes] in {"Nil", "FaaSHTTP", "FaaSTimer", "FaaSOther", "HTTPServer", "gRPCServer", "MaxCount"}; +IF [Kind] = "Client" THEN [Attributes] in {"Empty", "DatabaseSQL", "DatabaseNoSQL", "HTTPClient", "gRPCClient"}; +IF [Kind] = "Producer" THEN [Attributes] in {"Empty", "MessagingProducer", "FaaSPubSub"}; +IF [Kind] = "Consumer" THEN [Attributes] in {"Nil", "MessagingConsumer", "FaaSDatasource"}; diff --git a/internal/goldendataset/testdata/pict_input_traces.txt b/internal/goldendataset/testdata/pict_input_traces.txt new file mode 100644 index 00000000000..7e342474da7 --- /dev/null +++ b/internal/goldendataset/testdata/pict_input_traces.txt @@ -0,0 +1,3 @@ +Resource: Nil, Empty, VMOnPrem, VMCloud, K8sOnPrem, K8sCloud, Faas +InstrumentationLibrary: None, One, Two +Spans: None, One, Several, All diff --git a/internal/goldendataset/traces_generator.go b/internal/goldendataset/traces_generator.go new file mode 100644 index 00000000000..f499fb58be7 --- /dev/null +++ b/internal/goldendataset/traces_generator.go @@ -0,0 +1,139 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + "fmt" + "io" + + otlpcommon "github.com/open-telemetry/opentelemetry-proto/gen/go/common/v1" + otlptrace "github.com/open-telemetry/opentelemetry-proto/gen/go/trace/v1" +) + +//GenerateResourceSpans generates a slice of OTLP ResourceSpans objects based on the PICT-generated pairwise +//parameters defined in the parameters file specified by the tracePairsFile parameter. The pairs to generate +//spans for for defined in the file specified by the spanPairsFile parameter. The random parameter injects the +//random number generator to use in generating IDs and other random values. +//The slice of ResourceSpans are returned. If an err is returned, the slice elements will be nil. +func GenerateResourceSpans(tracePairsFile string, spanPairsFile string, + random io.Reader) ([]*otlptrace.ResourceSpans, error) { + pairsData, err := loadPictOutputFile(tracePairsFile) + if err != nil { + return nil, err + } + pairsTotal := len(pairsData) - 1 + spans := make([]*otlptrace.ResourceSpans, pairsTotal) + for index, values := range pairsData { + if index == 0 { + continue + } + tracingInputs := &PICTTracingInputs{ + Resource: PICTInputResource(values[TracesColumnResource]), + InstrumentationLibrary: PICTInputInstrumentationLibrary(values[TracesColumnInstrumentationLibrary]), + Spans: PICTInputSpans(values[TracesColumnSpans]), + } + rscSpan, spanErr := GenerateResourceSpan(tracingInputs, spanPairsFile, random) + if spanErr != nil { + err = spanErr + } + spans[index-1] = rscSpan + } + return spans, err +} + +//GenerateResourceSpan generates a single OTLP ResourceSpans populated based on the provided inputs. They are: +// tracingInputs - the pairwise combination of field value variations for this ResourceSpans +// spanPairsFile - the file with the PICT-generated parameter combinations to generate spans for +// random - the random number generator to use in generating ID values +// +//The generated resource spans. If err is not nil, some or all of the resource spans fields will be nil. +func GenerateResourceSpan(tracingInputs *PICTTracingInputs, spanPairsFile string, + random io.Reader) (*otlptrace.ResourceSpans, error) { + libSpans, err := generateLibrarySpansArray(tracingInputs, spanPairsFile, random) + return &otlptrace.ResourceSpans{ + Resource: GenerateResource(tracingInputs.Resource), + InstrumentationLibrarySpans: libSpans, + }, err +} + +func generateLibrarySpansArray(tracingInputs *PICTTracingInputs, spanPairsFile string, + random io.Reader) ([]*otlptrace.InstrumentationLibrarySpans, error) { + var count int + switch tracingInputs.InstrumentationLibrary { + case LibraryNone: + count = 1 + case LibraryOne: + count = 1 + case LibraryTwo: + count = 2 + } + var err error + libSpans := make([]*otlptrace.InstrumentationLibrarySpans, count) + for i := 0; i < count; i++ { + libSpans[i], err = generateLibrarySpans(tracingInputs, i, spanPairsFile, random) + } + return libSpans, err +} + +func generateLibrarySpans(tracingInputs *PICTTracingInputs, index int, spanPairsFile string, + random io.Reader) (*otlptrace.InstrumentationLibrarySpans, error) { + spanCaseCount, err := countTotalSpanCases(spanPairsFile) + if err != nil { + return nil, err + } + var spans []*otlptrace.Span + switch tracingInputs.Spans { + case LibrarySpansNone: + spans = make([]*otlptrace.Span, 0) + case LibrarySpansOne: + spans, _, err = GenerateSpans(1, 0, spanPairsFile, random) + case LibrarySpansSeveral: + spans, _, err = GenerateSpans(spanCaseCount/4, 0, spanPairsFile, random) + case LibrarySpansAll: + spans, _, err = GenerateSpans(spanCaseCount, 0, spanPairsFile, random) + default: + spans, _, err = GenerateSpans(16, 0, spanPairsFile, random) + } + return &otlptrace.InstrumentationLibrarySpans{ + InstrumentationLibrary: generateInstrumentationLibrary(tracingInputs, index), + Spans: spans, + }, err +} + +func countTotalSpanCases(spanPairsFile string) (int, error) { + pairsData, err := loadPictOutputFile(spanPairsFile) + if err != nil { + return 0, err + } + count := len(pairsData) - 1 + return count, err +} + +func generateInstrumentationLibrary(tracingInputs *PICTTracingInputs, + index int) *otlpcommon.InstrumentationLibrary { + if LibraryNone == tracingInputs.InstrumentationLibrary { + return nil + } + nameStr := fmt.Sprintf("%s-%s-%s-%d", tracingInputs.Resource, tracingInputs.InstrumentationLibrary, + tracingInputs.Spans, index) + verStr := "semver:1.1.7" + if index > 0 { + verStr = "" + } + return &otlpcommon.InstrumentationLibrary{ + Name: nameStr, + Version: verStr, + } +} diff --git a/internal/goldendataset/traces_generator_test.go b/internal/goldendataset/traces_generator_test.go new file mode 100644 index 00000000000..e742cb506ee --- /dev/null +++ b/internal/goldendataset/traces_generator_test.go @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry 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 goldendataset + +import ( + "io" + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGenerateTraces(t *testing.T) { + random := io.Reader(rand.New(rand.NewSource(42))) + rscSpans, err := GenerateResourceSpans("testdata/generated_pict_pairs_traces.txt", + "testdata/generated_pict_pairs_spans.txt", random) + assert.Nil(t, err) + assert.Equal(t, 28, len(rscSpans)) +} diff --git a/translator/conventions/opentelemetry.go b/translator/conventions/opentelemetry.go index 298b0b28506..e2b55ed593d 100644 --- a/translator/conventions/opentelemetry.go +++ b/translator/conventions/opentelemetry.go @@ -15,18 +15,22 @@ package conventions // OpenTelemetry Semantic Convention values for Resource attribute names. -// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-resource-semantic-conventions.md +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/README.md const ( AttributeServiceName = "service.name" AttributeServiceNamespace = "service.namespace" AttributeServiceInstance = "service.instance.id" AttributeServiceVersion = "service.version" - AttributeLibraryName = "library.name" - AttributeLibraryLanguage = "library.language" - AttributeLibraryVersion = "library.version" + AttributeTelemetrySDKName = "telemetry.sdk.name" + AttributeTelemetrySDKLanguage = "telemetry.sdk.language" + AttributeTelemetrySDKVersion = "telemetry.sdk.version" AttributeContainerName = "container.name" AttributeContainerImage = "container.image.name" AttributeContainerTag = "container.image.tag" + AttributeFaasName = "faas.name" + AttributeFaasID = "faas.id" + AttributeFaasVersion = "faas.version" + AttributeFaasInstance = "faas.instance" AttributeK8sCluster = "k8s.cluster.name" AttributeK8sNamespace = "k8s.namespace.name" AttributeK8sPod = "k8s.pod.name" @@ -42,13 +46,10 @@ const ( AttributeCloudAccount = "cloud.account.id" AttributeCloudRegion = "cloud.region" AttributeCloudZone = "cloud.zone" - AttributeTelemetrySDKName = "telemetry.sdk.name" - AttributeTelemetrySDKLanguage = "telemetry.sdk.language" - AttributeTelemetrySDKVersion = "telemetry.sdk.version" ) // OpenTelemetry Semantic Convention values for general Span attribute names. -// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-span-general.md +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md const ( AttributeComponent = "component" AttributeNetTransport = "net.transport" @@ -71,7 +72,7 @@ const ( ) // OpenTelemetry Semantic Convention attribute names for HTTP related attributes -// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md const ( AttributeHTTPMethod = "http.method" AttributeHTTPURL = "http.url" @@ -90,7 +91,7 @@ const ( ) // OpenTelemetry Semantic Convention attribute names for database related attributes -// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-database.md +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md const ( AttributeDBType = "db.type" AttributeDBInstance = "db.instance" @@ -100,7 +101,7 @@ const ( ) // OpenTelemetry Semantic Convention attribute names for gRPC related attributes -// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-rpc.md +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md const ( AttributeRPCService = "rpc.service" EventTypeMessage = "message" @@ -111,3 +112,38 @@ const ( AttributeMessageCompressedSize = "message.compressed_size" AttributeMessageUncompressedSize = "message.uncompressed_size" ) + +// OpenTelemetry Semantic Convention attribute names for FaaS related attributes +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/faas.md +const ( + AttributeFaaSTrigger = "faas.trigger" + AttributeFaaSExecution = "faas.execution" + AttributeFaaSDocumentCollection = "faas.document.collection" + AttributeFaaSDocumentOperation = "faas.document.operation" + AttributeFaaSDocumentTime = "faas.document.time" + AttributeFaaSDocumentName = "faas.document.name" + AttributeFaaSTime = "faas.time" + AttributeFaaSCron = "faas.cron" + FaaSTriggerDataSource = "datasource" + FaaSTriggerHTTP = "http" + FaaSTriggerPubSub = "pubsub" + FaaSTriggerTimer = "timer" + FaaSTriggerOther = "other" +) + +// OpenTelemetry Semantic Convention attribute names for messaging system related attributes +// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md +const ( + AttributeMessagingSystem = "messaging.system" + AttributeMessagingDestination = "messaging.destination" + AttributeMessagingDestinationKind = "messaging.destination_kind" + AttributeMessagingTempDestination = "messaging.temp_destination" + AttributeMessagingProtocol = "messaging.protocol" + AttributeMessagingProtocolVersion = "messaging.protocol_version" + AttributeMessagingURL = "messaging.url" + AttributeMessagingMessageID = "messaging.message_id" + AttributeMessagingConversationID = "messaging.conversation_id" + AttributeMessagingPayloadSize = "messaging.message_payload_size_bytes" + AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes" + AttributeMessagingOperation = "messaging.operation" +) diff --git a/translator/internaldata/metrics_to_oc_test.go b/translator/internaldata/metrics_to_oc_test.go index 5ac491ccf24..b4c52481abc 100644 --- a/translator/internaldata/metrics_to_oc_test.go +++ b/translator/internaldata/metrics_to_oc_test.go @@ -38,8 +38,8 @@ func TestMetricsDataToOC(t *testing.T) { attrs.Upsert(conventions.AttributeHostHostname, pdata.NewAttributeValueString("host1")) attrs.Upsert(conventions.OCAttributeProcessID, pdata.NewAttributeValueInt(123)) attrs.Upsert(conventions.OCAttributeProcessStartTime, pdata.NewAttributeValueString("2020-02-11T20:26:00Z")) - attrs.Upsert(conventions.AttributeLibraryLanguage, pdata.NewAttributeValueString("CPP")) - attrs.Upsert(conventions.AttributeLibraryVersion, pdata.NewAttributeValueString("v2.0.1")) + attrs.Upsert(conventions.AttributeTelemetrySDKLanguage, pdata.NewAttributeValueString("CPP")) + attrs.Upsert(conventions.AttributeTelemetrySDKVersion, pdata.NewAttributeValueString("v2.0.1")) attrs.Upsert(conventions.OCAttributeExporterVersion, pdata.NewAttributeValueString("v1.2.0")) tests := []struct { diff --git a/translator/internaldata/oc_testdata_test.go b/translator/internaldata/oc_testdata_test.go index 2f528ac254f..9575cfd6eae 100644 --- a/translator/internaldata/oc_testdata_test.go +++ b/translator/internaldata/oc_testdata_test.go @@ -492,16 +492,16 @@ func generateResourceWithOcNodeAndResource() pdata.Resource { resource := pdata.NewResource() resource.InitEmpty() resource.Attributes().InitFromMap(map[string]pdata.AttributeValue{ - conventions.OCAttributeProcessStartTime: pdata.NewAttributeValueString("2020-02-11T20:26:00Z"), - conventions.AttributeHostHostname: pdata.NewAttributeValueString("host1"), - conventions.OCAttributeProcessID: pdata.NewAttributeValueInt(123), - conventions.AttributeLibraryVersion: pdata.NewAttributeValueString("v2.0.1"), - conventions.OCAttributeExporterVersion: pdata.NewAttributeValueString("v1.2.0"), - conventions.AttributeLibraryLanguage: pdata.NewAttributeValueString("CPP"), - conventions.OCAttributeResourceType: pdata.NewAttributeValueString("good-resource"), - "node-str-attr": pdata.NewAttributeValueString("node-str-attr-val"), - "resource-str-attr": pdata.NewAttributeValueString("resource-str-attr-val"), - "resource-int-attr": pdata.NewAttributeValueInt(123), + conventions.OCAttributeProcessStartTime: pdata.NewAttributeValueString("2020-02-11T20:26:00Z"), + conventions.AttributeHostHostname: pdata.NewAttributeValueString("host1"), + conventions.OCAttributeProcessID: pdata.NewAttributeValueInt(123), + conventions.AttributeTelemetrySDKVersion: pdata.NewAttributeValueString("v2.0.1"), + conventions.OCAttributeExporterVersion: pdata.NewAttributeValueString("v1.2.0"), + conventions.AttributeTelemetrySDKLanguage: pdata.NewAttributeValueString("CPP"), + conventions.OCAttributeResourceType: pdata.NewAttributeValueString("good-resource"), + "node-str-attr": pdata.NewAttributeValueString("node-str-attr-val"), + "resource-str-attr": pdata.NewAttributeValueString("resource-str-attr-val"), + "resource-int-attr": pdata.NewAttributeValueInt(123), }) return resource } diff --git a/translator/internaldata/oc_to_resource.go b/translator/internaldata/oc_to_resource.go index 56e0395a108..d6eca00a086 100644 --- a/translator/internaldata/oc_to_resource.go +++ b/translator/internaldata/oc_to_resource.go @@ -90,13 +90,13 @@ func ocNodeResourceToInternal(ocNode *occommon.Node, ocResource *ocresource.Reso } if ocNode.LibraryInfo != nil { if ocNode.LibraryInfo.CoreLibraryVersion != "" { - attrs.UpsertString(conventions.AttributeLibraryVersion, ocNode.LibraryInfo.CoreLibraryVersion) + attrs.UpsertString(conventions.AttributeTelemetrySDKVersion, ocNode.LibraryInfo.CoreLibraryVersion) } if ocNode.LibraryInfo.ExporterVersion != "" { attrs.UpsertString(conventions.OCAttributeExporterVersion, ocNode.LibraryInfo.ExporterVersion) } if ocNode.LibraryInfo.Language != occommon.LibraryInfo_LANGUAGE_UNSPECIFIED { - attrs.UpsertString(conventions.AttributeLibraryLanguage, ocNode.LibraryInfo.Language.String()) + attrs.UpsertString(conventions.AttributeTelemetrySDKLanguage, ocNode.LibraryInfo.Language.String()) } } } diff --git a/translator/internaldata/resource_to_oc.go b/translator/internaldata/resource_to_oc.go index 80cfcb7af2a..fb0a36e24b5 100644 --- a/translator/internaldata/resource_to_oc.go +++ b/translator/internaldata/resource_to_oc.go @@ -79,7 +79,7 @@ func internalResourceToOC(resource pdata.Resource) (*occommon.Node, *ocresource. ocNode.Identifier = &occommon.ProcessIdentifier{} } ocNode.Identifier.Pid = uint32(pid) - case conventions.AttributeLibraryVersion: + case conventions.AttributeTelemetrySDKVersion: if ocNode.LibraryInfo == nil { ocNode.LibraryInfo = &occommon.LibraryInfo{} } @@ -89,7 +89,7 @@ func internalResourceToOC(resource pdata.Resource) (*occommon.Node, *ocresource. ocNode.LibraryInfo = &occommon.LibraryInfo{} } ocNode.LibraryInfo.ExporterVersion = val - case conventions.AttributeLibraryLanguage: + case conventions.AttributeTelemetrySDKLanguage: if code, ok := occommon.LibraryInfo_Language_value[val]; ok { if ocNode.LibraryInfo == nil { ocNode.LibraryInfo = &occommon.LibraryInfo{}