diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index b8af798489d..a3f78e7152a 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -56,7 +56,7 @@ export interface LogRowModel { uniqueLabels?: LogsStreamLabels; } -export interface LogsLabelStat { +export interface LogLabelStatsModel { active?: boolean; count: number; proportion: number; @@ -188,7 +188,7 @@ export const LogsParsers: { [name: string]: LogsParser } = { }, }; -export function calculateFieldStats(rows: LogRowModel[], extractor: RegExp): LogsLabelStat[] { +export function calculateFieldStats(rows: LogRowModel[], extractor: RegExp): LogLabelStatsModel[] { // Consider only rows that satisfy the matcher const rowsWithField = rows.filter(row => extractor.test(row.entry)); const rowCount = rowsWithField.length; @@ -204,7 +204,7 @@ export function calculateFieldStats(rows: LogRowModel[], extractor: RegExp): Log return sortedCounts; } -export function calculateLogsLabelStats(rows: LogRowModel[], label: string): LogsLabelStat[] { +export function calculateLogsLabelStats(rows: LogRowModel[], label: string): LogLabelStatsModel[] { // Consider only rows that have the given label const rowsWithLabel = rows.filter(row => row.labels[label] !== undefined); const rowCount = rowsWithLabel.length; diff --git a/public/app/features/explore/LogLabel.tsx b/public/app/features/explore/LogLabel.tsx new file mode 100644 index 00000000000..b4570f10c82 --- /dev/null +++ b/public/app/features/explore/LogLabel.tsx @@ -0,0 +1,74 @@ +import React, { PureComponent } from 'react'; + +import { calculateLogsLabelStats, LogLabelStatsModel, LogRowModel } from 'app/core/logs_model'; +import { LogLabelStats } from './LogLabelStats'; + +interface Props { + getRows?: () => LogRowModel[]; + label: string; + plain?: boolean; + value: string; + onClickLabel?: (label: string, value: string) => void; +} + +interface State { + showStats: boolean; + stats: LogLabelStatsModel[]; +} + +export class LogLabel extends PureComponent { + state = { + stats: null, + showStats: false, + }; + + onClickClose = () => { + this.setState({ showStats: false }); + }; + + onClickLabel = () => { + const { onClickLabel, label, value } = this.props; + if (onClickLabel) { + onClickLabel(label, value); + } + }; + + onClickStats = () => { + this.setState(state => { + if (state.showStats) { + return { showStats: false, stats: null }; + } + const allRows = this.props.getRows(); + const stats = calculateLogsLabelStats(allRows, this.props.label); + return { showStats: true, stats }; + }); + }; + + render() { + const { getRows, label, plain, value } = this.props; + const { showStats, stats } = this.state; + const tooltip = `${label}: ${value}`; + return ( + + + {value} + + {!plain && ( + + )} + {!plain && getRows && } + {showStats && ( + + + + )} + + ); + } +} diff --git a/public/app/features/explore/LogLabelStats.tsx b/public/app/features/explore/LogLabelStats.tsx new file mode 100644 index 00000000000..b0bd69170c5 --- /dev/null +++ b/public/app/features/explore/LogLabelStats.tsx @@ -0,0 +1,72 @@ +import React, { PureComponent } from 'react'; +import classnames from 'classnames'; +import { LogLabelStatsModel } from 'app/core/logs_model'; + +function LogLabelStatsRow(logLabelStatsModel: LogLabelStatsModel) { + const { active, count, proportion, value } = logLabelStatsModel; + const percent = `${Math.round(proportion * 100)}%`; + const barStyle = { width: percent }; + const className = classnames('logs-stats-row', { 'logs-stats-row--active': active }); + + return ( +
+
+
{value}
+
{count}
+
{percent}
+
+
+
+
+
+ ); +} + +const STATS_ROW_LIMIT = 5; + +interface Props { + stats: LogLabelStatsModel[]; + label: string; + value: string; + rowCount: number; + onClickClose: () => void; +} + +export class LogLabelStats extends PureComponent { + render() { + const { label, rowCount, stats, value, onClickClose } = this.props; + const topRows = stats.slice(0, STATS_ROW_LIMIT); + let activeRow = topRows.find(row => row.value === value); + let otherRows = stats.slice(STATS_ROW_LIMIT); + const insertActiveRow = !activeRow; + + // Remove active row from other to show extra + if (insertActiveRow) { + activeRow = otherRows.find(row => row.value === value); + otherRows = otherRows.filter(row => row.value !== value); + } + + const otherCount = otherRows.reduce((sum, row) => sum + row.count, 0); + const topCount = topRows.reduce((sum, row) => sum + row.count, 0); + const total = topCount + otherCount; + const otherProportion = otherCount / total; + + return ( +
+
+ + {label}: {total} of {rowCount} rows have that label + + +
+
+ {topRows.map(stat => )} + {insertActiveRow && activeRow && } + {otherCount > 0 && ( + + )} +
+
+ ); + } +} diff --git a/public/app/features/explore/LogLabels.tsx b/public/app/features/explore/LogLabels.tsx index 1dc82637f58..7105a2a5370 100644 --- a/public/app/features/explore/LogLabels.tsx +++ b/public/app/features/explore/LogLabels.tsx @@ -1,147 +1,20 @@ import React, { PureComponent } from 'react'; -import classnames from 'classnames'; -import { calculateLogsLabelStats, LogsLabelStat, LogsStreamLabels, LogRowModel } from 'app/core/logs_model'; +import { LogsStreamLabels, LogRowModel } from 'app/core/logs_model'; +import { LogLabel } from './LogLabel'; -function StatsRow({ active, count, proportion, value }: LogsLabelStat) { - const percent = `${Math.round(proportion * 100)}%`; - const barStyle = { width: percent }; - const className = classnames('logs-stats-row', { 'logs-stats-row--active': active }); - - return ( -
-
-
{value}
-
{count}
-
{percent}
-
-
-
-
-
- ); -} - -const STATS_ROW_LIMIT = 5; -export class Stats extends PureComponent<{ - stats: LogsLabelStat[]; - label: string; - value: string; - rowCount: number; - onClickClose: () => void; -}> { - render() { - const { label, rowCount, stats, value, onClickClose } = this.props; - const topRows = stats.slice(0, STATS_ROW_LIMIT); - let activeRow = topRows.find(row => row.value === value); - let otherRows = stats.slice(STATS_ROW_LIMIT); - const insertActiveRow = !activeRow; - // Remove active row from other to show extra - if (insertActiveRow) { - activeRow = otherRows.find(row => row.value === value); - otherRows = otherRows.filter(row => row.value !== value); - } - const otherCount = otherRows.reduce((sum, row) => sum + row.count, 0); - const topCount = topRows.reduce((sum, row) => sum + row.count, 0); - const total = topCount + otherCount; - const otherProportion = otherCount / total; - - return ( -
-
- - {label}: {total} of {rowCount} rows have that label - - -
-
- {topRows.map(stat => )} - {insertActiveRow && activeRow && } - {otherCount > 0 && ( - - )} -
-
- ); - } -} - -class Label extends PureComponent< - { - getRows?: () => LogRowModel[]; - label: string; - plain?: boolean; - value: string; - onClickLabel?: (label: string, value: string) => void; - }, - { showStats: boolean; stats: LogsLabelStat[] } -> { - state = { - stats: null, - showStats: false, - }; - - onClickClose = () => { - this.setState({ showStats: false }); - }; - - onClickLabel = () => { - const { onClickLabel, label, value } = this.props; - if (onClickLabel) { - onClickLabel(label, value); - } - }; - - onClickStats = () => { - this.setState(state => { - if (state.showStats) { - return { showStats: false, stats: null }; - } - const allRows = this.props.getRows(); - const stats = calculateLogsLabelStats(allRows, this.props.label); - return { showStats: true, stats }; - }); - }; - - render() { - const { getRows, label, plain, value } = this.props; - const { showStats, stats } = this.state; - const tooltip = `${label}: ${value}`; - return ( - - - {value} - - {!plain && ( - - )} - {!plain && getRows && } - {showStats && ( - - - - )} - - ); - } -} - -export default class LogLabels extends PureComponent<{ +interface Props { getRows?: () => LogRowModel[]; labels: LogsStreamLabels; plain?: boolean; onClickLabel?: (label: string, value: string) => void; -}> { +} + +export class LogLabels extends PureComponent { render() { const { getRows, labels, onClickLabel, plain } = this.props; return Object.keys(labels).map(key => ( -