mirror of https://github.com/grafana/grafana.git
LogsView + TraceView: Add time range to resource attributes extension range (#111171)
This commit is contained in:
parent
3cc2fb3728
commit
2669e0a770
|
@ -6,7 +6,7 @@ import { ScopedVars } from './ScopedVars';
|
||||||
import { DataSourcePluginMeta, DataSourceSettings } from './datasource';
|
import { DataSourcePluginMeta, DataSourceSettings } from './datasource';
|
||||||
import { IconName } from './icon';
|
import { IconName } from './icon';
|
||||||
import { PanelData } from './panel';
|
import { PanelData } from './panel';
|
||||||
import { RawTimeRange, TimeZone } from './time';
|
import { AbsoluteTimeRange, RawTimeRange, TimeZone } from './time';
|
||||||
|
|
||||||
// Plugin Extensions types
|
// Plugin Extensions types
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
@ -273,6 +273,7 @@ export type PluginExtensionResourceAttributesContext = {
|
||||||
// Key-value pairs of resource attributes, attribute name is the key
|
// Key-value pairs of resource attributes, attribute name is the key
|
||||||
attributes: Record<string, string[]>;
|
attributes: Record<string, string[]>;
|
||||||
spanAttributes?: Record<string, string[]>;
|
spanAttributes?: Record<string, string[]>;
|
||||||
|
timeRange: AbsoluteTimeRange;
|
||||||
datasource: {
|
datasource: {
|
||||||
type: string;
|
type: string;
|
||||||
uid: string;
|
uid: string;
|
||||||
|
|
|
@ -1056,6 +1056,7 @@ const UnthemedLogs: React.FunctionComponent<Props> = (props: Props) => {
|
||||||
logOptionsStorageKey={SETTING_KEY_ROOT}
|
logOptionsStorageKey={SETTING_KEY_ROOT}
|
||||||
onLogOptionsChange={onLogOptionsChange}
|
onLogOptionsChange={onLogOptionsChange}
|
||||||
filterLevels={filterLevels}
|
filterLevels={filterLevels}
|
||||||
|
timeRange={props.range}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1113,6 +1114,7 @@ const UnthemedLogs: React.FunctionComponent<Props> = (props: Props) => {
|
||||||
onPinLine={onPinToContentOutlineClick}
|
onPinLine={onPinToContentOutlineClick}
|
||||||
pinLineButtonTooltipTitle={pinLineButtonTooltipTitle}
|
pinLineButtonTooltipTitle={pinLineButtonTooltipTitle}
|
||||||
renderPreview
|
renderPreview
|
||||||
|
timeRange={props.range}
|
||||||
/>
|
/>
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -138,6 +138,7 @@ export function LogsSamplePanel(props: Props) {
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
enableLogDetails
|
enableLogDetails
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={props.timeRange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ import userEvent from '@testing-library/user-event';
|
||||||
import { createRef } from 'react';
|
import { createRef } from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
import { DataFrame, MutableDataFrame, TimeRange } from '@grafana/data';
|
import { DataFrame, MutableDataFrame } from '@grafana/data';
|
||||||
|
import { mockTimeRange } from '@grafana/plugin-ui';
|
||||||
import { DataSourceSrv, setDataSourceSrv, setPluginLinksHook, setPluginComponentsHook } from '@grafana/runtime';
|
import { DataSourceSrv, setDataSourceSrv, setPluginLinksHook, setPluginComponentsHook } from '@grafana/runtime';
|
||||||
|
|
||||||
import { configureStore } from '../../../store/configureStore';
|
import { configureStore } from '../../../store/configureStore';
|
||||||
|
@ -24,7 +25,7 @@ function getTraceView(frames: DataFrame[]) {
|
||||||
traceProp={transformDataFrames(frames[0])!}
|
traceProp={transformDataFrames(frames[0])!}
|
||||||
datasource={undefined}
|
datasource={undefined}
|
||||||
topOfViewRef={topOfViewRef}
|
topOfViewRef={topOfViewRef}
|
||||||
timeRange={{} as TimeRange}
|
timeRange={mockTimeRange()}
|
||||||
/>
|
/>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,7 +17,7 @@ jest.mock('../utils');
|
||||||
import { act, render, screen } from '@testing-library/react';
|
import { act, render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import { createDataFrame, DataSourceInstanceSettings } from '@grafana/data';
|
import { createDataFrame, DataSourceInstanceSettings, dateTime } from '@grafana/data';
|
||||||
import { data } from '@grafana/flamegraph';
|
import { data } from '@grafana/flamegraph';
|
||||||
import { DataSourceSrv, setDataSourceSrv, setPluginLinksHook } from '@grafana/runtime';
|
import { DataSourceSrv, setDataSourceSrv, setPluginLinksHook } from '@grafana/runtime';
|
||||||
|
|
||||||
|
@ -71,6 +71,8 @@ describe('<SpanDetail>', () => {
|
||||||
traceFlameGraphs: { [span.spanID]: createDataFrame(data) },
|
traceFlameGraphs: { [span.spanID]: createDataFrame(data) },
|
||||||
setRedrawListView: jest.fn(),
|
setRedrawListView: jest.fn(),
|
||||||
timeRange: {
|
timeRange: {
|
||||||
|
from: dateTime(0),
|
||||||
|
to: dateTime(1000000000000),
|
||||||
raw: {
|
raw: {
|
||||||
from: 0,
|
from: 0,
|
||||||
to: 1000000000000,
|
to: 1000000000000,
|
||||||
|
@ -273,6 +275,10 @@ describe('<SpanDetail>', () => {
|
||||||
attributes: expect.objectContaining({
|
attributes: expect.objectContaining({
|
||||||
'http.url': expect.arrayContaining([expect.any(String)]),
|
'http.url': expect.arrayContaining([expect.any(String)]),
|
||||||
}),
|
}),
|
||||||
|
timeRange: {
|
||||||
|
from: 0,
|
||||||
|
to: 1000000000000,
|
||||||
|
},
|
||||||
datasource: {
|
datasource: {
|
||||||
type: 'tempo',
|
type: 'tempo',
|
||||||
uid: 'grafanacloud-traces',
|
uid: 'grafanacloud-traces',
|
||||||
|
|
|
@ -51,12 +51,19 @@ import { ShareSpanButton } from './ShareSpanButton';
|
||||||
import { getSpanDetailLinkButtons } from './SpanDetailLinkButtons';
|
import { getSpanDetailLinkButtons } from './SpanDetailLinkButtons';
|
||||||
import SpanFlameGraph from './SpanFlameGraph';
|
import SpanFlameGraph from './SpanFlameGraph';
|
||||||
|
|
||||||
const useResourceAttributesExtensionLinks = (
|
const useResourceAttributesExtensionLinks = ({
|
||||||
process: TraceProcess,
|
process,
|
||||||
spanTags: TraceKeyValuePair[],
|
spanTags,
|
||||||
datasourceType: string,
|
datasourceType,
|
||||||
datasourceUid: string
|
datasourceUid,
|
||||||
) => {
|
timeRange,
|
||||||
|
}: {
|
||||||
|
process: TraceProcess;
|
||||||
|
spanTags: TraceKeyValuePair[];
|
||||||
|
datasourceType: string;
|
||||||
|
datasourceUid: string;
|
||||||
|
timeRange: TimeRange;
|
||||||
|
}) => {
|
||||||
// Stable context for useMemo inside usePluginLinks
|
// Stable context for useMemo inside usePluginLinks
|
||||||
const context: PluginExtensionResourceAttributesContext = useMemo(() => {
|
const context: PluginExtensionResourceAttributesContext = useMemo(() => {
|
||||||
const attributes = (process.tags ?? []).reduce<Record<string, string[]>>((acc, tag) => {
|
const attributes = (process.tags ?? []).reduce<Record<string, string[]>>((acc, tag) => {
|
||||||
|
@ -80,12 +87,13 @@ const useResourceAttributesExtensionLinks = (
|
||||||
return {
|
return {
|
||||||
attributes,
|
attributes,
|
||||||
spanAttributes,
|
spanAttributes,
|
||||||
|
timeRange: { from: timeRange.from.valueOf(), to: timeRange.to.valueOf() },
|
||||||
datasource: {
|
datasource: {
|
||||||
type: datasourceType,
|
type: datasourceType,
|
||||||
uid: datasourceUid,
|
uid: datasourceUid,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, [process.tags, spanTags, datasourceType, datasourceUid]);
|
}, [process.tags, spanTags, datasourceType, datasourceUid, timeRange]);
|
||||||
|
|
||||||
const { links } = usePluginLinks({
|
const { links } = usePluginLinks({
|
||||||
extensionPointId: PluginExtensionPoints.TraceViewResourceAttributes,
|
extensionPointId: PluginExtensionPoints.TraceViewResourceAttributes,
|
||||||
|
@ -343,7 +351,13 @@ export default function SpanDetail(props: SpanDetailProps) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const focusSpanLink = createFocusSpanLink(traceID, spanID);
|
const focusSpanLink = createFocusSpanLink(traceID, spanID);
|
||||||
const resourceLinksGetter = useResourceAttributesExtensionLinks(process, tags, datasourceType, datasourceUid);
|
const resourceLinksGetter = useResourceAttributesExtensionLinks({
|
||||||
|
process,
|
||||||
|
spanTags: tags,
|
||||||
|
datasourceType,
|
||||||
|
datasourceUid,
|
||||||
|
timeRange,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="span-detail-component">
|
<div data-testid="span-detail-component">
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
|
|
||||||
import { createTheme } from '@grafana/data';
|
import { createTheme, dateTime } from '@grafana/data';
|
||||||
import { setPluginLinksHook } from '@grafana/runtime';
|
import { setPluginLinksHook } from '@grafana/runtime';
|
||||||
|
|
||||||
import DetailState from './SpanDetail/DetailState';
|
import DetailState from './SpanDetail/DetailState';
|
||||||
|
@ -48,6 +48,8 @@ const setup = (propOverrides?: SpanDetailRowProps) => {
|
||||||
theme: createTheme(),
|
theme: createTheme(),
|
||||||
traceFlameGraphs: {},
|
traceFlameGraphs: {},
|
||||||
timeRange: {
|
timeRange: {
|
||||||
|
from: dateTime(0),
|
||||||
|
to: dateTime(1000000000000),
|
||||||
raw: {
|
raw: {
|
||||||
from: 0,
|
from: 0,
|
||||||
to: 1000000000000,
|
to: 1000000000000,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
DataFrameType,
|
DataFrameType,
|
||||||
CoreApp,
|
CoreApp,
|
||||||
PluginExtensionPoints,
|
PluginExtensionPoints,
|
||||||
|
dateTime,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { setPluginLinksHook } from '@grafana/runtime';
|
import { setPluginLinksHook } from '@grafana/runtime';
|
||||||
|
|
||||||
|
@ -43,6 +44,14 @@ const setup = (propOverrides?: Partial<Props>, rowOverrides?: Partial<LogRowMode
|
||||||
theme,
|
theme,
|
||||||
styles,
|
styles,
|
||||||
app: CoreApp.Explore,
|
app: CoreApp.Explore,
|
||||||
|
timeRange: {
|
||||||
|
from: dateTime(1757937009041),
|
||||||
|
to: dateTime(1757940609041),
|
||||||
|
raw: {
|
||||||
|
from: 'now-1h',
|
||||||
|
to: 'now',
|
||||||
|
},
|
||||||
|
},
|
||||||
...(propOverrides || {}),
|
...(propOverrides || {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -325,6 +334,10 @@ describe('LogDetails', () => {
|
||||||
type: 'loki',
|
type: 'loki',
|
||||||
uid: 'grafanacloud-logs',
|
uid: 'grafanacloud-logs',
|
||||||
},
|
},
|
||||||
|
timeRange: {
|
||||||
|
from: 1757937009041,
|
||||||
|
to: 1757940609041,
|
||||||
|
},
|
||||||
attributes: { key1: ['label1'], key2: ['label2'] },
|
attributes: { key1: ['label1'], key2: ['label2'] },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { cx } from '@emotion/css';
|
||||||
import { PureComponent, useMemo } from 'react';
|
import { PureComponent, useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
TimeRange,
|
||||||
CoreApp,
|
CoreApp,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
DataFrameType,
|
DataFrameType,
|
||||||
|
@ -44,23 +45,25 @@ export interface Props extends Themeable2 {
|
||||||
onPinLine?: (row: LogRowModel) => void;
|
onPinLine?: (row: LogRowModel) => void;
|
||||||
pinLineButtonTooltipTitle?: PopoverContent;
|
pinLineButtonTooltipTitle?: PopoverContent;
|
||||||
links?: Record<string, LinkModel[]>;
|
links?: Record<string, LinkModel[]>;
|
||||||
|
timeRange: TimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LinkModelWithIcon extends LinkModel {
|
interface LinkModelWithIcon extends LinkModel {
|
||||||
icon?: IconName;
|
icon?: IconName;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAttributesExtensionLinks = (row: LogRowModel) => {
|
export const useAttributesExtensionLinks = (row: LogRowModel, timeRange: TimeRange) => {
|
||||||
// Stable context for useMemo inside usePluginLinks
|
// Stable context for useMemo inside usePluginLinks
|
||||||
const context: PluginExtensionResourceAttributesContext = useMemo(() => {
|
const context: PluginExtensionResourceAttributesContext = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
attributes: Object.fromEntries(Object.entries(row.labels).map(([key, value]) => [key, [value]])),
|
attributes: Object.fromEntries(Object.entries(row.labels).map(([key, value]) => [key, [value]])),
|
||||||
|
timeRange: { from: timeRange.from.valueOf(), to: timeRange.to.valueOf() },
|
||||||
datasource: {
|
datasource: {
|
||||||
type: row.datasourceType ?? '',
|
type: row.datasourceType ?? '',
|
||||||
uid: row.datasourceUid ?? '',
|
uid: row.datasourceUid ?? '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, [row.labels, row.datasourceType, row.datasourceUid]);
|
}, [row.labels, row.datasourceType, row.datasourceUid, timeRange]);
|
||||||
|
|
||||||
const { links } = usePluginLinks({
|
const { links } = usePluginLinks({
|
||||||
extensionPointId: PluginExtensionPoints.LogsViewResourceAttributes,
|
extensionPointId: PluginExtensionPoints.LogsViewResourceAttributes,
|
||||||
|
@ -93,7 +96,7 @@ export const useAttributesExtensionLinks = (row: LogRowModel) => {
|
||||||
|
|
||||||
const withAttributesExtensionLinks = (Component: React.ComponentType<Props>) => {
|
const withAttributesExtensionLinks = (Component: React.ComponentType<Props>) => {
|
||||||
function ComponentWithLinks(props: Props) {
|
function ComponentWithLinks(props: Props) {
|
||||||
const labelLinks = useAttributesExtensionLinks(props.row);
|
const labelLinks = useAttributesExtensionLinks(props.row, props.timeRange);
|
||||||
return <Component {...props} links={labelLinks} />;
|
return <Component {...props} links={labelLinks} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ComponentProps } from 'react';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
import { CoreApp, createTheme, LogLevel, LogRowModel } from '@grafana/data';
|
import { CoreApp, createTheme, LogLevel, LogRowModel } from '@grafana/data';
|
||||||
|
import { mockTimeRange } from '@grafana/plugin-ui';
|
||||||
|
|
||||||
import { LogRow } from './LogRow';
|
import { LogRow } from './LogRow';
|
||||||
import { getLogRowStyles } from './getLogRowStyles';
|
import { getLogRowStyles } from './getLogRowStyles';
|
||||||
|
@ -40,6 +41,7 @@ const setup = (propOverrides?: Partial<ComponentProps<typeof LogRow>>, rowOverri
|
||||||
wrapLogMessage: false,
|
wrapLogMessage: false,
|
||||||
timeZone: 'utc',
|
timeZone: 'utc',
|
||||||
styles,
|
styles,
|
||||||
|
timeRange: mockTimeRange(),
|
||||||
...(propOverrides || {}),
|
...(propOverrides || {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { MouseEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { MouseEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { CoreApp, DataFrame, dateTimeFormat, LogRowContextOptions, LogRowModel, LogsSortOrder } from '@grafana/data';
|
import {
|
||||||
|
CoreApp,
|
||||||
|
DataFrame,
|
||||||
|
dateTimeFormat,
|
||||||
|
LogRowContextOptions,
|
||||||
|
LogRowModel,
|
||||||
|
LogsSortOrder,
|
||||||
|
TimeRange,
|
||||||
|
} from '@grafana/data';
|
||||||
import { t } from '@grafana/i18n';
|
import { t } from '@grafana/i18n';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { DataQuery, TimeZone } from '@grafana/schema';
|
import { DataQuery, TimeZone } from '@grafana/schema';
|
||||||
|
@ -56,6 +64,7 @@ export interface Props {
|
||||||
handleTextSelection?: (e: MouseEvent<HTMLTableRowElement>, row: LogRowModel) => boolean;
|
handleTextSelection?: (e: MouseEvent<HTMLTableRowElement>, row: LogRowModel) => boolean;
|
||||||
logRowMenuIconsBefore?: ReactNode[];
|
logRowMenuIconsBefore?: ReactNode[];
|
||||||
logRowMenuIconsAfter?: ReactNode[];
|
logRowMenuIconsAfter?: ReactNode[];
|
||||||
|
timeRange: TimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LogRow = ({
|
export const LogRow = ({
|
||||||
|
@ -314,6 +323,7 @@ export const LogRow = ({
|
||||||
styles={styles}
|
styles={styles}
|
||||||
isFilterLabelActive={props.isFilterLabelActive}
|
isFilterLabelActive={props.isFilterLabelActive}
|
||||||
pinLineButtonTooltipTitle={props.pinLineButtonTooltipTitle}
|
pinLineButtonTooltipTitle={props.pinLineButtonTooltipTitle}
|
||||||
|
timeRange={props.timeRange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import { LogRowModel, LogsDedupStrategy, LogsSortOrder } from '@grafana/data';
|
import { LogRowModel, LogsDedupStrategy, LogsSortOrder } from '@grafana/data';
|
||||||
|
import { mockTimeRange } from '@grafana/plugin-ui';
|
||||||
|
|
||||||
import { disablePopoverMenu, enablePopoverMenu, isPopoverMenuDisabled } from '../utils';
|
import { disablePopoverMenu, enablePopoverMenu, isPopoverMenuDisabled } from '../utils';
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ describe('LogRows', () => {
|
||||||
onClickHideField={() => {}}
|
onClickHideField={() => {}}
|
||||||
onClickShowField={() => {}}
|
onClickShowField={() => {}}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={mockTimeRange()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -75,6 +77,7 @@ describe('LogRows', () => {
|
||||||
onClickHideField={() => {}}
|
onClickHideField={() => {}}
|
||||||
onClickShowField={() => {}}
|
onClickShowField={() => {}}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={mockTimeRange()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
expect(screen.queryAllByRole('row')).toHaveLength(2);
|
expect(screen.queryAllByRole('row')).toHaveLength(2);
|
||||||
|
@ -105,6 +108,7 @@ describe('LogRows', () => {
|
||||||
onClickHideField={() => {}}
|
onClickHideField={() => {}}
|
||||||
onClickShowField={() => {}}
|
onClickShowField={() => {}}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={mockTimeRange()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -135,6 +139,7 @@ describe('LogRows', () => {
|
||||||
onClickHideField={() => {}}
|
onClickHideField={() => {}}
|
||||||
onClickShowField={() => {}}
|
onClickShowField={() => {}}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={mockTimeRange()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -162,6 +167,7 @@ describe('Popover menu', () => {
|
||||||
onClickFilterOutString={() => {}}
|
onClickFilterOutString={() => {}}
|
||||||
onClickFilterString={() => {}}
|
onClickFilterString={() => {}}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={mockTimeRange()}
|
||||||
{...overrides}
|
{...overrides}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
CoreApp,
|
CoreApp,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
LogRowContextOptions,
|
LogRowContextOptions,
|
||||||
|
TimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { Trans, t } from '@grafana/i18n';
|
import { Trans, t } from '@grafana/i18n';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
|
@ -61,6 +62,7 @@ export interface Props {
|
||||||
scrollIntoView?: (element: HTMLElement) => void;
|
scrollIntoView?: (element: HTMLElement) => void;
|
||||||
isFilterLabelActive?: (key: string, value: string, refId?: string) => Promise<boolean>;
|
isFilterLabelActive?: (key: string, value: string, refId?: string) => Promise<boolean>;
|
||||||
pinnedLogs?: string[];
|
pinnedLogs?: string[];
|
||||||
|
timeRange: TimeRange;
|
||||||
/**
|
/**
|
||||||
* If false or undefined, the `contain:strict` css property will be added to the wrapping `<table>` for performance reasons.
|
* If false or undefined, the `contain:strict` css property will be added to the wrapping `<table>` for performance reasons.
|
||||||
* Any overflowing content will be clipped at the table boundary.
|
* Any overflowing content will be clipped at the table boundary.
|
||||||
|
|
|
@ -492,6 +492,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
|
||||||
|
|
||||||
const loadingStateAbove = context.above.loadingState;
|
const loadingStateAbove = context.above.loadingState;
|
||||||
const loadingStateBelow = context.below.loadingState;
|
const loadingStateBelow = context.below.loadingState;
|
||||||
|
const timeRange = getFullTimeRange();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -552,6 +553,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
|
||||||
onClickShowField={showField}
|
onClickShowField={showField}
|
||||||
onClickHideField={hideField}
|
onClickHideField={hideField}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={timeRange}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -575,6 +577,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
|
||||||
pinnedLogs={sticky ? [row.uid] : undefined}
|
pinnedLogs={sticky ? [row.uid] : undefined}
|
||||||
overflowingContent={true}
|
overflowingContent={true}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={timeRange}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -594,6 +597,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
|
||||||
onClickShowField={showField}
|
onClickShowField={showField}
|
||||||
onClickHideField={hideField}
|
onClickHideField={hideField}
|
||||||
scrollElement={null}
|
scrollElement={null}
|
||||||
|
timeRange={timeRange}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</td>
|
</td>
|
||||||
|
@ -637,7 +641,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
|
||||||
dispatch(
|
dispatch(
|
||||||
splitOpen({
|
splitOpen({
|
||||||
queries: [contextQuery],
|
queries: [contextQuery],
|
||||||
range: getFullTimeRange(),
|
range: timeRange,
|
||||||
datasourceUid: contextQuery.datasource!.uid!,
|
datasourceUid: contextQuery.datasource!.uid!,
|
||||||
panelsState: {
|
panelsState: {
|
||||||
logs: {
|
logs: {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
LogsSortOrder,
|
LogsSortOrder,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
ScopedVars,
|
ScopedVars,
|
||||||
|
dateTime,
|
||||||
getDefaultTimeRange,
|
getDefaultTimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { setPluginLinksHook } from '@grafana/runtime';
|
import { setPluginLinksHook } from '@grafana/runtime';
|
||||||
|
@ -64,7 +65,14 @@ const setup = (
|
||||||
containerElement: document.createElement('div'),
|
containerElement: document.createElement('div'),
|
||||||
focusLogLine: jest.fn(),
|
focusLogLine: jest.fn(),
|
||||||
logs,
|
logs,
|
||||||
timeRange: getDefaultTimeRange(),
|
timeRange: {
|
||||||
|
from: dateTime(1757937009041),
|
||||||
|
to: dateTime(1757940609041),
|
||||||
|
raw: {
|
||||||
|
from: 'now-1h',
|
||||||
|
to: 'now',
|
||||||
|
},
|
||||||
|
},
|
||||||
timeZone: 'browser',
|
timeZone: 'browser',
|
||||||
showControls: true,
|
showControls: true,
|
||||||
...(propOverrides || {}),
|
...(propOverrides || {}),
|
||||||
|
@ -372,6 +380,10 @@ describe('LogLineDetails', () => {
|
||||||
type: 'loki',
|
type: 'loki',
|
||||||
uid: 'grafanacloud-logs',
|
uid: 'grafanacloud-logs',
|
||||||
},
|
},
|
||||||
|
timeRange: {
|
||||||
|
from: 1757937009041,
|
||||||
|
to: 1757940609041,
|
||||||
|
},
|
||||||
attributes: { key1: ['label1'], key2: ['label2'] },
|
attributes: { key1: ['label1'], key2: ['label2'] },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const LogLineDetailsComponent = memo(
|
||||||
const inputRef = useRef('');
|
const inputRef = useRef('');
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const extensionLinks = useAttributesExtensionLinks(log);
|
const extensionLinks = useAttributesExtensionLinks(log, timeRange);
|
||||||
|
|
||||||
const fieldsWithLinks = useMemo(() => {
|
const fieldsWithLinks = useMemo(() => {
|
||||||
const fieldsWithLinks = log.fields.filter((f) => f.links?.length);
|
const fieldsWithLinks = log.fields.filter((f) => f.links?.length);
|
||||||
|
|
|
@ -674,6 +674,7 @@ export const LogsPanel = ({
|
||||||
logRowMenuIconsAfter={isReactNodeArray(logRowMenuIconsAfter) ? logRowMenuIconsAfter : undefined}
|
logRowMenuIconsAfter={isReactNodeArray(logRowMenuIconsAfter) ? logRowMenuIconsAfter : undefined}
|
||||||
// Ascending order causes scroll to stick to the bottom, so previewing is futile
|
// Ascending order causes scroll to stick to the bottom, so previewing is futile
|
||||||
renderPreview={isAscending ? false : true}
|
renderPreview={isAscending ? false : true}
|
||||||
|
timeRange={data.timeRange}
|
||||||
/>
|
/>
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
{showCommonLabels && isAscending && renderCommonLabels()}
|
{showCommonLabels && isAscending && renderCommonLabels()}
|
||||||
|
@ -727,6 +728,7 @@ export const LogsPanel = ({
|
||||||
logOptionsStorageKey={controlsStorageKey}
|
logOptionsStorageKey={controlsStorageKey}
|
||||||
// Ascending order causes scroll to stick to the bottom, so previewing is futile
|
// Ascending order causes scroll to stick to the bottom, so previewing is futile
|
||||||
renderPreview={isAscending ? false : true}
|
renderPreview={isAscending ? false : true}
|
||||||
|
timeRange={data.timeRange}
|
||||||
/>
|
/>
|
||||||
{showCommonLabels && isAscending && renderCommonLabels()}
|
{showCommonLabels && isAscending && renderCommonLabels()}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue