diff --git a/docs/sources/datasources/jaeger.md b/docs/sources/datasources/jaeger.md index 001574b4c45..fb1c841c8b4 100644 --- a/docs/sources/datasources/jaeger.md +++ b/docs/sources/datasources/jaeger.md @@ -47,11 +47,12 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t To configure trace to metrics, select the target Prometheus data source and create any desired linked queries. -- **Data source -** Target data source. +-- **Tags -** You can use tags in the linked queries. The key is the span attribute name. The optional value is the corresponding metric label name (for example, map `k8s.pod` to `pod`). You may interpolate these tags into your queries using the `$__tags` keyword. Each linked query consists of: -- **Link Label -** (Optional) Descriptive label for the linked query. --- **Query -** Query that runs when navigating from a trace to the metrics data source. +-- **Query -** Query that runs when navigating from a trace to the metrics data source. Interpolate tags using the `$__tags` keyword. For example, when you configure the query `requests_total{$__tags}`with the tags `k8s.pod=pod` and `cluster`, it results in `requests_total{pod="nginx-554b9", cluster="us-east-1"}`. ### Node Graph @@ -169,6 +170,12 @@ datasources: spanEndTimeShift: '1h' filterByTraceID: false filterBySpanID: false + tracesToMetrics: + datasourceUid: 'prom' + tags: [{ key: 'service.name', value: 'service' }, { key: 'job' }] + queries: + - name: 'Sample query' + query: 'sum(rate(tempo_spanmetrics_latency_bucket{$__tags}[5m]))' secureJsonData: basicAuthPassword: my_password ``` diff --git a/docs/sources/datasources/tempo.md b/docs/sources/datasources/tempo.md index 7993df800f8..7f9d0040d00 100644 --- a/docs/sources/datasources/tempo.md +++ b/docs/sources/datasources/tempo.md @@ -46,11 +46,12 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t To configure trace to metrics, select the target Prometheus data source and create any desired linked queries. -- **Data source -** Target data source. +-- **Tags -** You can use tags in the linked queries. The key is the span attribute name. The optional value is the corresponding metric label name (for example, map `k8s.pod` to `pod`). You may interpolate these tags into your queries using the `$__tags` keyword. Each linked query consists of: -- **Link Label -** (Optional) Descriptive label for the linked query. --- **Query -** Query that runs when navigating from a trace to the metrics data source. +-- **Query -** Query that runs when navigating from a trace to the metrics data source. Interpolate tags using the `$__tags` keyword. For example, when you configure the query `requests_total{$__tags}`with the tags `k8s.pod=pod` and `cluster`, it results in `requests_total{pod="nginx-554b9", cluster="us-east-1"}`. ### Service Graph @@ -216,6 +217,12 @@ datasources: spanEndTimeShift: '1h' filterByTraceID: false filterBySpanID: false + tracesToMetrics: + datasourceUid: 'prom' + tags: [{ key: 'service.name', value: 'service' }, { key: 'job' }] + queries: + - name: 'Sample query' + query: 'sum(rate(tempo_spanmetrics_latency_bucket{$__tags}[5m]))' serviceMap: datasourceUid: 'prometheus' search: diff --git a/docs/sources/datasources/zipkin.md b/docs/sources/datasources/zipkin.md index 88ba196ce12..a4284ad2177 100644 --- a/docs/sources/datasources/zipkin.md +++ b/docs/sources/datasources/zipkin.md @@ -47,11 +47,12 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t To configure trace to metrics, select the target Prometheus data source and create any desired linked queries. -- **Data source -** Target data source. +-- **Tags -** You can use tags in the linked queries. The key is the span attribute name. The optional value is the corresponding metric label name (for example, map `k8s.pod` to `pod`). You may interpolate these tags into your queries using the `$__tags` keyword. Each linked query consists of: -- **Link Label -** (Optional) Descriptive label for the linked query. --- **Query -** Query that runs when navigating from a trace to the metrics data source. +-- **Query -** Query that runs when navigating from a trace to the metrics data source. Interpolate tags using the `$__tags` keyword. For example, when you configure the query `requests_total{$__tags}`with the tags `k8s.pod=pod` and `cluster`, it results in `requests_total{pod="nginx-554b9", cluster="us-east-1"}`. ### Node Graph diff --git a/public/app/core/components/TraceToLogs/KeyValueInput.tsx b/public/app/core/components/TraceToLogs/KeyValueInput.tsx index 45934073fdb..7d648c10293 100644 --- a/public/app/core/components/TraceToLogs/KeyValueInput.tsx +++ b/public/app/core/components/TraceToLogs/KeyValueInput.tsx @@ -65,6 +65,7 @@ const KeyValueInput = ({ onClick={() => onChange([...values.slice(0, idx), ...values.slice(idx + 1)])} className="gf-form-label query-part" aria-label="Remove tag" + type="button" > @@ -73,6 +74,7 @@ const KeyValueInput = ({ onClick={() => onChange([...values, { key: '', value: '' }])} className="gf-form-label query-part" aria-label="Add tag" + type="button" > @@ -84,6 +86,7 @@ const KeyValueInput = ({ onClick={() => onChange([...values, { key: '', value: '' }])} className="gf-form-label query-part" aria-label="Add tag" + type="button" > diff --git a/public/app/core/components/TraceToMetrics/TraceToMetricsSettings.tsx b/public/app/core/components/TraceToMetrics/TraceToMetricsSettings.tsx index 4612892f0d8..3bc5c202f05 100644 --- a/public/app/core/components/TraceToMetrics/TraceToMetricsSettings.tsx +++ b/public/app/core/components/TraceToMetrics/TraceToMetricsSettings.tsx @@ -5,19 +5,23 @@ import { DataSourceJsonData, DataSourcePluginOptionsEditorProps, GrafanaTheme, + KeyValue, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { DataSourcePicker } from '@grafana/runtime'; import { Button, InlineField, InlineFieldRow, Input, useStyles } from '@grafana/ui'; +import KeyValueInput from '../TraceToLogs/KeyValueInput'; + export interface TraceToMetricsOptions { datasourceUid?: string; + tags?: Array>; queries: TraceToMetricQuery[]; } export interface TraceToMetricQuery { name?: string; - query: string; + query?: string; } export interface TraceToMetricsData extends DataSourceJsonData { @@ -71,6 +75,21 @@ export function TraceToMetricsSettings({ options, onOptionsChange }: Props) { ) : null} + + + + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToMetrics', { + ...options.jsonData.tracesToMetrics, + tags: v, + }) + } + /> + + + {options.jsonData.tracesToMetrics?.queries?.map((query, i) => (
@@ -92,7 +111,7 @@ export function TraceToMetricsSettings({ options, onOptionsChange }: Props) { { }); }); + it('correctly interpolates span attributes', () => { + const splitOpenFn = jest.fn(); + const createLink = createSpanLinkFactory({ + splitOpenFn, + traceToMetricsOptions: { + datasourceUid: 'prom1', + queries: [{ name: 'Named Query', query: 'metric{$__tags}[5m]' }], + tags: [ + { key: 'job', value: '' }, + { key: 'k8s.pod', value: 'pod' }, + ], + } as TraceToMetricsOptions, + }); + expect(createLink).toBeDefined(); + + const links = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [ + { key: 'job', value: 'tns/app' }, + { key: 'k8s.pod', value: 'sample-pod' }, + ], + }, + }) + ); + expect(links).toBeDefined(); + expect(links!.metricLinks![0]!.href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1","queries":[{"expr":"metric{job=\\"tns/app\\", pod=\\"sample-pod\\"}[5m]","refId":"A"}],"panelsState":{}}' + )}` + ); + }); + describe('should return span links', () => { beforeAll(() => { setDataSourceSrv(new DatasourceSrv()); diff --git a/public/app/features/explore/TraceView/createSpanLink.tsx b/public/app/features/explore/TraceView/createSpanLink.tsx index f9b009dff03..57db5e73979 100644 --- a/public/app/features/explore/TraceView/createSpanLink.tsx +++ b/public/app/features/explore/TraceView/createSpanLink.tsx @@ -20,7 +20,7 @@ import { getTemplateSrv } from '@grafana/runtime'; import { Icon } from '@grafana/ui'; import { SpanLinkFunc, TraceSpan } from '@jaegertracing/jaeger-ui-components'; import { TraceToLogsOptions } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; -import { TraceToMetricsOptions } from 'app/core/components/TraceToMetrics/TraceToMetricsSettings'; +import { TraceToMetricQuery, TraceToMetricsOptions } from 'app/core/components/TraceToMetrics/TraceToMetricsSettings'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { PromQuery } from 'app/plugins/datasource/prometheus/types'; @@ -150,10 +150,9 @@ function legacyCreateSpanLinkFactory( // Get metrics links if (metricsDataSourceSettings && traceToMetricsOptions?.queries) { - const defaultQuery = `histogram_quantile(0.5, sum(rate(tempo_spanmetrics_latency_bucket{operation="${span.operationName}"}[5m])) by (le))`; - links.metricLinks = []; for (const query of traceToMetricsOptions.queries) { + const expr = buildMetricsQuery(query, traceToMetricsOptions?.tags, span); const dataLink: DataLink = { title: metricsDataSourceSettings.name, url: '', @@ -161,7 +160,7 @@ function legacyCreateSpanLinkFactory( datasourceUid: metricsDataSourceSettings.uid, datasourceName: metricsDataSourceSettings.name, query: { - expr: query.query || defaultQuery, + expr, refId: 'A', }, }, @@ -362,3 +361,27 @@ function getTimeRangeFromSpan( }, }; } + +// Interpolates span attributes into trace to metric query, or returns default query +function buildMetricsQuery(query: TraceToMetricQuery, tags: Array> = [], span: TraceSpan): string { + if (!query.query) { + return `histogram_quantile(0.5, sum(rate(tempo_spanmetrics_latency_bucket{operation="${span.operationName}"}[5m])) by (le))`; + } + + let expr = query.query; + if (tags.length && expr.indexOf('$__tags') !== -1) { + const spanTags = [...span.process.tags, ...span.tags]; + const labels = tags.reduce((acc, tag) => { + const tagValue = spanTags.find((t) => t.key === tag.key)?.value; + if (tagValue) { + acc.push(`${tag.value ? tag.value : tag.key}="${tagValue}"`); + } + return acc; + }, [] as string[]); + + const labelsQuery = labels?.join(', '); + expr = expr.replace('$__tags', labelsQuery); + } + + return expr; +}