import _ from 'lodash'; import React, { PureComponent } from 'react'; import { rangeUtil } from '@grafana/data'; import { Switch } from '@grafana/ui'; import { RawTimeRange, LogLevel, TimeZone, AbsoluteTimeRange, LogsMetaKind, LogsModel, LogsDedupStrategy, LogRowModel, } from '@grafana/data'; import TimeSeries from 'app/core/time_series2'; import ToggleButtonGroup, { ToggleButton } from 'app/core/components/ToggleButtonGroup/ToggleButtonGroup'; import Graph from './Graph'; import { LogLabels } from './LogLabels'; import { LogRow } from './LogRow'; import { LogsDedupDescription } from 'app/core/logs_model'; const PREVIEW_LIMIT = 100; const graphOptions = { series: { stack: true, bars: { show: true, lineWidth: 5, // barWidth: 10, }, // stack: true, }, yaxis: { tickDecimals: 0, }, }; function renderMetaItem(value: any, kind: LogsMetaKind) { if (kind === LogsMetaKind.LabelsMap) { return ( ); } return value; } interface Props { data?: LogsModel; dedupedData?: LogsModel; width: number; exploreId: string; highlighterExpressions: string[]; loading: boolean; absoluteRange: AbsoluteTimeRange; timeZone: TimeZone; scanning?: boolean; scanRange?: RawTimeRange; dedupStrategy: LogsDedupStrategy; hiddenLogLevels: Set; onChangeTime?: (range: AbsoluteTimeRange) => void; onClickLabel?: (label: string, value: string) => void; onStartScanning?: () => void; onStopScanning?: () => void; onDedupStrategyChange: (dedupStrategy: LogsDedupStrategy) => void; onToggleLogLevel: (hiddenLogLevels: LogLevel[]) => void; getRowContext?: (row: LogRowModel, options?: any) => Promise; } interface State { deferLogs: boolean; renderAll: boolean; showLabels: boolean; showTime: boolean; } export default class Logs extends PureComponent { deferLogsTimer: NodeJS.Timer; renderAllTimer: NodeJS.Timer; state = { deferLogs: true, renderAll: false, showLabels: false, showTime: true, }; componentDidMount() { // Staged rendering if (this.state.deferLogs) { const { data } = this.props; const rowCount = data && data.rows ? data.rows.length : 0; // Render all right away if not too far over the limit const renderAll = rowCount <= PREVIEW_LIMIT * 2; this.deferLogsTimer = setTimeout(() => this.setState({ deferLogs: false, renderAll }), rowCount); } } componentDidUpdate(prevProps: Props, prevState: State) { // Staged rendering if (prevState.deferLogs && !this.state.deferLogs && !this.state.renderAll) { this.renderAllTimer = setTimeout(() => this.setState({ renderAll: true }), 2000); } } componentWillUnmount() { clearTimeout(this.deferLogsTimer); clearTimeout(this.renderAllTimer); } onChangeDedup = (dedup: LogsDedupStrategy) => { const { onDedupStrategyChange } = this.props; if (this.props.dedupStrategy === dedup) { return onDedupStrategyChange(LogsDedupStrategy.none); } return onDedupStrategyChange(dedup); }; onChangeLabels = (event: React.SyntheticEvent) => { const target = event.target as HTMLInputElement; this.setState({ showLabels: target.checked, }); }; onChangeTime = (event: React.SyntheticEvent) => { const target = event.target as HTMLInputElement; this.setState({ showTime: target.checked, }); }; onToggleLogLevel = (rawLevel: string, hiddenRawLevels: string[]) => { const hiddenLogLevels: LogLevel[] = hiddenRawLevels.map(level => LogLevel[level]); this.props.onToggleLogLevel(hiddenLogLevels); }; onClickScan = (event: React.SyntheticEvent) => { event.preventDefault(); this.props.onStartScanning(); }; onClickStopScan = (event: React.SyntheticEvent) => { event.preventDefault(); this.props.onStopScanning(); }; render() { const { data, exploreId, highlighterExpressions, loading = false, onClickLabel, absoluteRange, timeZone, scanning, scanRange, width, dedupedData, } = this.props; if (!data) { return null; } const { deferLogs, renderAll, showLabels, showTime } = this.state; const { dedupStrategy } = this.props; const hasData = data && data.rows && data.rows.length > 0; const hasLabel = hasData && dedupedData.hasUniqueLabels; const dedupCount = dedupedData.rows.reduce((sum, row) => sum + row.duplicates, 0); const showDuplicates = dedupStrategy !== LogsDedupStrategy.none && dedupCount > 0; const meta = data.meta ? [...data.meta] : []; if (dedupStrategy !== LogsDedupStrategy.none) { meta.push({ label: 'Dedup count', value: dedupCount, kind: LogsMetaKind.Number, }); } // Staged rendering const processedRows = dedupedData.rows; const firstRows = processedRows.slice(0, PREVIEW_LIMIT); const lastRows = processedRows.slice(PREVIEW_LIMIT); const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...'; // React profiler becomes unusable if we pass all rows to all rows and their labels, using getter instead const getRows = () => processedRows; const timeSeries = data.series ? data.series.map(series => new TimeSeries(series)) : [new TimeSeries({ datapoints: [] })]; return (
{Object.keys(LogsDedupStrategy).map((dedupType, i) => ( {dedupType} ))}
{hasData && meta && (
{meta.map(item => (
{item.label}: {renderMetaItem(item.value, item.kind)}
))}
)}
{hasData && !deferLogs && // Only inject highlighterExpression in the first set for performance reasons firstRows.map((row, index) => ( ))} {hasData && !deferLogs && renderAll && lastRows.map((row, index) => ( ))} {hasData && deferLogs && Rendering {dedupedData.rows.length} rows...}
{!loading && !hasData && !scanning && (
No logs found. Scan for older logs
)} {scanning && (
{scanText} Stop scan
)}
); } }