Commit b3838d37 by Zoltán Bedi Committed by GitHub

Tracing: Release trace to logs feature (#29443)

* Remove feature flag

* Add data source setting for Jaeger

* Refactor trace to logs settings

* Fix tests

* Get ds settings in two steps

* Add info to settings

* Update docs for trace to logs

* Update yarn.lock

* Apply suggestions from code review

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>

* Update TraceToLogsSettings after merge with master

* Add config for tags

* Add tags to check for keys

* Apply suggestions from code review

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>

* Update docs

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
parent 8a3fdc30
...@@ -11,18 +11,28 @@ weight = 800 ...@@ -11,18 +11,28 @@ weight = 800
Grafana ships with built-in support for Jaeger, which provides open source, end-to-end distributed tracing. Grafana ships with built-in support for Jaeger, which provides open source, end-to-end distributed tracing.
Just add it as a data source and you are ready to query your traces in [Explore]({{< relref "../explore/index.md" >}}). Just add it as a data source and you are ready to query your traces in [Explore]({{< relref "../explore/index.md" >}}).
## Adding the data source ## Add data source
To access Jaeger settings, click the **Configuration** (gear) icon, then click **Data Sources**, and then click **Jaeger**.
To access Jaeger settings, click the **Configuration** (gear) icon, then click **Data Sources** > **Jaeger**.
| Name | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Name | Description |
| `Name` | The data source name. This is how you refer to the data source in panels, queries, and Explore. | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `Default` | Default data source means that it will be pre-selected for new panels. | | `Name` | The data source name. This is how you refer to the data source in panels, queries, and Explore. |
| `URL` | The URL of the Jaeger instance, e.g., `http://localhost:16686` | | `Default` | Data source will be pre-selected for new panels. |
| `Access` | Server (default) = URL needs to be accessible from the Grafana backend/server, Browser = URL needs to be accessible from the browser. | | `URL` | The URL of the Jaeger instance, for example, `http://localhost:16686` |
| `Basic Auth` | Enable basic authentication to the Jaeger data source. | | `Access` | Server (default) = URL needs to be accessible from the Grafana backend/server. Browser = URL needs to be accessible from the browser. |
| `User` | User name for basic authentication. | | `Basic Auth` | Enable basic authentication to the Jaeger data source. |
| `Password` | Password for basic authentication. | | `User` | User name for basic authentication. |
| `Password` | Password for basic authentication. |
### Trace to logs
{{< docs-imagebox img="/img/docs/v74/trace-to-logs-settings.png" class="docs-image--no-shadow" caption="Screenshot of the trace to logs settings" >}}
This is a configuration for the [trace to logs feature]({{< relref "../explore/index.md#trace-to-logs" >}}). Select target data source (at this moment limited to Loki data sources) and select which tags will be used in the logs query.
- **Data source -** Target data source.
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
## Query traces ## Query traces
...@@ -35,6 +45,7 @@ The Jaeger query editor allows you to query by trace ID directly or selecting a ...@@ -35,6 +45,7 @@ The Jaeger query editor allows you to query by trace ID directly or selecting a
{{< docs-imagebox img="/img/docs/v70/jaeger-query-editor-open.png" class="docs-image--no-shadow" caption="Screenshot of the Jaeger query editor with trace selector expanded" >}} {{< docs-imagebox img="/img/docs/v70/jaeger-query-editor-open.png" class="docs-image--no-shadow" caption="Screenshot of the Jaeger query editor with trace selector expanded" >}}
Use the trace selector to pick particular trace from all traces logged in the time range you have selected in Explore. The trace selector has three levels of nesting: Use the trace selector to pick particular trace from all traces logged in the time range you have selected in Explore. The trace selector has three levels of nesting:
1. The service you are interested in. 1. The service you are interested in.
1. Particular operation is part of the selected service. 1. Particular operation is part of the selected service.
1. Specific trace in which the selected operation occurred, represented by the root operation name and trace duration. 1. Specific trace in which the selected operation occurred, represented by the root operation name and trace duration.
......
...@@ -3,24 +3,34 @@ title = "Tempo" ...@@ -3,24 +3,34 @@ title = "Tempo"
description = "High volume, minimal dependency trace storage. OSS tracing solution from Grafana Labs." description = "High volume, minimal dependency trace storage. OSS tracing solution from Grafana Labs."
keywords = ["grafana", "tempo", "guide", "tracing"] keywords = ["grafana", "tempo", "guide", "tracing"]
aliases = ["/docs/grafana/latest/features/datasources/tempo"] aliases = ["/docs/grafana/latest/features/datasources/tempo"]
weight = 800 weight = 1400
+++ +++
# Tempo data source # Tempo data source
Grafana ships with built-in support for Tempo a high volume, minimal dependency trace storage, OSS tracing solution from Grafana Labs. Add it as a data source, and you are ready to query your traces in [Explore]({{< relref "../explore/index.md" >}}). Grafana ships with built-in support for Tempo a high volume, minimal dependency trace storage, OSS tracing solution from Grafana Labs. Add it as a data source, and you are ready to query your traces in [Explore]({{< relref "../explore/index.md" >}}).
## Adding the data source ## Add data source
To access Tempo settings, click the **Configuration** (gear) icon, then click **Data Sources** > **Tempo**. To access Tempo settings, click the **Configuration** (gear) icon, then click **Data Sources** > **Tempo**.
| Name | Description | | Name | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | ------------ | --------------------------------------------------------------------------------------- |
| _Name_ | The data source name using which you will refer to the data source in panels, queries, and Explore. | | `Name` | The name using which you will refer to the data source in panels, queries, and Explore. |
| _Default_ | The default data source will be pre-selected for new panels. | | `Default` | The default data source will be pre-selected for new panels. |
| _URL_ | The URL of the Tempo instance, e.g., `http://localhost:16686` | | `URL` | The URL of the Tempo instance, e.g., `http://localhost:16686` |
| _Basic Auth_ | Enable basic authentication to the Tempo data source. | | `Basic Auth` | Enable basic authentication to the Tempo data source. |
| _User_ | User name for basic authentication. | | `User` | User name for basic authentication. |
| _Password_ | Password for basic authentication. | | `Password` | Password for basic authentication. |
### Trace to logs
{{< docs-imagebox img="/img/docs/v74/trace-to-logs-settings.png" class="docs-image--no-shadow" caption="Screenshot of the trace to logs settings" >}}
This is a configuration for the [trace to logs feature]({{< relref "../explore/index.md#trace-to-logs" >}}). Select target data source (at this moment limited to Loki data sources) and select which tags will be used in the logs query.
- **Data source -** Target data source.
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
## Query traces ## Query traces
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
title = "TestData" title = "TestData"
keywords = ["grafana", "dashboard", "documentation", "panels", "testdata"] keywords = ["grafana", "dashboard", "documentation", "panels", "testdata"]
aliases = ["/docs/grafana/latest/features/datasources/testdata"] aliases = ["/docs/grafana/latest/features/datasources/testdata"]
weight = 1400 weight = 1500
+++ +++
# Grafana TestData DB # Grafana TestData DB
......
...@@ -3,7 +3,7 @@ title = "Zipkin" ...@@ -3,7 +3,7 @@ title = "Zipkin"
description = "Guide for using Zipkin in Grafana" description = "Guide for using Zipkin in Grafana"
keywords = ["grafana", "zipkin", "guide", "tracing"] keywords = ["grafana", "zipkin", "guide", "tracing"]
aliases = ["/docs/grafana/latest/datasources/zipkin"] aliases = ["/docs/grafana/latest/datasources/zipkin"]
weight = 1500 weight = 1600
+++ +++
# Zipkin data source # Zipkin data source
...@@ -12,17 +12,27 @@ Grafana ships with built-in support for Zipkin, an open source, distributed trac ...@@ -12,17 +12,27 @@ Grafana ships with built-in support for Zipkin, an open source, distributed trac
Just add it as a data source and you are ready to query your traces in [Explore]({{< relref "../explore" >}}). Just add it as a data source and you are ready to query your traces in [Explore]({{< relref "../explore" >}}).
## Adding the data source ## Adding the data source
To access Zipkin settings, click the **Configuration** (gear) icon, then click **Data Sources**, and then click **Zipkin**.
To access Zipkin settings, click the **Configuration** (gear) icon, then click **Data Sources** > **Zipkin**.
| Name | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Name | Description |
| `Name` | The data source name. This is how you refer to the data source in panels, queries, and Explore. | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `Default` | Default data source means that it will be pre-selected for new panels. | | `Name` | The data source name. This is how you refer to the data source in panels, queries, and Explore. |
| `URL` | The URL of the Zipkin instance, e.g., `http://localhost:9411` | | `Default` | Data source will be pre-selected for new panels. |
| `Access` | Server (default) = URL needs to be accessible from the Grafana backend/server, Browser = URL needs to be accessible from the browser. | | `URL` | The URL of the Zipkin instance, e.g., `http://localhost:9411`. |
| `Basic Auth` | Enable basic authentication to the Zipkin data source. | | `Access` | Server (default) = URL needs to be accessible from the Grafana backend/server. Browser = URL needs to be accessible from the browser. |
| `User` | User name for basic authentication. | | `Basic Auth` | Enable basic authentication to the Zipkin data source. |
| `Password` | Password for basic authentication. | | `User` | User name for basic authentication. |
| `Password` | Password for basic authentication. |
### Trace to logs
{{< docs-imagebox img="/img/docs/v74/trace-to-logs-settings.png" class="docs-image--no-shadow" caption="Screenshot of the trace to logs settings" >}}
This is a configuration for the [trace to logs feature]({{< relref "../explore/index.md#trace-to-logs" >}}). Select target data source (at this moment limited to Loki data sources) and select which tags will be used in the logs query.
- **Data source -** Target data source.
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
## Query traces ## Query traces
...@@ -35,6 +45,7 @@ The Zipkin query editor allows you to query by trace ID directly or selecting a ...@@ -35,6 +45,7 @@ The Zipkin query editor allows you to query by trace ID directly or selecting a
{{< docs-imagebox img="/img/docs/v70/zipkin-query-editor-open.png" class="docs-image--no-shadow" caption="Screenshot of the Zipkin query editor with trace selector expanded" >}} {{< docs-imagebox img="/img/docs/v70/zipkin-query-editor-open.png" class="docs-image--no-shadow" caption="Screenshot of the Zipkin query editor with trace selector expanded" >}}
Use the trace selector to pick particular trace from all traces logged in the time range you have selected in Explore. The trace selector has three levels of nesting: Use the trace selector to pick particular trace from all traces logged in the time range you have selected in Explore. The trace selector has three levels of nesting:
1. The service you are interested in. 1. The service you are interested in.
1. Particular operation is part of the selected service 1. Particular operation is part of the selected service
1. Specific trace in which the selected operation occurred, represented by the root operation name and trace duration. 1. Specific trace in which the selected operation occurred, represented by the root operation name and trace duration.
......
...@@ -258,6 +258,8 @@ You can visualize traces from tracing data sources in explore. Data sources curr ...@@ -258,6 +258,8 @@ You can visualize traces from tracing data sources in explore. Data sources curr
- [Jaeger]({{< relref "../datasources/jaeger.md" >}}) - [Jaeger]({{< relref "../datasources/jaeger.md" >}})
- [Zipkin]({{< relref "../datasources/zipkin.md" >}}) - [Zipkin]({{< relref "../datasources/zipkin.md" >}})
- [Tempo]({{< relref "../datasources/tempo.md" >}})
- [X-Ray](https://grafana.com/grafana/plugins/grafana-x-ray-datasource)
For information about how to use the query editor see documentation for specific data source. For information about how to use the query editor see documentation for specific data source.
...@@ -300,10 +302,19 @@ Clicking anywhere on the span row shows span details. ...@@ -300,10 +302,19 @@ Clicking anywhere on the span row shows span details.
- Process metadata: Metadata about the process that logged this span. - Process metadata: Metadata about the process that logged this span.
- Logs: List of logs logged by this span and associated key values. In case of Zipkin logs section shows Zipkin annotations. - Logs: List of logs logged by this span and associated key values. In case of Zipkin logs section shows Zipkin annotations.
## Navigating between Explore and a dashboard ##### Trace to logs
To help accelerate workflows that involve regularly switching from Explore to a dashboard and vice-versa, we've added the ability to return to the origin dashboard > This feature is only available in Grafana 7.4+.
after navigating to Explore from the panel's dropdown.
You can navigate from a span in a trace view directly to logs relevant for that span. This is available for Tempo, Jaeger and Zipkin data source at this moment. See their relevant documentation for instruction how to configure this feature.
{{< docs-imagebox img="/img/docs/v74/trace-to-logs.png" class="docs-image--no-shadow" caption="Screenshot of the trace view in Explore with new icon next to the spans" >}}
Click the document icon to open a split view in Explore with the configured data source and query relevant logs for the span.
## Navigate between Explore and a dashboard
To help accelerate workflows that involve regularly switching from Explore to a dashboard and vice-versa, we've added the ability to return to the origin dashboard after navigating to Explore from the panel's dropdown.
{{< docs-imagebox img="/img/docs/v64/panel_dropdown.png" class="docs-image--no-shadow" caption="Screenshot of the panel dropdown" >}} {{< docs-imagebox img="/img/docs/v64/panel_dropdown.png" class="docs-image--no-shadow" caption="Screenshot of the panel dropdown" >}}
......
...@@ -35,8 +35,6 @@ export interface FeatureToggles { ...@@ -35,8 +35,6 @@ export interface FeatureToggles {
live: boolean; live: boolean;
expressions: boolean; expressions: boolean;
ngalert: boolean; ngalert: boolean;
// Just for demo at the moment
traceToLogs: boolean;
panelLibrary: boolean; panelLibrary: boolean;
/** /**
......
...@@ -56,7 +56,6 @@ export class GrafanaBootConfig implements GrafanaConfig { ...@@ -56,7 +56,6 @@ export class GrafanaBootConfig implements GrafanaConfig {
expressions: false, expressions: false,
meta: false, meta: false,
ngalert: false, ngalert: false,
traceToLogs: false,
panelLibrary: false, panelLibrary: false,
}; };
licenseInfo: LicenseInfo = {} as LicenseInfo; licenseInfo: LicenseInfo = {} as LicenseInfo;
......
import {
DataSourceJsonData,
DataSourcePluginOptionsEditorProps,
GrafanaTheme,
updateDatasourcePluginJsonDataOption,
} from '@grafana/data';
import { InlineFormLabel, TagsInput, useStyles } from '@grafana/ui';
import { css } from 'emotion';
import React from 'react';
import { DataSourcePicker } from './Select/DataSourcePicker';
export interface TraceToLogsOptions {
datasourceUid?: string;
tags?: string[];
}
export interface TraceToLogsData extends DataSourceJsonData {
tracesToLogs?: TraceToLogsOptions;
}
interface Props extends DataSourcePluginOptionsEditorProps<TraceToLogsData> {}
export function TraceToLogsSettings({ options, onOptionsChange }: Props) {
const styles = useStyles(getStyles);
return (
<>
<h3 className="page-heading">Trace to logs</h3>
<div className={styles.infoText}>
Trace to logs let&apos;s you navigate from a trace span to the selected data source&apos;s log.
</div>
<div className="gf-form">
<InlineFormLabel tooltip="The data source the trace is going to navigate to">Data source</InlineFormLabel>
<DataSourcePicker
pluginId="loki"
current={options.jsonData.tracesToLogs?.datasourceUid}
noDefault={true}
onChange={ds =>
updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', {
datasourceUid: ds.uid,
tags: options.jsonData.tracesToLogs?.tags,
})
}
/>
</div>
<div className="gf-form">
<InlineFormLabel tooltip="Tags that will be used in the Loki query. Default tags: 'cluster', 'hostname', 'namespace', 'pod'">
Tags
</InlineFormLabel>
<TagsInput
tags={options.jsonData.tracesToLogs?.tags}
onChange={tags =>
updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', {
datasourceUid: options.jsonData.tracesToLogs?.datasourceUid,
tags: tags,
})
}
/>
</div>
</>
);
}
const getStyles = (theme: GrafanaTheme) => ({
infoText: css`
padding-bottom: ${theme.spacing.md};
color: ${theme.colors.textSemiWeak};
`,
});
...@@ -4,6 +4,11 @@ import { render } from '@testing-library/react'; ...@@ -4,6 +4,11 @@ import { render } from '@testing-library/react';
import { TraceView } from './TraceView'; import { TraceView } from './TraceView';
import { TracePageHeader, TraceTimelineViewer } from '@jaegertracing/jaeger-ui-components'; import { TracePageHeader, TraceTimelineViewer } from '@jaegertracing/jaeger-ui-components';
import { TraceSpanData, TraceData } from '@grafana/data'; import { TraceSpanData, TraceData } from '@grafana/data';
import { setDataSourceSrv } from '@grafana/runtime';
jest.mock('react-redux', () => ({
useSelector: jest.fn(() => undefined),
}));
function renderTraceView() { function renderTraceView() {
const wrapper = shallow(<TraceView trace={response} splitOpenFn={() => {}} />); const wrapper = shallow(<TraceView trace={response} splitOpenFn={() => {}} />);
...@@ -15,6 +20,14 @@ function renderTraceView() { ...@@ -15,6 +20,14 @@ function renderTraceView() {
} }
describe('TraceView', () => { describe('TraceView', () => {
beforeAll(() => {
setDataSourceSrv({
getInstanceSettings() {
return undefined;
},
} as any);
});
it('renders TraceTimelineViewer', () => { it('renders TraceTimelineViewer', () => {
const { timeline, header } = renderTraceView(); const { timeline, header } = renderTraceView();
expect(timeline).toHaveLength(1); expect(timeline).toHaveLength(1);
......
...@@ -18,6 +18,10 @@ import { useHoverIndentGuide } from './useHoverIndentGuide'; ...@@ -18,6 +18,10 @@ import { useHoverIndentGuide } from './useHoverIndentGuide';
import { colors, useTheme } from '@grafana/ui'; import { colors, useTheme } from '@grafana/ui';
import { TraceViewData, Trace, TraceSpan, TraceKeyValuePair, TraceLink } from '@grafana/data'; import { TraceViewData, Trace, TraceSpan, TraceKeyValuePair, TraceLink } from '@grafana/data';
import { createSpanLinkFactory } from './createSpanLink'; import { createSpanLinkFactory } from './createSpanLink';
import { useSelector } from 'react-redux';
import { StoreState } from 'app/types';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { TraceToLogsData } from 'app/core/components/TraceToLogsSettings';
type Props = { type Props = {
trace?: TraceViewData; trace?: TraceViewData;
...@@ -54,6 +58,9 @@ export function TraceView(props: Props) { ...@@ -54,6 +58,9 @@ export function TraceView(props: Props) {
const traceProp = useMemo(() => transformTraceData(props.trace), [props.trace]); const traceProp = useMemo(() => transformTraceData(props.trace), [props.trace]);
const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans); const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans);
const dataSourceName = useSelector((state: StoreState) => state.explore.left.datasourceInstance?.name);
const traceToLogsOptions = (getDatasourceSrv().getInstanceSettings(dataSourceName)?.jsonData as TraceToLogsData)
?.tracesToLogs;
const theme = useTheme(); const theme = useTheme();
const traceTheme = useMemo( const traceTheme = useMemo(
...@@ -82,7 +89,10 @@ export function TraceView(props: Props) { ...@@ -82,7 +89,10 @@ export function TraceView(props: Props) {
[childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp?.traceID] [childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp?.traceID]
); );
const createSpanLink = useMemo(() => createSpanLinkFactory(props.splitOpenFn), [props.splitOpenFn]); const createSpanLink = useMemo(() => createSpanLinkFactory(props.splitOpenFn, traceToLogsOptions), [
props.splitOpenFn,
traceToLogsOptions,
]);
const scrollElement = document.getElementsByClassName('scroll-canvas')[0]; const scrollElement = document.getElementsByClassName('scroll-canvas')[0];
if (!traceProp) { if (!traceProp) {
......
import { createSpanLinkFactory } from './createSpanLink';
import { config, setDataSourceSrv, setTemplateSrv } from '@grafana/runtime';
import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data';
import { setDataSourceSrv, setTemplateSrv } from '@grafana/runtime';
import { createSpanLinkFactory } from './createSpanLink';
describe('createSpanLinkFactory', () => { describe('createSpanLinkFactory', () => {
beforeAll(() => { it('returns undefined if there is no data source uid', () => {
config.featureToggles.traceToLogs = true;
});
afterAll(() => {
config.featureToggles.traceToLogs = false;
});
it('returns undefined if there is no loki data source', () => {
setDataSourceSrv({
getList() {
return [];
},
} as any);
const splitOpenFn = jest.fn(); const splitOpenFn = jest.fn();
const createLink = createSpanLinkFactory(splitOpenFn); const createLink = createSpanLinkFactory(splitOpenFn);
expect(createLink).not.toBeDefined(); expect(createLink).not.toBeDefined();
}); });
it('creates correct link', () => { describe('should return link', () => {
setDataSourceSrv({ beforeAll(() => {
getList() { setDataSourceSrv({
return [ getInstanceSettings(uid: string): DataSourceInstanceSettings | undefined {
{
name: 'loki1',
uid: 'lokiUid',
meta: {
id: 'loki',
},
} as DataSourceInstanceSettings,
];
},
getInstanceSettings(uid: string): DataSourceInstanceSettings | undefined {
if (uid === 'lokiUid') {
return { return {
uid: 'loki1',
name: 'loki1', name: 'loki1',
} as any; } as any;
} },
return undefined; } as any);
},
} as any);
setTemplateSrv({ setTemplateSrv({
replace(target?: string, scopedVars?: ScopedVars, format?: string | Function): string { replace(target?: string, scopedVars?: ScopedVars, format?: string | Function): string {
return target!; return target!;
}, },
} as any); } as any);
});
const splitOpenFn = jest.fn(); it('with default keys when tags not configured', () => {
const createLink = createSpanLinkFactory(splitOpenFn); const splitOpenFn = jest.fn();
expect(createLink).toBeDefined(); const createLink = createSpanLinkFactory(splitOpenFn, { datasourceUid: 'lokiUid' });
const linkDef = createLink!({ expect(createLink).toBeDefined();
startTime: new Date('2020-10-14T01:00:00Z').valueOf() * 1000, const linkDef = createLink!({
duration: 1000 * 1000, startTime: new Date('2020-10-14T01:00:00Z').valueOf() * 1000,
process: { duration: 1000 * 1000,
tags: [ tags: [
{ {
key: 'cluster', key: 'host',
value: 'cluster1', value: 'host',
}, },
],
process: {
tags: [
{
key: 'cluster',
value: 'cluster1',
},
{
key: 'hostname',
value: 'hostname1',
},
{
key: 'label2',
value: 'val2',
},
],
} as any,
} as any);
expect(linkDef.href).toBe(
`/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}]}`
);
});
it('with tags that passed in and without tags that are not in the span', () => {
const splitOpenFn = jest.fn();
const createLink = createSpanLinkFactory(splitOpenFn, { datasourceUid: 'lokiUid', tags: ['ip', 'newTag'] });
expect(createLink).toBeDefined();
const linkDef = createLink!({
startTime: new Date('2020-10-14T01:00:00Z').valueOf() * 1000,
duration: 1000 * 1000,
tags: [
{ {
key: 'hostname', key: 'host',
value: 'hostname1', value: 'host',
}, },
],
process: {
tags: [
{
key: 'hostname',
value: 'hostname1',
},
{
key: 'ip',
value: '192.168.0.1',
},
],
} as any,
} as any);
expect(linkDef.href).toBe(
`/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}]}`
);
});
it('from tags and process tags as well', () => {
const splitOpenFn = jest.fn();
const createLink = createSpanLinkFactory(splitOpenFn, {
datasourceUid: 'lokiUid',
tags: ['ip', 'host'],
});
expect(createLink).toBeDefined();
const linkDef = createLink!({
startTime: new Date('2020-10-14T01:00:00Z').valueOf() * 1000,
duration: 1000 * 1000,
tags: [
{ {
key: 'label2', key: 'host',
value: 'val2', value: 'host',
}, },
], ],
} as any, process: {
} as any); tags: [
{
key: 'hostname',
value: 'hostname1',
},
{
key: 'ip',
value: '192.168.0.1',
},
],
} as any,
} as any);
expect(linkDef.href).toBe( expect(linkDef.href).toBe(
`/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}]}` `/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}]}`
); );
});
}); });
}); });
import React from 'react';
import { config, getDataSourceSrv, getTemplateSrv } from '@grafana/runtime';
import { DataLink, dateTime, Field, mapInternalLinkToExplore, TimeRange, TraceSpan } from '@grafana/data'; import { DataLink, dateTime, Field, mapInternalLinkToExplore, TimeRange, TraceSpan } from '@grafana/data';
import { LokiQuery } from '../../../plugins/datasource/loki/types'; import { getTemplateSrv } from '@grafana/runtime';
import { Icon } from '@grafana/ui'; import { Icon } from '@grafana/ui';
import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import React from 'react';
import { LokiQuery } from '../../../plugins/datasource/loki/types';
/** /**
* This is a factory for the link creator. It returns the function mainly so it can return undefined in which case * This is a factory for the link creator. It returns the function mainly so it can return undefined in which case
* the trace view won't create any links and to capture the datasource and split function making it easier to memoize * the trace view won't create any links and to capture the datasource and split function making it easier to memoize
* with useMemo. * with useMemo.
*/ */
export function createSpanLinkFactory(splitOpenFn: (options: { datasourceUid: string; query: any }) => void) { export function createSpanLinkFactory(
if (!config.featureToggles.traceToLogs) { splitOpenFn: (options: { datasourceUid: string; query: any }) => void,
traceToLogsOptions?: TraceToLogsOptions
) {
// We should return if dataSourceUid is undefined otherwise getInstanceSettings would return testDataSource.
if (!traceToLogsOptions?.datasourceUid) {
return undefined; return undefined;
} }
// Right now just hardcoded for first loki DS we can find const dataSourceSettings = getDatasourceSrv().getInstanceSettings(traceToLogsOptions.datasourceUid);
const lokiDs = getDataSourceSrv().getList({ pluginId: 'loki' })[0];
if (!lokiDs) { if (!dataSourceSettings) {
return undefined; return undefined;
} }
...@@ -28,13 +33,13 @@ export function createSpanLinkFactory(splitOpenFn: (options: { datasourceUid: st ...@@ -28,13 +33,13 @@ export function createSpanLinkFactory(splitOpenFn: (options: { datasourceUid: st
// it manually here instead of leaving it for the data source to supply the config. // it manually here instead of leaving it for the data source to supply the config.
const dataLink: DataLink<LokiQuery> = { const dataLink: DataLink<LokiQuery> = {
title: lokiDs.name, title: dataSourceSettings.name,
url: '', url: '',
internal: { internal: {
datasourceUid: lokiDs.uid, datasourceUid: dataSourceSettings.uid,
datasourceName: lokiDs.name, datasourceName: dataSourceSettings.name,
query: { query: {
expr: getLokiQueryFromSpan(span), expr: getLokiQueryFromSpan(span, traceToLogsOptions.tags),
refId: '', refId: '',
}, },
}, },
...@@ -59,13 +64,14 @@ export function createSpanLinkFactory(splitOpenFn: (options: { datasourceUid: st ...@@ -59,13 +64,14 @@ export function createSpanLinkFactory(splitOpenFn: (options: { datasourceUid: st
} }
/** /**
* Right now this is just hardcoded and later will probably be part of some user configuration. * Default keys to use when there are no configured tags.
*/ */
const allowedKeys = ['cluster', 'hostname', 'namespace', 'pod']; const defaultKeys = ['cluster', 'hostname', 'namespace', 'pod'];
function getLokiQueryFromSpan(span: TraceSpan): string { function getLokiQueryFromSpan(span: TraceSpan, keys?: string[]): string {
const tags = span.process.tags.reduce((acc, tag) => { const keysToCheck = keys?.length ? keys : defaultKeys;
if (allowedKeys.includes(tag.key)) { const tags = [...span.process.tags, ...span.tags].reduce((acc, tag) => {
if (keysToCheck.includes(tag.key)) {
acc.push(`${tag.key}="${tag.value}"`); acc.push(`${tag.key}="${tag.value}"`);
} }
return acc; return acc;
......
import React from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { DataSourceHttpSettings } from '@grafana/ui'; import { DataSourceHttpSettings } from '@grafana/ui';
import { TraceToLogsSettings } from 'app/core/components/TraceToLogsSettings';
import React from 'react';
export type Props = DataSourcePluginOptionsEditorProps; export type Props = DataSourcePluginOptionsEditorProps;
...@@ -8,11 +9,13 @@ export const ConfigEditor: React.FC<Props> = ({ options, onOptionsChange }) => { ...@@ -8,11 +9,13 @@ export const ConfigEditor: React.FC<Props> = ({ options, onOptionsChange }) => {
return ( return (
<> <>
<DataSourceHttpSettings <DataSourceHttpSettings
defaultUrl={'http://localhost:16686'} defaultUrl="http://localhost:16686"
dataSourceConfig={options} dataSourceConfig={options}
showAccessOptions={true} showAccessOptions={true}
onChange={onOptionsChange} onChange={onOptionsChange}
/> />
<TraceToLogsSettings options={options} onOptionsChange={onOptionsChange} />
</> </>
); );
}; };
import React from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { DataSourceHttpSettings } from '@grafana/ui'; import { DataSourceHttpSettings } from '@grafana/ui';
import { TraceToLogsSettings } from 'app/core/components/TraceToLogsSettings';
import React from 'react';
export type Props = DataSourcePluginOptionsEditorProps; export type Props = DataSourcePluginOptionsEditorProps;
export const ConfigEditor: React.FC<Props> = ({ options, onOptionsChange }) => { export const ConfigEditor: React.FC<Props> = ({ options, onOptionsChange }) => {
return ( return (
<DataSourceHttpSettings <>
defaultUrl="http://localhost:16686" <DataSourceHttpSettings
dataSourceConfig={options} defaultUrl="http://localhost:16686"
showAccessOptions={false} dataSourceConfig={options}
onChange={onOptionsChange} showAccessOptions={false}
/> onChange={onOptionsChange}
/>
<TraceToLogsSettings options={options} onOptionsChange={onOptionsChange} />
</>
); );
}; };
import React from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { DataSourceHttpSettings } from '@grafana/ui'; import { DataSourceHttpSettings } from '@grafana/ui';
import { TraceToLogsSettings } from 'app/core/components/TraceToLogsSettings';
import React from 'react';
export type Props = DataSourcePluginOptionsEditorProps; export type Props = DataSourcePluginOptionsEditorProps;
export const ConfigEditor: React.FC<Props> = ({ options, onOptionsChange }) => { export const ConfigEditor: React.FC<Props> = ({ options, onOptionsChange }) => {
return ( return (
<DataSourceHttpSettings <>
defaultUrl={'http://localhost:9411'} <DataSourceHttpSettings
dataSourceConfig={options} defaultUrl="http://localhost:9411"
showAccessOptions={true} dataSourceConfig={options}
onChange={onOptionsChange} showAccessOptions={true}
/> onChange={onOptionsChange}
/>
<TraceToLogsSettings options={options} onOptionsChange={onOptionsChange} />
</>
); );
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment