Commit 347343f5 by Ivana Huckova Committed by GitHub

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
parent 80294b2d
...@@ -56,7 +56,7 @@ function renderMetaItem(value: any, kind: LogsMetaKind) { ...@@ -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> { ...@@ -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)
......
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'; ...@@ -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> { ...@@ -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> { ...@@ -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 } ...@@ -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 = { ...@@ -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 '@ ...@@ -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) {
......
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'; ...@@ -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 { ...@@ -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'; ...@@ -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 { ...@@ -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) => {
......
// 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> { ...@@ -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 { ...@@ -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 { ...@@ -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;
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