diff --git a/public/app/features/explore/LogRowContext.tsx b/public/app/features/explore/LogRowContext.tsx index da9c3ec4812..2e0e4b4c186 100644 --- a/public/app/features/explore/LogRowContext.tsx +++ b/public/app/features/explore/LogRowContext.tsx @@ -103,12 +103,6 @@ const LogRowContextGroupHeader: React.FunctionComponent contextRow !== row.raw); - return (
- Found {logRowsToRender.length} rows. + Found {rows.length} rows. {(rows.length >= 10 || (rows.length > 10 && rows.length % 10 !== 0)) && canLoadMoreRows && ( JSX.Element; } +export const getRowContexts = async ( + getRowContext: (row: LogRowModel, options?: any) => Promise, + row: LogRowModel, + limit: number +) => { + const promises = [ + getRowContext(row, { + limit, + }), + getRowContext(row, { + limit: limit + 1, // Lets add one more to the limit as we're filtering out one row see comment below + direction: 'FORWARD', + }), + ]; + + const results: Array = await Promise.all(promises.map(p => p.catch(e => e))); + + return { + data: results.map((result, index) => { + const dataResult: DataQueryResponse = result as DataQueryResponse; + if (!dataResult.data) { + return []; + } + + // We need to filter out the row we're basing our search from because of how start/end params work in Loki API + // see https://github.com/grafana/loki/issues/597#issuecomment-506408980 + // the alternative to create our own add 1 nanosecond method to the a timestamp string would be quite complex + return dataResult.data.map(series => { + const filteredRows = series.rows.filter((r: any) => r[0] !== row.timestamp); + return filteredRows.map((row: any) => row[1]); + }); + }), + errors: results.map(result => { + const errorResult: DataQueryError = result as DataQueryError; + if (!errorResult.message) { + return null; + } + + return errorResult.message; + }), + }; +}; + export const LogRowContextProvider: React.FunctionComponent = ({ getRowContext, row, children, }) => { + // React Hook that creates a number state value called limit to component state and a setter function called setLimit + // The intial value for limit is 10 + // Used for the number of rows to retrieve from backend from a specific point in time const [limit, setLimit] = useState(10); + + // React Hook that creates an object state value called result to component state and a setter function called setResult + // The intial value for result is null + // Used for sorting the response from backend const [result, setResult] = useState<{ data: string[][]; errors: string[]; }>(null); + + // React Hook that creates an object state value called hasMoreContextRows to component state and a setter function called setHasMoreContextRows + // The intial value for hasMoreContextRows is {before: true, after: true} + // Used for indicating in UI if there are more rows to load in a given direction const [hasMoreContextRows, setHasMoreContextRows] = useState({ before: true, after: true, }); + // React Hook that resolves two promises every time the limit prop changes + // First promise fetches limit number of rows backwards in time from a specific point in time + // Second promise fetches limit number of rows forwards in time from a specific point in time const { value } = useAsync(async () => { - const promises = [ - getRowContext(row, { - limit, - }), - getRowContext(row, { - limit, - direction: 'FORWARD', - }), - ]; - - const results: Array = await Promise.all(promises.map(p => p.catch(e => e))); - - return { - data: results.map(result => { - if ((result as DataQueryResponse).data) { - return (result as DataQueryResponse).data.map(series => { - return series.rows.map(row => row[1]); - }); - } else { - return []; - } - }), - errors: results.map(result => { - if ((result as DataQueryError).message) { - return (result as DataQueryError).message; - } else { - return null; - } - }), - }; + return await getRowContexts(getRowContext, row, limit); // Moved it to a separate function for debugging purposes }, [limit]); + // React Hook that performs a side effect every time the value (from useAsync hook) prop changes + // The side effect changes the result state with the response from the useAsync hook + // The side effect changes the hasMoreContextRows state if there are more context rows before or after the current result useEffect(() => { if (value) { setResult(currentResult => { diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 55bb4f52609..7a4a3f6ab43 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -310,13 +310,13 @@ export class LokiDatasource extends DataSourceApi { return { ...commontTargetOptons, start: timeEpochNs - contextTimeBuffer, - end: timeEpochNs, + end: row.timestamp, direction, }; } else { return { ...commontTargetOptons, - start: timeEpochNs, // TODO: We should add 1ns here for the original row not no be included in the result + start: row.timestamp, // start param in Loki API is inclusive so we'll have to filter out the row that this request is based from end: timeEpochNs + contextTimeBuffer, }; }