2018-12-03 04:58:25 -06:00
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
|
2019-01-22 01:59:22 -06:00
|
|
|
import { calculateLogsLabelStats, LogsLabelStat, LogsStreamLabels, LogRowModel } from 'app/core/logs_model';
|
2018-12-03 04:58:25 -06:00
|
|
|
|
2018-12-03 10:00:50 -06:00
|
|
|
function StatsRow({ active, count, proportion, value }: LogsLabelStat) {
|
2018-12-03 04:58:25 -06:00
|
|
|
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;
|
2018-12-05 05:18:51 -06:00
|
|
|
export class Stats extends PureComponent<{
|
2018-12-03 10:00:50 -06:00
|
|
|
stats: LogsLabelStat[];
|
2018-12-03 04:58:25 -06:00
|
|
|
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 (
|
2018-12-07 04:28:22 -06:00
|
|
|
<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} />
|
2018-12-03 04:58:25 -06:00
|
|
|
</div>
|
2018-12-07 04:28:22 -06:00
|
|
|
<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>
|
2018-12-03 04:58:25 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Label extends PureComponent<
|
|
|
|
{
|
2019-01-22 01:59:22 -06:00
|
|
|
getRows?: () => LogRowModel[];
|
2018-12-03 04:58:25 -06:00
|
|
|
label: string;
|
|
|
|
plain?: boolean;
|
|
|
|
value: string;
|
|
|
|
onClickLabel?: (label: string, value: string) => void;
|
|
|
|
},
|
2018-12-03 10:00:50 -06:00
|
|
|
{ showStats: boolean; stats: LogsLabelStat[] }
|
2018-12-03 04:58:25 -06:00
|
|
|
> {
|
|
|
|
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 };
|
|
|
|
}
|
2018-12-09 06:23:44 -06:00
|
|
|
const allRows = this.props.getRows();
|
|
|
|
const stats = calculateLogsLabelStats(allRows, this.props.label);
|
2018-12-03 04:58:25 -06:00
|
|
|
return { showStats: true, stats };
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
render() {
|
2018-12-09 06:23:44 -06:00
|
|
|
const { getRows, label, plain, value } = this.props;
|
2018-12-03 04:58:25 -06:00
|
|
|
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" />
|
|
|
|
)}
|
2018-12-09 06:23:44 -06:00
|
|
|
{!plain && getRows && <span onClick={this.onClickStats} className="logs-label__icon fa fa-signal" />}
|
2018-12-03 04:58:25 -06:00
|
|
|
{showStats && (
|
|
|
|
<span className="logs-label__stats">
|
|
|
|
<Stats
|
|
|
|
stats={stats}
|
2018-12-09 06:23:44 -06:00
|
|
|
rowCount={getRows().length}
|
2018-12-03 04:58:25 -06:00
|
|
|
label={label}
|
|
|
|
value={value}
|
|
|
|
onClickClose={this.onClickClose}
|
|
|
|
/>
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</span>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class LogLabels extends PureComponent<{
|
2019-01-22 01:59:22 -06:00
|
|
|
getRows?: () => LogRowModel[];
|
2018-12-03 04:58:25 -06:00
|
|
|
labels: LogsStreamLabels;
|
|
|
|
plain?: boolean;
|
|
|
|
onClickLabel?: (label: string, value: string) => void;
|
|
|
|
}> {
|
|
|
|
render() {
|
2018-12-09 06:23:44 -06:00
|
|
|
const { getRows, labels, onClickLabel, plain } = this.props;
|
2018-12-03 04:58:25 -06:00
|
|
|
return Object.keys(labels).map(key => (
|
2018-12-09 06:23:44 -06:00
|
|
|
<Label key={key} getRows={getRows} label={key} value={labels[key]} plain={plain} onClickLabel={onClickLabel} />
|
2018-12-03 04:58:25 -06:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|