mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
Loki: Open log context in new tab (#79723)
* pass `getLogRowContextQuery` to component * reset `appliedContextFilters` in `getLogRowContextQuery` * open show context in new tab * open window before crafting url * only open in new tab if getRowContextQuery is set * only open `about:blank` * change conditional
This commit is contained in:
parent
d195c3807e
commit
f05cbe589a
@ -791,6 +791,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
onClickFilterLabel={onClickFilterLabel}
|
||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||
showContextToggle={showContextToggle}
|
||||
getRowContextQuery={getRowContextQuery}
|
||||
showLabels={showLabels}
|
||||
showTime={showTime}
|
||||
enableLogDetails={true}
|
||||
|
@ -3,10 +3,20 @@ import { debounce } from 'lodash';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React, { PureComponent, MouseEvent } from 'react';
|
||||
|
||||
import { Field, LinkModel, LogRowModel, LogsSortOrder, dateTimeFormat, CoreApp, DataFrame } from '@grafana/data';
|
||||
import {
|
||||
Field,
|
||||
LinkModel,
|
||||
LogRowModel,
|
||||
LogsSortOrder,
|
||||
dateTimeFormat,
|
||||
CoreApp,
|
||||
DataFrame,
|
||||
LogRowContextOptions,
|
||||
} from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { TimeZone } from '@grafana/schema';
|
||||
import { DataQuery, TimeZone } from '@grafana/schema';
|
||||
import { withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui';
|
||||
import { LokiQuery } from 'app/plugins/datasource/loki/types';
|
||||
|
||||
import { checkLogsError, escapeUnescapedString } from '../utils';
|
||||
|
||||
@ -39,6 +49,11 @@ interface Props extends Themeable2 {
|
||||
onClickHideField?: (key: string) => void;
|
||||
onLogRowHover?: (row?: LogRowModel) => void;
|
||||
onOpenContext: (row: LogRowModel, onClose: () => void) => void;
|
||||
getRowContextQuery?: (
|
||||
row: LogRowModel,
|
||||
options?: LogRowContextOptions,
|
||||
origQuery?: LokiQuery
|
||||
) => Promise<DataQuery | null>;
|
||||
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
|
||||
styles: LogRowStyles;
|
||||
permalinkedRowId?: string;
|
||||
@ -202,6 +217,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
||||
forceEscape,
|
||||
app,
|
||||
styles,
|
||||
getRowContextQuery,
|
||||
} = this.props;
|
||||
const { showDetails, showingContext, permalinked } = this.state;
|
||||
const levelStyles = getLogLevelStyles(theme, row.logLevel);
|
||||
@ -276,6 +292,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
||||
<LogRowMessage
|
||||
row={processedRow}
|
||||
showContextToggle={showContextToggle}
|
||||
getRowContextQuery={getRowContextQuery}
|
||||
wrapLogMessage={wrapLogMessage}
|
||||
prettifyLogMessage={prettifyLogMessage}
|
||||
onOpenContext={this.onOpenContext}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { FocusEvent, SyntheticEvent, useCallback } from 'react';
|
||||
|
||||
import { LogRowModel } from '@grafana/data';
|
||||
import { LogRowContextOptions, LogRowModel, getDefaultTimeRange, locationUtil, urlUtil } from '@grafana/data';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { ClipboardButton, IconButton } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
|
||||
import { LogRowStyles } from './getLogRowStyles';
|
||||
|
||||
@ -10,6 +12,7 @@ interface Props {
|
||||
row: LogRowModel;
|
||||
showContextToggle?: (row: LogRowModel) => boolean;
|
||||
onOpenContext: (row: LogRowModel) => void;
|
||||
getRowContextQuery?: (row: LogRowModel, options?: LogRowContextOptions) => Promise<DataQuery | null>;
|
||||
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
|
||||
onPinLine?: (row: LogRowModel) => void;
|
||||
onUnpinLine?: (row: LogRowModel) => void;
|
||||
@ -32,17 +35,39 @@ export const LogRowMenuCell = React.memo(
|
||||
styles,
|
||||
mouseIsOver,
|
||||
onBlur,
|
||||
getRowContextQuery,
|
||||
}: Props) => {
|
||||
const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false;
|
||||
const onLogRowClick = useCallback((e: SyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
const onShowContextClick = useCallback(
|
||||
(e: SyntheticEvent<HTMLElement, Event>) => {
|
||||
e.stopPropagation();
|
||||
async (event: SyntheticEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.stopPropagation();
|
||||
// if ctrl or meta key is pressed, open query in new Explore tab
|
||||
if (
|
||||
getRowContextQuery &&
|
||||
(event.nativeEvent.ctrlKey || event.nativeEvent.metaKey || event.nativeEvent.shiftKey)
|
||||
) {
|
||||
const win = window.open('about:blank');
|
||||
const query = await getRowContextQuery(row);
|
||||
if (query && win) {
|
||||
const url = urlUtil.renderUrl(locationUtil.assureBaseUrl(`${getConfig().appSubUrl}explore`), {
|
||||
left: JSON.stringify({
|
||||
datasource: query.datasource,
|
||||
queries: [query],
|
||||
range: getDefaultTimeRange(),
|
||||
}),
|
||||
});
|
||||
win.location = url;
|
||||
|
||||
return;
|
||||
}
|
||||
win?.close();
|
||||
}
|
||||
onOpenContext(row);
|
||||
},
|
||||
[onOpenContext, row]
|
||||
[onOpenContext, getRowContextQuery, row]
|
||||
);
|
||||
/**
|
||||
* For better accessibility support, we listen to the onBlur event here (to hide this component), and
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
|
||||
import { CoreApp, findHighlightChunksInText, LogRowModel } from '@grafana/data';
|
||||
import { CoreApp, findHighlightChunksInText, LogRowContextOptions, LogRowModel } from '@grafana/data';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
|
||||
import { LogMessageAnsi } from './LogMessageAnsi';
|
||||
import { LogRowMenuCell } from './LogRowMenuCell';
|
||||
@ -16,6 +17,7 @@ interface Props {
|
||||
app?: CoreApp;
|
||||
showContextToggle?: (row: LogRowModel) => boolean;
|
||||
onOpenContext: (row: LogRowModel) => void;
|
||||
getRowContextQuery?: (row: LogRowModel, options?: LogRowContextOptions) => Promise<DataQuery | null>;
|
||||
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
|
||||
onPinLine?: (row: LogRowModel) => void;
|
||||
onUnpinLine?: (row: LogRowModel) => void;
|
||||
@ -77,6 +79,7 @@ export const LogRowMessage = React.memo((props: Props) => {
|
||||
pinned,
|
||||
mouseIsOver,
|
||||
onBlur,
|
||||
getRowContextQuery,
|
||||
} = props;
|
||||
const { hasAnsi, raw } = row;
|
||||
const restructuredEntry = useMemo(() => restructureLog(raw, prettifyLogMessage), [raw, prettifyLogMessage]);
|
||||
@ -100,6 +103,7 @@ export const LogRowMessage = React.memo((props: Props) => {
|
||||
logText={restructuredEntry}
|
||||
row={row}
|
||||
showContextToggle={showContextToggle}
|
||||
getRowContextQuery={getRowContextQuery}
|
||||
onOpenContext={onOpenContext}
|
||||
onPermalinkClick={onPermalinkClick}
|
||||
onPinLine={onPinLine}
|
||||
|
@ -11,8 +11,10 @@ import {
|
||||
LogsSortOrder,
|
||||
CoreApp,
|
||||
DataFrame,
|
||||
LogRowContextOptions,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { withTheme2, Themeable2 } from '@grafana/ui';
|
||||
|
||||
import { PopoverMenu } from '../../explore/Logs/PopoverMenu';
|
||||
@ -50,6 +52,7 @@ export interface Props extends Themeable2 {
|
||||
onUnpinLine?: (row: LogRowModel) => void;
|
||||
onLogRowHover?: (row?: LogRowModel) => void;
|
||||
onOpenContext?: (row: LogRowModel, onClose: () => void) => void;
|
||||
getRowContextQuery?: (row: LogRowModel, options?: LogRowContextOptions) => Promise<DataQuery | null>;
|
||||
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
|
||||
permalinkedRowId?: string;
|
||||
scrollIntoView?: (element: HTMLElement) => void;
|
||||
|
@ -130,18 +130,22 @@ describe('LogContextProvider', () => {
|
||||
direction: LogRowContextQueryDirection.Backward,
|
||||
});
|
||||
expect(query.expr).toBe('{bar="baz"}');
|
||||
expect(logContextProvider.getInitContextFilters).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call getInitContextFilters if appliedContextFilters', async () => {
|
||||
it('should also call getInitContextFilters if appliedContextFilters is set', async () => {
|
||||
logContextProvider.getInitContextFilters = jest
|
||||
.fn()
|
||||
.mockResolvedValue([{ value: 'baz', enabled: true, fromParser: false, label: 'bar' }]);
|
||||
logContextProvider.appliedContextFilters = [
|
||||
{ value: 'baz', enabled: true, fromParser: false, label: 'bar' },
|
||||
{ value: 'abc', enabled: true, fromParser: false, label: 'xyz' },
|
||||
];
|
||||
const query = await logContextProvider.getLogRowContextQuery(defaultLogRow, {
|
||||
await logContextProvider.getLogRowContextQuery(defaultLogRow, {
|
||||
limit: 10,
|
||||
direction: LogRowContextQueryDirection.Backward,
|
||||
});
|
||||
expect(query.expr).toBe('{bar="baz",xyz="abc"}');
|
||||
expect(logContextProvider.getInitContextFilters).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -77,7 +77,13 @@ export class LogContextProvider {
|
||||
options?: LogRowContextOptions,
|
||||
origQuery?: LokiQuery
|
||||
): Promise<LokiQuery> => {
|
||||
// FIXME: This is a hack to make sure that the context query is created with
|
||||
// the correct set of filters. The whole `appliedContextFilters` property
|
||||
// should be revisted.
|
||||
const cachedFilters = this.appliedContextFilters;
|
||||
this.appliedContextFilters = [];
|
||||
const { query } = await this.getQueryAndRange(row, options, origQuery);
|
||||
this.appliedContextFilters = cachedFilters;
|
||||
|
||||
return query;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user