diff --git a/stackpacks/open-telemetry/settings/component-mappings/databases.sty b/stackpacks/open-telemetry/settings/component-mappings/databases.sty index f9006da..2db05dd 100644 --- a/stackpacks/open-telemetry/settings/component-mappings/databases.sty +++ b/stackpacks/open-telemetry/settings/component-mappings/databases.sty @@ -42,3 +42,69 @@ nodes: expireAfter: 900000 rank: specificity: 200 + - _type: "OtelComponentMapping" + name: "Database (span enrichment DB system)" + description: "Enriches database components with db.system.name and connection details from spans" + identifier: "urn:stackpack:open-telemetry:otel-component-mapping:database-span-enrichment-db-system" + input: + signal: + - "TRACES" + resource: + condition: "'service.namespace' in resource.attributes && 'service.name' in resource.attributes" + scope: + span: + action: "'CREATE'" + condition: >- + 'db.system.name' in span.attributes || 'db.system' in span.attributes + vars: + - name: "clientServiceNamespace" + value: "resource.attributes['service.namespace']" + - name: "client" + value: "resource.attributes['service.name']" + - name: "clusterName" + value: "resource.attributes['k8s.cluster.name']" + - name: "server" + value: >- + 'peer.service' in span.attributes ? span.attributes['peer.service'] : + 'db.namespace' in span.attributes ? span.attributes['db.namespace'] : + 'db.name' in span.attributes ? span.attributes['db.name'] : + 'db.system.name' in span.attributes ? span.attributes['db.system.name'] : + span.attributes['db.system'] + # Logical database name for display. Prefers the specific database name (db.namespace is the current + # attribute, db.name the deprecated legacy one), then the user-assigned peer service label, falling + # back to the generic DBMS product type only so the component is never nameless. Kept separate from + # `server` (identity), which leads with peer.service. + - name: "databaseName" + value: >- + 'db.namespace' in span.attributes ? span.attributes['db.namespace'] : + 'db.name' in span.attributes ? span.attributes['db.name'] : + 'peer.service' in span.attributes ? span.attributes['peer.service'] : + 'db.system.name' in span.attributes ? span.attributes['db.system.name'] : + span.attributes['db.system'] + output: + identifier: >- + 'urn:opentelemetry:namespace/' + vars.clientServiceNamespace + ':service/' + vars.client + ':database/span/' + vars.server + name: "vars.databaseName" + typeName: "'database'" + required: + additionalIdentifiers: + - >- + 'urn:opentelemetry:namespace/' + vars.clientServiceNamespace + ':service/' + vars.client + ':database/' + vars.server + tags: + - source: "'open-telemetry'" + target: "stackpack" + - source: "vars.clusterName" + target: "cluster-name" + - source: "resource.attributes" + pattern: "(.*)" + target: "${1}" + - source: "span.attributes" + pattern: "db.(.*)" + target: "db.${1}" + optional: + tags: + - source: "resource.attributes['k8s.namespace.name']" + target: "namespace" + expireAfter: 900000 + rank: + specificity: 190 diff --git a/stackpacks/open-telemetry/settings/presentations/databases.sty b/stackpacks/open-telemetry/settings/presentations/databases.sty index ded5cb4..6c32f0a 100644 --- a/stackpacks/open-telemetry/settings/presentations/databases.sty +++ b/stackpacks/open-telemetry/settings/presentations/databases.sty @@ -12,7 +12,8 @@ nodes: icon: !icon databases.svg filters: - filterId: "otel_namespace" - - filterId: "otel_service" + - filterId: "k8s_cluster" + - filterId: "k8s_namespace" topology: groupingEnabled: true showIndirectRelations: false @@ -34,6 +35,19 @@ nodes: - columnId: "otel_name" title: "Database" - columnId: otel_namespace + title: "OTel Namespace" + - columnId: otel_k8s_cluster + title: "Kubernetes Cluster" + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('cluster-name')" + identifier: "'urn:cluster:/kubernetes:' + tags.singleValue('cluster-name')" + - columnId: otel_k8s_namespace + title: "Kubernetes Namespace" + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('namespace')" + identifier: "'urn:kubernetes:/' + tags.singleValue('cluster-name') + ':namespace/' + tags.singleValue('namespace')" - columnId: otel_database_span_error_rate title: Span error % projection: @@ -77,7 +91,39 @@ nodes: - columnId: otel_name highlight: title: "Otel database" - fields: [] + fields: + - fieldId: otel_db_system + title: "Database type" + order: 85.0 + projection: + _type: TextProjection + value: "'db.system.name' in tags ? tags.singleValue('db.system.name') : tags.singleValue('db.system')" + - fieldId: otel_db_server_address + title: "Server address" + order: 84.0 + projection: + _type: TextProjection + value: "'net.peer.name' in tags ? tags.singleValue('net.peer.name') : tags.singleValue('server.address')" + - fieldId: otel_db_server_port + title: "Server port" + order: 83.0 + projection: + _type: TextProjection + value: "'net.peer.port' in tags ? tags.singleValue('net.peer.port') : tags.singleValue('server.port')" + - fieldId: "k8s_cluster" + title: "K8s Cluster" + order: 77.5 + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('cluster-name')" + identifier: "'urn:cluster:/kubernetes:' + tags.singleValue('cluster-name')" + - fieldId: "k8s_namespace" + title: "K8s Namespace" + order: 75.0 + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('namespace')" + identifier: "'urn:kubernetes:/' + tags.singleValue('cluster-name') + ':namespace/' + tags.singleValue('namespace')" provisioning: externalComponentSelector: "external.mapping.identifier == 'urn:stackpack:open-telemetry:otel-component-mapping:database'" showConfiguration: false @@ -88,11 +134,23 @@ nodes: order: 2.0 topologyQuery: '(withNeighborsOf(direction = "both", components = (identifier = "${identifiers[0]}"), levels = "1")) and type = "otel service instance"' presentationIdentifier: "urn:stackpack:open-telemetry:component-presentation:service-instance" - - resourceId: "otel_datbase_services" + - resourceId: "otel_database_services" title: "Related services" order: 1.0 topologyQuery: '(withNeighborsOf(direction = "up", components = (withNeighborsOf(direction = "both", components = (identifier = "${identifiers[0]}"), levels = "1") and type = "otel service instance") , levels = "1") and type = "otel service")' presentationIdentifier: "urn:stackpack:open-telemetry:component-presentation:service" + - resourceId: "otel_database_pods" + title: "Related pods" + order: 3.0 + topologyQuery: >- + label = "source-name:${tags.singleValue('k8s.pod.name')}" + presentationIdentifier: "urn:stackpack:open-telemetry:view-type:pod-link" + - resourceId: "otel_database_hosts" + title: "Related hosts" + order: 4.0 + topologyQuery: >- + label = "source-type:host" AND label = "source-name:${tags.singleValue('k8s.node.name')}" + presentationIdentifier: "urn:stackpack:open-telemetry:view-type:host-link" metrics: - sectionId: traces title: "Traces" @@ -142,6 +200,13 @@ nodes: client_service_namespace="${tags.singleValue('service.namespace')}", client="${tags.singleValue('service.name')}", server="${name}" }[${__rate_interval}])) by (client_service_namespace, client, server, le)) metricId: database-spans-duration + tracesPerspective: + showTraces: true + spanFilter: + attributes: + service.namespace: ["${tags.singleValue('service.namespace')}"] + service.name: ["${tags.singleValue('service.name')}"] + db.system: ["${tags.singleValue('db.system')}"] metricPerspective: tabs: - diff --git a/stackpacks/open-telemetry/settings/presentations/k8s-hosts.sty b/stackpacks/open-telemetry/settings/presentations/k8s-hosts.sty new file mode 100644 index 0000000..19549f3 --- /dev/null +++ b/stackpacks/open-telemetry/settings/presentations/k8s-hosts.sty @@ -0,0 +1,19 @@ +nodes: + - _type: ComponentPresentation + name: "Otel Host Extension" + description: "Extends the Host presentation for OTel hosts with related service instances" + identifier: urn:stackpack:open-telemetry:component-presentation:host + binding: + _type: ComponentPresentationQueryBinding + query: (label = "source-type:host") + rank: + specificity: 10 + presentation: + highlight: + relatedResources: + - resourceId: "otel_host_service_instances" + title: "Related service instances" + order: 1.0 + topologyQuery: >- + type = "otel service instance" AND label = "k8s.node.name:${name}" + presentationIdentifier: "urn:stackpack:open-telemetry:component-presentation:service-instance" diff --git a/stackpacks/open-telemetry/settings/presentations/k8s-pods.sty b/stackpacks/open-telemetry/settings/presentations/k8s-pods.sty new file mode 100644 index 0000000..233433e --- /dev/null +++ b/stackpacks/open-telemetry/settings/presentations/k8s-pods.sty @@ -0,0 +1,19 @@ +nodes: + - _type: ComponentPresentation + name: "Otel Pod Extension" + description: "Extends the Pod presentation for OTel pods with related service instances" + identifier: urn:stackpack:open-telemetry:component-presentation:pod + binding: + _type: ComponentPresentationQueryBinding + query: (label = "stackpack:open-telemetry" and type = "pod") + rank: + specificity: 10 + presentation: + highlight: + relatedResources: + - resourceId: "otel_pod_service_instances" + title: "Related service instances" + order: 1.0 + topologyQuery: >- + type = "otel service instance" AND label = "k8s.pod.name:${name}" + presentationIdentifier: "urn:stackpack:open-telemetry:component-presentation:service-instance" diff --git a/stackpacks/open-telemetry/settings/presentations/service-instances.sty b/stackpacks/open-telemetry/settings/presentations/service-instances.sty index 4f75d3b..d4dcb4c 100644 --- a/stackpacks/open-telemetry/settings/presentations/service-instances.sty +++ b/stackpacks/open-telemetry/settings/presentations/service-instances.sty @@ -36,12 +36,25 @@ nodes: - columnId: otel_name title: Service Instance - columnId: otel_namespace + title: OTel Namespace - columnId: otel_service title: "Service" projection: _type: "ComponentLinkProjection" name: "tags.singleValue('service.name')" identifier: "'urn:opentelemetry:namespace/' + tags.singleValue('service.namespace') + ':service/' + tags.singleValue('service.name')" + - columnId: otel_k8s_cluster + title: "Kubernetes Cluster" + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('cluster-name')" + identifier: "'urn:cluster:/kubernetes:' + tags.singleValue('cluster-name')" + - columnId: otel_k8s_namespace + title: "Kubernetes Namespace" + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('namespace')" + identifier: "'urn:kubernetes:/' + tags.singleValue('cluster-name') + ':namespace/' + tags.singleValue('namespace')" - columnId: otel_service_instance_span_error_percent_summary title: Span error % projection: @@ -91,10 +104,10 @@ nodes: - columnId: health - columnId: otel_name highlight: - title: "Otel service" + title: "OTel service" fields: - fieldId: "namespace" - title: "Namespace" + title: "OTel Namespace" order: 85.0 projection: _type: ComponentLinkProjection @@ -107,6 +120,26 @@ nodes: _type: ComponentLinkProjection name: "tags.singleValue('service.name')" identifier: "'urn:opentelemetry:namespace/' + tags.singleValue('service.namespace') + ':service/' + tags.singleValue('service.name')" + - fieldId: "language" + title: "Language" + order: 81.0 + projection: + _type: TextProjection + value: "tags.singleValue('telemetry.sdk.language')" + - fieldId: "k8s_cluster" + title: "K8s Cluster" + order: 77.5 + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('cluster-name')" + identifier: "'urn:cluster:/kubernetes:' + tags.singleValue('cluster-name')" + - fieldId: "k8s_namespace" + title: "K8s Namespace" + order: 75.0 + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('namespace')" + identifier: "'urn:kubernetes:/' + tags.singleValue('cluster-name') + ':namespace/' + tags.singleValue('namespace')" provisioning: externalComponentSelector: "external.mapping.identifier == 'urn:stackpack:open-telemetry:otel-component-mapping:service-instance'" showConfiguration: false @@ -122,6 +155,18 @@ nodes: order: 1.0 topologyQuery: '(withNeighborsOf(direction = "up", components = (withNeighborsOf(direction = "both", components = (identifier = "${identifiers[0]}"), levels = "1") and type = "otel service instance") , levels = "1") and type = "otel service")' presentationIdentifier: "urn:stackpack:open-telemetry:component-presentation:service" + - resourceId: "otel_service_instance_pods" + title: "Related pods" + order: 3.0 + topologyQuery: >- + label = "source-name:${tags.singleValue('k8s.pod.name')}" + presentationIdentifier: "urn:stackpack:open-telemetry:view-type:pod-link" + - resourceId: "otel_service_instance_hosts" + title: "Related hosts" + order: 4.0 + topologyQuery: >- + label = "source-type:host" AND label = "source-name:${tags.singleValue('k8s.node.name')}" + presentationIdentifier: "urn:stackpack:open-telemetry:view-type:host-link" events: showEvents: true relatedResourcesQuery: | @@ -179,6 +224,13 @@ nodes: }[${__rate_interval}])) by (le)) / 1000) metricId: service-instance-spans-duration + tracesPerspective: + showTraces: true + spanFilter: + attributes: + service.namespace: ["${tags.singleValue('service.namespace')}"] + service.name: ["${tags.singleValue('service.name')}"] + service.instance.id: ["${tags.singleValue('service.instance.id')}"] metricPerspective: tabs: - diff --git a/stackpacks/open-telemetry/settings/presentations/services.sty b/stackpacks/open-telemetry/settings/presentations/services.sty index 3a40cd2..96cd696 100644 --- a/stackpacks/open-telemetry/settings/presentations/services.sty +++ b/stackpacks/open-telemetry/settings/presentations/services.sty @@ -35,6 +35,19 @@ nodes: - columnId: otel_name title: Service - columnId: otel_namespace + title: "OTel Namespace" + - columnId: otel_k8s_cluster + title: "Kubernetes Cluster" + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('cluster-name')" + identifier: "'urn:cluster:/kubernetes:' + tags.singleValue('cluster-name')" + - columnId: otel_k8s_namespace + title: "Kubernetes Namespace" + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('namespace')" + identifier: "'urn:kubernetes:/' + tags.singleValue('cluster-name') + ':namespace/' + tags.singleValue('namespace')" - columnId: otel_service_span_error_percent_summary title: Span error % projection: @@ -84,15 +97,35 @@ nodes: - columnId: health - columnId: otel_name highlight: - title: "Otel service" + title: "OTel service" fields: - fieldId: "namespace" - title: "Otel Namespace" + title: "OTel Namespace" order: 85.0 projection: _type: ComponentLinkProjection name: tags.singleValue('service.namespace') identifier: "'urn:opentelemetry:namespace/' + tags.singleValue('service.namespace')" + - fieldId: "language" + title: "Language" + order: 82.5 + projection: + _type: TextProjection + value: "tags.singleValue('telemetry.sdk.language')" + - fieldId: "k8s_cluster" + title: "K8s Cluster" + order: 77.5 + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('cluster-name')" + identifier: "'urn:cluster:/kubernetes:' + tags.singleValue('cluster-name')" + - fieldId: "k8s_namespace" + title: "K8s Namespace" + order: 75.0 + projection: + _type: ComponentLinkProjection + name: "tags.singleValue('namespace')" + identifier: "'urn:kubernetes:/' + tags.singleValue('cluster-name') + ':namespace/' + tags.singleValue('namespace')" provisioning: externalComponentSelector: "external.mapping.identifier == 'urn:stackpack:open-telemetry:otel-component-mapping:service'" showConfiguration: false @@ -108,6 +141,11 @@ nodes: order: 1.0 topologyQuery: '(withNeighborsOf(direction = "up", components = (withNeighborsOf(direction = "both", components = (withNeighborsOf(direction = "down", components = (identifier = "${identifiers[0]}"), levels = "1") and type = "otel service instance"), levels = "1") and type = "otel service instance") , levels = "1") and type = "otel service")' presentationIdentifier: "urn:stackpack:open-telemetry:component-presentation:service" + - resourceId: "otel_service_pods" + title: "Related pods" + order: 3.0 + topologyQuery: '(withNeighborsOf(direction = "both", components = (withNeighborsOf(direction = "down", components = (identifier = "${identifiers[0]}"), levels = "1") and type = "otel service instance"), levels = "1")) and type = "pod"' + presentationIdentifier: "urn:stackpack:open-telemetry:view-type:pod-link" events: showEvents: true relatedResourcesQuery: | @@ -175,6 +213,12 @@ nodes: service_namespace="${tags.singleValue('service.namespace')}", service_name="${name}" }[${__rate_interval}])) metricId: service-spans-rate + tracesPerspective: + showTraces: true + spanFilter: + attributes: + service.namespace: ["${tags.singleValue('service.namespace')}"] + service.name: ["${name}"] metricPerspective: tabs: - diff --git a/stackpacks/open-telemetry/settings/relation-mappings/database.sty b/stackpacks/open-telemetry/settings/relation-mappings/database.sty index 251f68a..d52f8c8 100644 --- a/stackpacks/open-telemetry/settings/relation-mappings/database.sty +++ b/stackpacks/open-telemetry/settings/relation-mappings/database.sty @@ -41,3 +41,42 @@ nodes: typeName: "'database'" dependencyType: "'CONNECTION'" expireAfter: 900000 + - _type: "OtelRelationMapping" + name: "Database Relation (span enrichment)" + description: "Adds the service instance -> database connection from database spans, complementing the service-graph metrics relation" + identifier: "urn:stackpack:open-telemetry:otel-relation-mapping:database-span-enrichment" + input: + signal: + - "TRACES" + resource: + condition: >- + 'service.namespace' in resource.attributes && + 'service.name' in resource.attributes && + 'service.instance.id' in resource.attributes + scope: + span: + action: "'CREATE'" + condition: >- + 'db.system.name' in span.attributes || 'db.system' in span.attributes + vars: + - name: "clientServiceNamespace" + value: "resource.attributes['service.namespace']" + - name: "client" + value: "resource.attributes['service.name']" + - name: "clientServiceInstanceId" + value: "resource.attributes['service.instance.id']" + - name: "server" + value: >- + 'peer.service' in span.attributes ? span.attributes['peer.service'] : + 'db.namespace' in span.attributes ? span.attributes['db.namespace'] : + 'db.name' in span.attributes ? span.attributes['db.name'] : + 'db.system.name' in span.attributes ? span.attributes['db.system.name'] : + span.attributes['db.system'] + output: + sourceId: >- + 'urn:opentelemetry:namespace/' + vars.clientServiceNamespace + ':service/' + vars.client + ':serviceInstance/' + vars.clientServiceInstanceId + targetId: >- + 'urn:opentelemetry:namespace/' + vars.clientServiceNamespace + ':service/' + vars.client + ':database/' + vars.server + typeName: "'database'" + dependencyType: "'CONNECTION'" + expireAfter: 900000 diff --git a/stackpacks/open-telemetry/stackpack.yaml b/stackpacks/open-telemetry/stackpack.yaml index bbc1fab..a7f6778 100644 --- a/stackpacks/open-telemetry/stackpack.yaml +++ b/stackpacks/open-telemetry/stackpack.yaml @@ -1,5 +1,5 @@ name: "open-telemetry" -version: "0.1.1" +version: "0.1.2" schemaVersion: "2.0" displayName: "Open Telemetry" categories: [ "Open Telemetry" ]