From d444bb65e99b77220864586e7cc87575589ba6ca Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Mon, 20 Oct 2025 11:42:31 -0600 Subject: [PATCH 1/7] WIP: udpated schemas and first failing test to 1.34 --- .../azuremonitorexporter/contracts_utils.go | 2 +- exporter/azuremonitorexporter/conventions.go | 378 +++++++++++------- .../azuremonitorexporter/conventions_test.go | 112 +++--- .../azuremonitorexporter/log_to_envelope.go | 2 +- .../azuremonitorexporter/logexporter_test.go | 2 +- .../azuremonitorexporter/trace_to_envelope.go | 6 +- .../trace_to_envelope_test.go | 2 +- .../traceexporter_test.go | 2 +- 8 files changed, 301 insertions(+), 205 deletions(-) diff --git a/exporter/azuremonitorexporter/contracts_utils.go b/exporter/azuremonitorexporter/contracts_utils.go index db314d1efca9f..f502d8f508b19 100644 --- a/exporter/azuremonitorexporter/contracts_utils.go +++ b/exporter/azuremonitorexporter/contracts_utils.go @@ -6,7 +6,7 @@ package azuremonitorexporter // import "github.com/open-telemetry/opentelemetry- import ( "github.com/microsoft/ApplicationInsights-Go/appinsights/contracts" "go.opentelemetry.io/collector/pdata/pcommon" // Applies resource attributes values to data properties - conventions "go.opentelemetry.io/otel/semconv/v1.27.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" ) const ( diff --git a/exporter/azuremonitorexporter/conventions.go b/exporter/azuremonitorexporter/conventions.go index e584947cd843f..784ec895f4b34 100644 --- a/exporter/azuremonitorexporter/conventions.go +++ b/exporter/azuremonitorexporter/conventions.go @@ -5,9 +5,10 @@ package azuremonitorexporter // import "github.com/open-telemetry/opentelemetry- import ( "strconv" + "strings" "go.opentelemetry.io/collector/pdata/pcommon" - conventions "go.opentelemetry.io/otel/semconv/v1.7.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" ) /* @@ -22,117 +23,187 @@ const ( attributeApplicationInsightsEventMarkerAttribute string = "APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE" ) +// clientAttributes is the set of known client attributes +type clientAttributes struct { + // see https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/client.md + ClientAddress string + ClientPort int64 +} + +// serverAttributes is the set of known server attributes +type serverAttributes struct { + // see https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/server.md + ServerAddress string + ServerPort int64 +} + // networkAttributes is the set of known network attributes type networkAttributes struct { - // see https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/network.md#network-attributes - NetTransport string - NetPeerIP string - NetPeerPort int64 - NetPeerName string - NetHostIP string - NetHostPort int64 - NetHostName string + // see https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/network.md + NetworkLocalAddress string + NetworkLocalPort int64 + NetworkPeerAddress string + NetworkPeerPort int64 + NetworkProtocolName string + NetworkProtocolVersion string + NetworkTransport string + NetworkType string } // MapAttribute attempts to map a Span attribute to one of the known types func (attrs *networkAttributes) MapAttribute(k string, v pcommon.Value) bool { switch k { - case string(conventions.NetTransportKey): - attrs.NetTransport = v.Str() - case string(conventions.NetPeerIPKey): - attrs.NetPeerIP = v.Str() - case string(conventions.NetPeerPortKey): + case string(conventions.NetworkLocalAddressKey): + attrs.NetworkLocalAddress = v.Str() + case string(conventions.NetworkLocalPortKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.NetPeerPort = val + attrs.NetworkLocalPort = val } - case string(conventions.NetPeerNameKey): - attrs.NetPeerName = v.Str() - case string(conventions.NetHostIPKey): - attrs.NetHostIP = v.Str() - case string(conventions.NetHostPortKey): + case string(conventions.NetworkPeerAddressKey): + attrs.NetworkPeerAddress = v.Str() + case string(conventions.NetworkPeerPortKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.NetHostPort = val + attrs.NetworkPeerPort = val } - case string(conventions.NetHostNameKey): - attrs.NetHostName = v.Str() + case string(conventions.NetworkProtocolNameKey): + attrs.NetworkProtocolName = v.Str() + case string(conventions.NetworkProtocolVersionKey): + attrs.NetworkProtocolVersion = v.Str() + case string(conventions.NetworkTransportKey): + attrs.NetworkTransport = v.Str() + case string(conventions.NetworkTypeKey): } return true } +// urlAttributes is the set of known attributes for URL +type urlAttributes struct { + // common attributes + // https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/url.md + UrlFragment string + UrlFull string + UrlPath string + UrlQuery string + UrlScheme string +} + +type userAgentAttributes struct { + UserAgentOriginal string + UserAgentName string + UserAgentVersion string +} + // httpAttributes is the set of known attributes for HTTP Spans type httpAttributes struct { // common attributes - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#common-attributes - HTTPMethod string - HTTPURL string - HTTPTarget string - HTTPHost string - HTTPScheme string - HTTPStatusCode int64 - HTTPStatusText string - HTTPFlavor string - HTTPUserAgent string - HTTPRequestContentLength int64 - HTTPRequestContentLengthUncompressed int64 - HTTPResponseContentLength int64 - HTTPResponseContentLengthUncompressed int64 - - // Server Span specific - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-server-semantic-conventions - HTTPRoute string - HTTPServerName string - HTTPClientIP string + // https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/http.md + HttpRequestHeaders map[string][]string + HttpRequestMethod string + HttpRequestMethodOriginal string + HttpRequestResendCount int64 + HttpResponseHeaders map[string][]string + HttpResponseStatusCode int64 + HttpRoute string + + HttpRequestBodySize int64 + HttpResponseBodySize int64 + + UrlAttributes urlAttributes + ClientAttributes clientAttributes + ServerAttributes serverAttributes + UserAgentAttributes userAgentAttributes // any net.* NetworkAttributes networkAttributes } +func setHeader(headers *map[string][]string, key string, v pcommon.Value) { + if *headers == nil { + *headers = make(map[string][]string) + } + if v.Type() == pcommon.ValueTypeSlice { + slice := v.Slice() + var vals []string + for i := 0; i < slice.Len(); i++ { + vals = append(vals, slice.At(i).Str()) + } + (*headers)[key] = vals + } +} + // MapAttribute attempts to map a Span attribute to one of the known types func (attrs *httpAttributes) MapAttribute(k string, v pcommon.Value) bool { + + const reqHeaderPrefix = "http.request.header." + const respHeaderPrefix = "http.response.header." + + switch { + case strings.HasPrefix(k, reqHeaderPrefix): + headerName := k[len(reqHeaderPrefix):] + setHeader(&attrs.HttpRequestHeaders, headerName, v) + return true + + case strings.HasPrefix(k, respHeaderPrefix): + headerName := k[len(respHeaderPrefix):] + setHeader(&attrs.HttpResponseHeaders, headerName, v) + return true + } + switch k { - case string(conventions.HTTPMethodKey): - attrs.HTTPMethod = v.Str() - case string(conventions.HTTPURLKey): - attrs.HTTPURL = v.Str() - case string(conventions.HTTPTargetKey): - attrs.HTTPTarget = v.Str() - case string(conventions.HTTPHostKey): - attrs.HTTPHost = v.Str() - case string(conventions.HTTPSchemeKey): - attrs.HTTPScheme = v.Str() - case string(conventions.HTTPStatusCodeKey): + case string(conventions.HTTPRequestMethodKey): + attrs.HttpRequestMethod = v.Str() + case string(conventions.HTTPRequestMethodOriginalKey): + attrs.HttpRequestMethodOriginal = v.Str() + case string(conventions.HTTPRequestResendCountKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HTTPStatusCode = val + attrs.HttpRequestResendCount = val } - case "http.status_text": - attrs.HTTPStatusText = v.Str() - case string(conventions.HTTPFlavorKey): - attrs.HTTPFlavor = v.Str() - case string(conventions.HTTPUserAgentKey): - attrs.HTTPUserAgent = v.Str() - case string(conventions.HTTPRequestContentLengthKey): + case string(conventions.HTTPResponseStatusCodeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HTTPRequestContentLength = val + attrs.HttpResponseStatusCode = val } - case string(conventions.HTTPRequestContentLengthUncompressedKey): + case string(conventions.HTTPRouteKey): + attrs.HttpRoute = v.Str() + + case string(conventions.HTTPResponseBodySizeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HTTPRequestContentLengthUncompressed = val + attrs.HttpResponseBodySize = val } - case string(conventions.HTTPResponseContentLengthKey): + case string(conventions.HTTPRequestBodySizeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HTTPResponseContentLength = val + attrs.HttpRequestBodySize = val } - case string(conventions.HTTPResponseContentLengthUncompressedKey): + // URL attributes + case string(conventions.URLFragmentKey): + attrs.UrlAttributes.UrlFragment = v.Str() + case string(conventions.URLFullKey): + attrs.UrlAttributes.UrlFull = v.Str() + case string(conventions.URLPathKey): + attrs.UrlAttributes.UrlPath = v.Str() + case string(conventions.URLQueryKey): + attrs.UrlAttributes.UrlQuery = v.Str() + case string(conventions.URLSchemeKey): + attrs.UrlAttributes.UrlScheme = v.Str() + // Network/server/client address attributes (new, replacing http.host) + case string(conventions.ServerAddressKey): + attrs.ServerAttributes.ServerAddress = v.Str() + case string(conventions.ServerPortKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HTTPResponseContentLengthUncompressed = val + attrs.ServerAttributes.ServerPort = val } - - case string(conventions.HTTPRouteKey): - attrs.HTTPRoute = v.Str() - case string(conventions.HTTPServerNameKey): - attrs.HTTPServerName = v.Str() - case string(conventions.HTTPClientIPKey): - attrs.HTTPClientIP = v.Str() + case string(conventions.ClientAddressKey): + attrs.ClientAttributes.ClientAddress = v.Str() + case string(conventions.ClientPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ClientAttributes.ClientPort = val + } + // User agent attributes + case string(conventions.UserAgentOriginalKey): + attrs.UserAgentAttributes.UserAgentOriginal = v.Str() + case string(conventions.UserAgentNameKey): + attrs.UserAgentAttributes.UserAgentName = v.Str() + case string(conventions.UserAgentVersionKey): + attrs.UserAgentAttributes.UserAgentVersion = v.Str() default: attrs.NetworkAttributes.MapAttribute(k, v) @@ -170,46 +241,42 @@ func (attrs *rpcAttributes) MapAttribute(k string, v pcommon.Value) bool { // databaseAttributes is the set of known attributes for Database Spans type databaseAttributes struct { - DBSystem string - DBConnectionString string - DBUser string - DBName string - DBStatement string - DBOperation string - DBMSSQLInstanceName string - DBJDBCDriverClassName string - DBCassandraKeyspace string - DBHBaseNamespace string - DBRedisDatabaseIndex string - DBMongoDBCollection string - NetworkAttributes networkAttributes + DBCollectionName string + DBNamespace string + DBOperationBatchSize int64 + DBOperationName string + DBQuerySummary string + DBQueryText string + DBResponseStatusCode string + DBStoredProcedureName string + DBSystemName string + + NetworkAttributes networkAttributes } // MapAttribute attempts to map a Span attribute to one of the known types func (attrs *databaseAttributes) MapAttribute(k string, v pcommon.Value) bool { switch k { - case string(conventions.DBSystemKey): - attrs.DBSystem = v.Str() - case string(conventions.DBConnectionStringKey): - attrs.DBConnectionString = v.Str() - case string(conventions.DBUserKey): - attrs.DBUser = v.Str() - case string(conventions.DBStatementKey): - attrs.DBStatement = v.Str() - case string(conventions.DBOperationKey): - attrs.DBOperation = v.Str() - case string(conventions.DBMSSQLInstanceNameKey): - attrs.DBMSSQLInstanceName = v.Str() - case string(conventions.DBJDBCDriverClassnameKey): - attrs.DBJDBCDriverClassName = v.Str() - case string(conventions.DBCassandraKeyspaceKey): - attrs.DBCassandraKeyspace = v.Str() - case string(conventions.DBHBaseNamespaceKey): - attrs.DBHBaseNamespace = v.Str() - case string(conventions.DBRedisDBIndexKey): - attrs.DBRedisDatabaseIndex = v.Str() - case string(conventions.DBMongoDBCollectionKey): - attrs.DBMongoDBCollection = v.Str() + case string(conventions.DBCollectionNameKey): + attrs.DBCollectionName = v.Str() + case string(conventions.DBNamespaceKey): + attrs.DBNamespace = v.Str() + case string(conventions.DBOperationBatchSizeKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.DBOperationBatchSize = val + } + case string(conventions.DBOperationNameKey): + attrs.DBOperationName = v.Str() + case string(conventions.DBQuerySummaryKey): + attrs.DBQuerySummary = v.Str() + case string(conventions.DBQueryTextKey): + attrs.DBQueryText = v.Str() + case string(conventions.DBResponseStatusCodeKey): + attrs.DBResponseStatusCode = v.Str() + case string(conventions.DBStoredProcedureNameKey): + attrs.DBStoredProcedureName = v.Str() + case string(conventions.DBSystemNameKey): + attrs.DBSystemName = v.Str() default: attrs.NetworkAttributes.MapAttribute(k, v) @@ -219,52 +286,68 @@ func (attrs *databaseAttributes) MapAttribute(k string, v pcommon.Value) bool { // messagingAttributes is the set of known attributes for Messaging Spans type messagingAttributes struct { - MessagingSystem string - MessagingDestination string - MessagingDestinationKind string - MessagingTempDestination string - MessagingProtocol string - MessagingProtocolVersion string - MessagingURL string - MessagingMessageID string - MessagingConversationID string - MessagingMessagePayloadSize int64 - MessagingMessagePayloadCompressedSize int64 - MessagingOperation string - NetworkAttributes networkAttributes + MessagingBatchMessageCount int64 + MessagingClientID string + MessagingConsumerGroup string + MessagingDestinationAnonymous bool + MessagingDestination string + MessagingDestinationKind string + MessagingDestinationPartitionID string + MessagingDestinationSubName string + MessagingDestinationTemplate string + MessagingDestinationTemporary bool + MessagingMessageBodySize int64 + MessagingMessageConversationID string + MessagingMessageEnvelopeSize int64 + MessagingMessageID string + MessagingOperation string + MessagingOperationType string + MessagingSystem string + + NetworkAttributes networkAttributes } // MapAttribute attempts to map a Span attribute to one of the known types func (attrs *messagingAttributes) MapAttribute(k string, v pcommon.Value) bool { switch k { - case string(conventions.MessagingSystemKey): - attrs.MessagingSystem = v.Str() - case string(conventions.MessagingDestinationKey): + case string(conventions.MessagingBatchMessageCountKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.MessagingBatchMessageCount = val + } + case string(conventions.MessagingClientIDKey): + attrs.MessagingClientID = v.Str() + case string(conventions.MessagingConsumerGroupNameKey): + attrs.MessagingConsumerGroup = v.Str() + case string(conventions.MessagingDestinationAnonymousKey): + attrs.MessagingDestinationAnonymous = v.Bool() + case string(conventions.MessagingDestinationNameKey): attrs.MessagingDestination = v.Str() - case string(conventions.MessagingDestinationKindKey): - attrs.MessagingDestinationKind = v.Str() - case string(conventions.MessagingTempDestinationKey): - attrs.MessagingTempDestination = v.Str() - case string(conventions.MessagingProtocolKey): - attrs.MessagingProtocol = v.Str() - case string(conventions.MessagingProtocolVersionKey): - attrs.MessagingProtocolVersion = v.Str() - case string(conventions.MessagingURLKey): - attrs.MessagingURL = v.Str() - case string(conventions.MessagingMessageIDKey): - attrs.MessagingMessageID = v.Str() - case string(conventions.MessagingConversationIDKey): - attrs.MessagingConversationID = v.Str() - case string(conventions.MessagingMessagePayloadSizeBytesKey): + case string(conventions.MessagingDestinationPartitionIDKey): + attrs.MessagingDestinationPartitionID = v.Str() + case string(conventions.MessagingDestinationSubscriptionNameKey): + attrs.MessagingDestinationSubName = v.Str() + case string(conventions.MessagingDestinationTemplateKey): + attrs.MessagingDestinationTemplate = v.Str() + case string(conventions.MessagingDestinationTemporaryKey): + attrs.MessagingDestinationTemporary = v.Bool() + case string(conventions.MessagingMessageBodySizeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.MessagingMessagePayloadSize = val + attrs.MessagingMessageBodySize = val } - case string(conventions.MessagingMessagePayloadCompressedSizeBytesKey): + case string(conventions.MessagingMessageConversationIDKey): + attrs.MessagingMessageConversationID = v.Str() + case string(conventions.MessagingMessageEnvelopeSizeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.MessagingMessagePayloadCompressedSize = val + attrs.MessagingMessageEnvelopeSize = val } - case string(conventions.MessagingOperationKey): + case string(conventions.MessagingMessageIDKey): + attrs.MessagingMessageID = v.Str() + case string(conventions.MessagingOperationNameKey): attrs.MessagingOperation = v.Str() + case string(conventions.MessagingOperationTypeKey): + attrs.MessagingOperationType = v.Str() + case string(conventions.MessagingSystemKey): + attrs.MessagingSystem = v.Str() default: attrs.NetworkAttributes.MapAttribute(k, v) @@ -274,7 +357,6 @@ func (attrs *messagingAttributes) MapAttribute(k string, v pcommon.Value) bool { // exceptionAttributes is the set of known attributes for Exception events type exceptionAttributes struct { - ExceptionEscaped string ExceptionMessage string ExceptionStackTrace string ExceptionType string @@ -283,8 +365,6 @@ type exceptionAttributes struct { // MapAttribute attempts to map a SpanEvent attribute to one of the known types func (attrs *exceptionAttributes) MapAttribute(k string, v pcommon.Value) bool { switch k { - case string(conventions.ExceptionEscapedKey): - attrs.ExceptionEscaped = v.Str() case string(conventions.ExceptionMessageKey): attrs.ExceptionMessage = v.Str() case string(conventions.ExceptionStacktraceKey): diff --git a/exporter/azuremonitorexporter/conventions_test.go b/exporter/azuremonitorexporter/conventions_test.go index d035fc09f14cc..12c54bf0f7fd5 100644 --- a/exporter/azuremonitorexporter/conventions_test.go +++ b/exporter/azuremonitorexporter/conventions_test.go @@ -4,34 +4,34 @@ package azuremonitorexporter import ( + "strconv" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" - conventions "go.opentelemetry.io/otel/semconv/v1.7.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" ) func TestHTTPAttributeMapping(t *testing.T) { httpAttributeValues := map[string]any{ - string(conventions.HTTPMethodKey): string(conventions.HTTPMethodKey), - string(conventions.HTTPURLKey): string(conventions.HTTPURLKey), - string(conventions.HTTPTargetKey): string(conventions.HTTPTargetKey), - string(conventions.HTTPHostKey): string(conventions.HTTPHostKey), - string(conventions.HTTPSchemeKey): string(conventions.HTTPSchemeKey), + string(conventions.HTTPRequestMethodKey): string(conventions.HTTPRequestMethodKey), + string(conventions.URLFullKey): string(conventions.URLFullKey), + string(conventions.URLPathKey): string(conventions.URLPathKey), + string(conventions.URLQueryKey): string(conventions.URLQueryKey), + string(conventions.URLSchemeKey): string(conventions.URLSchemeKey), // Exercise the INT or STRING logic - string(conventions.HTTPStatusCodeKey): "200", - "http.status_text": "http.status_text", - string(conventions.HTTPFlavorKey): string(conventions.HTTPFlavorKey), - string(conventions.HTTPUserAgentKey): string(conventions.HTTPUserAgentKey), - string(conventions.HTTPRequestContentLengthKey): 1, - string(conventions.HTTPRequestContentLengthUncompressedKey): 2, - string(conventions.HTTPResponseContentLengthKey): 3, - string(conventions.HTTPResponseContentLengthUncompressedKey): 4, - - string(conventions.HTTPRouteKey): string(conventions.HTTPRouteKey), - string(conventions.HTTPServerNameKey): string(conventions.HTTPServerNameKey), - string(conventions.HTTPClientIPKey): string(conventions.HTTPClientIPKey), + string(conventions.HTTPResponseStatusCodeKey): "200", + string(conventions.NetworkProtocolNameKey): string(conventions.NetworkProtocolNameKey), + string(conventions.UserAgentOriginalKey): string(conventions.UserAgentOriginalKey), + string(conventions.HTTPRequestHeader("content-length").Key): "1", + string(conventions.HTTPRequestBodySizeKey): 2, + string(conventions.HTTPResponseHeader("content-length").Key): "3", + string(conventions.HTTPResponseBodySizeKey): 4, + + string(conventions.HTTPRouteKey): string(conventions.HTTPRouteKey), + string(conventions.ServerAddressKey): string(conventions.ServerAddressKey), + string(conventions.ClientAddressKey): string(conventions.ClientAddressKey), } attributeMap := pcommon.NewMap() @@ -42,22 +42,32 @@ func TestHTTPAttributeMapping(t *testing.T) { httpAttributes := &httpAttributes{} attributeMap.Range(httpAttributes.MapAttribute) - assert.Equal(t, string(conventions.HTTPMethodKey), httpAttributes.HTTPMethod) - assert.Equal(t, string(conventions.HTTPURLKey), httpAttributes.HTTPURL) - assert.Equal(t, string(conventions.HTTPTargetKey), httpAttributes.HTTPTarget) - assert.Equal(t, string(conventions.HTTPHostKey), httpAttributes.HTTPHost) - assert.Equal(t, string(conventions.HTTPSchemeKey), httpAttributes.HTTPScheme) - assert.Equal(t, int64(200), httpAttributes.HTTPStatusCode) - assert.Equal(t, "http.status_text", httpAttributes.HTTPStatusText) - assert.Equal(t, string(conventions.HTTPFlavorKey), httpAttributes.HTTPFlavor) - assert.Equal(t, string(conventions.HTTPUserAgentKey), httpAttributes.HTTPUserAgent) - assert.Equal(t, int64(1), httpAttributes.HTTPRequestContentLength) - assert.Equal(t, int64(2), httpAttributes.HTTPRequestContentLengthUncompressed) - assert.Equal(t, int64(3), httpAttributes.HTTPResponseContentLength) - assert.Equal(t, int64(4), httpAttributes.HTTPResponseContentLengthUncompressed) - assert.Equal(t, string(conventions.HTTPRouteKey), httpAttributes.HTTPRoute) - assert.Equal(t, string(conventions.HTTPServerNameKey), httpAttributes.HTTPServerName) - assert.Equal(t, string(conventions.HTTPClientIPKey), httpAttributes.HTTPClientIP) + assert.Equal(t, string(conventions.HTTPRequestMethodKey), httpAttributes.HttpRequestMethod) + assert.Equal(t, string(conventions.URLFullKey), httpAttributes.UrlAttributes.UrlFull) + assert.Equal(t, string(conventions.URLPathKey), httpAttributes.UrlAttributes.UrlPath) + assert.Equal(t, string(conventions.URLQueryKey), httpAttributes.UrlAttributes.UrlQuery) + + assert.Equal(t, string(conventions.URLSchemeKey), httpAttributes.UrlAttributes.UrlScheme) + assert.Equal(t, int64(200), httpAttributes.HttpResponseStatusCode) + assert.Equal(t, string(conventions.NetworkProtocolNameKey), httpAttributes.NetworkAttributes.NetworkProtocolName) + assert.Equal(t, string(conventions.UserAgentOriginalKey), httpAttributes.UserAgentAttributes.UserAgentOriginal) + + reqCL := httpAttributes.HttpRequestHeaders["content-length"][0] + reqCLInt, err := strconv.ParseInt(reqCL, 10, 64) + assert.NoError(t, err) + assert.Equal(t, int64(1), reqCLInt) + + assert.Equal(t, int64(2), httpAttributes.HttpRequestBodySize) + + resCL := httpAttributes.HttpResponseHeaders["content-length"][0] + resCLInt, err := strconv.ParseInt(resCL, 10, 64) + assert.NoError(t, err) + assert.Equal(t, int64(3), resCLInt) + + assert.Equal(t, int64(4), httpAttributes.HttpResponseBodySize) + assert.Equal(t, string(conventions.HTTPRouteKey), httpAttributes.HttpRoute) + assert.Equal(t, string(conventions.ServerAddressKey), httpAttributes.ServerAttributes.ServerAddress) + assert.Equal(t, string(conventions.ClientAddressKey), httpAttributes.ClientAttributes.ClientAddress) networkAttributesValidations(t, httpAttributes.NetworkAttributes) } @@ -172,21 +182,27 @@ func TestAttributeMappingWithSomeBadValues(t *testing.T) { } func addNetworkAttributes(m pcommon.Map) { - m.PutStr(string(conventions.NetTransportKey), string(conventions.NetTransportKey)) - m.PutStr(string(conventions.NetPeerIPKey), string(conventions.NetPeerIPKey)) - m.PutInt(string(conventions.NetPeerPortKey), 1) - m.PutStr(string(conventions.NetPeerNameKey), string(conventions.NetPeerNameKey)) - m.PutStr(string(conventions.NetHostIPKey), string(conventions.NetHostIPKey)) - m.PutInt(string(conventions.NetHostPortKey), 2) - m.PutStr(string(conventions.NetHostNameKey), string(conventions.NetHostNameKey)) + m.PutStr(string(conventions.NetworkTransportKey), string(conventions.NetworkTransportKey)) + m.PutStr(string(conventions.NetworkPeerAddressKey), string(conventions.NetworkPeerAddressKey)) + m.PutInt(string(conventions.NetworkPeerPortKey), 1) + //TODO: This is either client address or server address, not both + m.PutStr(string(conventions.NetworkPeerNameKey), string(conventions.NetworkPeerNameKey)) + + m.PutStr(string(conventions.NetworkLocalAddressKey), string(conventions.NetworkLocalAddressKey)) + m.PutInt(string(conventions.ServerPortKey), 2) + m.PutStr(string(conventions.ServerAddressKey), string(conventions.ServerAddressKey)) } func networkAttributesValidations(t *testing.T, networkAttributes networkAttributes) { - assert.Equal(t, string(conventions.NetTransportKey), networkAttributes.NetTransport) - assert.Equal(t, string(conventions.NetPeerIPKey), networkAttributes.NetPeerIP) - assert.Equal(t, int64(1), networkAttributes.NetPeerPort) - assert.Equal(t, string(conventions.NetPeerNameKey), networkAttributes.NetPeerName) - assert.Equal(t, string(conventions.NetHostIPKey), networkAttributes.NetHostIP) - assert.Equal(t, int64(2), networkAttributes.NetHostPort) - assert.Equal(t, string(conventions.NetHostNameKey), networkAttributes.NetHostName) + assert.Equal(t, string(conventions.NetworkTransportKey), networkAttributes.NetworkTransport) + assert.Equal(t, string(conventions.NetworkPeerAddressKey), networkAttributes.NetworkPeerAddress) + assert.Equal(t, int64(1), networkAttributes.NetworkPeerPort) + //TODO: This is either client address or server address, not both + assert.Equal(t, string(conventions.NetworkPeerNameKey), networkAttributes.NetworkPeerName) + + assert.Equal(t, string(conventions.NetworkLocalAddressKey), networkAttributes.NetworkLocalAddress) + //TODO: Replace with SeverPort + assert.Equal(t, int64(2), networkAttributes.NetworkHostPort) + //TODO: Replace with ServerAddress + assert.Equal(t, string(conventions.NetworkHostNameKey), networkAttributes.NetworkHostName) } diff --git a/exporter/azuremonitorexporter/log_to_envelope.go b/exporter/azuremonitorexporter/log_to_envelope.go index 862e13724fa09..f83c9a1e60bd1 100644 --- a/exporter/azuremonitorexporter/log_to_envelope.go +++ b/exporter/azuremonitorexporter/log_to_envelope.go @@ -9,7 +9,7 @@ import ( "github.com/microsoft/ApplicationInsights-Go/appinsights/contracts" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" - conventions "go.opentelemetry.io/otel/semconv/v1.27.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/traceutil" diff --git a/exporter/azuremonitorexporter/logexporter_test.go b/exporter/azuremonitorexporter/logexporter_test.go index 3e29b7c2dc9c5..805f8f80ac2d2 100644 --- a/exporter/azuremonitorexporter/logexporter_test.go +++ b/exporter/azuremonitorexporter/logexporter_test.go @@ -17,7 +17,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" - conventions "go.opentelemetry.io/otel/semconv/v1.27.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" "go.uber.org/zap" ) diff --git a/exporter/azuremonitorexporter/trace_to_envelope.go b/exporter/azuremonitorexporter/trace_to_envelope.go index 821a847c08a69..35dd4f16938fe 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope.go +++ b/exporter/azuremonitorexporter/trace_to_envelope.go @@ -16,7 +16,7 @@ import ( "github.com/microsoft/ApplicationInsights-Go/appinsights/contracts" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" - conventions "go.opentelemetry.io/otel/semconv/v1.12.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/traceutil" @@ -691,12 +691,12 @@ func mapIncomingSpanToType(attributeMap pcommon.Map) spanType { } // HTTP - if _, exists := attributeMap.Get(string(conventions.HTTPMethodKey)); exists { + if _, exists := attributeMap.Get(string(conventions.HTTPRequestMethodKey)); exists { return httpSpanType } // Database - if _, exists := attributeMap.Get(string(conventions.DBSystemKey)); exists { + if _, exists := attributeMap.Get(string(conventions.DBSystemNameKey)); exists { return databaseSpanType } diff --git a/exporter/azuremonitorexporter/trace_to_envelope_test.go b/exporter/azuremonitorexporter/trace_to_envelope_test.go index beb0e0276b6be..e25c94a733a81 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope_test.go +++ b/exporter/azuremonitorexporter/trace_to_envelope_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" - conventions "go.opentelemetry.io/otel/semconv/v1.12.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/traceutil" diff --git a/exporter/azuremonitorexporter/traceexporter_test.go b/exporter/azuremonitorexporter/traceexporter_test.go index c705a49937edf..8ed23b9e8f2d0 100644 --- a/exporter/azuremonitorexporter/traceexporter_test.go +++ b/exporter/azuremonitorexporter/traceexporter_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/mock" "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/pdata/ptrace" - conventions "go.opentelemetry.io/otel/semconv/v1.27.0" + conventions "go.opentelemetry.io/otel/semconv/v1.34.0" "go.uber.org/zap" ) From c2ece4109709779c6f34342e0fc32aebb2e4b80c Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Mon, 20 Oct 2025 13:30:28 -0600 Subject: [PATCH 2/7] updated tests; added client and server to each RPC, DB, Messaging --- exporter/azuremonitorexporter/conventions.go | 50 +++- .../azuremonitorexporter/conventions_test.go | 241 +++++++++++++----- 2 files changed, 220 insertions(+), 71 deletions(-) diff --git a/exporter/azuremonitorexporter/conventions.go b/exporter/azuremonitorexporter/conventions.go index 784ec895f4b34..829e76ee7b81e 100644 --- a/exporter/azuremonitorexporter/conventions.go +++ b/exporter/azuremonitorexporter/conventions.go @@ -184,6 +184,7 @@ func (attrs *httpAttributes) MapAttribute(k string, v pcommon.Value) bool { attrs.UrlAttributes.UrlQuery = v.Str() case string(conventions.URLSchemeKey): attrs.UrlAttributes.UrlScheme = v.Str() + // Network/server/client address attributes (new, replacing http.host) case string(conventions.ServerAddressKey): attrs.ServerAttributes.ServerAddress = v.Str() @@ -218,6 +219,9 @@ type rpcAttributes struct { RPCService string RPCMethod string RPCGRPCStatusCode int64 + + ClientAttributes clientAttributes + ServerAttributes serverAttributes NetworkAttributes networkAttributes } @@ -233,6 +237,19 @@ func (attrs *rpcAttributes) MapAttribute(k string, v pcommon.Value) bool { case string(conventions.RPCGRPCStatusCodeKey): attrs.RPCGRPCStatusCode = v.Int() + case string(conventions.ServerAddressKey): + attrs.ServerAttributes.ServerAddress = v.Str() + case string(conventions.ServerPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ServerAttributes.ServerPort = val + } + case string(conventions.ClientAddressKey): + attrs.ClientAttributes.ClientAddress = v.Str() + case string(conventions.ClientPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ClientAttributes.ClientPort = val + } + default: attrs.NetworkAttributes.MapAttribute(k, v) } @@ -251,6 +268,9 @@ type databaseAttributes struct { DBStoredProcedureName string DBSystemName string + ClientAttributes clientAttributes + ServerAttributes serverAttributes + NetworkAttributes networkAttributes } @@ -278,6 +298,19 @@ func (attrs *databaseAttributes) MapAttribute(k string, v pcommon.Value) bool { case string(conventions.DBSystemNameKey): attrs.DBSystemName = v.Str() + case string(conventions.ServerAddressKey): + attrs.ServerAttributes.ServerAddress = v.Str() + case string(conventions.ServerPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ServerAttributes.ServerPort = val + } + case string(conventions.ClientAddressKey): + attrs.ClientAttributes.ClientAddress = v.Str() + case string(conventions.ClientPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ClientAttributes.ClientPort = val + } + default: attrs.NetworkAttributes.MapAttribute(k, v) } @@ -291,7 +324,6 @@ type messagingAttributes struct { MessagingConsumerGroup string MessagingDestinationAnonymous bool MessagingDestination string - MessagingDestinationKind string MessagingDestinationPartitionID string MessagingDestinationSubName string MessagingDestinationTemplate string @@ -304,6 +336,9 @@ type messagingAttributes struct { MessagingOperationType string MessagingSystem string + ClientAttributes clientAttributes + ServerAttributes serverAttributes + NetworkAttributes networkAttributes } @@ -349,6 +384,19 @@ func (attrs *messagingAttributes) MapAttribute(k string, v pcommon.Value) bool { case string(conventions.MessagingSystemKey): attrs.MessagingSystem = v.Str() + case string(conventions.ServerAddressKey): + attrs.ServerAttributes.ServerAddress = v.Str() + case string(conventions.ServerPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ServerAttributes.ServerPort = val + } + case string(conventions.ClientAddressKey): + attrs.ClientAttributes.ClientAddress = v.Str() + case string(conventions.ClientPortKey): + if val, err := getAttributeValueAsInt(v); err == nil { + attrs.ClientAttributes.ClientPort = val + } + default: attrs.NetworkAttributes.MapAttribute(k, v) } diff --git a/exporter/azuremonitorexporter/conventions_test.go b/exporter/azuremonitorexporter/conventions_test.go index 12c54bf0f7fd5..51e55fa9aaa4d 100644 --- a/exporter/azuremonitorexporter/conventions_test.go +++ b/exporter/azuremonitorexporter/conventions_test.go @@ -12,7 +12,7 @@ import ( conventions "go.opentelemetry.io/otel/semconv/v1.34.0" ) -func TestHTTPAttributeMapping(t *testing.T) { +func testHTTPAttributeMapping(t *testing.T, variant string) { httpAttributeValues := map[string]any{ string(conventions.HTTPRequestMethodKey): string(conventions.HTTPRequestMethodKey), string(conventions.URLFullKey): string(conventions.URLFullKey), @@ -20,7 +20,6 @@ func TestHTTPAttributeMapping(t *testing.T) { string(conventions.URLQueryKey): string(conventions.URLQueryKey), string(conventions.URLSchemeKey): string(conventions.URLSchemeKey), - // Exercise the INT or STRING logic string(conventions.HTTPResponseStatusCodeKey): "200", string(conventions.NetworkProtocolNameKey): string(conventions.NetworkProtocolNameKey), string(conventions.UserAgentOriginalKey): string(conventions.UserAgentOriginalKey), @@ -29,24 +28,30 @@ func TestHTTPAttributeMapping(t *testing.T) { string(conventions.HTTPResponseHeader("content-length").Key): "3", string(conventions.HTTPResponseBodySizeKey): 4, - string(conventions.HTTPRouteKey): string(conventions.HTTPRouteKey), - string(conventions.ServerAddressKey): string(conventions.ServerAddressKey), - string(conventions.ClientAddressKey): string(conventions.ClientAddressKey), + string(conventions.HTTPRouteKey): string(conventions.HTTPRouteKey), } attributeMap := pcommon.NewMap() assert.NoError(t, attributeMap.FromRaw(httpAttributeValues)) - addNetworkAttributes(attributeMap) httpAttributes := &httpAttributes{} + + switch variant { + case "client": + addClientAttributes(attributeMap) + case "server": + addServerAttributes(attributeMap) + default: + t.Fatalf("Unknown variant: %s", variant) + } + attributeMap.Range(httpAttributes.MapAttribute) assert.Equal(t, string(conventions.HTTPRequestMethodKey), httpAttributes.HttpRequestMethod) assert.Equal(t, string(conventions.URLFullKey), httpAttributes.UrlAttributes.UrlFull) assert.Equal(t, string(conventions.URLPathKey), httpAttributes.UrlAttributes.UrlPath) assert.Equal(t, string(conventions.URLQueryKey), httpAttributes.UrlAttributes.UrlQuery) - assert.Equal(t, string(conventions.URLSchemeKey), httpAttributes.UrlAttributes.UrlScheme) assert.Equal(t, int64(200), httpAttributes.HttpResponseStatusCode) assert.Equal(t, string(conventions.NetworkProtocolNameKey), httpAttributes.NetworkAttributes.NetworkProtocolName) @@ -66,13 +71,25 @@ func TestHTTPAttributeMapping(t *testing.T) { assert.Equal(t, int64(4), httpAttributes.HttpResponseBodySize) assert.Equal(t, string(conventions.HTTPRouteKey), httpAttributes.HttpRoute) - assert.Equal(t, string(conventions.ServerAddressKey), httpAttributes.ServerAttributes.ServerAddress) - assert.Equal(t, string(conventions.ClientAddressKey), httpAttributes.ClientAttributes.ClientAddress) networkAttributesValidations(t, httpAttributes.NetworkAttributes) + + if variant == "client" { + clientAttributesValidations(t, httpAttributes.ClientAttributes) + } else { + serverAttributesValidations(t, httpAttributes.ServerAttributes) + } } -func TestRPCPAttributeMapping(t *testing.T) { +func TestHTTPAttributeMapping(t *testing.T) { + for _, variant := range []string{"client", "server"} { + t.Run(variant, func(t *testing.T) { + testHTTPAttributeMapping(t, variant) + }) + } +} + +func testRPCPAttributeMapping(t *testing.T, variant string) { rpcAttributeValues := map[string]any{ string(conventions.RPCSystemKey): string(conventions.RPCSystemKey), string(conventions.RPCServiceKey): string(conventions.RPCServiceKey), @@ -84,6 +101,15 @@ func TestRPCPAttributeMapping(t *testing.T) { addNetworkAttributes(attributeMap) + switch variant { + case "client": + addClientAttributes(attributeMap) + case "server": + addServerAttributes(attributeMap) + default: + t.Fatalf("Unknown variant: %s", variant) + } + rpcAttributes := &rpcAttributes{} attributeMap.Range(rpcAttributes.MapAttribute) @@ -92,21 +118,33 @@ func TestRPCPAttributeMapping(t *testing.T) { assert.Equal(t, string(conventions.RPCMethodKey), rpcAttributes.RPCMethod) networkAttributesValidations(t, rpcAttributes.NetworkAttributes) + + if variant == "client" { + clientAttributesValidations(t, rpcAttributes.ClientAttributes) + } else { + serverAttributesValidations(t, rpcAttributes.ServerAttributes) + } } -func TestDatabaseAttributeMapping(t *testing.T) { +func TestRPCPAttributeMapping(t *testing.T) { + for _, variant := range []string{"client", "server"} { + t.Run(variant, func(t *testing.T) { + testRPCPAttributeMapping(t, variant) + }) + } +} + +func testDatabaseAttributeMapping(t *testing.T, variant string) { databaseAttributeValues := map[string]any{ - string(conventions.DBSystemKey): string(conventions.DBSystemKey), - string(conventions.DBConnectionStringKey): string(conventions.DBConnectionStringKey), - string(conventions.DBUserKey): string(conventions.DBUserKey), - string(conventions.DBStatementKey): string(conventions.DBStatementKey), - string(conventions.DBOperationKey): string(conventions.DBOperationKey), - string(conventions.DBMSSQLInstanceNameKey): string(conventions.DBMSSQLInstanceNameKey), - string(conventions.DBJDBCDriverClassnameKey): string(conventions.DBJDBCDriverClassnameKey), - string(conventions.DBCassandraKeyspaceKey): string(conventions.DBCassandraKeyspaceKey), - string(conventions.DBHBaseNamespaceKey): string(conventions.DBHBaseNamespaceKey), - string(conventions.DBRedisDBIndexKey): string(conventions.DBRedisDBIndexKey), - string(conventions.DBMongoDBCollectionKey): string(conventions.DBMongoDBCollectionKey), + string(conventions.DBCollectionNameKey): string(conventions.DBCollectionNameKey), + string(conventions.DBNamespaceKey): string(conventions.DBNamespaceKey), + string(conventions.DBOperationBatchSizeKey): string(conventions.DBOperationBatchSizeKey), + string(conventions.DBOperationNameKey): string(conventions.DBOperationNameKey), + string(conventions.DBQuerySummaryKey): string(conventions.DBQuerySummaryKey), + string(conventions.DBQueryTextKey): string(conventions.DBQueryTextKey), + string(conventions.DBResponseStatusCodeKey): string(conventions.DBResponseStatusCodeKey), + string(conventions.DBStoredProcedureNameKey): string(conventions.DBStoredProcedureNameKey), + string(conventions.DBSystemNameKey): string(conventions.DBSystemNameKey), } attributeMap := pcommon.NewMap() @@ -114,36 +152,63 @@ func TestDatabaseAttributeMapping(t *testing.T) { addNetworkAttributes(attributeMap) + switch variant { + case "client": + addClientAttributes(attributeMap) + case "server": + addServerAttributes(attributeMap) + default: + t.Fatalf("Unknown variant: %s", variant) + } + databaseAttributes := &databaseAttributes{} attributeMap.Range(databaseAttributes.MapAttribute) - assert.Equal(t, string(conventions.DBSystemKey), databaseAttributes.DBSystem) - assert.Equal(t, string(conventions.DBConnectionStringKey), databaseAttributes.DBConnectionString) - assert.Equal(t, string(conventions.DBUserKey), databaseAttributes.DBUser) - assert.Equal(t, string(conventions.DBStatementKey), databaseAttributes.DBStatement) - assert.Equal(t, string(conventions.DBOperationKey), databaseAttributes.DBOperation) - assert.Equal(t, string(conventions.DBMSSQLInstanceNameKey), databaseAttributes.DBMSSQLInstanceName) - assert.Equal(t, string(conventions.DBJDBCDriverClassnameKey), databaseAttributes.DBJDBCDriverClassName) - assert.Equal(t, string(conventions.DBCassandraKeyspaceKey), databaseAttributes.DBCassandraKeyspace) - assert.Equal(t, string(conventions.DBHBaseNamespaceKey), databaseAttributes.DBHBaseNamespace) - assert.Equal(t, string(conventions.DBMongoDBCollectionKey), databaseAttributes.DBMongoDBCollection) + assert.Equal(t, string(conventions.DBCollectionNameKey), databaseAttributes.DBCollectionName) + assert.Equal(t, string(conventions.DBNamespaceKey), databaseAttributes.DBNamespace) + assert.Equal(t, string(conventions.DBOperationBatchSizeKey), databaseAttributes.DBOperationBatchSize) + assert.Equal(t, string(conventions.DBOperationNameKey), databaseAttributes.DBOperationName) + assert.Equal(t, string(conventions.DBQuerySummaryKey), databaseAttributes.DBQuerySummary) + assert.Equal(t, string(conventions.DBQueryTextKey), databaseAttributes.DBQueryText) + assert.Equal(t, string(conventions.DBResponseStatusCodeKey), databaseAttributes.DBResponseStatusCode) + assert.Equal(t, string(conventions.DBStoredProcedureNameKey), databaseAttributes.DBStoredProcedureName) + assert.Equal(t, string(conventions.DBSystemNameKey), databaseAttributes.DBSystemName) + networkAttributesValidations(t, databaseAttributes.NetworkAttributes) + + if variant == "client" { + clientAttributesValidations(t, databaseAttributes.ClientAttributes) + } else { + serverAttributesValidations(t, databaseAttributes.ServerAttributes) + } } -func TestMessagingAttributeMapping(t *testing.T) { +func TestDatabaseAttributeMapping(t *testing.T) { + for _, variant := range []string{"client", "server"} { + t.Run(variant, func(t *testing.T) { + testDatabaseAttributeMapping(t, variant) + }) + } +} + +func testMessagingAttributeMapping(t *testing.T, variant string) { messagingAttributeValues := map[string]any{ - string(conventions.MessagingSystemKey): string(conventions.MessagingSystemKey), - string(conventions.MessagingDestinationKey): string(conventions.MessagingDestinationKey), - string(conventions.MessagingDestinationKindKey): string(conventions.MessagingDestinationKindKey), - string(conventions.MessagingTempDestinationKey): string(conventions.MessagingTempDestinationKey), - string(conventions.MessagingProtocolKey): string(conventions.MessagingProtocolKey), - string(conventions.MessagingProtocolVersionKey): string(conventions.MessagingProtocolVersionKey), - string(conventions.MessagingURLKey): string(conventions.MessagingURLKey), - string(conventions.MessagingMessageIDKey): string(conventions.MessagingMessageIDKey), - string(conventions.MessagingConversationIDKey): string(conventions.MessagingConversationIDKey), - string(conventions.MessagingMessagePayloadSizeBytesKey): 1, - string(conventions.MessagingMessagePayloadCompressedSizeBytesKey): 2, - string(conventions.MessagingOperationKey): string(conventions.MessagingOperationKey), + string(conventions.MessagingBatchMessageCountKey): string(conventions.MessagingBatchMessageCountKey), + string(conventions.MessagingClientIDKey): string(conventions.MessagingClientIDKey), + string(conventions.MessagingConsumerGroupNameKey): string(conventions.MessagingConsumerGroupNameKey), + string(conventions.MessagingDestinationAnonymousKey): true, + string(conventions.MessagingDestinationNameKey): string(conventions.MessagingDestinationNameKey), + string(conventions.MessagingDestinationPartitionIDKey): string(conventions.MessagingDestinationPartitionIDKey), + string(conventions.MessagingDestinationSubscriptionNameKey): string(conventions.MessagingDestinationSubscriptionNameKey), + string(conventions.MessagingDestinationTemplateKey): string(conventions.MessagingDestinationTemplateKey), + string(conventions.MessagingDestinationTemporaryKey): false, + string(conventions.MessagingMessageBodySizeKey): 1, + string(conventions.MessagingMessageConversationIDKey): string(conventions.MessagingMessageConversationIDKey), + string(conventions.MessagingMessageEnvelopeSizeKey): 2, + string(conventions.MessagingMessageIDKey): string(conventions.MessagingMessageIDKey), + string(conventions.MessagingOperationNameKey): string(conventions.MessagingOperationNameKey), + string(conventions.MessagingOperationTypeKey): string(conventions.MessagingOperationTypeKey), + string(conventions.MessagingSystemKey): string(conventions.MessagingSystemKey), } attributeMap := pcommon.NewMap() @@ -151,58 +216,94 @@ func TestMessagingAttributeMapping(t *testing.T) { addNetworkAttributes(attributeMap) + switch variant { + case "client": + addClientAttributes(attributeMap) + case "server": + addServerAttributes(attributeMap) + default: + t.Fatalf("Unknown variant: %s", variant) + } + messagingAttributes := &messagingAttributes{} attributeMap.Range(messagingAttributes.MapAttribute) - assert.Equal(t, string(conventions.MessagingSystemKey), messagingAttributes.MessagingSystem) - assert.Equal(t, string(conventions.MessagingDestinationKey), messagingAttributes.MessagingDestination) - assert.Equal(t, string(conventions.MessagingDestinationKindKey), messagingAttributes.MessagingDestinationKind) - assert.Equal(t, string(conventions.MessagingTempDestinationKey), messagingAttributes.MessagingTempDestination) - assert.Equal(t, string(conventions.MessagingProtocolKey), messagingAttributes.MessagingProtocol) - assert.Equal(t, string(conventions.MessagingProtocolVersionKey), messagingAttributes.MessagingProtocolVersion) - assert.Equal(t, string(conventions.MessagingURLKey), messagingAttributes.MessagingURL) + assert.Equal(t, string(conventions.MessagingBatchMessageCountKey), messagingAttributes.MessagingBatchMessageCount) + assert.Equal(t, string(conventions.MessagingClientIDKey), messagingAttributes.MessagingClientID) + assert.Equal(t, string(conventions.MessagingConsumerGroupNameKey), messagingAttributes.MessagingConsumerGroup) + assert.Equal(t, true, messagingAttributes.MessagingDestinationAnonymous) + assert.Equal(t, string(conventions.MessagingDestinationNameKey), messagingAttributes.MessagingDestination) + assert.Equal(t, string(conventions.MessagingDestinationPartitionIDKey), messagingAttributes.MessagingDestinationPartitionID) + assert.Equal(t, string(conventions.MessagingDestinationSubscriptionNameKey), messagingAttributes.MessagingDestinationSubName) + assert.Equal(t, string(conventions.MessagingDestinationTemplateKey), messagingAttributes.MessagingDestinationTemplate) + assert.Equal(t, false, messagingAttributes.MessagingDestinationTemporary) + assert.Equal(t, int64(1), messagingAttributes.MessagingMessageBodySize) + assert.Equal(t, string(conventions.MessagingMessageConversationIDKey), messagingAttributes.MessagingMessageConversationID) + assert.Equal(t, int64(2), messagingAttributes.MessagingMessageEnvelopeSize) assert.Equal(t, string(conventions.MessagingMessageIDKey), messagingAttributes.MessagingMessageID) - assert.Equal(t, string(conventions.MessagingConversationIDKey), messagingAttributes.MessagingConversationID) - assert.Equal(t, string(conventions.MessagingOperationKey), messagingAttributes.MessagingOperation) - assert.Equal(t, int64(1), messagingAttributes.MessagingMessagePayloadSize) - assert.Equal(t, int64(2), messagingAttributes.MessagingMessagePayloadCompressedSize) + assert.Equal(t, string(conventions.MessagingOperationNameKey), messagingAttributes.MessagingOperation) + assert.Equal(t, string(conventions.MessagingOperationTypeKey), messagingAttributes.MessagingOperationType) + assert.Equal(t, string(conventions.MessagingSystemKey), messagingAttributes.MessagingSystem) networkAttributesValidations(t, messagingAttributes.NetworkAttributes) + + if variant == "client" { + clientAttributesValidations(t, messagingAttributes.ClientAttributes) + } else { + serverAttributesValidations(t, messagingAttributes.ServerAttributes) + } +} + +func TestMessagingAttributeMapping(t *testing.T) { + for _, variant := range []string{"client", "server"} { + t.Run(variant, func(t *testing.T) { + testMessagingAttributeMapping(t, variant) + }) + } } // Tests what happens when an attribute that should be an int is not func TestAttributeMappingWithSomeBadValues(t *testing.T) { attributeMap := pcommon.NewMap() - attributeMap.PutStr(string(conventions.NetPeerPortKey), "xx") + attributeMap.PutStr(string(conventions.NetworkPeerPortKey), "xx") attrs := &networkAttributes{} attributeMap.Range(attrs.MapAttribute) // unset from default - assert.Equal(t, int64(0), attrs.NetPeerPort) + assert.Equal(t, int64(0), attrs.NetworkPeerPort) +} + +func addClientAttributes(m pcommon.Map) { + m.PutStr(string(conventions.ClientAddressKey), string(conventions.ClientAddressKey)) + m.PutInt(string(conventions.ClientPortKey), 3000) +} + +func addServerAttributes(m pcommon.Map) { + m.PutStr(string(conventions.ServerAddressKey), string(conventions.ServerAddressKey)) + m.PutInt(string(conventions.ServerPortKey), 61461) } func addNetworkAttributes(m pcommon.Map) { m.PutStr(string(conventions.NetworkTransportKey), string(conventions.NetworkTransportKey)) m.PutStr(string(conventions.NetworkPeerAddressKey), string(conventions.NetworkPeerAddressKey)) m.PutInt(string(conventions.NetworkPeerPortKey), 1) - //TODO: This is either client address or server address, not both - m.PutStr(string(conventions.NetworkPeerNameKey), string(conventions.NetworkPeerNameKey)) - m.PutStr(string(conventions.NetworkLocalAddressKey), string(conventions.NetworkLocalAddressKey)) - m.PutInt(string(conventions.ServerPortKey), 2) - m.PutStr(string(conventions.ServerAddressKey), string(conventions.ServerAddressKey)) + } func networkAttributesValidations(t *testing.T, networkAttributes networkAttributes) { assert.Equal(t, string(conventions.NetworkTransportKey), networkAttributes.NetworkTransport) assert.Equal(t, string(conventions.NetworkPeerAddressKey), networkAttributes.NetworkPeerAddress) assert.Equal(t, int64(1), networkAttributes.NetworkPeerPort) - //TODO: This is either client address or server address, not both - assert.Equal(t, string(conventions.NetworkPeerNameKey), networkAttributes.NetworkPeerName) - assert.Equal(t, string(conventions.NetworkLocalAddressKey), networkAttributes.NetworkLocalAddress) - //TODO: Replace with SeverPort - assert.Equal(t, int64(2), networkAttributes.NetworkHostPort) - //TODO: Replace with ServerAddress - assert.Equal(t, string(conventions.NetworkHostNameKey), networkAttributes.NetworkHostName) +} + +func serverAttributesValidations(t *testing.T, serverAttributes serverAttributes) { + assert.Equal(t, string(conventions.ServerAddressKey), serverAttributes.ServerAddress) + assert.Equal(t, int64(61461), serverAttributes.ServerPort) +} + +func clientAttributesValidations(t *testing.T, clientAttributes clientAttributes) { + assert.Equal(t, string(conventions.ClientAddressKey), clientAttributes.ClientAddress) + assert.Equal(t, int64(3000), clientAttributes.ClientPort) } From ce18f14951e82ced835e6f94cf908a260620084f Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Tue, 21 Oct 2025 11:39:47 -0600 Subject: [PATCH 3/7] fixed failing tests --- exporter/azuremonitorexporter/conventions.go | 5 +- .../azuremonitorexporter/conventions_test.go | 23 +- .../azuremonitorexporter/trace_to_envelope.go | 219 +++++++++--------- .../trace_to_envelope_test.go | 161 +++++++------ 4 files changed, 212 insertions(+), 196 deletions(-) diff --git a/exporter/azuremonitorexporter/conventions.go b/exporter/azuremonitorexporter/conventions.go index 829e76ee7b81e..2af4a0f55e652 100644 --- a/exporter/azuremonitorexporter/conventions.go +++ b/exporter/azuremonitorexporter/conventions.go @@ -121,13 +121,16 @@ func setHeader(headers *map[string][]string, key string, v pcommon.Value) { if *headers == nil { *headers = make(map[string][]string) } - if v.Type() == pcommon.ValueTypeSlice { + switch v.Type() { + case pcommon.ValueTypeSlice: slice := v.Slice() var vals []string for i := 0; i < slice.Len(); i++ { vals = append(vals, slice.At(i).Str()) } (*headers)[key] = vals + case pcommon.ValueTypeStr: + (*headers)[key] = []string{v.Str()} } } diff --git a/exporter/azuremonitorexporter/conventions_test.go b/exporter/azuremonitorexporter/conventions_test.go index 51e55fa9aaa4d..4d7f635a58e2f 100644 --- a/exporter/azuremonitorexporter/conventions_test.go +++ b/exporter/azuremonitorexporter/conventions_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" conventions "go.opentelemetry.io/otel/semconv/v1.34.0" ) @@ -57,17 +58,23 @@ func testHTTPAttributeMapping(t *testing.T, variant string) { assert.Equal(t, string(conventions.NetworkProtocolNameKey), httpAttributes.NetworkAttributes.NetworkProtocolName) assert.Equal(t, string(conventions.UserAgentOriginalKey), httpAttributes.UserAgentAttributes.UserAgentOriginal) - reqCL := httpAttributes.HttpRequestHeaders["content-length"][0] + vals, ok := httpAttributes.HttpRequestHeaders["content-length"] + require.True(t, ok) + require.NotEmpty(t, vals) + reqCL := vals[0] reqCLInt, err := strconv.ParseInt(reqCL, 10, 64) assert.NoError(t, err) assert.Equal(t, int64(1), reqCLInt) assert.Equal(t, int64(2), httpAttributes.HttpRequestBodySize) - resCL := httpAttributes.HttpResponseHeaders["content-length"][0] - resCLInt, err := strconv.ParseInt(resCL, 10, 64) + valsRes, okRes := httpAttributes.HttpResponseHeaders["content-length"] + require.True(t, okRes) + require.NotEmpty(t, valsRes) + reqRes := valsRes[0] + reqResInt, err := strconv.ParseInt(reqRes, 10, 64) assert.NoError(t, err) - assert.Equal(t, int64(3), resCLInt) + assert.Equal(t, int64(3), reqResInt) assert.Equal(t, int64(4), httpAttributes.HttpResponseBodySize) assert.Equal(t, string(conventions.HTTPRouteKey), httpAttributes.HttpRoute) @@ -138,7 +145,7 @@ func testDatabaseAttributeMapping(t *testing.T, variant string) { databaseAttributeValues := map[string]any{ string(conventions.DBCollectionNameKey): string(conventions.DBCollectionNameKey), string(conventions.DBNamespaceKey): string(conventions.DBNamespaceKey), - string(conventions.DBOperationBatchSizeKey): string(conventions.DBOperationBatchSizeKey), + string(conventions.DBOperationBatchSizeKey): 0, string(conventions.DBOperationNameKey): string(conventions.DBOperationNameKey), string(conventions.DBQuerySummaryKey): string(conventions.DBQuerySummaryKey), string(conventions.DBQueryTextKey): string(conventions.DBQueryTextKey), @@ -166,7 +173,7 @@ func testDatabaseAttributeMapping(t *testing.T, variant string) { assert.Equal(t, string(conventions.DBCollectionNameKey), databaseAttributes.DBCollectionName) assert.Equal(t, string(conventions.DBNamespaceKey), databaseAttributes.DBNamespace) - assert.Equal(t, string(conventions.DBOperationBatchSizeKey), databaseAttributes.DBOperationBatchSize) + assert.Equal(t, int64(0), databaseAttributes.DBOperationBatchSize) assert.Equal(t, string(conventions.DBOperationNameKey), databaseAttributes.DBOperationName) assert.Equal(t, string(conventions.DBQuerySummaryKey), databaseAttributes.DBQuerySummary) assert.Equal(t, string(conventions.DBQueryTextKey), databaseAttributes.DBQueryText) @@ -193,7 +200,7 @@ func TestDatabaseAttributeMapping(t *testing.T) { func testMessagingAttributeMapping(t *testing.T, variant string) { messagingAttributeValues := map[string]any{ - string(conventions.MessagingBatchMessageCountKey): string(conventions.MessagingBatchMessageCountKey), + string(conventions.MessagingBatchMessageCountKey): 0, string(conventions.MessagingClientIDKey): string(conventions.MessagingClientIDKey), string(conventions.MessagingConsumerGroupNameKey): string(conventions.MessagingConsumerGroupNameKey), string(conventions.MessagingDestinationAnonymousKey): true, @@ -228,7 +235,7 @@ func testMessagingAttributeMapping(t *testing.T, variant string) { messagingAttributes := &messagingAttributes{} attributeMap.Range(messagingAttributes.MapAttribute) - assert.Equal(t, string(conventions.MessagingBatchMessageCountKey), messagingAttributes.MessagingBatchMessageCount) + assert.Equal(t, int64(0), messagingAttributes.MessagingBatchMessageCount) assert.Equal(t, string(conventions.MessagingClientIDKey), messagingAttributes.MessagingClientID) assert.Equal(t, string(conventions.MessagingConsumerGroupNameKey), messagingAttributes.MessagingConsumerGroup) assert.Equal(t, true, messagingAttributes.MessagingDestinationAnonymous) diff --git a/exporter/azuremonitorexporter/trace_to_envelope.go b/exporter/azuremonitorexporter/trace_to_envelope.go index 35dd4f16938fe..d4523f871563b 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope.go +++ b/exporter/azuremonitorexporter/trace_to_envelope.go @@ -307,8 +307,8 @@ func getFormattedHTTPStatusValues(statusCode int64) (statusAsString string, succ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { attrs := copyAndExtractHTTPAttributes(span.Attributes(), data.Properties) - if attrs.HTTPStatusCode != 0 { - data.ResponseCode, data.Success = getFormattedHTTPStatusValues(attrs.HTTPStatusCode) + if attrs.HttpResponseStatusCode != 0 { + data.ResponseCode, data.Success = getFormattedHTTPStatusValues(attrs.HttpResponseStatusCode) } var sb strings.Builder @@ -316,13 +316,13 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { // Construct data.Name // The data.Name should be {HTTP METHOD} {HTTP SERVER ROUTE TEMPLATE} // https://github.com/microsoft/ApplicationInsights-Home/blob/f1f9f619d74557c8db3dbde4b49c4193e10d8a81/EndpointSpecs/Schemas/Bond/RequestData.bond#L32 - sb.WriteString(attrs.HTTPMethod) + sb.WriteString(attrs.HttpRequestMethod) sb.WriteString(" ") // Use httpRoute if available otherwise fallback to the span name // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name - if attrs.HTTPRoute != "" { - sb.WriteString(prefixIfNecessary(attrs.HTTPRoute, "/")) + if attrs.HttpRoute != "" { + sb.WriteString(prefixIfNecessary(attrs.HttpRoute, "/")) } else { sb.WriteString(span.Name()) } @@ -339,53 +339,50 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { http.url */ - if attrs.HTTPTarget != "" { - attrs.HTTPTarget = prefixIfNecessary(attrs.HTTPTarget, "/") + if attrs.UrlAttributes.UrlPath != "" { + attrs.UrlAttributes.UrlPath = prefixIfNecessary(attrs.UrlAttributes.UrlPath, "/") } - netHostPortAsString := "" - if attrs.NetworkAttributes.NetHostPort != 0 { - netHostPortAsString = strconv.FormatInt(attrs.NetworkAttributes.NetHostPort, 10) + serverPort := "" + if attrs.ServerAttributes.ServerPort != 0 { + serverPort = strconv.FormatInt(attrs.ServerAttributes.ServerPort, 10) } switch { - case attrs.HTTPScheme != "" && attrs.HTTPHost != "" && attrs.HTTPTarget != "": - sb.WriteString(attrs.HTTPScheme) + case attrs.UrlAttributes.UrlScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort == "" && attrs.UrlAttributes.UrlPath != "": + sb.WriteString(attrs.UrlAttributes.UrlScheme) sb.WriteString("://") - sb.WriteString(attrs.HTTPHost) - sb.WriteString(attrs.HTTPTarget) - data.Url = sb.String() - case attrs.HTTPScheme != "" && attrs.HTTPServerName != "" && netHostPortAsString != "" && attrs.HTTPTarget != "": - sb.WriteString(attrs.HTTPScheme) - sb.WriteString("://") - sb.WriteString(attrs.HTTPServerName) - sb.WriteString(":") - sb.WriteString(netHostPortAsString) - sb.WriteString(attrs.HTTPTarget) + sb.WriteString(attrs.ServerAttributes.ServerAddress) + sb.WriteString(attrs.UrlAttributes.UrlPath) + if attrs.UrlAttributes.UrlQuery != "" { + sb.WriteString("?") + sb.WriteString(attrs.UrlAttributes.UrlQuery) + } data.Url = sb.String() - case attrs.HTTPScheme != "" && attrs.NetworkAttributes.NetHostName != "" && netHostPortAsString != "" && attrs.HTTPTarget != "": - sb.WriteString(attrs.HTTPScheme) + case attrs.UrlAttributes.UrlScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort != "" && attrs.UrlAttributes.UrlPath != "": + sb.WriteString(attrs.UrlAttributes.UrlScheme) sb.WriteString("://") - sb.WriteString(attrs.NetworkAttributes.NetHostName) + sb.WriteString(attrs.ServerAttributes.ServerAddress) sb.WriteString(":") - sb.WriteString(netHostPortAsString) - sb.WriteString(attrs.HTTPTarget) + sb.WriteString(serverPort) + sb.WriteString(attrs.UrlAttributes.UrlPath) + if attrs.UrlAttributes.UrlQuery != "" { + sb.WriteString("?") + sb.WriteString(attrs.UrlAttributes.UrlQuery) + } data.Url = sb.String() - case attrs.HTTPURL != "": - if _, err := url.Parse(attrs.HTTPURL); err == nil { - data.Url = attrs.HTTPURL + case attrs.UrlAttributes.UrlFull != "": + if _, err := url.Parse(attrs.UrlAttributes.UrlFull); err == nil { + data.Url = attrs.UrlAttributes.UrlFull } } sb.Reset() - // data.Source should be the client ip if available or fallback to net.peer.ip - // https://github.com/microsoft/ApplicationInsights-Home/blob/f1f9f619d74557c8db3dbde4b49c4193e10d8a81/EndpointSpecs/Schemas/Bond/RequestData.bond#L28 - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-server-semantic-conventions - if attrs.HTTPClientIP != "" { - data.Source = attrs.HTTPClientIP - } else if attrs.NetworkAttributes.NetPeerIP != "" { - data.Source = attrs.NetworkAttributes.NetPeerIP + if attrs.ClientAttributes.ClientAddress != "" { + data.Source = attrs.ClientAttributes.ClientAddress + } else if attrs.NetworkAttributes.NetworkPeerAddress != "" { + data.Source = attrs.NetworkAttributes.NetworkPeerAddress } } @@ -395,84 +392,85 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend attrs := copyAndExtractHTTPAttributes(span.Attributes(), data.Properties) data.Type = "HTTP" - if attrs.HTTPStatusCode != 0 { - data.ResultCode, data.Success = getFormattedHTTPStatusValues(attrs.HTTPStatusCode) + if attrs.HttpResponseStatusCode != 0 { + data.ResultCode, data.Success = getFormattedHTTPStatusValues(attrs.HttpResponseStatusCode) } var sb strings.Builder - // Construct data.Name - // The data.Name should default to {HTTP METHOD} and include {HTTP ROUTE TEMPLATE} (if available) - sb.WriteString(attrs.HTTPMethod) + sb.WriteString(attrs.HttpRequestMethod) - // Use httpRoute if available otherwise fallback to the HTTP method - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name - if attrs.HTTPRoute != "" { + if attrs.HttpRoute != "" { sb.WriteString(" ") - sb.WriteString(attrs.HTTPRoute) + sb.WriteString(attrs.HttpRoute) } data.Name = sb.String() sb.Reset() - /* - Order of preference is: - http.url - http.scheme, http.host, http.target - http.scheme, net.peer.name, net.peer.port, http.target - http.scheme, net.peer.ip, net.peer.port, http.target - */ - - // prefix httpTarget, if specified - if attrs.HTTPTarget != "" { - attrs.HTTPTarget = prefixIfNecessary(attrs.HTTPTarget, "/") + if attrs.UrlAttributes.UrlPath != "" { + attrs.UrlAttributes.UrlPath = prefixIfNecessary(attrs.UrlAttributes.UrlPath, "/") } - netPeerPortAsString := "" - if attrs.NetworkAttributes.NetPeerPort != 0 { - netPeerPortAsString = strconv.FormatInt(attrs.NetworkAttributes.NetPeerPort, 10) + clientPortStr := "" + if attrs.ClientAttributes.ClientPort != 0 { + clientPortStr = strconv.FormatInt(attrs.ClientAttributes.ClientPort, 10) } switch { - case attrs.HTTPURL != "": - if u, err := url.Parse(attrs.HTTPURL); err == nil { - data.Data = attrs.HTTPURL + case attrs.UrlAttributes.UrlFull != "": + if u, err := url.Parse(attrs.UrlAttributes.UrlFull); err == nil { + data.Data = attrs.UrlAttributes.UrlFull data.Target = u.Host } - case attrs.HTTPScheme != "" && attrs.HTTPHost != "" && attrs.HTTPTarget != "": - sb.WriteString(attrs.HTTPScheme) + case attrs.UrlAttributes.UrlScheme != "" && attrs.ClientAttributes.ClientAddress != "" && clientPortStr == "" && attrs.UrlAttributes.UrlPath != "": + sb.WriteString(attrs.UrlAttributes.UrlScheme) sb.WriteString("://") - sb.WriteString(attrs.HTTPHost) - sb.WriteString(attrs.HTTPTarget) + sb.WriteString(attrs.ClientAttributes.ClientAddress) + sb.WriteString(attrs.UrlAttributes.UrlPath) + if attrs.UrlAttributes.UrlQuery != "" { + sb.WriteString("?") + sb.WriteString(attrs.UrlAttributes.UrlQuery) + } data.Data = sb.String() - data.Target = attrs.HTTPHost - case attrs.HTTPScheme != "" && attrs.NetworkAttributes.NetPeerName != "" && netPeerPortAsString != "" && attrs.HTTPTarget != "": - sb.WriteString(attrs.HTTPScheme) + data.Target = attrs.ClientAttributes.ClientAddress + + case attrs.UrlAttributes.UrlScheme != "" && attrs.ClientAttributes.ClientAddress != "" && clientPortStr != "" && attrs.UrlAttributes.UrlPath != "": + sb.WriteString(attrs.UrlAttributes.UrlScheme) sb.WriteString("://") - sb.WriteString(attrs.NetworkAttributes.NetPeerName) + sb.WriteString(attrs.ClientAttributes.ClientAddress) sb.WriteString(":") - sb.WriteString(netPeerPortAsString) - sb.WriteString(attrs.HTTPTarget) + sb.WriteString(clientPortStr) + sb.WriteString(attrs.UrlAttributes.UrlPath) + if attrs.UrlAttributes.UrlQuery != "" { + sb.WriteString("?") + sb.WriteString(attrs.UrlAttributes.UrlQuery) + } data.Data = sb.String() sb.Reset() - sb.WriteString(attrs.NetworkAttributes.NetPeerName) + sb.WriteString(attrs.ClientAttributes.ClientAddress) sb.WriteString(":") - sb.WriteString(netPeerPortAsString) + sb.WriteString(clientPortStr) data.Target = sb.String() - case attrs.HTTPScheme != "" && attrs.NetworkAttributes.NetPeerIP != "" && netPeerPortAsString != "" && attrs.HTTPTarget != "": - sb.WriteString(attrs.HTTPScheme) + + case attrs.UrlAttributes.UrlScheme != "" && attrs.NetworkAttributes.NetworkPeerAddress != "" && clientPortStr != "" && attrs.UrlAttributes.UrlPath != "": + sb.WriteString(attrs.UrlAttributes.UrlScheme) sb.WriteString("://") - sb.WriteString(attrs.NetworkAttributes.NetPeerIP) + sb.WriteString(attrs.NetworkAttributes.NetworkPeerAddress) sb.WriteString(":") - sb.WriteString(netPeerPortAsString) - sb.WriteString(attrs.HTTPTarget) + sb.WriteString(clientPortStr) + sb.WriteString(attrs.UrlAttributes.UrlPath) + if attrs.UrlAttributes.UrlQuery != "" { + sb.WriteString("?") + sb.WriteString(attrs.UrlAttributes.UrlQuery) + } data.Data = sb.String() sb.Reset() - sb.WriteString(attrs.NetworkAttributes.NetPeerIP) + sb.WriteString(attrs.NetworkAttributes.NetworkPeerAddress) sb.WriteString(":") - sb.WriteString(netPeerPortAsString) + sb.WriteString(clientPortStr) data.Target = sb.String() } } @@ -498,7 +496,7 @@ func fillRequestDataRPC(span ptrace.Span, data *contracts.RequestData) { sb.Reset() - writeFormattedPeerAddressFromNetworkAttributes(&attrs.NetworkAttributes, &sb) + writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ServerAttributes.ServerAddress, attrs.ServerAttributes.ServerPort, &sb) data.Source = sb.String() } @@ -516,7 +514,8 @@ func fillRemoteDependencyDataRPC(span ptrace.Span, data *contracts.RemoteDepende data.Type = attrs.RPCSystem var sb strings.Builder - writeFormattedPeerAddressFromNetworkAttributes(&attrs.NetworkAttributes, &sb) + + writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ClientAttributes.ClientAddress, attrs.ClientAttributes.ClientPort, &sb) data.Target = sb.String() } @@ -534,16 +533,16 @@ func getRPCStatusCodeAsString(rpcAttributes *rpcAttributes) (statusCodeAsString func fillRemoteDependencyDataDatabase(span ptrace.Span, data *contracts.RemoteDependencyData) { attrs := copyAndExtractDatabaseAttributes(span.Attributes(), data.Properties) - data.Type = attrs.DBSystem + data.Type = attrs.DBSystemName - if attrs.DBStatement != "" { - data.Data = attrs.DBStatement - } else if attrs.DBOperation != "" { - data.Data = attrs.DBOperation + if attrs.DBQueryText != "" { + data.Data = attrs.DBQueryText + } else if attrs.DBOperationName != "" { + data.Data = attrs.DBOperationName } var sb strings.Builder - writeFormattedPeerAddressFromNetworkAttributes(&attrs.NetworkAttributes, &sb) + writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ClientAttributes.ClientAddress, attrs.ClientAttributes.ClientPort, &sb) data.Target = sb.String() } @@ -553,13 +552,10 @@ func fillRequestDataMessaging(span ptrace.Span, data *contracts.RequestData) { attrs := copyAndExtractMessagingAttributes(span.Attributes(), data.Properties) // TODO Understand how to map attributes to RequestData fields - if attrs.MessagingURL != "" { - data.Source = attrs.MessagingURL - } else { - var sb strings.Builder - writeFormattedPeerAddressFromNetworkAttributes(&attrs.NetworkAttributes, &sb) - data.Source = sb.String() - } + var sb strings.Builder + writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ServerAttributes.ServerAddress, attrs.ServerAttributes.ServerPort, &sb) + data.Source = sb.String() + } // Maps Messaging Producer/Client Span to AppInsights RemoteDependencyData @@ -568,16 +564,12 @@ func fillRemoteDependencyDataMessaging(span ptrace.Span, data *contracts.RemoteD attrs := copyAndExtractMessagingAttributes(span.Attributes(), data.Properties) // TODO Understand how to map attributes to RemoteDependencyData fields - data.Data = attrs.MessagingURL data.Type = attrs.MessagingSystem - if attrs.MessagingURL != "" { - data.Target = attrs.MessagingURL - } else { - var sb strings.Builder - writeFormattedPeerAddressFromNetworkAttributes(&attrs.NetworkAttributes, &sb) - data.Target = sb.String() - } + var sb strings.Builder + writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ClientAttributes.ClientAddress, attrs.ClientAttributes.ClientPort, &sb) + data.Target = sb.String() + } // Copies all attributes to either properties or measurements and passes the key/value to another mapping function @@ -719,17 +711,18 @@ func getDefaultFormattedSpanStatus(spanStatus ptrace.Status) (statusCodeAsString return strconv.FormatInt(int64(code), 10), code != ptrace.StatusCodeError } -func writeFormattedPeerAddressFromNetworkAttributes(networkAttributes *networkAttributes, sb *strings.Builder) { - // Favor name over IP for - if networkAttributes.NetPeerName != "" { - sb.WriteString(networkAttributes.NetPeerName) - } else if networkAttributes.NetPeerIP != "" { - sb.WriteString(networkAttributes.NetPeerIP) +func writeFormatedFromNetworkServerOrClient(networkAttributes *networkAttributes, addressName string, addressPort int64, sb *strings.Builder) { + + //server.address or client.address + if addressName != "" { + sb.WriteString(addressName) + } else { + sb.WriteString(networkAttributes.NetworkPeerAddress) } - if networkAttributes.NetPeerPort != 0 { + if addressPort != 0 { sb.WriteString(":") - sb.WriteString(strconv.FormatInt(networkAttributes.NetPeerPort, 10)) + sb.WriteString(strconv.FormatInt(addressPort, 10)) } } diff --git a/exporter/azuremonitorexporter/trace_to_envelope_test.go b/exporter/azuremonitorexporter/trace_to_envelope_test.go index e25c94a733a81..6ee4f1e7a28e3 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope_test.go +++ b/exporter/azuremonitorexporter/trace_to_envelope_test.go @@ -22,6 +22,8 @@ import ( ) const ( + defaultServerAddress = "foo.com" + defaultRequestDataEnvelopeName = "Microsoft.ApplicationInsights.Request" defaultRemoteDependencyDataEnvelopeName = "Microsoft.ApplicationInsights.RemoteDependency" defaultMessageDataEnvelopeName = "Microsoft.ApplicationInsights.Message" @@ -32,6 +34,8 @@ const ( defaultScopeName = "myinstrumentationlib" defaultScopeVersion = "1.0" defaultHTTPMethod = http.MethodGet + defaultHttpServerPort = 80 + defaultUrlFull = "https://foo/bar/12345?biz=baz" defaultHTTPServerSpanName = "/bar" defaultHTTPClientSpanName = defaultHTTPMethod defaultHTTPStatusCode = 200 @@ -39,14 +43,13 @@ const ( defaultRPCSpanName = "com.example.ExampleRmiService/exampleMethod" defaultRPCStatusCode = 0 defaultDBSystem = "mssql" - defaultDBName = "adventureworks" defaultDBSpanName = "astoredproc" defaultDBStatement = "exec astoredproc1" defaultDBOperation = "exec astoredproc2" defaultMessagingSpanName = "MyQueue" defaultMessagingSystem = "kafka" defaultMessagingDestination = "MyQueue" - defaultMessagingURL = "https://queue.amazonaws.com/80398EXAMPLE/MyQueue" + defaultMessagingOperationName = "ack" defaultInternalSpanName = "MethodX" ) @@ -70,22 +73,25 @@ var ( // Required attribute for any HTTP Span requiredHTTPAttributes = map[string]any{ - string(conventions.HTTPMethodKey): defaultHTTPMethod, + string(conventions.HTTPRequestMethodKey): defaultHTTPMethod, + string(conventions.ServerAddressKey): defaultServerAddress, + string(conventions.ServerPortKey): defaultHttpServerPort, + string(conventions.URLFullKey): defaultUrlFull, } // Required attribute for any RPC Span requiredRPCAttributes = map[string]any{ - string(conventions.RPCSystemKey): defaultRPCSystem, + string(conventions.RPCSystemKey): defaultRPCSystem, + string(conventions.ServerAddressKey): defaultServerAddress, } requiredDatabaseAttributes = map[string]any{ - string(conventions.DBSystemKey): defaultDBSystem, - string(conventions.DBNameKey): defaultDBName, + string(conventions.DBSystemNameKey): defaultDBSystem, } requiredMessagingAttributes = map[string]any{ - string(conventions.MessagingSystemKey): defaultMessagingSystem, - string(conventions.MessagingDestinationKey): defaultMessagingDestination, + string(conventions.MessagingOperationNameKey): defaultMessagingOperationName, + string(conventions.MessagingSystemKey): defaultMessagingSystem, } defaultResource = getResource() @@ -116,12 +122,16 @@ func TestHTTPServerSpanToRequestDataAttributeSet1(t *testing.T) { spanAttributes := span.Attributes() // http.scheme, http.host, http.target => data.Url - spanAttributes.PutStr(string(conventions.HTTPSchemeKey), "https") - spanAttributes.PutStr(string(conventions.HTTPHostKey), "foo") - spanAttributes.PutStr(string(conventions.HTTPTargetKey), "/bar?biz=baz") + spanAttributes.PutStr(string(conventions.URLSchemeKey), "https") + spanAttributes.PutStr(string(conventions.ServerAddressKey), "foo") + spanAttributes.PutStr(string(conventions.URLPathKey), "/bar") + spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") + + //Reset server port to null + spanAttributes.PutStr(string(conventions.ServerPortKey), "") // A non 2xx status code - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), 400) + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), 400) // A specific http route spanAttributes.PutStr(string(conventions.HTTPRouteKey), "bizzle") @@ -154,13 +164,14 @@ func TestHTTPServerSpanToRequestDataAttributeSet2(t *testing.T) { span := getDefaultHTTPServerSpan() spanAttributes := span.Attributes() - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), defaultHTTPStatusCode) - spanAttributes.PutStr(string(conventions.HTTPSchemeKey), "https") - spanAttributes.PutStr(string(conventions.HTTPServerNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetHostPortKey), 81) - spanAttributes.PutStr(string(conventions.HTTPTargetKey), "/bar?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), defaultHTTPStatusCode) + spanAttributes.PutStr(string(conventions.URLSchemeKey), "https") + spanAttributes.PutStr(string(conventions.ServerAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ServerPortKey), 81) + spanAttributes.PutStr(string(conventions.URLPathKey), "/bar") + spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -180,14 +191,15 @@ func TestHTTPServerSpanToRequestDataAttributeSet3(t *testing.T) { span := getDefaultHTTPServerSpan() spanAttributes := span.Attributes() - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), defaultHTTPStatusCode) - spanAttributes.PutStr(string(conventions.HTTPSchemeKey), "https") - spanAttributes.PutStr(string(conventions.NetHostNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetHostPortKey), 81) - spanAttributes.PutStr(string(conventions.HTTPTargetKey), "/bar?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), defaultHTTPStatusCode) + spanAttributes.PutStr(string(conventions.URLSchemeKey), "https") + spanAttributes.PutStr(string(conventions.ServerAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ServerPortKey), 81) + spanAttributes.PutStr(string(conventions.URLPathKey), "/bar") + spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") - spanAttributes.PutStr(string(conventions.HTTPClientIPKey), "127.0.0.2") - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") + spanAttributes.PutStr(string(conventions.ClientAddressKey), "127.0.0.2") + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -204,8 +216,8 @@ func TestHTTPServerSpanToRequestDataAttributeSet4(t *testing.T) { span := getDefaultHTTPServerSpan() spanAttributes := span.Attributes() - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), defaultHTTPStatusCode) - spanAttributes.PutStr(string(conventions.HTTPURLKey), "https://foo:81/bar?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), defaultHTTPStatusCode) + spanAttributes.PutStr(string(conventions.URLFullKey), "https://foo:81/bar?biz=baz") envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -232,8 +244,8 @@ func TestHTTPClientSpanToRemoteDependencyAttributeSet1(t *testing.T) { span := getDefaultHTTPClientSpan() spanAttributes := span.Attributes() - spanAttributes.PutStr(string(conventions.HTTPURLKey), "https://foo:81/bar?biz=baz") - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), 400) + spanAttributes.PutStr(string(conventions.URLFullKey), "https://foo:81/bar?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), 400) envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -257,10 +269,14 @@ func TestHTTPClientSpanToRemoteDependencyAttributeSet2(t *testing.T) { spanAttributes := span.Attributes() // http.scheme, http.host, http.target => data.Url - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), defaultHTTPStatusCode) - spanAttributes.PutStr(string(conventions.HTTPSchemeKey), "https") - spanAttributes.PutStr(string(conventions.HTTPHostKey), "foo") - spanAttributes.PutStr(string(conventions.HTTPTargetKey), "bar/12345?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), defaultHTTPStatusCode) + spanAttributes.PutStr(string(conventions.URLSchemeKey), "https") + spanAttributes.PutStr(string(conventions.ClientAddressKey), "foo") + spanAttributes.PutStr(string(conventions.URLPathKey), "/bar/12345") + spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") + + // Removing URL Full Key to test fallback although http client span requires url.full see https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/http/http-spans.md + spanAttributes.PutStr(string(conventions.URLFullKey), "") // A specific http.route spanAttributes.PutStr(string(conventions.HTTPRouteKey), "/bar/:baz_id") @@ -284,11 +300,15 @@ func TestHTTPClientSpanToRemoteDependencyAttributeSet3(t *testing.T) { span := getDefaultHTTPClientSpan() spanAttributes := span.Attributes() - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), defaultHTTPStatusCode) - spanAttributes.PutStr(string(conventions.HTTPSchemeKey), "https") - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) - spanAttributes.PutStr(string(conventions.HTTPTargetKey), "/bar?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), defaultHTTPStatusCode) + spanAttributes.PutStr(string(conventions.URLSchemeKey), "https") + spanAttributes.PutStr(string(conventions.ClientAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ClientPortKey), 81) + spanAttributes.PutStr(string(conventions.URLPathKey), "/bar") + spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") + + // Removing URL Full Key to test fallback although http client span requires url.full see https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/http/http-spans.md + spanAttributes.PutStr(string(conventions.URLFullKey), "") envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -304,13 +324,17 @@ func TestHTTPClientSpanToRemoteDependencyAttributeSet4(t *testing.T) { span := getDefaultHTTPClientSpan() spanAttributes := span.Attributes() - spanAttributes.PutInt(string(conventions.HTTPStatusCodeKey), defaultHTTPStatusCode) - spanAttributes.PutStr(string(conventions.HTTPSchemeKey), "https") - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) - spanAttributes.PutStr(string(conventions.HTTPTargetKey), "/bar?biz=baz") + spanAttributes.PutInt(string(conventions.HTTPResponseStatusCodeKey), defaultHTTPStatusCode) + spanAttributes.PutStr(string(conventions.URLSchemeKey), "https") + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") + spanAttributes.PutInt(string(conventions.ClientPortKey), 81) + spanAttributes.PutStr(string(conventions.URLPathKey), "/bar") + spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") spanAttributes.PutStr(string(conventions.EnduserIDKey), "12345") + // Removing URL Full Key to test fallback although http client span requires url.full see https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/http/http-spans.md + spanAttributes.PutStr(string(conventions.URLFullKey), "") + envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] commonEnvelopeValidations(t, span, envelope, defaultRemoteDependencyDataEnvelopeName) @@ -325,9 +349,10 @@ func TestRPCServerSpanToRequestData(t *testing.T) { span := getDefaultRPCServerSpan() spanAttributes := span.Attributes() - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "foo") - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) + spanAttributes.PutStr(string(conventions.ServerAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ServerPortKey), 81) + + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -336,8 +361,8 @@ func TestRPCServerSpanToRequestData(t *testing.T) { defaultRPCRequestDataValidations(t, span, data, "foo:81") // test fallback to peerip - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "") - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") + spanAttributes.PutStr(string(conventions.ServerAddressKey), "") + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") envelopes, _ = spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope = envelopes[0] @@ -350,9 +375,9 @@ func TestRPCClientSpanToRemoteDependencyData(t *testing.T) { span := getDefaultRPCClientSpan() spanAttributes := span.Attributes() - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") + spanAttributes.PutStr(string(conventions.ClientAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ClientPortKey), 81) + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -361,8 +386,8 @@ func TestRPCClientSpanToRemoteDependencyData(t *testing.T) { defaultRPCRemoteDependencyDataValidations(t, span, data, "foo:81") // test fallback to peerip - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "") - spanAttributes.PutStr(string(conventions.NetPeerIPKey), "127.0.0.1") + spanAttributes.PutStr(string(conventions.ClientAddressKey), "") + spanAttributes.PutStr(string(conventions.NetworkPeerAddressKey), "127.0.0.1") envelopes, _ = spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope = envelopes[0] @@ -388,9 +413,9 @@ func TestDatabaseClientSpanToRemoteDependencyData(t *testing.T) { span := getDefaultDatabaseClientSpan() spanAttributes := span.Attributes() - spanAttributes.PutStr(string(conventions.DBStatementKey), defaultDBStatement) - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) + spanAttributes.PutStr(string(conventions.DBQueryTextKey), defaultDBStatement) + spanAttributes.PutStr(string(conventions.ClientAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ClientPortKey), 81) envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -402,8 +427,8 @@ func TestDatabaseClientSpanToRemoteDependencyData(t *testing.T) { assert.Equal(t, defaultDBStatement, data.Data) // Test the fallback to data.Data fallback to DBOperation - spanAttributes.PutStr(string(conventions.DBStatementKey), "") - spanAttributes.PutStr(string(conventions.DBOperationKey), defaultDBOperation) + spanAttributes.PutStr(string(conventions.DBQueryTextKey), "") + spanAttributes.PutStr(string(conventions.DBQueryTextKey), defaultDBOperation) envelopes, _ = spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope = envelopes[0] @@ -416,9 +441,8 @@ func TestMessagingConsumerSpanToRequestData(t *testing.T) { span := getDefaultMessagingConsumerSpan() spanAttributes := span.Attributes() - spanAttributes.PutStr(string(conventions.MessagingURLKey), defaultMessagingURL) - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) + spanAttributes.PutStr(string(conventions.ServerAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ServerPortKey), 81) envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -426,11 +450,6 @@ func TestMessagingConsumerSpanToRequestData(t *testing.T) { data := envelope.Data.(*contracts.Data).BaseData.(*contracts.RequestData) defaultMessagingRequestDataValidations(t, span, data) - assert.Equal(t, defaultMessagingURL, data.Source) - - // test fallback from MessagingURL to net.* properties - spanAttributes.PutStr(string(conventions.MessagingURLKey), "") - envelopes, _ = spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope = envelopes[0] data = envelope.Data.(*contracts.Data).BaseData.(*contracts.RequestData) @@ -443,9 +462,8 @@ func TestMessagingProducerSpanToRequestData(t *testing.T) { span := getDefaultMessagingProducerSpan() spanAttributes := span.Attributes() - spanAttributes.PutStr(string(conventions.MessagingURLKey), defaultMessagingURL) - spanAttributes.PutStr(string(conventions.NetPeerNameKey), "foo") - spanAttributes.PutInt(string(conventions.NetPeerPortKey), 81) + spanAttributes.PutStr(string(conventions.ClientAddressKey), "foo") + spanAttributes.PutInt(string(conventions.ClientPortKey), 81) envelopes, _ := spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope := envelopes[0] @@ -453,11 +471,6 @@ func TestMessagingProducerSpanToRequestData(t *testing.T) { data := envelope.Data.(*contracts.Data).BaseData.(*contracts.RemoteDependencyData) defaultMessagingRemoteDependencyDataValidations(t, span, data) - assert.Equal(t, defaultMessagingURL, data.Target) - - // test fallback from MessagingURL to net.* properties - spanAttributes.PutStr(string(conventions.MessagingURLKey), "") - envelopes, _ = spanToEnvelopes(defaultResource, defaultInstrumentationLibrary, span, true, zap.NewNop()) envelope = envelopes[0] data = envelope.Data.(*contracts.Data).BaseData.(*contracts.RemoteDependencyData) From 49d1fb7817e643be8b8c336f7f46e1d8dcaafd9e Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Tue, 21 Oct 2025 12:14:20 -0600 Subject: [PATCH 4/7] Fixed bug with extra ? --- exporter/azuremonitorexporter/trace_to_envelope.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/exporter/azuremonitorexporter/trace_to_envelope.go b/exporter/azuremonitorexporter/trace_to_envelope.go index d4523f871563b..fcc24f6183b13 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope.go +++ b/exporter/azuremonitorexporter/trace_to_envelope.go @@ -355,7 +355,6 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { sb.WriteString(attrs.ServerAttributes.ServerAddress) sb.WriteString(attrs.UrlAttributes.UrlPath) if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString("?") sb.WriteString(attrs.UrlAttributes.UrlQuery) } data.Url = sb.String() @@ -367,7 +366,6 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { sb.WriteString(serverPort) sb.WriteString(attrs.UrlAttributes.UrlPath) if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString("?") sb.WriteString(attrs.UrlAttributes.UrlQuery) } data.Url = sb.String() @@ -429,7 +427,6 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(attrs.ClientAttributes.ClientAddress) sb.WriteString(attrs.UrlAttributes.UrlPath) if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString("?") sb.WriteString(attrs.UrlAttributes.UrlQuery) } data.Data = sb.String() @@ -443,7 +440,6 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(clientPortStr) sb.WriteString(attrs.UrlAttributes.UrlPath) if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString("?") sb.WriteString(attrs.UrlAttributes.UrlQuery) } data.Data = sb.String() @@ -462,7 +458,6 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(clientPortStr) sb.WriteString(attrs.UrlAttributes.UrlPath) if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString("?") sb.WriteString(attrs.UrlAttributes.UrlQuery) } data.Data = sb.String() From 7e53b95a94f0041181f52bd92f935fc0556475d7 Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Tue, 21 Oct 2025 13:14:58 -0600 Subject: [PATCH 5/7] Fixed formating/lint issues --- exporter/azuremonitorexporter/conventions.go | 63 ++++++------ .../azuremonitorexporter/conventions_test.go | 27 +++--- .../azuremonitorexporter/trace_to_envelope.go | 95 +++++++++---------- .../trace_to_envelope_test.go | 10 +- 4 files changed, 95 insertions(+), 100 deletions(-) diff --git a/exporter/azuremonitorexporter/conventions.go b/exporter/azuremonitorexporter/conventions.go index 2af4a0f55e652..1ece75caa0a68 100644 --- a/exporter/azuremonitorexporter/conventions.go +++ b/exporter/azuremonitorexporter/conventions.go @@ -80,11 +80,11 @@ func (attrs *networkAttributes) MapAttribute(k string, v pcommon.Value) bool { type urlAttributes struct { // common attributes // https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/url.md - UrlFragment string - UrlFull string - UrlPath string - UrlQuery string - UrlScheme string + URLFragment string + URLFull string + URLPath string + URLQuery string + URLScheme string } type userAgentAttributes struct { @@ -97,18 +97,18 @@ type userAgentAttributes struct { type httpAttributes struct { // common attributes // https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/registry/attributes/http.md - HttpRequestHeaders map[string][]string - HttpRequestMethod string - HttpRequestMethodOriginal string - HttpRequestResendCount int64 - HttpResponseHeaders map[string][]string - HttpResponseStatusCode int64 - HttpRoute string - - HttpRequestBodySize int64 - HttpResponseBodySize int64 - - UrlAttributes urlAttributes + HTTPRequestHeaders map[string][]string + HTTPRequestMethod string + HTTPRequestMethodOriginal string + HTTPRequestResendCount int64 + HTTPResponseHeaders map[string][]string + HTTPResponseStatusCode int64 + HTTPRoute string + + HTTPRequestBodySize int64 + HTTPResponseBodySize int64 + + URLAttributes urlAttributes ClientAttributes clientAttributes ServerAttributes serverAttributes UserAgentAttributes userAgentAttributes @@ -136,57 +136,56 @@ func setHeader(headers *map[string][]string, key string, v pcommon.Value) { // MapAttribute attempts to map a Span attribute to one of the known types func (attrs *httpAttributes) MapAttribute(k string, v pcommon.Value) bool { - const reqHeaderPrefix = "http.request.header." const respHeaderPrefix = "http.response.header." switch { case strings.HasPrefix(k, reqHeaderPrefix): headerName := k[len(reqHeaderPrefix):] - setHeader(&attrs.HttpRequestHeaders, headerName, v) + setHeader(&attrs.HTTPRequestHeaders, headerName, v) return true case strings.HasPrefix(k, respHeaderPrefix): headerName := k[len(respHeaderPrefix):] - setHeader(&attrs.HttpResponseHeaders, headerName, v) + setHeader(&attrs.HTTPResponseHeaders, headerName, v) return true } switch k { case string(conventions.HTTPRequestMethodKey): - attrs.HttpRequestMethod = v.Str() + attrs.HTTPRequestMethod = v.Str() case string(conventions.HTTPRequestMethodOriginalKey): - attrs.HttpRequestMethodOriginal = v.Str() + attrs.HTTPRequestMethodOriginal = v.Str() case string(conventions.HTTPRequestResendCountKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HttpRequestResendCount = val + attrs.HTTPRequestResendCount = val } case string(conventions.HTTPResponseStatusCodeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HttpResponseStatusCode = val + attrs.HTTPResponseStatusCode = val } case string(conventions.HTTPRouteKey): - attrs.HttpRoute = v.Str() + attrs.HTTPRoute = v.Str() case string(conventions.HTTPResponseBodySizeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HttpResponseBodySize = val + attrs.HTTPResponseBodySize = val } case string(conventions.HTTPRequestBodySizeKey): if val, err := getAttributeValueAsInt(v); err == nil { - attrs.HttpRequestBodySize = val + attrs.HTTPRequestBodySize = val } // URL attributes case string(conventions.URLFragmentKey): - attrs.UrlAttributes.UrlFragment = v.Str() + attrs.URLAttributes.URLFragment = v.Str() case string(conventions.URLFullKey): - attrs.UrlAttributes.UrlFull = v.Str() + attrs.URLAttributes.URLFull = v.Str() case string(conventions.URLPathKey): - attrs.UrlAttributes.UrlPath = v.Str() + attrs.URLAttributes.URLPath = v.Str() case string(conventions.URLQueryKey): - attrs.UrlAttributes.UrlQuery = v.Str() + attrs.URLAttributes.URLQuery = v.Str() case string(conventions.URLSchemeKey): - attrs.UrlAttributes.UrlScheme = v.Str() + attrs.URLAttributes.URLScheme = v.Str() // Network/server/client address attributes (new, replacing http.host) case string(conventions.ServerAddressKey): diff --git a/exporter/azuremonitorexporter/conventions_test.go b/exporter/azuremonitorexporter/conventions_test.go index 4d7f635a58e2f..e897370c13b8c 100644 --- a/exporter/azuremonitorexporter/conventions_test.go +++ b/exporter/azuremonitorexporter/conventions_test.go @@ -49,16 +49,16 @@ func testHTTPAttributeMapping(t *testing.T, variant string) { attributeMap.Range(httpAttributes.MapAttribute) - assert.Equal(t, string(conventions.HTTPRequestMethodKey), httpAttributes.HttpRequestMethod) - assert.Equal(t, string(conventions.URLFullKey), httpAttributes.UrlAttributes.UrlFull) - assert.Equal(t, string(conventions.URLPathKey), httpAttributes.UrlAttributes.UrlPath) - assert.Equal(t, string(conventions.URLQueryKey), httpAttributes.UrlAttributes.UrlQuery) - assert.Equal(t, string(conventions.URLSchemeKey), httpAttributes.UrlAttributes.UrlScheme) - assert.Equal(t, int64(200), httpAttributes.HttpResponseStatusCode) + assert.Equal(t, string(conventions.HTTPRequestMethodKey), httpAttributes.HTTPRequestMethod) + assert.Equal(t, string(conventions.URLFullKey), httpAttributes.URLAttributes.URLFull) + assert.Equal(t, string(conventions.URLPathKey), httpAttributes.URLAttributes.URLPath) + assert.Equal(t, string(conventions.URLQueryKey), httpAttributes.URLAttributes.URLQuery) + assert.Equal(t, string(conventions.URLSchemeKey), httpAttributes.URLAttributes.URLScheme) + assert.Equal(t, int64(200), httpAttributes.HTTPResponseStatusCode) assert.Equal(t, string(conventions.NetworkProtocolNameKey), httpAttributes.NetworkAttributes.NetworkProtocolName) assert.Equal(t, string(conventions.UserAgentOriginalKey), httpAttributes.UserAgentAttributes.UserAgentOriginal) - vals, ok := httpAttributes.HttpRequestHeaders["content-length"] + vals, ok := httpAttributes.HTTPRequestHeaders["content-length"] require.True(t, ok) require.NotEmpty(t, vals) reqCL := vals[0] @@ -66,9 +66,9 @@ func testHTTPAttributeMapping(t *testing.T, variant string) { assert.NoError(t, err) assert.Equal(t, int64(1), reqCLInt) - assert.Equal(t, int64(2), httpAttributes.HttpRequestBodySize) + assert.Equal(t, int64(2), httpAttributes.HTTPRequestBodySize) - valsRes, okRes := httpAttributes.HttpResponseHeaders["content-length"] + valsRes, okRes := httpAttributes.HTTPResponseHeaders["content-length"] require.True(t, okRes) require.NotEmpty(t, valsRes) reqRes := valsRes[0] @@ -76,8 +76,8 @@ func testHTTPAttributeMapping(t *testing.T, variant string) { assert.NoError(t, err) assert.Equal(t, int64(3), reqResInt) - assert.Equal(t, int64(4), httpAttributes.HttpResponseBodySize) - assert.Equal(t, string(conventions.HTTPRouteKey), httpAttributes.HttpRoute) + assert.Equal(t, int64(4), httpAttributes.HTTPResponseBodySize) + assert.Equal(t, string(conventions.HTTPRouteKey), httpAttributes.HTTPRoute) networkAttributesValidations(t, httpAttributes.NetworkAttributes) @@ -238,12 +238,12 @@ func testMessagingAttributeMapping(t *testing.T, variant string) { assert.Equal(t, int64(0), messagingAttributes.MessagingBatchMessageCount) assert.Equal(t, string(conventions.MessagingClientIDKey), messagingAttributes.MessagingClientID) assert.Equal(t, string(conventions.MessagingConsumerGroupNameKey), messagingAttributes.MessagingConsumerGroup) - assert.Equal(t, true, messagingAttributes.MessagingDestinationAnonymous) + assert.True(t, messagingAttributes.MessagingDestinationAnonymous) assert.Equal(t, string(conventions.MessagingDestinationNameKey), messagingAttributes.MessagingDestination) assert.Equal(t, string(conventions.MessagingDestinationPartitionIDKey), messagingAttributes.MessagingDestinationPartitionID) assert.Equal(t, string(conventions.MessagingDestinationSubscriptionNameKey), messagingAttributes.MessagingDestinationSubName) assert.Equal(t, string(conventions.MessagingDestinationTemplateKey), messagingAttributes.MessagingDestinationTemplate) - assert.Equal(t, false, messagingAttributes.MessagingDestinationTemporary) + assert.False(t, messagingAttributes.MessagingDestinationTemporary) assert.Equal(t, int64(1), messagingAttributes.MessagingMessageBodySize) assert.Equal(t, string(conventions.MessagingMessageConversationIDKey), messagingAttributes.MessagingMessageConversationID) assert.Equal(t, int64(2), messagingAttributes.MessagingMessageEnvelopeSize) @@ -295,7 +295,6 @@ func addNetworkAttributes(m pcommon.Map) { m.PutStr(string(conventions.NetworkPeerAddressKey), string(conventions.NetworkPeerAddressKey)) m.PutInt(string(conventions.NetworkPeerPortKey), 1) m.PutStr(string(conventions.NetworkLocalAddressKey), string(conventions.NetworkLocalAddressKey)) - } func networkAttributesValidations(t *testing.T, networkAttributes networkAttributes) { diff --git a/exporter/azuremonitorexporter/trace_to_envelope.go b/exporter/azuremonitorexporter/trace_to_envelope.go index fcc24f6183b13..bded520d0f270 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope.go +++ b/exporter/azuremonitorexporter/trace_to_envelope.go @@ -307,8 +307,8 @@ func getFormattedHTTPStatusValues(statusCode int64) (statusAsString string, succ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { attrs := copyAndExtractHTTPAttributes(span.Attributes(), data.Properties) - if attrs.HttpResponseStatusCode != 0 { - data.ResponseCode, data.Success = getFormattedHTTPStatusValues(attrs.HttpResponseStatusCode) + if attrs.HTTPResponseStatusCode != 0 { + data.ResponseCode, data.Success = getFormattedHTTPStatusValues(attrs.HTTPResponseStatusCode) } var sb strings.Builder @@ -316,13 +316,13 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { // Construct data.Name // The data.Name should be {HTTP METHOD} {HTTP SERVER ROUTE TEMPLATE} // https://github.com/microsoft/ApplicationInsights-Home/blob/f1f9f619d74557c8db3dbde4b49c4193e10d8a81/EndpointSpecs/Schemas/Bond/RequestData.bond#L32 - sb.WriteString(attrs.HttpRequestMethod) + sb.WriteString(attrs.HTTPRequestMethod) sb.WriteString(" ") // Use httpRoute if available otherwise fallback to the span name // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name - if attrs.HttpRoute != "" { - sb.WriteString(prefixIfNecessary(attrs.HttpRoute, "/")) + if attrs.HTTPRoute != "" { + sb.WriteString(prefixIfNecessary(attrs.HTTPRoute, "/")) } else { sb.WriteString(span.Name()) } @@ -339,8 +339,8 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { http.url */ - if attrs.UrlAttributes.UrlPath != "" { - attrs.UrlAttributes.UrlPath = prefixIfNecessary(attrs.UrlAttributes.UrlPath, "/") + if attrs.URLAttributes.URLPath != "" { + attrs.URLAttributes.URLPath = prefixIfNecessary(attrs.URLAttributes.URLPath, "/") } serverPort := "" @@ -349,29 +349,29 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { } switch { - case attrs.UrlAttributes.UrlScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort == "" && attrs.UrlAttributes.UrlPath != "": - sb.WriteString(attrs.UrlAttributes.UrlScheme) + case attrs.URLAttributes.URLScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort == "" && attrs.URLAttributes.URLPath != "": + sb.WriteString(attrs.URLAttributes.URLScheme) sb.WriteString("://") sb.WriteString(attrs.ServerAttributes.ServerAddress) - sb.WriteString(attrs.UrlAttributes.UrlPath) - if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString(attrs.UrlAttributes.UrlQuery) + sb.WriteString(attrs.URLAttributes.URLPath) + if attrs.URLAttributes.URLQuery != "" { + sb.WriteString(attrs.URLAttributes.URLQuery) } data.Url = sb.String() - case attrs.UrlAttributes.UrlScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort != "" && attrs.UrlAttributes.UrlPath != "": - sb.WriteString(attrs.UrlAttributes.UrlScheme) + case attrs.URLAttributes.URLScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort != "" && attrs.URLAttributes.URLPath != "": + sb.WriteString(attrs.URLAttributes.URLScheme) sb.WriteString("://") sb.WriteString(attrs.ServerAttributes.ServerAddress) sb.WriteString(":") sb.WriteString(serverPort) - sb.WriteString(attrs.UrlAttributes.UrlPath) - if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString(attrs.UrlAttributes.UrlQuery) + sb.WriteString(attrs.URLAttributes.URLPath) + if attrs.URLAttributes.URLQuery != "" { + sb.WriteString(attrs.URLAttributes.URLQuery) } data.Url = sb.String() - case attrs.UrlAttributes.UrlFull != "": - if _, err := url.Parse(attrs.UrlAttributes.UrlFull); err == nil { - data.Url = attrs.UrlAttributes.UrlFull + case attrs.URLAttributes.URLFull != "": + if _, err := url.Parse(attrs.URLAttributes.URLFull); err == nil { + data.Url = attrs.URLAttributes.URLFull } } @@ -390,24 +390,24 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend attrs := copyAndExtractHTTPAttributes(span.Attributes(), data.Properties) data.Type = "HTTP" - if attrs.HttpResponseStatusCode != 0 { - data.ResultCode, data.Success = getFormattedHTTPStatusValues(attrs.HttpResponseStatusCode) + if attrs.HTTPResponseStatusCode != 0 { + data.ResultCode, data.Success = getFormattedHTTPStatusValues(attrs.HTTPResponseStatusCode) } var sb strings.Builder - sb.WriteString(attrs.HttpRequestMethod) + sb.WriteString(attrs.HTTPRequestMethod) - if attrs.HttpRoute != "" { + if attrs.HTTPRoute != "" { sb.WriteString(" ") - sb.WriteString(attrs.HttpRoute) + sb.WriteString(attrs.HTTPRoute) } data.Name = sb.String() sb.Reset() - if attrs.UrlAttributes.UrlPath != "" { - attrs.UrlAttributes.UrlPath = prefixIfNecessary(attrs.UrlAttributes.UrlPath, "/") + if attrs.URLAttributes.URLPath != "" { + attrs.URLAttributes.URLPath = prefixIfNecessary(attrs.URLAttributes.URLPath, "/") } clientPortStr := "" @@ -416,31 +416,31 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend } switch { - case attrs.UrlAttributes.UrlFull != "": - if u, err := url.Parse(attrs.UrlAttributes.UrlFull); err == nil { - data.Data = attrs.UrlAttributes.UrlFull + case attrs.URLAttributes.URLFull != "": + if u, err := url.Parse(attrs.URLAttributes.URLFull); err == nil { + data.Data = attrs.URLAttributes.URLFull data.Target = u.Host } - case attrs.UrlAttributes.UrlScheme != "" && attrs.ClientAttributes.ClientAddress != "" && clientPortStr == "" && attrs.UrlAttributes.UrlPath != "": - sb.WriteString(attrs.UrlAttributes.UrlScheme) + case attrs.URLAttributes.URLScheme != "" && attrs.ClientAttributes.ClientAddress != "" && clientPortStr == "" && attrs.URLAttributes.URLPath != "": + sb.WriteString(attrs.URLAttributes.URLScheme) sb.WriteString("://") sb.WriteString(attrs.ClientAttributes.ClientAddress) - sb.WriteString(attrs.UrlAttributes.UrlPath) - if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString(attrs.UrlAttributes.UrlQuery) + sb.WriteString(attrs.URLAttributes.URLPath) + if attrs.URLAttributes.URLQuery != "" { + sb.WriteString(attrs.URLAttributes.URLQuery) } data.Data = sb.String() data.Target = attrs.ClientAttributes.ClientAddress - case attrs.UrlAttributes.UrlScheme != "" && attrs.ClientAttributes.ClientAddress != "" && clientPortStr != "" && attrs.UrlAttributes.UrlPath != "": - sb.WriteString(attrs.UrlAttributes.UrlScheme) + case attrs.URLAttributes.URLScheme != "" && attrs.ClientAttributes.ClientAddress != "" && clientPortStr != "" && attrs.URLAttributes.URLPath != "": + sb.WriteString(attrs.URLAttributes.URLScheme) sb.WriteString("://") sb.WriteString(attrs.ClientAttributes.ClientAddress) sb.WriteString(":") sb.WriteString(clientPortStr) - sb.WriteString(attrs.UrlAttributes.UrlPath) - if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString(attrs.UrlAttributes.UrlQuery) + sb.WriteString(attrs.URLAttributes.URLPath) + if attrs.URLAttributes.URLQuery != "" { + sb.WriteString(attrs.URLAttributes.URLQuery) } data.Data = sb.String() @@ -450,15 +450,15 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(clientPortStr) data.Target = sb.String() - case attrs.UrlAttributes.UrlScheme != "" && attrs.NetworkAttributes.NetworkPeerAddress != "" && clientPortStr != "" && attrs.UrlAttributes.UrlPath != "": - sb.WriteString(attrs.UrlAttributes.UrlScheme) + case attrs.URLAttributes.URLScheme != "" && attrs.NetworkAttributes.NetworkPeerAddress != "" && clientPortStr != "" && attrs.URLAttributes.URLPath != "": + sb.WriteString(attrs.URLAttributes.URLScheme) sb.WriteString("://") sb.WriteString(attrs.NetworkAttributes.NetworkPeerAddress) sb.WriteString(":") sb.WriteString(clientPortStr) - sb.WriteString(attrs.UrlAttributes.UrlPath) - if attrs.UrlAttributes.UrlQuery != "" { - sb.WriteString(attrs.UrlAttributes.UrlQuery) + sb.WriteString(attrs.URLAttributes.URLPath) + if attrs.URLAttributes.URLQuery != "" { + sb.WriteString(attrs.URLAttributes.URLQuery) } data.Data = sb.String() @@ -550,7 +550,6 @@ func fillRequestDataMessaging(span ptrace.Span, data *contracts.RequestData) { var sb strings.Builder writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ServerAttributes.ServerAddress, attrs.ServerAttributes.ServerPort, &sb) data.Source = sb.String() - } // Maps Messaging Producer/Client Span to AppInsights RemoteDependencyData @@ -564,7 +563,6 @@ func fillRemoteDependencyDataMessaging(span ptrace.Span, data *contracts.RemoteD var sb strings.Builder writeFormatedFromNetworkServerOrClient(&attrs.NetworkAttributes, attrs.ClientAttributes.ClientAddress, attrs.ClientAttributes.ClientPort, &sb) data.Target = sb.String() - } // Copies all attributes to either properties or measurements and passes the key/value to another mapping function @@ -707,8 +705,7 @@ func getDefaultFormattedSpanStatus(spanStatus ptrace.Status) (statusCodeAsString } func writeFormatedFromNetworkServerOrClient(networkAttributes *networkAttributes, addressName string, addressPort int64, sb *strings.Builder) { - - //server.address or client.address + // server.address or client.address if addressName != "" { sb.WriteString(addressName) } else { diff --git a/exporter/azuremonitorexporter/trace_to_envelope_test.go b/exporter/azuremonitorexporter/trace_to_envelope_test.go index 6ee4f1e7a28e3..d3102012b1c82 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope_test.go +++ b/exporter/azuremonitorexporter/trace_to_envelope_test.go @@ -34,8 +34,8 @@ const ( defaultScopeName = "myinstrumentationlib" defaultScopeVersion = "1.0" defaultHTTPMethod = http.MethodGet - defaultHttpServerPort = 80 - defaultUrlFull = "https://foo/bar/12345?biz=baz" + defaultHTTPServerPort = 80 + defaultURLFull = "https://foo/bar/12345?biz=baz" defaultHTTPServerSpanName = "/bar" defaultHTTPClientSpanName = defaultHTTPMethod defaultHTTPStatusCode = 200 @@ -75,8 +75,8 @@ var ( requiredHTTPAttributes = map[string]any{ string(conventions.HTTPRequestMethodKey): defaultHTTPMethod, string(conventions.ServerAddressKey): defaultServerAddress, - string(conventions.ServerPortKey): defaultHttpServerPort, - string(conventions.URLFullKey): defaultUrlFull, + string(conventions.ServerPortKey): defaultHTTPServerPort, + string(conventions.URLFullKey): defaultURLFull, } // Required attribute for any RPC Span @@ -127,7 +127,7 @@ func TestHTTPServerSpanToRequestDataAttributeSet1(t *testing.T) { spanAttributes.PutStr(string(conventions.URLPathKey), "/bar") spanAttributes.PutStr(string(conventions.URLQueryKey), "biz=baz") - //Reset server port to null + // Reset server port to null spanAttributes.PutStr(string(conventions.ServerPortKey), "") // A non 2xx status code From fe9a08f09548a2cf52a0d2bd21f881020d65fee0 Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Tue, 21 Oct 2025 13:23:08 -0600 Subject: [PATCH 6/7] added chlog --- ...y_azureMontiorExporter_semanticUpdate.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .chloggen/kmccray_azureMontiorExporter_semanticUpdate.yaml diff --git a/.chloggen/kmccray_azureMontiorExporter_semanticUpdate.yaml b/.chloggen/kmccray_azureMontiorExporter_semanticUpdate.yaml new file mode 100644 index 0000000000000..2f74f9a1d6900 --- /dev/null +++ b/.chloggen/kmccray_azureMontiorExporter_semanticUpdate.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. receiver/filelog) +component: exporter/azuremonitor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Updated azure monitor exporter to use OTEL semantic conventions 1.34.0 + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [41289] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] From c4cc8c4c45e089300efd7336b51cc18101449ecf Mon Sep 17 00:00:00 2001 From: Kyle McCray Date: Tue, 21 Oct 2025 14:37:08 -0600 Subject: [PATCH 7/7] updated url query to be prefixed by ? if it doesn't already exist --- exporter/azuremonitorexporter/trace_to_envelope.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/exporter/azuremonitorexporter/trace_to_envelope.go b/exporter/azuremonitorexporter/trace_to_envelope.go index bded520d0f270..76fce7573fb11 100644 --- a/exporter/azuremonitorexporter/trace_to_envelope.go +++ b/exporter/azuremonitorexporter/trace_to_envelope.go @@ -355,7 +355,7 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { sb.WriteString(attrs.ServerAttributes.ServerAddress) sb.WriteString(attrs.URLAttributes.URLPath) if attrs.URLAttributes.URLQuery != "" { - sb.WriteString(attrs.URLAttributes.URLQuery) + sb.WriteString(prefixIfNecessary(attrs.URLAttributes.URLQuery, "?")) } data.Url = sb.String() case attrs.URLAttributes.URLScheme != "" && attrs.ServerAttributes.ServerAddress != "" && serverPort != "" && attrs.URLAttributes.URLPath != "": @@ -366,7 +366,7 @@ func fillRequestDataHTTP(span ptrace.Span, data *contracts.RequestData) { sb.WriteString(serverPort) sb.WriteString(attrs.URLAttributes.URLPath) if attrs.URLAttributes.URLQuery != "" { - sb.WriteString(attrs.URLAttributes.URLQuery) + sb.WriteString(prefixIfNecessary(attrs.URLAttributes.URLQuery, "?")) } data.Url = sb.String() case attrs.URLAttributes.URLFull != "": @@ -427,7 +427,7 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(attrs.ClientAttributes.ClientAddress) sb.WriteString(attrs.URLAttributes.URLPath) if attrs.URLAttributes.URLQuery != "" { - sb.WriteString(attrs.URLAttributes.URLQuery) + sb.WriteString(prefixIfNecessary(attrs.URLAttributes.URLQuery, "?")) } data.Data = sb.String() data.Target = attrs.ClientAttributes.ClientAddress @@ -440,7 +440,7 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(clientPortStr) sb.WriteString(attrs.URLAttributes.URLPath) if attrs.URLAttributes.URLQuery != "" { - sb.WriteString(attrs.URLAttributes.URLQuery) + sb.WriteString(prefixIfNecessary(attrs.URLAttributes.URLQuery, "?")) } data.Data = sb.String() @@ -458,7 +458,7 @@ func fillRemoteDependencyDataHTTP(span ptrace.Span, data *contracts.RemoteDepend sb.WriteString(clientPortStr) sb.WriteString(attrs.URLAttributes.URLPath) if attrs.URLAttributes.URLQuery != "" { - sb.WriteString(attrs.URLAttributes.URLQuery) + sb.WriteString(prefixIfNecessary(attrs.URLAttributes.URLQuery, "?")) } data.Data = sb.String()