From 111118dd686611e7404673d89818ea94c137f2d8 Mon Sep 17 00:00:00 2001 From: Aleksandr Mikhailov Date: Thu, 4 May 2023 14:23:13 +0200 Subject: [PATCH] TraceToLogs: Enable trace-to-log feature for cloud-logging-data-source-plugin (#65614) * TraceToLogs: Enable trace-to-log feature for cloud-logging-data-source-plugin * TraceToLogs: Add tests for googlecloud-logging-datasource --- .../TraceToLogs/TraceToLogsSettings.tsx | 1 + .../explore/TraceView/createSpanLink.test.ts | 183 ++++++++++++++++++ .../explore/TraceView/createSpanLink.tsx | 35 ++++ 3 files changed, 219 insertions(+) diff --git a/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx b/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx index d572983fdb9..68b9c9011c1 100644 --- a/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx +++ b/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx @@ -78,6 +78,7 @@ export function TraceToLogsSettings({ options, onOptionsChange }: Props) { 'grafana-splunk-datasource', // external 'grafana-opensearch-datasource', // external 'grafana-falconlogscale-datasource', // external + 'googlecloud-logging-datasource', // external ]; const traceToLogs = useMemo( diff --git a/public/app/features/explore/TraceView/createSpanLink.test.ts b/public/app/features/explore/TraceView/createSpanLink.test.ts index d8fa3ab3c27..0a25952d025 100644 --- a/public/app/features/explore/TraceView/createSpanLink.test.ts +++ b/public/app/features/explore/TraceView/createSpanLink.test.ts @@ -810,6 +810,189 @@ describe('createSpanLinkFactory', () => { }); }); + describe('google cloud link', () => { + const searchUID = 'searchUID'; + + beforeAll(() => { + setDataSourceSrv({ + getInstanceSettings() { + return { + uid: searchUID, + name: 'Google Cloud Logging', + type: 'googlecloud-logging-datasource', + } as unknown as DataSourceInstanceSettings; + }, + } as unknown as DataSourceSrv); + + setLinkSrv(new LinkSrv()); + setTemplateSrv(new TemplateSrv()); + }); + + it('creates link with correct simple query', () => { + const createLink = setupSpanLinkFactory({ + datasourceUid: searchUID, + }); + const links = createLink!(createTraceSpan()); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(decodeURIComponent(linkDef!.href)).toContain( + `datasource":"${searchUID}","queries":[{"query":"cluster=\\"cluster1\\" AND hostname=\\"hostname1\\"","refId":""}]` + ); + }); + + it('automatically timeshifts the time range by one second in a query', () => { + const createLink = setupSpanLinkFactory({ + datasourceUid: searchUID, + }); + const links = createLink!(createTraceSpan()); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(linkDef!.href).toContain( + `${encodeURIComponent('{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"}')}` + ); + expect(linkDef!.href).not.toContain( + `${encodeURIComponent('{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:00.000Z"}')}` + ); + }); + + it('formats query correctly if filterByTraceID and or filterBySpanID is true', () => { + const createLink = setupSpanLinkFactory( + { + datasourceUid: searchUID, + filterByTraceID: true, + filterBySpanID: true, + }, + searchUID + ); + + expect(createLink).toBeDefined(); + const links = createLink!(createTraceSpan()); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(linkDef!.href).toBe( + `/explore?left=${encodeURIComponent( + `{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"${searchUID}","queries":[{"query":"\\"6605c7b08e715d6c\\" AND \\"7946b05c2e2e4e5a\\" AND cluster=\\"cluster1\\" AND hostname=\\"hostname1\\"","refId":""}]}` + )}` + ); + }); + + it('formats query correctly if only filterByTraceID is true', () => { + const createLink = setupSpanLinkFactory( + { + datasourceUid: searchUID, + filterByTraceID: true, + }, + searchUID + ); + + expect(createLink).toBeDefined(); + const links = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [], + }, + }) + ); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(decodeURIComponent(linkDef!.href)).toBe( + `/explore?left={"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"searchUID","queries":[{"query":"\\"7946b05c2e2e4e5a\\"","refId":""}]}` + ); + }); + + it('should format one tag correctly', () => { + const createLink = setupSpanLinkFactory( + { + tags: [{ key: 'ip' }], + }, + searchUID + ); + expect(createLink).toBeDefined(); + const links = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [{ key: 'ip', value: '192.168.0.1' }], + }, + }) + ); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(linkDef!.href).toBe( + `/explore?left=${encodeURIComponent( + `{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"${searchUID}","queries":[{"query":"ip=\\"192.168.0.1\\"","refId":""}]}` + )}` + ); + }); + + it('should format multiple tags correctly', () => { + const createLink = setupSpanLinkFactory( + { + tags: [{ key: 'ip' }, { key: 'hostname' }], + }, + searchUID + ); + expect(createLink).toBeDefined(); + const links = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [ + { key: 'hostname', value: 'hostname1' }, + { key: 'ip', value: '192.168.0.1' }, + ], + }, + }) + ); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(linkDef!.href).toBe( + `/explore?left=${encodeURIComponent( + `{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"${searchUID}","queries":[{"query":"hostname=\\"hostname1\\" AND ip=\\"192.168.0.1\\"","refId":""}]}` + )}` + ); + }); + + it('handles renamed tags', () => { + const createLink = setupSpanLinkFactory( + { + tags: [ + { key: 'service.name', value: 'service' }, + { key: 'k8s.pod.name', value: 'pod' }, + ], + }, + searchUID + ); + expect(createLink).toBeDefined(); + const links = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [ + { key: 'service.name', value: 'serviceName' }, + { key: 'k8s.pod.name', value: 'podName' }, + ], + }, + }) + ); + + const linkDef = links?.logLinks?.[0]; + expect(linkDef).toBeDefined(); + expect(linkDef!.href).toBe( + `/explore?left=${encodeURIComponent( + `{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"${searchUID}","queries":[{"query":"service=\\"serviceName\\" AND pod=\\"podName\\"","refId":""}]}` + )}` + ); + }); + }); + describe('custom query', () => { beforeAll(() => { setDataSourceSrv({ diff --git a/public/app/features/explore/TraceView/createSpanLink.tsx b/public/app/features/explore/TraceView/createSpanLink.tsx index c2286c7f931..d6cf1ce1dcb 100644 --- a/public/app/features/explore/TraceView/createSpanLink.tsx +++ b/public/app/features/explore/TraceView/createSpanLink.tsx @@ -165,6 +165,10 @@ function legacyCreateSpanLinkFactory( case 'grafana-falconlogscale-datasource': tags = getFormattedTags(span, tagsToUse, { joinBy: ' OR ' }); query = getQueryForFalconLogScale(span, traceToLogsOptions, tags, customQuery); + break; + case 'googlecloud-logging-datasource': + tags = getFormattedTags(span, tagsToUse, { joinBy: ' AND ' }); + query = getQueryForGoogleCloudLogging(span, traceToLogsOptions, tags, customQuery); } // query can be false in case the simple UI tag mapping is used but none of them are present in the span. @@ -406,6 +410,37 @@ function getQueryForSplunk(span: TraceSpan, options: TraceToLogsOptionsV2, tags: }; } +function getQueryForGoogleCloudLogging( + span: TraceSpan, + options: TraceToLogsOptionsV2, + tags: string, + customQuery?: string +) { + const { filterByTraceID, filterBySpanID } = options; + + if (customQuery) { + return { query: customQuery, refId: '' }; + } + + let queryArr = []; + if (filterBySpanID && span.spanID) { + queryArr.push('"${__span.spanId}"'); + } + + if (filterByTraceID && span.traceID) { + queryArr.push('"${__span.traceId}"'); + } + + if (tags) { + queryArr.push('${__tags}'); + } + + return { + query: queryArr.join(' AND '), + refId: '', + }; +} + function getQueryForFalconLogScale(span: TraceSpan, options: TraceToLogsOptionsV2, tags: string, customQuery?: string) { const { filterByTraceID, filterBySpanID } = options;