diff --git a/docs/sources/datasources/jaeger.md b/docs/sources/datasources/jaeger.md
index a06984415eb..98920398b4a 100644
--- a/docs/sources/datasources/jaeger.md
+++ b/docs/sources/datasources/jaeger.md
@@ -32,8 +32,10 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t
- **Data source -** Target data source.
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
+- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
+- **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
-![Trace to logs settings](/static/img/docs/explore/trace-to-logs-settings-7-4.png 'Screenshot of the trace to logs settings')
+![Trace to logs settings](/static/img/docs/explore/trace-to-logs-settings-8.png 'Screenshot of the trace to logs settings')
## Query traces
diff --git a/docs/sources/datasources/tempo.md b/docs/sources/datasources/tempo.md
index 83946b7172e..fcbad90fefe 100644
--- a/docs/sources/datasources/tempo.md
+++ b/docs/sources/datasources/tempo.md
@@ -31,8 +31,10 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t
- **Data source -** Target data source.
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
+- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
+- **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
-![Trace to logs settings](/static/img/docs/explore/trace-to-logs-settings-7-4.png 'Screenshot of the trace to logs settings')
+![Trace to logs settings](/static/img/docs/explore/trace-to-logs-settings-8.png 'Screenshot of the trace to logs settings')
## Query traces
diff --git a/docs/sources/datasources/zipkin.md b/docs/sources/datasources/zipkin.md
index 61b078a6175..6edb7208000 100644
--- a/docs/sources/datasources/zipkin.md
+++ b/docs/sources/datasources/zipkin.md
@@ -32,8 +32,10 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t
- **Data source -** Target data source.
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
+- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
+- **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
-![Trace to logs settings](/static/img/docs/explore/trace-to-logs-settings-7-4.png "Screenshot of the trace to logs settings")
+![Trace to logs settings](/static/img/docs/explore/trace-to-logs-settings-8.png 'Screenshot of the trace to logs settings')
## Query traces
diff --git a/packages/grafana-data/src/datetime/rangeutil.test.ts b/packages/grafana-data/src/datetime/rangeutil.test.ts
index 18c0bbd9a75..9d3980fb35a 100644
--- a/packages/grafana-data/src/datetime/rangeutil.test.ts
+++ b/packages/grafana-data/src/datetime/rangeutil.test.ts
@@ -41,6 +41,14 @@ describe('Range Utils', () => {
expect(() => rangeUtil.describeInterval('123xyz')).toThrow();
expect(() => rangeUtil.describeInterval('xyz')).toThrow();
});
+
+ it('should be able to parse negative values as well', () => {
+ expect(rangeUtil.describeInterval('-50ms')).toEqual({
+ sec: 0.001,
+ type: 'ms',
+ count: -50,
+ });
+ });
});
describe('relativeToTimeRange', () => {
diff --git a/packages/grafana-data/src/datetime/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts
index 254ae80e6dd..15b52d443cb 100644
--- a/packages/grafana-data/src/datetime/rangeutil.ts
+++ b/packages/grafana-data/src/datetime/rangeutil.ts
@@ -276,7 +276,7 @@ export function calculateInterval(range: TimeRange, resolution: number, lowLimit
};
}
-const interval_regex = /(\d+(?:\.\d+)?)(ms|[Mwdhmsy])/;
+const interval_regex = /(-?\d+(?:\.\d+)?)(ms|[Mwdhmsy])/;
// histogram & trends
const intervals_in_seconds = {
y: 31536000,
diff --git a/public/app/core/components/TraceToLogsSettings.tsx b/public/app/core/components/TraceToLogsSettings.tsx
index dc1c35a1ad9..742cd8e994f 100644
--- a/public/app/core/components/TraceToLogsSettings.tsx
+++ b/public/app/core/components/TraceToLogsSettings.tsx
@@ -6,12 +6,14 @@ import {
updateDatasourcePluginJsonDataOption,
} from '@grafana/data';
import { DataSourcePicker } from '@grafana/runtime';
-import { InlineField, InlineFieldRow, TagsInput, useStyles } from '@grafana/ui';
+import { InlineField, InlineFieldRow, Input, TagsInput, useStyles } from '@grafana/ui';
import React from 'react';
export interface TraceToLogsOptions {
datasourceUid?: string;
tags?: string[];
+ spanStartTimeShift?: string;
+ spanEndTimeShift?: string;
}
export interface TraceToLogsData extends DataSourceJsonData {
@@ -66,6 +68,50 @@ export function TraceToLogsSettings({ options, onOptionsChange }: Props) {
/>
+
+
+
+
+ updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', {
+ ...options.jsonData.tracesToLogs,
+ spanStartTimeShift: v.currentTarget.value,
+ })
+ }
+ value={options.jsonData.tracesToLogs?.spanStartTimeShift || ''}
+ />
+
+
+
+
+
+
+ updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', {
+ ...options.jsonData.tracesToLogs,
+ spanEndTimeShift: v.currentTarget.value,
+ })
+ }
+ value={options.jsonData.tracesToLogs?.spanEndTimeShift || ''}
+ />
+
+
);
}
diff --git a/public/app/features/explore/TraceView/createSpanLink.test.ts b/public/app/features/explore/TraceView/createSpanLink.test.ts
index 2374e252861..e39f36148d6 100644
--- a/public/app/features/explore/TraceView/createSpanLink.test.ts
+++ b/public/app/features/explore/TraceView/createSpanLink.test.ts
@@ -59,7 +59,7 @@ describe('createSpanLinkFactory', () => {
} as any);
expect(linkDef.href).toBe(
- `/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}]}`
+ `/explore?left={"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}]}`
);
});
@@ -91,7 +91,7 @@ describe('createSpanLinkFactory', () => {
} as any);
expect(linkDef.href).toBe(
- `/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}]}`
+ `/explore?left={"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}]}`
);
});
@@ -126,7 +126,44 @@ describe('createSpanLinkFactory', () => {
} as any);
expect(linkDef.href).toBe(
- `/explore?left={"range":{"from":"20201014T000000","to":"20201014T010006"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}]}`
+ `/explore?left={"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}]}`
+ );
+ });
+
+ it('with adjusted start and end time', () => {
+ const splitOpenFn = jest.fn();
+ const createLink = createSpanLinkFactory(splitOpenFn, {
+ datasourceUid: 'lokiUid',
+ spanStartTimeShift: '1m',
+ spanEndTimeShift: '1m',
+ });
+
+ expect(createLink).toBeDefined();
+ const linkDef = createLink!({
+ startTime: new Date('2020-10-14T01:00:00Z').valueOf() * 1000,
+ duration: 1000 * 1000,
+ tags: [
+ {
+ key: 'host',
+ 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":"2020-10-14T01:01:00.000Z","to":"2020-10-14T01:01:01.000Z"},"datasource":"loki1","queries":[{"expr":"{hostname=\\"hostname1\\"}","refId":""}]}`
);
});
});
diff --git a/public/app/features/explore/TraceView/createSpanLink.tsx b/public/app/features/explore/TraceView/createSpanLink.tsx
index 882f029dc02..185a263d3d3 100644
--- a/public/app/features/explore/TraceView/createSpanLink.tsx
+++ b/public/app/features/explore/TraceView/createSpanLink.tsx
@@ -1,12 +1,12 @@
-import { DataLink, dateTime, Field, mapInternalLinkToExplore, TimeRange } from '@grafana/data';
+import { DataLink, dateTime, Field, mapInternalLinkToExplore, rangeUtil, TimeRange } from '@grafana/data';
import { getTemplateSrv } from '@grafana/runtime';
import { Icon } from '@grafana/ui';
-import { SplitOpen } from 'app/types/explore';
+import { TraceSpan } from '@jaegertracing/jaeger-ui-components';
import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
+import { SplitOpen } from 'app/types/explore';
import React from 'react';
import { LokiQuery } from '../../../plugins/datasource/loki/types';
-import { TraceSpan } from '@jaegertracing/jaeger-ui-components';
/**
* This is a factory for the link creator. It returns the function mainly so it can return undefined in which case
@@ -48,7 +48,7 @@ export function createSpanLinkFactory(splitOpenFn: SplitOpen, traceToLogsOptions
link: dataLink,
internalLink: dataLink.internal!,
scopedVars: {},
- range: getTimeRangeFromSpan(span),
+ range: getTimeRangeFromSpan(span, traceToLogsOptions),
field: {} as Field,
onClickFn: splitOpenFn,
replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()),
@@ -79,25 +79,32 @@ function getLokiQueryFromSpan(span: TraceSpan, keys?: string[]): string {
}
/**
- * Gets a time range from the span. Naively this could be just start and end time of the span but we also want some
- * buffer around that just so we do not miss some logs which may not have timestamps aligned with the span. Right
- * now the buffers are hardcoded which may be a bit weird for very short spans but at the same time, fractional buffers
- * with very short spans could mean microseconds and that could miss some logs relevant to that spans. In the future
- * something more intelligent should probably be implemented
+ * Gets a time range from the span.
*/
-function getTimeRangeFromSpan(span: TraceSpan): TimeRange {
- const from = dateTime(span.startTime / 1000 - 1000 * 60 * 60);
+function getTimeRangeFromSpan(span: TraceSpan, traceToLogsOptions?: TraceToLogsOptions): TimeRange {
+ const adjustedStartTime = traceToLogsOptions?.spanStartTimeShift
+ ? Math.floor(span.startTime / 1000 + rangeUtil.intervalToMs(traceToLogsOptions.spanStartTimeShift))
+ : Math.floor(span.startTime / 1000);
+ const from = dateTime(adjustedStartTime);
const spanEndMs = (span.startTime + span.duration) / 1000;
- const to = dateTime(spanEndMs + 5 * 1000);
+ let adjustedEndTime = traceToLogsOptions?.spanEndTimeShift
+ ? Math.floor(spanEndMs + rangeUtil.intervalToMs(traceToLogsOptions.spanEndTimeShift))
+ : Math.floor(spanEndMs);
+ // Because we can only pass milliseconds in the url we need to check if they equal.
+ // We need end time to be later than start time
+ if (adjustedStartTime === adjustedEndTime) {
+ adjustedEndTime++;
+ }
+ const to = dateTime(adjustedEndTime);
+
+ // Beware that public/app/features/explore/state/main.ts SplitOpen fn uses the range from here. No matter what is in the url.
return {
from,
to,
- // Weirdly Explore does not handle ISO string which would have been the default stringification if passed as object
- // and we have to use this custom format :( .
raw: {
- from: from.utc().format('YYYYMMDDTHHmmss'),
- to: to.utc().format('YYYYMMDDTHHmmss'),
+ from,
+ to,
},
};
}
diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts
index 5e6e236e456..8c4be28a7a9 100644
--- a/public/app/plugins/datasource/loki/datasource.ts
+++ b/public/app/plugins/datasource/loki/datasource.ts
@@ -184,14 +184,10 @@ export class LokiDatasource extends DataSourceApi {
this.adjustInterval((options as DataQueryRequest).intervalMs || 1000, rangeMs) / 1000;
// We want to ceil to 3 decimal places
const step = Math.ceil(adjustedInterval * 1000) / 1000;
- const alignedTimes = {
- start: startNs - (startNs % 1e9),
- end: endNs + (1e9 - (endNs % 1e9)),
- };
range = {
- start: alignedTimes.start,
- end: alignedTimes.end,
+ start: startNs,
+ end: endNs,
step,
};
}