mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #14178 from grafana/davkal/explore-scan-log-range
Explore: Scan for older logs
This commit is contained in:
commit
32511533c3
@ -97,6 +97,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
* Local ID cache to compare requested vs selected datasource
|
* Local ID cache to compare requested vs selected datasource
|
||||||
*/
|
*/
|
||||||
requestedDatasourceId: string;
|
requestedDatasourceId: string;
|
||||||
|
scanTimer: NodeJS.Timer;
|
||||||
|
/**
|
||||||
|
* Timepicker to control scanning
|
||||||
|
*/
|
||||||
|
timepickerRef: React.RefObject<TimePicker>;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -122,6 +127,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
history: [],
|
history: [],
|
||||||
queryTransactions: [],
|
queryTransactions: [],
|
||||||
range: initialRange,
|
range: initialRange,
|
||||||
|
scanning: false,
|
||||||
showingGraph: true,
|
showingGraph: true,
|
||||||
showingLogs: true,
|
showingLogs: true,
|
||||||
showingStartPage: false,
|
showingStartPage: false,
|
||||||
@ -132,6 +138,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.modifiedQueries = initialQueries.slice();
|
this.modifiedQueries = initialQueries.slice();
|
||||||
|
this.timepickerRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@ -164,6 +171,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
clearTimeout(this.scanTimer);
|
||||||
|
}
|
||||||
|
|
||||||
async setDatasource(datasource: any, origin?: DataSource) {
|
async setDatasource(datasource: any, origin?: DataSource) {
|
||||||
const supportsGraph = datasource.meta.metrics;
|
const supportsGraph = datasource.meta.metrics;
|
||||||
const supportsLogs = datasource.meta.logs;
|
const supportsLogs = datasource.meta.logs;
|
||||||
@ -317,11 +328,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onChangeTime = (nextRange: RawTimeRange) => {
|
onChangeTime = (nextRange: RawTimeRange, scanning?: boolean) => {
|
||||||
const range: RawTimeRange = {
|
const range: RawTimeRange = {
|
||||||
...nextRange,
|
...nextRange,
|
||||||
};
|
};
|
||||||
this.setState({ range }, () => this.onSubmit());
|
if (this.state.scanning && !scanning) {
|
||||||
|
this.onStopScanning();
|
||||||
|
}
|
||||||
|
this.setState({ range, scanning }, () => this.onSubmit());
|
||||||
};
|
};
|
||||||
|
|
||||||
onClickClear = () => {
|
onClickClear = () => {
|
||||||
@ -496,6 +510,24 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onStartScanning = () => {
|
||||||
|
this.setState({ scanning: true }, this.scanPreviousRange);
|
||||||
|
};
|
||||||
|
|
||||||
|
scanPreviousRange = () => {
|
||||||
|
const scanRange = this.timepickerRef.current.move(-1, true);
|
||||||
|
this.setState({ scanRange });
|
||||||
|
};
|
||||||
|
|
||||||
|
onStopScanning = () => {
|
||||||
|
clearTimeout(this.scanTimer);
|
||||||
|
this.setState(state => {
|
||||||
|
const { queryTransactions } = state;
|
||||||
|
const nextQueryTransactions = queryTransactions.filter(qt => qt.scanning && !qt.done);
|
||||||
|
return { queryTransactions: nextQueryTransactions, scanning: false, scanRange: undefined };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onSubmit = () => {
|
onSubmit = () => {
|
||||||
const { showingLogs, showingGraph, showingTable, supportsGraph, supportsLogs, supportsTable } = this.state;
|
const { showingLogs, showingGraph, showingTable, supportsGraph, supportsLogs, supportsTable } = this.state;
|
||||||
// Keep table queries first since they need to return quickly
|
// Keep table queries first since they need to return quickly
|
||||||
@ -563,6 +595,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
done: false,
|
done: false,
|
||||||
latency: 0,
|
latency: 0,
|
||||||
options: queryOptions,
|
options: queryOptions,
|
||||||
|
scanning: this.state.scanning,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Using updater style because we might be modifying queryTransactions in quick succession
|
// Using updater style because we might be modifying queryTransactions in quick succession
|
||||||
@ -599,7 +632,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState(state => {
|
this.setState(state => {
|
||||||
const { history, queryTransactions } = state;
|
const { history, queryTransactions, scanning } = state;
|
||||||
|
|
||||||
// Transaction might have been discarded
|
// Transaction might have been discarded
|
||||||
const transaction = queryTransactions.find(qt => qt.id === transactionId);
|
const transaction = queryTransactions.find(qt => qt.id === transactionId);
|
||||||
@ -629,6 +662,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
|
|
||||||
const nextHistory = updateHistory(history, datasourceId, queries);
|
const nextHistory = updateHistory(history, datasourceId, queries);
|
||||||
|
|
||||||
|
// Keep scanning for results if this was the last scanning transaction
|
||||||
|
if (_.size(result) === 0 && scanning) {
|
||||||
|
const other = nextQueryTransactions.find(qt => qt.scanning && !qt.done);
|
||||||
|
if (!other) {
|
||||||
|
this.scanTimer = setTimeout(this.scanPreviousRange, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
history: nextHistory,
|
history: nextHistory,
|
||||||
queryTransactions: nextQueryTransactions,
|
queryTransactions: nextQueryTransactions,
|
||||||
@ -740,6 +781,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
initialQueries,
|
initialQueries,
|
||||||
queryTransactions,
|
queryTransactions,
|
||||||
range,
|
range,
|
||||||
|
scanning,
|
||||||
|
scanRange,
|
||||||
showingGraph,
|
showingGraph,
|
||||||
showingLogs,
|
showingLogs,
|
||||||
showingStartPage,
|
showingStartPage,
|
||||||
@ -822,7 +865,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<TimePicker range={range} onChangeTime={this.onChangeTime} />
|
<TimePicker ref={this.timepickerRef} range={range} onChangeTime={this.onChangeTime} />
|
||||||
<div className="navbar-buttons">
|
<div className="navbar-buttons">
|
||||||
<button className="btn navbar-button navbar-button--no-icon" onClick={this.onClickClear}>
|
<button className="btn navbar-button navbar-button--no-icon" onClick={this.onClickClear}>
|
||||||
Clear All
|
Clear All
|
||||||
@ -898,7 +941,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|||||||
loading={logsLoading}
|
loading={logsLoading}
|
||||||
position={position}
|
position={position}
|
||||||
onChangeTime={this.onChangeTime}
|
onChangeTime={this.onChangeTime}
|
||||||
|
onStartScanning={this.onStartScanning}
|
||||||
|
onStopScanning={this.onStopScanning}
|
||||||
range={range}
|
range={range}
|
||||||
|
scanning={scanning}
|
||||||
|
scanRange={scanRange}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
)}
|
)}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { Fragment, PureComponent } from 'react';
|
import React, { Fragment, PureComponent } from 'react';
|
||||||
import Highlighter from 'react-highlight-words';
|
import Highlighter from 'react-highlight-words';
|
||||||
|
|
||||||
|
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||||
import { RawTimeRange } from 'app/types/series';
|
import { RawTimeRange } from 'app/types/series';
|
||||||
import { LogsDedupStrategy, LogsModel, dedupLogRows, filterLogLevels, LogLevel } from 'app/core/logs_model';
|
import { LogsDedupStrategy, LogsModel, dedupLogRows, filterLogLevels, LogLevel } from 'app/core/logs_model';
|
||||||
import { findHighlightChunksInText } from 'app/core/utils/text';
|
import { findHighlightChunksInText } from 'app/core/utils/text';
|
||||||
@ -28,7 +29,11 @@ interface LogsProps {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
position: string;
|
position: string;
|
||||||
range?: RawTimeRange;
|
range?: RawTimeRange;
|
||||||
|
scanning?: boolean;
|
||||||
|
scanRange?: RawTimeRange;
|
||||||
onChangeTime?: (range: RawTimeRange) => void;
|
onChangeTime?: (range: RawTimeRange) => void;
|
||||||
|
onStartScanning?: () => void;
|
||||||
|
onStopScanning?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LogsState {
|
interface LogsState {
|
||||||
@ -83,8 +88,18 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
|
|||||||
this.setState({ hiddenLogLevels });
|
this.setState({ hiddenLogLevels });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onClickScan = (event: React.SyntheticEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.props.onStartScanning();
|
||||||
|
};
|
||||||
|
|
||||||
|
onClickStopScan = (event: React.SyntheticEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.props.onStopScanning();
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className = '', data, loading = false, position, range } = this.props;
|
const { className = '', data, loading = false, position, range, scanning, scanRange } = this.props;
|
||||||
const { dedup, hiddenLogLevels, showLabels, showLocalTime, showUtc } = this.state;
|
const { dedup, hiddenLogLevels, showLabels, showLocalTime, showUtc } = this.state;
|
||||||
const hasData = data && data.rows && data.rows.length > 0;
|
const hasData = data && data.rows && data.rows.length > 0;
|
||||||
const filteredData = filterLogLevels(data, hiddenLogLevels);
|
const filteredData = filterLogLevels(data, hiddenLogLevels);
|
||||||
@ -111,6 +126,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
|
|||||||
const logEntriesStyle = {
|
const logEntriesStyle = {
|
||||||
gridTemplateColumns: cssColumnSizes.join(' '),
|
gridTemplateColumns: cssColumnSizes.join(' '),
|
||||||
};
|
};
|
||||||
|
const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${className} logs`}>
|
<div className={`${className} logs`}>
|
||||||
@ -200,7 +216,25 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{!loading && !hasData && 'No data was returned.'}
|
{!loading &&
|
||||||
|
!hasData &&
|
||||||
|
!scanning && (
|
||||||
|
<div className="logs-nodata">
|
||||||
|
No logs found.
|
||||||
|
<a className="link" onClick={this.onClickScan}>
|
||||||
|
Scan for older logs
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{scanning && (
|
||||||
|
<div className="logs-nodata">
|
||||||
|
<span>{scanText}</span>
|
||||||
|
<a className="link" onClick={this.onClickStopScan}>
|
||||||
|
Stop scan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ interface TimePickerProps {
|
|||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
isUtc?: boolean;
|
isUtc?: boolean;
|
||||||
range?: RawTimeRange;
|
range?: RawTimeRange;
|
||||||
onChangeTime?: (Range) => void;
|
onChangeTime?: (range: RawTimeRange, scanning?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TimePickerState {
|
interface TimePickerState {
|
||||||
@ -92,12 +92,13 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
move(direction: number) {
|
move(direction: number, scanning?: boolean): RawTimeRange {
|
||||||
const { onChangeTime } = this.props;
|
const { onChangeTime } = this.props;
|
||||||
const { fromRaw, toRaw } = this.state;
|
const { fromRaw, toRaw } = this.state;
|
||||||
const from = dateMath.parse(fromRaw, false);
|
const from = dateMath.parse(fromRaw, false);
|
||||||
const to = dateMath.parse(toRaw, true);
|
const to = dateMath.parse(toRaw, true);
|
||||||
const timespan = (to.valueOf() - from.valueOf()) / 2;
|
const step = scanning ? 1 : 2;
|
||||||
|
const timespan = (to.valueOf() - from.valueOf()) / step;
|
||||||
|
|
||||||
let nextTo, nextFrom;
|
let nextTo, nextFrom;
|
||||||
if (direction === -1) {
|
if (direction === -1) {
|
||||||
@ -127,9 +128,11 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
|
|||||||
toRaw: nextRange.to.format(DATE_FORMAT),
|
toRaw: nextRange.to.format(DATE_FORMAT),
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
onChangeTime(nextRange);
|
onChangeTime(nextRange, scanning);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return nextRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangeFrom = e => {
|
handleChangeFrom = e => {
|
||||||
|
@ -140,6 +140,7 @@ export interface QueryTransaction {
|
|||||||
result?: any; // Table model / Timeseries[] / Logs
|
result?: any; // Table model / Timeseries[] / Logs
|
||||||
resultType: ResultType;
|
resultType: ResultType;
|
||||||
rowIndex: number;
|
rowIndex: number;
|
||||||
|
scanning?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextMatch {
|
export interface TextMatch {
|
||||||
@ -162,6 +163,8 @@ export interface ExploreState {
|
|||||||
initialQueries: DataQuery[];
|
initialQueries: DataQuery[];
|
||||||
queryTransactions: QueryTransaction[];
|
queryTransactions: QueryTransaction[];
|
||||||
range: RawTimeRange;
|
range: RawTimeRange;
|
||||||
|
scanning?: boolean;
|
||||||
|
scanRange?: RawTimeRange;
|
||||||
showingGraph: boolean;
|
showingGraph: boolean;
|
||||||
showingLogs: boolean;
|
showingLogs: boolean;
|
||||||
showingStartPage?: boolean;
|
showingStartPage?: boolean;
|
||||||
|
@ -267,6 +267,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logs-nodata {
|
||||||
|
> * {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.logs-meta {
|
.logs-meta {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
color: $text-color-weak;
|
color: $text-color-weak;
|
||||||
|
Loading…
Reference in New Issue
Block a user