mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Improve live tailing errors and fix Explore's logs container type errors (#30517)
* If error - catch and show, if no logs - return null * Refactor LogsContainer to use ConnectedProps * Fix typescript error * Remove no logs check * Include review feedback * Add SplitOpen type to createSpanLink and TraceView
This commit is contained in:
@@ -56,7 +56,7 @@ function renderMetaItem(value: any, kind: LogsMetaKind) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
logRows?: LogRowModel[];
|
logRows: LogRowModel[];
|
||||||
logsMeta?: LogsMetaItem[];
|
logsMeta?: LogsMetaItem[];
|
||||||
logsSeries?: GraphSeriesXY[];
|
logsSeries?: GraphSeriesXY[];
|
||||||
dedupedRows?: LogRowModel[];
|
dedupedRows?: LogRowModel[];
|
||||||
@@ -239,10 +239,6 @@ export class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
const { showLabels, showTime, wrapLogMessage, logsSortOrder, isFlipping, showDetectedFields } = this.state;
|
const { showLabels, showTime, wrapLogMessage, logsSortOrder, isFlipping, showDetectedFields } = this.state;
|
||||||
|
|
||||||
if (!logRows) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasData = logRows && logRows.length > 0;
|
const hasData = logRows && logRows.length > 0;
|
||||||
const dedupCount = dedupedRows
|
const dedupCount = dedupedRows
|
||||||
? dedupedRows.reduce((sum, row) => (row.duplicates ? sum + row.duplicates : sum), 0)
|
? dedupedRows.reduce((sum, row) => (row.duplicates ? sum + row.duplicates : sum), 0)
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import { connect } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
import { Collapse } from '@grafana/ui';
|
import { Collapse } from '@grafana/ui';
|
||||||
|
|
||||||
import {
|
import { AbsoluteTimeRange, Field, LogLevel, LogRowModel, LogsDedupStrategy, RawTimeRange } from '@grafana/data';
|
||||||
AbsoluteTimeRange,
|
|
||||||
DataSourceApi,
|
|
||||||
Field,
|
|
||||||
GraphSeriesXY,
|
|
||||||
LogLevel,
|
|
||||||
LogRowModel,
|
|
||||||
LogsDedupStrategy,
|
|
||||||
LogsMetaItem,
|
|
||||||
RawTimeRange,
|
|
||||||
TimeRange,
|
|
||||||
TimeZone,
|
|
||||||
} from '@grafana/data';
|
|
||||||
|
|
||||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
@@ -32,38 +20,17 @@ import { LiveTailControls } from './useLiveTailControls';
|
|||||||
import { getFieldLinksForExplore } from './utils/links';
|
import { getFieldLinksForExplore } from './utils/links';
|
||||||
|
|
||||||
interface LogsContainerProps {
|
interface LogsContainerProps {
|
||||||
datasourceInstance?: DataSourceApi;
|
|
||||||
exploreId: ExploreId;
|
exploreId: ExploreId;
|
||||||
loading: boolean;
|
scanRange?: RawTimeRange;
|
||||||
|
width: number;
|
||||||
logsHighlighterExpressions?: string[];
|
syncedTimes: boolean;
|
||||||
logRows?: LogRowModel[];
|
|
||||||
logsMeta?: LogsMetaItem[];
|
|
||||||
logsSeries?: GraphSeriesXY[];
|
|
||||||
dedupedRows?: LogRowModel[];
|
|
||||||
visibleRange?: AbsoluteTimeRange;
|
|
||||||
|
|
||||||
onClickFilterLabel?: (key: string, value: string) => void;
|
onClickFilterLabel?: (key: string, value: string) => void;
|
||||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||||
onStartScanning: () => void;
|
onStartScanning: () => void;
|
||||||
onStopScanning: () => void;
|
onStopScanning: () => void;
|
||||||
timeZone: TimeZone;
|
|
||||||
scanning?: boolean;
|
|
||||||
scanRange?: RawTimeRange;
|
|
||||||
toggleLogLevelAction: typeof toggleLogLevelAction;
|
|
||||||
changeDedupStrategy: typeof changeDedupStrategy;
|
|
||||||
dedupStrategy: LogsDedupStrategy;
|
|
||||||
width: number;
|
|
||||||
isLive: boolean;
|
|
||||||
updateTimeRange: typeof updateTimeRange;
|
|
||||||
range: TimeRange;
|
|
||||||
syncedTimes: boolean;
|
|
||||||
absoluteRange: AbsoluteTimeRange;
|
|
||||||
isPaused: boolean;
|
|
||||||
splitOpen: typeof splitOpen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogsContainer extends PureComponent<LogsContainerProps> {
|
export class LogsContainer extends PureComponent<PropsFromRedux & LogsContainerProps> {
|
||||||
onChangeTime = (absoluteRange: AbsoluteTimeRange) => {
|
onChangeTime = (absoluteRange: AbsoluteTimeRange) => {
|
||||||
const { exploreId, updateTimeRange } = this.props;
|
const { exploreId, updateTimeRange } = this.props;
|
||||||
updateTimeRange({ exploreId, absoluteRange });
|
updateTimeRange({ exploreId, absoluteRange });
|
||||||
@@ -102,7 +69,8 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getFieldLinks = (field: Field, rowIndex: number) => {
|
getFieldLinks = (field: Field, rowIndex: number) => {
|
||||||
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn: this.props.splitOpen, range: this.props.range });
|
const { splitOpen: splitOpenFn, range } = this.props;
|
||||||
|
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn, range });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -127,6 +95,10 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
|||||||
exploreId,
|
exploreId,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
if (!logRows) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LogsCrossFadeTransition visible={isLive}>
|
<LogsCrossFadeTransition visible={isLive}>
|
||||||
@@ -195,16 +167,16 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
|
|||||||
absoluteRange,
|
absoluteRange,
|
||||||
dedupStrategy,
|
dedupStrategy,
|
||||||
} = item;
|
} = item;
|
||||||
const dedupedRows = deduplicatedRowsSelector(item);
|
const dedupedRows = deduplicatedRowsSelector(item) || undefined;
|
||||||
const timeZone = getTimeZone(state.user);
|
const timeZone = getTimeZone(state.user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loading,
|
loading,
|
||||||
logsHighlighterExpressions,
|
logsHighlighterExpressions,
|
||||||
logRows: logsResult && logsResult.rows,
|
logRows: logsResult?.rows,
|
||||||
logsMeta: logsResult && logsResult.meta,
|
logsMeta: logsResult?.meta,
|
||||||
logsSeries: logsResult && logsResult.series,
|
logsSeries: logsResult?.series,
|
||||||
visibleRange: logsResult && logsResult.visibleRange,
|
visibleRange: logsResult?.visibleRange,
|
||||||
scanning,
|
scanning,
|
||||||
timeZone,
|
timeZone,
|
||||||
dedupStrategy,
|
dedupStrategy,
|
||||||
@@ -224,4 +196,7 @@ const mapDispatchToProps = {
|
|||||||
splitOpen,
|
splitOpen,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(LogsContainer));
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
type PropsFromRedux = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
|
export default hot(module)(connector(LogsContainer));
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ import { TraceViewData, Trace, TraceSpan, TraceKeyValuePair, TraceLink } from '@
|
|||||||
import { createSpanLinkFactory } from './createSpanLink';
|
import { createSpanLinkFactory } from './createSpanLink';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
|
import { SplitOpen } from 'app/types/explore';
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
import { TraceToLogsData } from 'app/core/components/TraceToLogsSettings';
|
import { TraceToLogsData } from 'app/core/components/TraceToLogsSettings';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
trace?: TraceViewData;
|
trace?: TraceViewData;
|
||||||
splitOpenFn: (options: { datasourceUid: string; query: any }) => void;
|
splitOpenFn: SplitOpen;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TraceView(props: Props) {
|
export function TraceView(props: Props) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { DataLink, dateTime, Field, mapInternalLinkToExplore, TimeRange, TraceSpan } from '@grafana/data';
|
import { DataLink, dateTime, Field, mapInternalLinkToExplore, TimeRange, TraceSpan } from '@grafana/data';
|
||||||
import { getTemplateSrv } from '@grafana/runtime';
|
import { getTemplateSrv } from '@grafana/runtime';
|
||||||
import { Icon } from '@grafana/ui';
|
import { Icon } from '@grafana/ui';
|
||||||
|
import { SplitOpen } from 'app/types/explore';
|
||||||
import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings';
|
import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings';
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -11,10 +12,7 @@ import { LokiQuery } from '../../../plugins/datasource/loki/types';
|
|||||||
* 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(
|
export function createSpanLinkFactory(splitOpenFn: SplitOpen, traceToLogsOptions?: TraceToLogsOptions) {
|
||||||
splitOpenFn: (options: { datasourceUid: string; query: any }) => void,
|
|
||||||
traceToLogsOptions?: TraceToLogsOptions
|
|
||||||
) {
|
|
||||||
// We should return if dataSourceUid is undefined otherwise getInstanceSettings would return testDataSource.
|
// We should return if dataSourceUid is undefined otherwise getInstanceSettings would return testDataSource.
|
||||||
if (!traceToLogsOptions?.datasourceUid) {
|
if (!traceToLogsOptions?.datasourceUid) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import {
|
|||||||
DataFrame,
|
DataFrame,
|
||||||
getFieldDisplayValuesProxy,
|
getFieldDisplayValuesProxy,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { getLinkSrv } from '../../panel/panellinks/link_srv';
|
|
||||||
import { config, getTemplateSrv } from '@grafana/runtime';
|
import { config, getTemplateSrv } from '@grafana/runtime';
|
||||||
import { splitOpen } from '../state/main';
|
import { SplitOpen } from 'app/types/explore';
|
||||||
|
import { getLinkSrv } from '../../panel/panellinks/link_srv';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get links from the field of a dataframe and in addition check if there is associated
|
* Get links from the field of a dataframe and in addition check if there is associated
|
||||||
@@ -23,7 +23,7 @@ import { splitOpen } from '../state/main';
|
|||||||
export const getFieldLinksForExplore = (options: {
|
export const getFieldLinksForExplore = (options: {
|
||||||
field: Field;
|
field: Field;
|
||||||
rowIndex: number;
|
rowIndex: number;
|
||||||
splitOpenFn?: typeof splitOpen;
|
splitOpenFn?: SplitOpen;
|
||||||
range: TimeRange;
|
range: TimeRange;
|
||||||
vars?: ScopedVars;
|
vars?: ScopedVars;
|
||||||
dataFrame?: DataFrame;
|
dataFrame?: DataFrame;
|
||||||
@@ -99,7 +99,7 @@ function getTitleFromHref(href: string): string {
|
|||||||
* all the fields so is useful for visualisation where the whole row is represented as single clickable item like a
|
* all the fields so is useful for visualisation where the whole row is represented as single clickable item like a
|
||||||
* service map.
|
* service map.
|
||||||
*/
|
*/
|
||||||
export function useLinks(range: TimeRange, splitOpenFn?: typeof splitOpen) {
|
export function useLinks(range: TimeRange, splitOpenFn?: SplitOpen) {
|
||||||
return useCallback(
|
return useCallback(
|
||||||
(dataFrame: DataFrame, rowIndex: number) => {
|
(dataFrame: DataFrame, rowIndex: number) => {
|
||||||
return dataFrame.fields.flatMap((f) => {
|
return dataFrame.fields.flatMap((f) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import { cloneDeep, isEmpty, map as lodashMap } from 'lodash';
|
import { cloneDeep, isEmpty, map as lodashMap } from 'lodash';
|
||||||
import { merge, Observable, of } from 'rxjs';
|
import { merge, Observable, of, throwError } from 'rxjs';
|
||||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
import Prism from 'prismjs';
|
import Prism from 'prismjs';
|
||||||
|
|
||||||
@@ -263,7 +263,10 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
|||||||
data: data || [],
|
data: data || [],
|
||||||
key: `loki-${liveTarget.refId}`,
|
key: `loki-${liveTarget.refId}`,
|
||||||
state: LoadingState.Streaming,
|
state: LoadingState.Streaming,
|
||||||
}))
|
})),
|
||||||
|
catchError((err: any) => {
|
||||||
|
return throwError(`Live tailing was stopped due to following error: ${err.reason}`);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export class LiveStreams {
|
|||||||
// Retry every 5s
|
// Retry every 5s
|
||||||
return timer(retryInterval);
|
return timer(retryInterval);
|
||||||
}
|
}
|
||||||
return throwError(`error: ${error.reason}`);
|
return throwError(error);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -221,3 +221,7 @@ export interface ExplorePanelData extends PanelData {
|
|||||||
tableResult: DataFrame | null;
|
tableResult: DataFrame | null;
|
||||||
logsResult: LogsModel | null;
|
logsResult: LogsModel | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SplitOpen = <T extends DataQuery = any>(
|
||||||
|
options?: { datasourceUid: string; query: T; range?: TimeRange } | undefined
|
||||||
|
) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user