From 95022bc22f7b3d3c18077aa8b1dc1cd2a4dadf1f Mon Sep 17 00:00:00 2001 From: Bram Schuur Date: Mon, 26 Oct 2026 15:22:47 +0100 Subject: [PATCH 1/2] fix(open-telemetry): model services without service.namespace instead of a default namespace Co-Authored-By: Claude Opus 5.5 (1M context) --- .../settings/component-mappings/namespaces.sty | 4 +++- .../component-mappings/service-instances.sty | 15 +++++++++++++-- .../settings/component-mappings/services.sty | 14 +++++++++++--- .../service-instance-span-duration/monitor.sty | 13 +++++++++---- .../monitor.sty | 10 ++++++++-- .../monitors/service-span-duration/monitor.sty | 13 +++++++++---- .../service-span-error-ratio/monitor.sty | 10 ++++++++-- .../asynchronous-consumer-peer.sty | 10 +++++----- .../asynchronous-producer-consumer.sty | 12 ++++++------ .../asynchronous-producer-peer.sty | 9 +++++---- .../relation-mappings/executes-ecs-task.sty | 10 ++++++---- .../relation-mappings/executes-function.sty | 10 ++++++---- .../settings/relation-mappings/executes-host.sty | 10 ++++++---- .../relation-mappings/k8s-pod-to-otel.sty | 10 ++++++---- .../settings/relation-mappings/provided-by.sty | 12 +++++++----- .../settings/relation-mappings/synchronous.sty | 16 ++++++++-------- stackpacks/open-telemetry/stackpack.yaml | 2 +- 17 files changed, 117 insertions(+), 63 deletions(-) diff --git a/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty b/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty index eebe21b..afc7c5f 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty @@ -9,7 +9,9 @@ nodes: - "METRICS" resource: action: "'CREATE'" - condition: "'service.namespace' in resource.attributes" + # OTel: service.namespace is optional and an empty value means the service has no namespace, + # so no namespace component exists for it: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + condition: "'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != ''" vars: - name: "namespace" value: "resource.attributes['service.namespace']" diff --git a/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty b/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty index 07a4bc0..7e6d423 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty @@ -11,8 +11,19 @@ nodes: action: "'CREATE'" condition: "'service.instance.id' in resource.attributes" vars: + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "namespace" - value: "resource.attributes['service.namespace']" + value: >- + 'service.namespace' in resource.attributes ? + resource.attributes['service.namespace'] : + '' + - name: "serviceUrnPrefix" + value: >- + 'service.namespace' in resource.attributes && + resource.attributes['service.namespace'] != '' ? + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "version" @@ -28,7 +39,7 @@ nodes: value: "resource.attributes['k8s.cluster.name']" output: identifier: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId + vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId name: "vars.instanceName" typeName: "'otel service instance'" required: diff --git a/stackpacks/open-telemetry/settings/component-mappings/services.sty b/stackpacks/open-telemetry/settings/component-mappings/services.sty index f1a1485..3e8241c 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/services.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/services.sty @@ -11,11 +11,19 @@ nodes: action: "'CREATE'" condition: "'service.name' in resource.attributes" vars: + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "namespace" value: >- - resource.attributes['service.namespace'] != '' ? + 'service.namespace' in resource.attributes ? resource.attributes['service.namespace'] : - 'default' + '' + - name: "serviceUrnPrefix" + value: >- + 'service.namespace' in resource.attributes && + resource.attributes['service.namespace'] != '' ? + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "version" @@ -27,7 +35,7 @@ nodes: value: "resource.attributes['k8s.cluster.name']" output: identifier: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + vars.serviceUrnPrefix + vars.service name: "vars.service" typeName: "'otel service'" required: diff --git a/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty index d25ed8b..e9dbe06 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty @@ -6,14 +6,19 @@ nodes: metric: # _seconds_ = telemetry gateway (target/new spanmetrics default); _milliseconds_ = standalone collector (current spanmetrics default, transitioning to s per OTel semantic conventions) query: |- - (histogram_quantile(0.95, sum by(service_namespace, service_name, service_instance_id, le)(rate(otel_span_duration_seconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])))) - or - (histogram_quantile(0.95, sum by(service_namespace, service_name, service_instance_id, le)(rate(otel_span_duration_milliseconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]))) / 1000) + label_replace(label_replace( + (histogram_quantile(0.95, sum by(service_namespace, service_name, service_instance_id, le)(rate(otel_span_duration_seconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])))) + or + (histogram_quantile(0.95, sum by(service_namespace, service_name, service_instance_id, le)(rate(otel_span_duration_milliseconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]))) / 1000) + , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") + , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") aliasTemplate: Span duration 95th percentile unit: s threshold: 5 titleTemplate: Span duration - urnTemplate: "urn:opentelemetry:namespace/${service_namespace}:service/${service_name}:serviceInstance/${service_instance_id}" + # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + urnTemplate: "${urn_prefix}${service_name}:serviceInstance/${service_instance_id}" description: | Monitors the duration of the server and consumer spans. When the 95th percentile of the duration is greater than the threshold (default 5s) the monitor has a Deviating state. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty index 60e27e4..7d18453 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty @@ -4,12 +4,18 @@ nodes: comparator: GT failureState: DEVIATING metric: - query: 'sum by(service_namespace, service_name, service_instance_id)(rate(otel_span_calls_total{status_code="STATUS_CODE_ERROR", span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) or (present_over_time(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) -1)) / sum by(service_namespace, service_name, service_instance_id)(rate(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])) * 100' + query: |- + label_replace(label_replace( + sum by(service_namespace, service_name, service_instance_id)(rate(otel_span_calls_total{status_code="STATUS_CODE_ERROR", span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) or (present_over_time(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) -1)) / sum by(service_namespace, service_name, service_instance_id)(rate(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])) * 100 + , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") + , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") aliasTemplate: Error ratio (errors/total) unit: percent threshold: 5 titleTemplate: Span error ratio - urnTemplate: "urn:opentelemetry:namespace/${service_namespace}:service/${service_name}:serviceInstance/${service_instance_id}" + # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + urnTemplate: "${urn_prefix}${service_name}:serviceInstance/${service_instance_id}" description: | Percentage of server and consumer spans that are in an error state for the service instance. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty index 021e761..813d2d3 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty @@ -6,14 +6,19 @@ nodes: metric: # _seconds_ = telemetry gateway (target/new spanmetrics default); _milliseconds_ = standalone collector (current spanmetrics default, transitioning to s per OTel semantic conventions) query: |- - (histogram_quantile(0.95, sum by(service_namespace, service_name, le)(rate(otel_span_duration_seconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])))) - or - (histogram_quantile(0.95, sum by(service_namespace, service_name, le)(rate(otel_span_duration_milliseconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]))) / 1000) + label_replace(label_replace( + (histogram_quantile(0.95, sum by(service_namespace, service_name, le)(rate(otel_span_duration_seconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])))) + or + (histogram_quantile(0.95, sum by(service_namespace, service_name, le)(rate(otel_span_duration_milliseconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]))) / 1000) + , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") + , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") aliasTemplate: Span duration 95th percentile unit: s threshold: 5 titleTemplate: Span duration - urnTemplate: "urn:opentelemetry:namespace/${service_namespace}:service/${service_name}" + # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + urnTemplate: "${urn_prefix}${service_name}" description: | Monitors the duration of the server and consumer spans. When the 95th percentile of the duration is greater than the threshold (default 5s) the monitor has a Deviating state. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty index e3a2ede..726cfff 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty @@ -4,12 +4,18 @@ nodes: comparator: GT failureState: DEVIATING metric: - query: 'sum by(service_namespace, service_name)(rate(otel_span_calls_total{status_code="STATUS_CODE_ERROR", span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) or (present_over_time(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) -1)) / sum by(service_namespace, service_name)(rate(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])) * 100' + query: |- + label_replace(label_replace( + sum by(service_namespace, service_name)(rate(otel_span_calls_total{status_code="STATUS_CODE_ERROR", span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) or (present_over_time(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) -1)) / sum by(service_namespace, service_name)(rate(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])) * 100 + , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") + , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") aliasTemplate: Error ratio (errors/total) unit: percent threshold: 5 titleTemplate: Span error ratio - urnTemplate: "urn:opentelemetry:namespace/${service_namespace}:service/${service_name}" + # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + urnTemplate: "${urn_prefix}${service_name}" description: | Percentage of server and consumer spans that are in an error state for the service. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty index d5579e4..0eab4f8 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty @@ -16,20 +16,20 @@ nodes: condition: >- 'client' in datapoint.attributes && 'server' in datapoint.attributes && - 'client_service.namespace' in datapoint.attributes && - 'server_service.namespace' in datapoint.attributes && 'connection_type' in datapoint.attributes && datapoint.attributes['connection_type'] == 'messaging_system' && 'client_peer.service' in datapoint.attributes vars: + # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "sourceId" value: >- 'server_service.instance.id' in datapoint.attributes ? - "urn:opentelemetry:namespace/" + datapoint.attributes['server_service.namespace'] + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : - "urn:opentelemetry:namespace/" + datapoint.attributes['server_service.namespace'] + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] + ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : + ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] - name: "targetId" value: >- - 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' + datapoint.attributes['client_peer.service'] + ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client_peer.service'] output: sourceId: "vars.sourceId" targetId: "vars.targetId" diff --git a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty index adb84d7..beb386b 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty @@ -16,22 +16,22 @@ nodes: condition: >- 'client' in datapoint.attributes && 'server' in datapoint.attributes && - 'client_service.namespace' in datapoint.attributes && - 'server_service.namespace' in datapoint.attributes && 'connection_type' in datapoint.attributes && datapoint.attributes['connection_type'] == 'messaging_system' && !('client_peer.service' in datapoint.attributes) vars: + # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "sourceId" value: >- 'client_service.instance.id' in datapoint.attributes ? - "urn:opentelemetry:namespace/" + datapoint.attributes['client_service.namespace'] + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : - "urn:opentelemetry:namespace/" + datapoint.attributes['client_service.namespace'] + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] + ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : + ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] - name: "targetId" value: >- 'server_service.instance.id' in datapoint.attributes ? - "urn:opentelemetry:namespace/" + datapoint.attributes['server_service.namespace'] + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : - "urn:opentelemetry:namespace/" + datapoint.attributes['server_service.namespace'] + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] + ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : + ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] output: sourceId: "vars.sourceId" targetId: "vars.targetId" diff --git a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty index e65c035..5efa1f0 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty @@ -16,19 +16,20 @@ nodes: condition: >- 'client' in datapoint.attributes && 'server' in datapoint.attributes && - 'client_service.namespace' in datapoint.attributes && 'connection_type' in datapoint.attributes && datapoint.attributes['connection_type'] == 'messaging_system' && 'client_peer.service' in datapoint.attributes vars: + # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "sourceId" value: >- 'client_service.instance.id' in datapoint.attributes ? - "urn:opentelemetry:namespace/" + datapoint.attributes['client_service.namespace'] + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : - "urn:opentelemetry:namespace/" + datapoint.attributes['client_service.namespace'] + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] + ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : + ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] - name: "targetId" value: >- - 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' + datapoint.attributes['client_peer.service'] + ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client_peer.service'] output: sourceId: "vars.sourceId" targetId: "vars.targetId" diff --git a/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty b/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty index b48aad8..58131b1 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty @@ -13,12 +13,14 @@ nodes: vars: - name: "awsEcsTaskId" value: "resource.attributes['aws.ecs.task.id']" - - name: "namespace" + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + - name: "serviceUrnPrefix" value: >- 'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != '' ? - resource.attributes['service.namespace'] : - 'default' + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -30,7 +32,7 @@ nodes: output: sourceId: "'urn:opentelemetry:task/' + vars.awsEcsTaskId" targetId: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId + vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'executes'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty b/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty index dc35c52..324e5a4 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty @@ -13,12 +13,14 @@ nodes: vars: - name: "faasId" value: "resource.attributes['faas.id']" - - name: "namespace" + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + - name: "serviceUrnPrefix" value: >- 'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != '' ? - resource.attributes['service.namespace'] : - 'default' + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -30,7 +32,7 @@ nodes: output: sourceId: "'urn:opentelemetry:function/' + vars.faasId" targetId: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId + vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'executes'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty b/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty index b91cef5..e9747fd 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty @@ -13,12 +13,14 @@ nodes: vars: - name: "hostId" value: "resource.attributes['host.id']" - - name: "namespace" + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + - name: "serviceUrnPrefix" value: >- 'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != '' ? - resource.attributes['service.namespace'] : - 'default' + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -30,7 +32,7 @@ nodes: output: sourceId: "'urn:opentelemetry:host/' + vars.hostId" targetId: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId + vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'executes'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty b/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty index da973a0..4d8211f 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty @@ -20,12 +20,14 @@ nodes: value: "resource.attributes['k8s.namespace.name']" - name: "podName" value: "resource.attributes['k8s.pod.name']" - - name: "serviceNamespace" + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + - name: "serviceUrnPrefix" value: >- 'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != '' ? - resource.attributes['service.namespace'] : - 'default' + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: >- 'service.name' in resource.attributes ? @@ -40,7 +42,7 @@ nodes: sourceId: >- 'urn:opentelemetry:kubernetes:/' + vars.clusterName + ':' + vars.namespaceName + ':pod/' + vars.podName targetId: >- - 'urn:opentelemetry:namespace/' + vars.serviceNamespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId + vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'kubernetes-to-otel'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty b/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty index 1162cdf..f8a5cfe 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty @@ -11,12 +11,14 @@ nodes: action: "'CREATE'" condition: "'service.name' in resource.attributes" vars: - - name: "namespace" + # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + - name: "serviceUrnPrefix" value: >- 'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != '' ? - resource.attributes['service.namespace'] : - 'default' + 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : + 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -26,9 +28,9 @@ nodes: resource.attributes['service.name'] output: sourceId: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + vars.serviceUrnPrefix + vars.service targetId: >- - 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId + vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'provided-by'" dependencyType: "'HIERARCHICAL'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty b/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty index 6ee708b..52a189d 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty @@ -16,15 +16,15 @@ nodes: condition: >- 'client' in datapoint.attributes && 'server' in datapoint.attributes && - 'client_service.namespace' in datapoint.attributes && - 'server_service.namespace' in datapoint.attributes && 'connection_type' in datapoint.attributes && datapoint.attributes['connection_type'] == '' vars: - - name: "clientServiceNamespace" - value: "datapoint.attributes['client_service.namespace']" - - name: "serverServiceNamespace" - value: "datapoint.attributes['server_service.namespace']" + # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + - name: "clientServiceUrnPrefix" + value: "('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/')" + - name: "serverServiceUrnPrefix" + value: "('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/')" - name: "client" value: "datapoint.attributes['client']" - name: "server" @@ -41,9 +41,9 @@ nodes: datapoint.attributes['server'] output: sourceId: >- - 'urn:opentelemetry:namespace/' + vars.clientServiceNamespace + ':service/' + vars.client + ':serviceInstance/' + vars.clientServiceInstanceId + vars.clientServiceUrnPrefix + vars.client + ':serviceInstance/' + vars.clientServiceInstanceId targetId: >- - 'urn:opentelemetry:namespace/' + vars.serverServiceNamespace + ':service/' + vars.server + ':serviceInstance/' + vars.serverServiceInstanceId + vars.serverServiceUrnPrefix + vars.server + ':serviceInstance/' + vars.serverServiceInstanceId typeName: "'synchronous'" dependencyType: "'CONNECTION'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/stackpack.yaml b/stackpacks/open-telemetry/stackpack.yaml index 43859e5..fde883a 100644 --- a/stackpacks/open-telemetry/stackpack.yaml +++ b/stackpacks/open-telemetry/stackpack.yaml @@ -1,5 +1,5 @@ name: "open-telemetry" -version: "0.1.5" +version: "0.1.6" schemaVersion: "2.0" displayName: "Open Telemetry" categories: [ "Open Telemetry" ] From b032958476060dd697494c3178fcf37fc191cbdb Mon Sep 17 00:00:00 2001 From: Bram Schuur Date: Mon, 26 Oct 2026 16:43:09 +0100 Subject: [PATCH 2/2] fix(open-telemetry): encode an unset service.namespace as the empty string Co-Authored-By: Claude Opus 5.5 (1M context) --- .../settings/component-mappings/namespaces.sty | 4 ++-- .../component-mappings/service-instances.sty | 10 ++-------- .../settings/component-mappings/services.sty | 10 ++-------- .../service-instance-span-duration/monitor.sty | 9 ++++----- .../service-instance-span-error-ratio/monitor.sty | 9 ++++----- .../monitors/service-span-duration/monitor.sty | 9 ++++----- .../monitors/service-span-error-ratio/monitor.sty | 9 ++++----- .../asynchronous-consumer-peer.sty | 8 ++++---- .../asynchronous-producer-consumer.sty | 10 +++++----- .../asynchronous-producer-peer.sty | 8 ++++---- .../relation-mappings/executes-ecs-task.sty | 13 ++++++------- .../relation-mappings/executes-function.sty | 13 ++++++------- .../settings/relation-mappings/executes-host.sty | 13 ++++++------- .../relation-mappings/k8s-pod-to-otel.sty | 13 ++++++------- .../settings/relation-mappings/provided-by.sty | 15 +++++++-------- .../settings/relation-mappings/synchronous.sty | 14 +++++++------- 16 files changed, 73 insertions(+), 94 deletions(-) diff --git a/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty b/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty index afc7c5f..796706d 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/namespaces.sty @@ -9,8 +9,8 @@ nodes: - "METRICS" resource: action: "'CREATE'" - # OTel: service.namespace is optional and an empty value means the service has no namespace, - # so no namespace component exists for it: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ + # OTel: service.namespace is optional; an unset or empty namespace is not a named namespace, + # so no namespace component is created for it: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ condition: "'service.namespace' in resource.attributes && resource.attributes['service.namespace'] != ''" vars: - name: "namespace" diff --git a/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty b/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty index 7e6d423..06e8e89 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/service-instances.sty @@ -11,19 +11,13 @@ nodes: action: "'CREATE'" condition: "'service.instance.id' in resource.attributes" vars: - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "namespace" value: >- 'service.namespace' in resource.attributes ? resource.attributes['service.namespace'] : '' - - name: "serviceUrnPrefix" - value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "version" @@ -39,7 +33,7 @@ nodes: value: "resource.attributes['k8s.cluster.name']" output: identifier: >- - vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId name: "vars.instanceName" typeName: "'otel service instance'" required: diff --git a/stackpacks/open-telemetry/settings/component-mappings/services.sty b/stackpacks/open-telemetry/settings/component-mappings/services.sty index 3e8241c..2e4bbf9 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/services.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/services.sty @@ -11,19 +11,13 @@ nodes: action: "'CREATE'" condition: "'service.name' in resource.attributes" vars: - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "namespace" value: >- 'service.namespace' in resource.attributes ? resource.attributes['service.namespace'] : '' - - name: "serviceUrnPrefix" - value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' - name: "service" value: "resource.attributes['service.name']" - name: "version" @@ -35,7 +29,7 @@ nodes: value: "resource.attributes['k8s.cluster.name']" output: identifier: >- - vars.serviceUrnPrefix + vars.service + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service name: "vars.service" typeName: "'otel service'" required: diff --git a/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty index e9dbe06..4fe5686 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-instance-span-duration/monitor.sty @@ -6,19 +6,18 @@ nodes: metric: # _seconds_ = telemetry gateway (target/new spanmetrics default); _milliseconds_ = standalone collector (current spanmetrics default, transitioning to s per OTel semantic conventions) query: |- - label_replace(label_replace( + label_replace( (histogram_quantile(0.95, sum by(service_namespace, service_name, service_instance_id, le)(rate(otel_span_duration_seconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])))) or (histogram_quantile(0.95, sum by(service_namespace, service_name, service_instance_id, le)(rate(otel_span_duration_milliseconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]))) / 1000) - , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") - , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") + , "service_urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.*)") aliasTemplate: Span duration 95th percentile unit: s threshold: 5 titleTemplate: Span duration - # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - urnTemplate: "${urn_prefix}${service_name}:serviceInstance/${service_instance_id}" + urnTemplate: "${service_urn_prefix}${service_name}:serviceInstance/${service_instance_id}" description: | Monitors the duration of the server and consumer spans. When the 95th percentile of the duration is greater than the threshold (default 5s) the monitor has a Deviating state. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty index 7d18453..cf95ae6 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-instance-span-error-ratio/monitor.sty @@ -5,17 +5,16 @@ nodes: failureState: DEVIATING metric: query: |- - label_replace(label_replace( + label_replace( sum by(service_namespace, service_name, service_instance_id)(rate(otel_span_calls_total{status_code="STATUS_CODE_ERROR", span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) or (present_over_time(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) -1)) / sum by(service_namespace, service_name, service_instance_id)(rate(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])) * 100 - , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") - , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") + , "service_urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.*)") aliasTemplate: Error ratio (errors/total) unit: percent threshold: 5 titleTemplate: Span error ratio - # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - urnTemplate: "${urn_prefix}${service_name}:serviceInstance/${service_instance_id}" + urnTemplate: "${service_urn_prefix}${service_name}:serviceInstance/${service_instance_id}" description: | Percentage of server and consumer spans that are in an error state for the service instance. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty index 813d2d3..0b5fd51 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-span-duration/monitor.sty @@ -6,19 +6,18 @@ nodes: metric: # _seconds_ = telemetry gateway (target/new spanmetrics default); _milliseconds_ = standalone collector (current spanmetrics default, transitioning to s per OTel semantic conventions) query: |- - label_replace(label_replace( + label_replace( (histogram_quantile(0.95, sum by(service_namespace, service_name, le)(rate(otel_span_duration_seconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])))) or (histogram_quantile(0.95, sum by(service_namespace, service_name, le)(rate(otel_span_duration_milliseconds_bucket{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]))) / 1000) - , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") - , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") + , "service_urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.*)") aliasTemplate: Span duration 95th percentile unit: s threshold: 5 titleTemplate: Span duration - # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - urnTemplate: "${urn_prefix}${service_name}" + urnTemplate: "${service_urn_prefix}${service_name}" description: | Monitors the duration of the server and consumer spans. When the 95th percentile of the duration is greater than the threshold (default 5s) the monitor has a Deviating state. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty b/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty index 726cfff..1b42c31 100644 --- a/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty +++ b/stackpacks/open-telemetry/settings/monitors/service-span-error-ratio/monitor.sty @@ -5,17 +5,16 @@ nodes: failureState: DEVIATING metric: query: |- - label_replace(label_replace( + label_replace( sum by(service_namespace, service_name)(rate(otel_span_calls_total{status_code="STATUS_CODE_ERROR", span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) or (present_over_time(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m]) -1)) / sum by(service_namespace, service_name)(rate(otel_span_calls_total{span_kind=~"SPAN_KIND_SERVER|SPAN_KIND_CONSUMER"}[5m])) * 100 - , "urn_prefix", "urn:opentelemetry:service/", "service_namespace", "") - , "urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.+)") + , "service_urn_prefix", "urn:opentelemetry:namespace/$1:service/", "service_namespace", "(.*)") aliasTemplate: Error ratio (errors/total) unit: percent threshold: 5 titleTemplate: Span error ratio - # OTel: service.namespace is optional; series without it belong to a service with no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - urnTemplate: "${urn_prefix}${service_name}" + urnTemplate: "${service_urn_prefix}${service_name}" description: | Percentage of server and consumer spans that are in an error state for the service. function: urn:stackpack:common:monitor-function:threshold diff --git a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty index 0eab4f8..66f1e8e 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-consumer-peer.sty @@ -20,16 +20,16 @@ nodes: datapoint.attributes['connection_type'] == 'messaging_system' && 'client_peer.service' in datapoint.attributes vars: - # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "sourceId" value: >- 'server_service.instance.id' in datapoint.attributes ? - ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : - ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] + "urn:opentelemetry:namespace/" + ('server_service.namespace' in datapoint.attributes ? datapoint.attributes['server_service.namespace'] : '') + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : + "urn:opentelemetry:namespace/" + ('server_service.namespace' in datapoint.attributes ? datapoint.attributes['server_service.namespace'] : '') + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] - name: "targetId" value: >- - ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client_peer.service'] + 'urn:opentelemetry:namespace/' + ('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '') + ':service/' + datapoint.attributes['client_peer.service'] output: sourceId: "vars.sourceId" targetId: "vars.targetId" diff --git a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty index beb386b..8fd28f1 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-consumer.sty @@ -20,18 +20,18 @@ nodes: datapoint.attributes['connection_type'] == 'messaging_system' && !('client_peer.service' in datapoint.attributes) vars: - # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "sourceId" value: >- 'client_service.instance.id' in datapoint.attributes ? - ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : - ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] + "urn:opentelemetry:namespace/" + ('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '') + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : + "urn:opentelemetry:namespace/" + ('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '') + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] - name: "targetId" value: >- 'server_service.instance.id' in datapoint.attributes ? - ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : - ('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] + "urn:opentelemetry:namespace/" + ('server_service.namespace' in datapoint.attributes ? datapoint.attributes['server_service.namespace'] : '') + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server_service.instance.id'] : + "urn:opentelemetry:namespace/" + ('server_service.namespace' in datapoint.attributes ? datapoint.attributes['server_service.namespace'] : '') + ":service/" + datapoint.attributes['server'] + ":serviceInstance/" + datapoint.attributes['server'] output: sourceId: "vars.sourceId" targetId: "vars.targetId" diff --git a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty index 5efa1f0..463bcdc 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/asynchronous-producer-peer.sty @@ -20,16 +20,16 @@ nodes: datapoint.attributes['connection_type'] == 'messaging_system' && 'client_peer.service' in datapoint.attributes vars: - # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - name: "sourceId" value: >- 'client_service.instance.id' in datapoint.attributes ? - ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : - ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] + "urn:opentelemetry:namespace/" + ('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '') + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client_service.instance.id'] : + "urn:opentelemetry:namespace/" + ('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '') + ":service/" + datapoint.attributes['client'] + ":serviceInstance/" + datapoint.attributes['client'] - name: "targetId" value: >- - ('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/') + datapoint.attributes['client_peer.service'] + 'urn:opentelemetry:namespace/' + ('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '') + ':service/' + datapoint.attributes['client_peer.service'] output: sourceId: "vars.sourceId" targetId: "vars.targetId" diff --git a/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty b/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty index 58131b1..9827a7a 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/executes-ecs-task.sty @@ -13,14 +13,13 @@ nodes: vars: - name: "awsEcsTaskId" value: "resource.attributes['aws.ecs.task.id']" - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - - name: "serviceUrnPrefix" + - name: "namespace" value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' + 'service.namespace' in resource.attributes ? + resource.attributes['service.namespace'] : + '' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -32,7 +31,7 @@ nodes: output: sourceId: "'urn:opentelemetry:task/' + vars.awsEcsTaskId" targetId: >- - vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'executes'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty b/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty index 324e5a4..c24414e 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/executes-function.sty @@ -13,14 +13,13 @@ nodes: vars: - name: "faasId" value: "resource.attributes['faas.id']" - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - - name: "serviceUrnPrefix" + - name: "namespace" value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' + 'service.namespace' in resource.attributes ? + resource.attributes['service.namespace'] : + '' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -32,7 +31,7 @@ nodes: output: sourceId: "'urn:opentelemetry:function/' + vars.faasId" targetId: >- - vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'executes'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty b/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty index e9747fd..81a888d 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/executes-host.sty @@ -13,14 +13,13 @@ nodes: vars: - name: "hostId" value: "resource.attributes['host.id']" - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - - name: "serviceUrnPrefix" + - name: "namespace" value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' + 'service.namespace' in resource.attributes ? + resource.attributes['service.namespace'] : + '' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -32,7 +31,7 @@ nodes: output: sourceId: "'urn:opentelemetry:host/' + vars.hostId" targetId: >- - vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'executes'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty b/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty index 4d8211f..3c287a8 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/k8s-pod-to-otel.sty @@ -20,14 +20,13 @@ nodes: value: "resource.attributes['k8s.namespace.name']" - name: "podName" value: "resource.attributes['k8s.pod.name']" - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - - name: "serviceUrnPrefix" + - name: "serviceNamespace" value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' + 'service.namespace' in resource.attributes ? + resource.attributes['service.namespace'] : + '' - name: "service" value: >- 'service.name' in resource.attributes ? @@ -42,7 +41,7 @@ nodes: sourceId: >- 'urn:opentelemetry:kubernetes:/' + vars.clusterName + ':' + vars.namespaceName + ':pod/' + vars.podName targetId: >- - vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId + 'urn:opentelemetry:namespace/' + vars.serviceNamespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'kubernetes-to-otel'" dependencyType: "'UNCLASSIFIED'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty b/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty index f8a5cfe..59b9c0a 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/provided-by.sty @@ -11,14 +11,13 @@ nodes: action: "'CREATE'" condition: "'service.name' in resource.attributes" vars: - # OTel: service.name is required, service.namespace is optional; unset or empty means "no namespace", not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - - name: "serviceUrnPrefix" + - name: "namespace" value: >- - 'service.namespace' in resource.attributes && - resource.attributes['service.namespace'] != '' ? - 'urn:opentelemetry:namespace/' + resource.attributes['service.namespace'] + ':service/' : - 'urn:opentelemetry:service/' + 'service.namespace' in resource.attributes ? + resource.attributes['service.namespace'] : + '' - name: "service" value: "resource.attributes['service.name']" - name: "instanceId" @@ -28,9 +27,9 @@ nodes: resource.attributes['service.name'] output: sourceId: >- - vars.serviceUrnPrefix + vars.service + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service targetId: >- - vars.serviceUrnPrefix + vars.service + ':serviceInstance/' + vars.instanceId + 'urn:opentelemetry:namespace/' + vars.namespace + ':service/' + vars.service + ':serviceInstance/' + vars.instanceId typeName: "'provided-by'" dependencyType: "'HIERARCHICAL'" expireAfter: 900000 diff --git a/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty b/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty index 52a189d..bd915d2 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/synchronous.sty @@ -19,12 +19,12 @@ nodes: 'connection_type' in datapoint.attributes && datapoint.attributes['connection_type'] == '' vars: - # OTel: service.namespace is optional; when unset or empty the service has no namespace, not a default one. + # OTel: service.namespace is optional and an unset namespace is itself a valid namespace, encoded here as the empty string. # service.instance.id is unique only within its (service.namespace, service.name) pair: https://opentelemetry.io/docs/specs/semconv/registry/attributes/service/ - - name: "clientServiceUrnPrefix" - value: "('client_service.namespace' in datapoint.attributes && datapoint.attributes['client_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['client_service.namespace'] + ':service/' : 'urn:opentelemetry:service/')" - - name: "serverServiceUrnPrefix" - value: "('server_service.namespace' in datapoint.attributes && datapoint.attributes['server_service.namespace'] != '' ? 'urn:opentelemetry:namespace/' + datapoint.attributes['server_service.namespace'] + ':service/' : 'urn:opentelemetry:service/')" + - name: "clientServiceNamespace" + value: "('client_service.namespace' in datapoint.attributes ? datapoint.attributes['client_service.namespace'] : '')" + - name: "serverServiceNamespace" + value: "('server_service.namespace' in datapoint.attributes ? datapoint.attributes['server_service.namespace'] : '')" - name: "client" value: "datapoint.attributes['client']" - name: "server" @@ -41,9 +41,9 @@ nodes: datapoint.attributes['server'] output: sourceId: >- - vars.clientServiceUrnPrefix + vars.client + ':serviceInstance/' + vars.clientServiceInstanceId + 'urn:opentelemetry:namespace/' + vars.clientServiceNamespace + ':service/' + vars.client + ':serviceInstance/' + vars.clientServiceInstanceId targetId: >- - vars.serverServiceUrnPrefix + vars.server + ':serviceInstance/' + vars.serverServiceInstanceId + 'urn:opentelemetry:namespace/' + vars.serverServiceNamespace + ':service/' + vars.server + ':serviceInstance/' + vars.serverServiceInstanceId typeName: "'synchronous'" dependencyType: "'CONNECTION'" expireAfter: 900000