import React, { Fragment, PureComponent } from 'react'; import Highlighter from 'react-highlight-words'; import * as rangeUtil from 'app/core/utils/rangeutil'; import { RawTimeRange } from 'app/types/series'; import { LogsDedupStrategy, LogsModel, dedupLogRows, filterLogLevels, LogLevel } from 'app/core/logs_model'; import { findHighlightChunksInText } from 'app/core/utils/text'; import { Switch } from 'app/core/components/Switch/Switch'; import Graph from './Graph'; const graphOptions = { series: { bars: { show: true, lineWidth: 5, // barWidth: 10, }, // stack: true, }, yaxis: { tickDecimals: 0, }, }; interface LogsProps { className?: string; data: LogsModel; loading: boolean; position: string; range?: RawTimeRange; scanning?: boolean; scanRange?: RawTimeRange; onChangeTime?: (range: RawTimeRange) => void; onStartScanning?: () => void; onStopScanning?: () => void; } interface LogsState { dedup: LogsDedupStrategy; hiddenLogLevels: Set; showLabels: boolean; showLocalTime: boolean; showUtc: boolean; } export default class Logs extends PureComponent { state = { dedup: LogsDedupStrategy.none, hiddenLogLevels: new Set(), showLabels: true, showLocalTime: true, showUtc: false, }; onChangeDedup = (dedup: LogsDedupStrategy) => { this.setState(prevState => { if (prevState.dedup === dedup) { return { dedup: LogsDedupStrategy.none }; } return { dedup }; }); }; onChangeLabels = (event: React.SyntheticEvent) => { const target = event.target as HTMLInputElement; this.setState({ showLabels: target.checked, }); }; onChangeLocalTime = (event: React.SyntheticEvent) => { const target = event.target as HTMLInputElement; this.setState({ showLocalTime: target.checked, }); }; onChangeUtc = (event: React.SyntheticEvent) => { const target = event.target as HTMLInputElement; this.setState({ showUtc: target.checked, }); }; onToggleLogLevel = (rawLevel: string, hiddenRawLevels: Set) => { const hiddenLogLevels: Set = new Set(Array.from(hiddenRawLevels).map(level => LogLevel[level])); this.setState({ hiddenLogLevels }); }; onClickScan = (event: React.SyntheticEvent) => { event.preventDefault(); this.props.onStartScanning(); }; onClickStopScan = (event: React.SyntheticEvent) => { event.preventDefault(); this.props.onStopScanning(); }; render() { const { className = '', data, loading = false, position, range, scanning, scanRange } = this.props; const { dedup, hiddenLogLevels, showLabels, showLocalTime, showUtc } = this.state; const hasData = data && data.rows && data.rows.length > 0; const filteredData = filterLogLevels(data, hiddenLogLevels); const dedupedData = dedupLogRows(filteredData, dedup); const dedupCount = dedupedData.rows.reduce((sum, row) => sum + row.duplicates, 0); const meta = [...data.meta]; if (dedup !== LogsDedupStrategy.none) { meta.push({ label: 'Dedup count', value: String(dedupCount), }); } const cssColumnSizes = ['3px']; // Log-level indicator line if (showUtc) { cssColumnSizes.push('minmax(100px, max-content)'); } if (showLocalTime) { cssColumnSizes.push('minmax(100px, max-content)'); } if (showLabels) { cssColumnSizes.push('minmax(100px, 25%)'); } cssColumnSizes.push('1fr'); const logEntriesStyle = { gridTemplateColumns: cssColumnSizes.join(' '), }; const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...'; return (
this.onChangeDedup(LogsDedupStrategy.none)} small /> this.onChangeDedup(LogsDedupStrategy.exact)} small /> this.onChangeDedup(LogsDedupStrategy.numbers)} small /> this.onChangeDedup(LogsDedupStrategy.signature)} small /> {hasData && meta && (
{meta.map(item => (
{item.label}: {item.value}
))}
)}
{hasData && dedupedData.rows.map(row => (
{row.duplicates > 0 && (
{Array.apply(null, { length: row.duplicates }).map((bogus, index) => (
))}
)}
{showUtc &&
{row.timestamp}
} {showLocalTime &&
{row.timeLocal}
} {showLabels && (
{row.labels}
)}
))}
{!loading && !hasData && !scanning && (
No logs found. Scan for older logs
)} {scanning && (
{scanText} Stop scan
)}
); } }