mirror of https://github.com/grafana/grafana
parent
23202ab130
commit
c369279401
@ -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<Props, State> { |
||||
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 ( |
||||
<span className="logs-label"> |
||||
<span className="logs-label__value" title={tooltip}> |
||||
{value} |
||||
</span> |
||||
{!plain && ( |
||||
<span title="Filter for label" onClick={this.onClickLabel} className="logs-label__icon fa fa-search-plus" /> |
||||
)} |
||||
{!plain && getRows && <span onClick={this.onClickStats} className="logs-label__icon fa fa-signal" />} |
||||
{showStats && ( |
||||
<span className="logs-label__stats"> |
||||
<LogLabelStats |
||||
stats={stats} |
||||
rowCount={getRows().length} |
||||
label={label} |
||||
value={value} |
||||
onClickClose={this.onClickClose} |
||||
/> |
||||
</span> |
||||
)} |
||||
</span> |
||||
); |
||||
} |
||||
} |
@ -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 ( |
||||
<div className={className}> |
||||
<div className="logs-stats-row__label"> |
||||
<div className="logs-stats-row__value">{value}</div> |
||||
<div className="logs-stats-row__count">{count}</div> |
||||
<div className="logs-stats-row__percent">{percent}</div> |
||||
</div> |
||||
<div className="logs-stats-row__bar"> |
||||
<div className="logs-stats-row__innerbar" style={barStyle} /> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
const STATS_ROW_LIMIT = 5; |
||||
|
||||
interface Props { |
||||
stats: LogLabelStatsModel[]; |
||||
label: string; |
||||
value: string; |
||||
rowCount: number; |
||||
onClickClose: () => void; |
||||
} |
||||
|
||||
export class LogLabelStats extends PureComponent<Props> { |
||||
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 ( |
||||
<div className="logs-stats"> |
||||
<div className="logs-stats__header"> |
||||
<span className="logs-stats__title"> |
||||
{label}: {total} of {rowCount} rows have that label |
||||
</span> |
||||
<span className="logs-stats__close fa fa-remove" onClick={onClickClose} /> |
||||
</div> |
||||
<div className="logs-stats__body"> |
||||
{topRows.map(stat => <LogLabelStatsRow key={stat.value} {...stat} active={stat.value === value} />)} |
||||
{insertActiveRow && activeRow && <LogLabelStatsRow key={activeRow.value} {...activeRow} active />} |
||||
{otherCount > 0 && ( |
||||
<LogLabelStatsRow key="__OTHERS__" count={otherCount} value="Other" proportion={otherProportion} /> |
||||
)} |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
@ -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 ( |
||||
<div className={className}> |
||||
<div className="logs-stats-row__label"> |
||||
<div className="logs-stats-row__value">{value}</div> |
||||
<div className="logs-stats-row__count">{count}</div> |
||||
<div className="logs-stats-row__percent">{percent}</div> |
||||
</div> |
||||
<div className="logs-stats-row__bar"> |
||||
<div className="logs-stats-row__innerbar" style={barStyle} /> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
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 ( |
||||
<div className="logs-stats"> |
||||
<div className="logs-stats__header"> |
||||
<span className="logs-stats__title"> |
||||
{label}: {total} of {rowCount} rows have that label |
||||
</span> |
||||
<span className="logs-stats__close fa fa-remove" onClick={onClickClose} /> |
||||
</div> |
||||
<div className="logs-stats__body"> |
||||
{topRows.map(stat => <StatsRow key={stat.value} {...stat} active={stat.value === value} />)} |
||||
{insertActiveRow && activeRow && <StatsRow key={activeRow.value} {...activeRow} active />} |
||||
{otherCount > 0 && ( |
||||
<StatsRow key="__OTHERS__" count={otherCount} value="Other" proportion={otherProportion} /> |
||||
)} |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
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 ( |
||||
<span className="logs-label"> |
||||
<span className="logs-label__value" title={tooltip}> |
||||
{value} |
||||
</span> |
||||
{!plain && ( |
||||
<span title="Filter for label" onClick={this.onClickLabel} className="logs-label__icon fa fa-search-plus" /> |
||||
)} |
||||
{!plain && getRows && <span onClick={this.onClickStats} className="logs-label__icon fa fa-signal" />} |
||||
{showStats && ( |
||||
<span className="logs-label__stats"> |
||||
<Stats |
||||
stats={stats} |
||||
rowCount={getRows().length} |
||||
label={label} |
||||
value={value} |
||||
onClickClose={this.onClickClose} |
||||
/> |
||||
</span> |
||||
)} |
||||
</span> |
||||
); |
||||
} |
||||
} |
||||
|
||||
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<Props> { |
||||
render() { |
||||
const { getRows, labels, onClickLabel, plain } = this.props; |
||||
return Object.keys(labels).map(key => ( |
||||
<Label key={key} getRows={getRows} label={key} value={labels[key]} plain={plain} onClickLabel={onClickLabel} /> |
||||
<LogLabel key={key} getRows={getRows} label={key} value={labels[key]} plain={plain} onClickLabel={onClickLabel} /> |
||||
)); |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue