mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Logs: Optional logs label column (#21025)
* Logs: Optional logs label column - reintroduces label column that was removed when log details were introduced - added to explore and also as a new option to logs panel - explore column settings now stored in localstorage - labels are rendered with font size xs - labels that start with `_` or are called `level` or `filename` are not displayed - removed click handlers and moved remaining `LogLabel` logic into `LogLabels` * Added prop to satisfy interface * Review feedback * removed comment * Changed label of label column switch
This commit is contained in:
parent
c1b74becce
commit
e7ae220cde
@ -1,122 +0,0 @@
|
|||||||
import React, { PureComponent } from 'react';
|
|
||||||
import { css, cx } from 'emotion';
|
|
||||||
import { LogRowModel, LogLabelStatsModel, calculateLogsLabelStats } from '@grafana/data';
|
|
||||||
|
|
||||||
import { LogLabelStats } from './LogLabelStats';
|
|
||||||
import { Themeable } from '../../types/theme';
|
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
|
||||||
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
|
||||||
import { withTheme } from '../../themes/ThemeContext';
|
|
||||||
import { stylesFactory } from '../../themes';
|
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|
||||||
return {
|
|
||||||
logsLabel: css`
|
|
||||||
label: logs-label;
|
|
||||||
display: flex;
|
|
||||||
padding: 0 2px;
|
|
||||||
background-color: ${selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark6 }, theme.type)};
|
|
||||||
border-radius: ${theme.border.radius};
|
|
||||||
margin: 0 4px 2px 0;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
`,
|
|
||||||
logsLabelValue: css`
|
|
||||||
label: logs-label__value;
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 20em;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
`,
|
|
||||||
logsLabelIcon: css`
|
|
||||||
label: logs-label__icon;
|
|
||||||
border-left: solid 1px ${selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark1 }, theme.type)};
|
|
||||||
padding: 0 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-left: 2px;
|
|
||||||
`,
|
|
||||||
logsLabelStats: css`
|
|
||||||
position: absolute;
|
|
||||||
top: 1.25em;
|
|
||||||
left: -10px;
|
|
||||||
z-index: 100;
|
|
||||||
justify-content: space-between;
|
|
||||||
box-shadow: 0 0 20px ${selectThemeVariant({ light: theme.colors.white, dark: theme.colors.black }, theme.type)};
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props extends Themeable {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
getRows: () => LogRowModel[];
|
|
||||||
plain?: boolean;
|
|
||||||
onClickLabel?: (label: string, value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
showStats: boolean;
|
|
||||||
stats: LogLabelStatsModel[];
|
|
||||||
}
|
|
||||||
|
|
||||||
class UnThemedLogLabel extends PureComponent<Props, State> {
|
|
||||||
state: State = {
|
|
||||||
stats: [],
|
|
||||||
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: [] };
|
|
||||||
}
|
|
||||||
const allRows = this.props.getRows();
|
|
||||||
const stats = calculateLogsLabelStats(allRows, this.props.label);
|
|
||||||
return { showStats: true, stats };
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { getRows, label, plain, value, theme } = this.props;
|
|
||||||
const styles = getStyles(theme);
|
|
||||||
const { showStats, stats } = this.state;
|
|
||||||
const tooltip = `${label}: ${value}`;
|
|
||||||
return (
|
|
||||||
<span className={cx([styles.logsLabel])}>
|
|
||||||
<span className={cx([styles.logsLabelValue])} title={tooltip}>
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
{!plain && (
|
|
||||||
<span
|
|
||||||
title="Filter for label"
|
|
||||||
onClick={this.onClickLabel}
|
|
||||||
className={cx([styles.logsLabelIcon, 'fa fa-search-plus'])}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!plain && getRows && (
|
|
||||||
<span onClick={this.onClickStats} className={cx([styles.logsLabelIcon, 'fa fa-signal'])} />
|
|
||||||
)}
|
|
||||||
{showStats && (
|
|
||||||
<span className={cx([styles.logsLabelStats])}>
|
|
||||||
<LogLabelStats stats={stats} rowCount={getRows().length} label={label} value={value} isLabel={true} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LogLabel = withTheme(UnThemedLogLabel);
|
|
||||||
LogLabel.displayName = 'LogLabel';
|
|
23
packages/grafana-ui/src/components/Logs/LogLabels.test.tsx
Normal file
23
packages/grafana-ui/src/components/Logs/LogLabels.test.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
|
||||||
|
import { UnThemedLogLabels as LogLabels } from './LogLabels';
|
||||||
|
import { getTheme } from '../../themes';
|
||||||
|
|
||||||
|
describe('<LogLabels />', () => {
|
||||||
|
it('renders notice when no labels are found', () => {
|
||||||
|
const wrapper = shallow(<LogLabels labels={{}} theme={getTheme()} />);
|
||||||
|
expect(wrapper.text()).toContain('no unique labels');
|
||||||
|
});
|
||||||
|
it('renders labels', () => {
|
||||||
|
const wrapper = shallow(<LogLabels labels={{ foo: 'bar', baz: '42' }} theme={getTheme()} />);
|
||||||
|
expect(wrapper.text()).toContain('bar');
|
||||||
|
expect(wrapper.text()).toContain('42');
|
||||||
|
});
|
||||||
|
it('exlcudes labels with certain names or labels starting with underscore', () => {
|
||||||
|
const wrapper = shallow(<LogLabels labels={{ foo: 'bar', level: '42', _private: '13' }} theme={getTheme()} />);
|
||||||
|
expect(wrapper.text()).toContain('bar');
|
||||||
|
expect(wrapper.text()).not.toContain('42');
|
||||||
|
expect(wrapper.text()).not.toContain('13');
|
||||||
|
});
|
||||||
|
});
|
@ -1,41 +1,76 @@
|
|||||||
import React, { FunctionComponent } from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { Labels, LogRowModel } from '@grafana/data';
|
import { Labels } from '@grafana/data';
|
||||||
|
|
||||||
import { LogLabel } from './LogLabel';
|
|
||||||
import { stylesFactory } from '../../themes';
|
import { stylesFactory } from '../../themes';
|
||||||
|
import { Themeable } from '../../types/theme';
|
||||||
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
|
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
||||||
|
import { withTheme } from '../../themes/ThemeContext';
|
||||||
|
|
||||||
const getStyles = stylesFactory(() => ({
|
// Levels are already encoded in color, filename is a Loki-ism
|
||||||
logsLabels: css`
|
const HIDDEN_LABELS = ['level', 'lvl', 'filename'];
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface Props {
|
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||||
|
return {
|
||||||
|
logsLabels: css`
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: ${theme.typography.size.xs};
|
||||||
|
`,
|
||||||
|
logsLabel: css`
|
||||||
|
label: logs-label;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 2px;
|
||||||
|
background-color: ${selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark6 }, theme.type)};
|
||||||
|
border-radius: ${theme.border.radius};
|
||||||
|
margin: 0 4px 2px 0;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
`,
|
||||||
|
logsLabelValue: css`
|
||||||
|
label: logs-label__value;
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 20em;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props extends Themeable {
|
||||||
labels: Labels;
|
labels: Labels;
|
||||||
getRows: () => LogRowModel[];
|
|
||||||
plain?: boolean;
|
|
||||||
onClickLabel?: (label: string, value: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LogLabels: FunctionComponent<Props> = ({ getRows, labels, onClickLabel, plain }) => {
|
export const UnThemedLogLabels: FunctionComponent<Props> = ({ labels, theme }) => {
|
||||||
const styles = getStyles();
|
const styles = getStyles(theme);
|
||||||
|
const displayLabels = Object.keys(labels).filter(label => !label.startsWith('_') && !HIDDEN_LABELS.includes(label));
|
||||||
|
|
||||||
|
if (displayLabels.length === 0) {
|
||||||
|
return (
|
||||||
|
<span className={cx([styles.logsLabels])}>
|
||||||
|
<span className={cx([styles.logsLabel])}>(no unique labels)</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={cx([styles.logsLabels])}>
|
<span className={cx([styles.logsLabels])}>
|
||||||
{Object.keys(labels).map(key => (
|
{displayLabels.map(label => {
|
||||||
<LogLabel
|
const value = labels[label];
|
||||||
key={key}
|
const tooltip = `${label}: ${value}`;
|
||||||
getRows={getRows}
|
return (
|
||||||
label={key}
|
<span key={label} className={cx([styles.logsLabel])}>
|
||||||
value={labels[key]}
|
<span className={cx([styles.logsLabelValue])} title={tooltip}>
|
||||||
plain={plain}
|
{value}
|
||||||
onClickLabel={onClickLabel}
|
</span>
|
||||||
/>
|
</span>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const LogLabels = withTheme(UnThemedLogLabels);
|
||||||
LogLabels.displayName = 'LogLabels';
|
LogLabels.displayName = 'LogLabels';
|
||||||
|
@ -16,11 +16,13 @@ import { stylesFactory } from '../../themes/stylesFactory';
|
|||||||
//Components
|
//Components
|
||||||
import { LogDetails } from './LogDetails';
|
import { LogDetails } from './LogDetails';
|
||||||
import { LogRowMessage } from './LogRowMessage';
|
import { LogRowMessage } from './LogRowMessage';
|
||||||
|
import { LogLabels } from './LogLabels';
|
||||||
|
|
||||||
interface Props extends Themeable {
|
interface Props extends Themeable {
|
||||||
highlighterExpressions?: string[];
|
highlighterExpressions?: string[];
|
||||||
row: LogRowModel;
|
row: LogRowModel;
|
||||||
showDuplicates: boolean;
|
showDuplicates: boolean;
|
||||||
|
showLabels: boolean;
|
||||||
showTime: boolean;
|
showTime: boolean;
|
||||||
wrapLogMessage: boolean;
|
wrapLogMessage: boolean;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
@ -93,6 +95,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
row,
|
row,
|
||||||
showDuplicates,
|
showDuplicates,
|
||||||
timeZone,
|
timeZone,
|
||||||
|
showLabels,
|
||||||
showTime,
|
showTime,
|
||||||
wrapLogMessage,
|
wrapLogMessage,
|
||||||
theme,
|
theme,
|
||||||
@ -135,6 +138,11 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
{row.timeLocal}
|
{row.timeLocal}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{showLabels && row.uniqueLabels && (
|
||||||
|
<div className={style.logsRowLabels}>
|
||||||
|
<LogLabels labels={row.uniqueLabels} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<LogRowMessage
|
<LogRowMessage
|
||||||
highlighterExpressions={highlighterExpressions}
|
highlighterExpressions={highlighterExpressions}
|
||||||
row={row}
|
row={row}
|
||||||
|
@ -13,6 +13,7 @@ describe('LogRows', () => {
|
|||||||
logRows={rows}
|
logRows={rows}
|
||||||
dedupStrategy={LogsDedupStrategy.none}
|
dedupStrategy={LogsDedupStrategy.none}
|
||||||
highlighterExpressions={[]}
|
highlighterExpressions={[]}
|
||||||
|
showLabels={false}
|
||||||
showTime={false}
|
showTime={false}
|
||||||
wrapLogMessage={true}
|
wrapLogMessage={true}
|
||||||
timeZone={'utc'}
|
timeZone={'utc'}
|
||||||
@ -33,6 +34,7 @@ describe('LogRows', () => {
|
|||||||
logRows={rows}
|
logRows={rows}
|
||||||
dedupStrategy={LogsDedupStrategy.none}
|
dedupStrategy={LogsDedupStrategy.none}
|
||||||
highlighterExpressions={[]}
|
highlighterExpressions={[]}
|
||||||
|
showLabels={false}
|
||||||
showTime={false}
|
showTime={false}
|
||||||
wrapLogMessage={true}
|
wrapLogMessage={true}
|
||||||
timeZone={'utc'}
|
timeZone={'utc'}
|
||||||
@ -62,6 +64,7 @@ describe('LogRows', () => {
|
|||||||
deduplicatedRows={dedupedRows}
|
deduplicatedRows={dedupedRows}
|
||||||
dedupStrategy={LogsDedupStrategy.none}
|
dedupStrategy={LogsDedupStrategy.none}
|
||||||
highlighterExpressions={[]}
|
highlighterExpressions={[]}
|
||||||
|
showLabels={false}
|
||||||
showTime={false}
|
showTime={false}
|
||||||
wrapLogMessage={true}
|
wrapLogMessage={true}
|
||||||
timeZone={'utc'}
|
timeZone={'utc'}
|
||||||
@ -81,6 +84,7 @@ describe('LogRows', () => {
|
|||||||
logRows={rows}
|
logRows={rows}
|
||||||
dedupStrategy={LogsDedupStrategy.none}
|
dedupStrategy={LogsDedupStrategy.none}
|
||||||
highlighterExpressions={[]}
|
highlighterExpressions={[]}
|
||||||
|
showLabels={false}
|
||||||
showTime={false}
|
showTime={false}
|
||||||
wrapLogMessage={true}
|
wrapLogMessage={true}
|
||||||
timeZone={'utc'}
|
timeZone={'utc'}
|
||||||
|
@ -17,6 +17,7 @@ export interface Props extends Themeable {
|
|||||||
deduplicatedRows?: LogRowModel[];
|
deduplicatedRows?: LogRowModel[];
|
||||||
dedupStrategy: LogsDedupStrategy;
|
dedupStrategy: LogsDedupStrategy;
|
||||||
highlighterExpressions?: string[];
|
highlighterExpressions?: string[];
|
||||||
|
showLabels: boolean;
|
||||||
showTime: boolean;
|
showTime: boolean;
|
||||||
wrapLogMessage: boolean;
|
wrapLogMessage: boolean;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
@ -71,6 +72,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
dedupStrategy,
|
dedupStrategy,
|
||||||
|
showLabels,
|
||||||
showTime,
|
showTime,
|
||||||
wrapLogMessage,
|
wrapLogMessage,
|
||||||
logRows,
|
logRows,
|
||||||
@ -117,6 +119,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
highlighterExpressions={highlighterExpressions}
|
highlighterExpressions={highlighterExpressions}
|
||||||
row={row}
|
row={row}
|
||||||
showDuplicates={showDuplicates}
|
showDuplicates={showDuplicates}
|
||||||
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
@ -135,6 +138,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
getRowContext={getRowContext}
|
getRowContext={getRowContext}
|
||||||
row={row}
|
row={row}
|
||||||
showDuplicates={showDuplicates}
|
showDuplicates={showDuplicates}
|
||||||
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
|
@ -136,6 +136,13 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
|
|||||||
width: 12.5em;
|
width: 12.5em;
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
`,
|
`,
|
||||||
|
logsRowLabels: css`
|
||||||
|
label: logs-row__labels;
|
||||||
|
display: table-cell;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 22em;
|
||||||
|
padding-right: 1em;
|
||||||
|
`,
|
||||||
logsRowMessage: css`
|
logsRowMessage: css`
|
||||||
label: logs-row__message;
|
label: logs-row__message;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
@ -9,7 +9,7 @@ export class Store {
|
|||||||
window.localStorage[key] = value;
|
window.localStorage[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBool(key: string, def: any) {
|
getBool(key: string, def: boolean): boolean {
|
||||||
if (def !== void 0 && !this.exists(key)) {
|
if (def !== void 0 && !this.exists(key)) {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,21 @@ import {
|
|||||||
Field,
|
Field,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { Switch, LogLabels, ToggleButtonGroup, ToggleButton, LogRows } from '@grafana/ui';
|
import { Switch, LogLabels, ToggleButtonGroup, ToggleButton, LogRows } from '@grafana/ui';
|
||||||
|
import store from 'app/core/store';
|
||||||
|
|
||||||
import { ExploreGraphPanel } from './ExploreGraphPanel';
|
import { ExploreGraphPanel } from './ExploreGraphPanel';
|
||||||
|
|
||||||
|
const SETTINGS_KEYS = {
|
||||||
|
showLabels: 'grafana.explore.logs.showLabels',
|
||||||
|
showTime: 'grafana.explore.logs.showTime',
|
||||||
|
wrapLogMessage: 'grafana.explore.logs.wrapLogMessage',
|
||||||
|
};
|
||||||
|
|
||||||
function renderMetaItem(value: any, kind: LogsMetaKind) {
|
function renderMetaItem(value: any, kind: LogsMetaKind) {
|
||||||
if (kind === LogsMetaKind.LabelsMap) {
|
if (kind === LogsMetaKind.LabelsMap) {
|
||||||
return (
|
return (
|
||||||
<span className="logs-meta-item__labels">
|
<span className="logs-meta-item__labels">
|
||||||
<LogLabels labels={value} plain getRows={() => []} />
|
<LogLabels labels={value} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -56,14 +63,16 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
showLabels: boolean;
|
||||||
showTime: boolean;
|
showTime: boolean;
|
||||||
wrapLogMessage: boolean;
|
wrapLogMessage: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Logs extends PureComponent<Props, State> {
|
export class Logs extends PureComponent<Props, State> {
|
||||||
state = {
|
state = {
|
||||||
showTime: true,
|
showLabels: store.getBool(SETTINGS_KEYS.showLabels, false),
|
||||||
wrapLogMessage: true,
|
showTime: store.getBool(SETTINGS_KEYS.showTime, true),
|
||||||
|
wrapLogMessage: store.getBool(SETTINGS_KEYS.wrapLogMessage, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
onChangeDedup = (dedup: LogsDedupStrategy) => {
|
onChangeDedup = (dedup: LogsDedupStrategy) => {
|
||||||
@ -74,21 +83,36 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
return onDedupStrategyChange(dedup);
|
return onDedupStrategyChange(dedup);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onChangeLabels = (event?: React.SyntheticEvent) => {
|
||||||
|
const target = event && (event.target as HTMLInputElement);
|
||||||
|
if (target) {
|
||||||
|
const showLabels = target.checked;
|
||||||
|
this.setState({
|
||||||
|
showLabels,
|
||||||
|
});
|
||||||
|
store.set(SETTINGS_KEYS.showLabels, showLabels);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onChangeTime = (event?: React.SyntheticEvent) => {
|
onChangeTime = (event?: React.SyntheticEvent) => {
|
||||||
const target = event && (event.target as HTMLInputElement);
|
const target = event && (event.target as HTMLInputElement);
|
||||||
if (target) {
|
if (target) {
|
||||||
|
const showTime = target.checked;
|
||||||
this.setState({
|
this.setState({
|
||||||
showTime: target.checked,
|
showTime,
|
||||||
});
|
});
|
||||||
|
store.set(SETTINGS_KEYS.showTime, showTime);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onChangewrapLogMessage = (event?: React.SyntheticEvent) => {
|
onChangewrapLogMessage = (event?: React.SyntheticEvent) => {
|
||||||
const target = event && (event.target as HTMLInputElement);
|
const target = event && (event.target as HTMLInputElement);
|
||||||
if (target) {
|
if (target) {
|
||||||
|
const wrapLogMessage = target.checked;
|
||||||
this.setState({
|
this.setState({
|
||||||
wrapLogMessage: target.checked,
|
wrapLogMessage,
|
||||||
});
|
});
|
||||||
|
store.set(SETTINGS_KEYS.wrapLogMessage, wrapLogMessage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,7 +158,7 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { showTime, wrapLogMessage } = this.state;
|
const { showLabels, showTime, wrapLogMessage } = this.state;
|
||||||
const { dedupStrategy } = this.props;
|
const { dedupStrategy } = this.props;
|
||||||
const hasData = logRows && logRows.length > 0;
|
const hasData = logRows && logRows.length > 0;
|
||||||
const dedupCount = dedupedRows
|
const dedupCount = dedupedRows
|
||||||
@ -175,6 +199,7 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
<div className="logs-panel-options">
|
<div className="logs-panel-options">
|
||||||
<div className="logs-panel-controls">
|
<div className="logs-panel-controls">
|
||||||
<Switch label="Time" checked={showTime} onChange={this.onChangeTime} transparent />
|
<Switch label="Time" checked={showTime} onChange={this.onChangeTime} transparent />
|
||||||
|
<Switch label="Unique labels" checked={showLabels} onChange={this.onChangeLabels} transparent />
|
||||||
<Switch label="Wrap lines" checked={wrapLogMessage} onChange={this.onChangewrapLogMessage} transparent />
|
<Switch label="Wrap lines" checked={wrapLogMessage} onChange={this.onChangewrapLogMessage} transparent />
|
||||||
<ToggleButtonGroup label="Dedup" transparent={true}>
|
<ToggleButtonGroup label="Dedup" transparent={true}>
|
||||||
{Object.keys(LogsDedupStrategy).map((dedupType: string, i) => (
|
{Object.keys(LogsDedupStrategy).map((dedupType: string, i) => (
|
||||||
@ -213,6 +238,7 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
rowLimit={logRows ? logRows.length : undefined}
|
rowLimit={logRows ? logRows.length : undefined}
|
||||||
onClickFilterLabel={onClickFilterLabel}
|
onClickFilterLabel={onClickFilterLabel}
|
||||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||||
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
|
@ -10,7 +10,7 @@ interface LogsPanelProps extends PanelProps<Options> {}
|
|||||||
export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
|
export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
|
||||||
data,
|
data,
|
||||||
timeZone,
|
timeZone,
|
||||||
options: { showTime, wrapLogMessage, sortOrder },
|
options: { showLabels, showTime, wrapLogMessage, sortOrder },
|
||||||
width,
|
width,
|
||||||
}) => {
|
}) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@ -30,6 +30,7 @@ export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
|
|||||||
logRows={sortedNewResults.rows}
|
logRows={sortedNewResults.rows}
|
||||||
dedupStrategy={LogsDedupStrategy.none}
|
dedupStrategy={LogsDedupStrategy.none}
|
||||||
highlighterExpressions={[]}
|
highlighterExpressions={[]}
|
||||||
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
|
@ -13,6 +13,13 @@ const sortOrderOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
||||||
|
onToggleLabels = () => {
|
||||||
|
const { options, onOptionsChange } = this.props;
|
||||||
|
const { showLabels } = options;
|
||||||
|
|
||||||
|
onOptionsChange({ ...options, showLabels: !showLabels });
|
||||||
|
};
|
||||||
|
|
||||||
onToggleTime = () => {
|
onToggleTime = () => {
|
||||||
const { options, onOptionsChange } = this.props;
|
const { options, onOptionsChange } = this.props;
|
||||||
const { showTime } = options;
|
const { showTime } = options;
|
||||||
@ -33,7 +40,7 @@ export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { showTime, wrapLogMessage, sortOrder } = this.props.options;
|
const { showLabels, showTime, wrapLogMessage, sortOrder } = this.props.options;
|
||||||
const value = sortOrderOptions.filter(option => option.value === sortOrder)[0];
|
const value = sortOrderOptions.filter(option => option.value === sortOrder)[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -41,6 +48,7 @@ export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|||||||
<PanelOptionsGrid>
|
<PanelOptionsGrid>
|
||||||
<PanelOptionsGroup title="Columns">
|
<PanelOptionsGroup title="Columns">
|
||||||
<Switch label="Time" labelClass="width-10" checked={showTime} onChange={this.onToggleTime} />
|
<Switch label="Time" labelClass="width-10" checked={showTime} onChange={this.onToggleTime} />
|
||||||
|
<Switch label="Labels" labelClass="width-10" checked={showLabels} onChange={this.onToggleLabels} />
|
||||||
<Switch
|
<Switch
|
||||||
label="Wrap lines"
|
label="Wrap lines"
|
||||||
labelClass="width-10"
|
labelClass="width-10"
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { SortOrder } from 'app/core/utils/explore';
|
import { SortOrder } from 'app/core/utils/explore';
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
|
showLabels: boolean;
|
||||||
showTime: boolean;
|
showTime: boolean;
|
||||||
wrapLogMessage: boolean;
|
wrapLogMessage: boolean;
|
||||||
sortOrder: SortOrder;
|
sortOrder: SortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: Options = {
|
export const defaults: Options = {
|
||||||
|
showLabels: false,
|
||||||
showTime: true,
|
showTime: true,
|
||||||
wrapLogMessage: true,
|
wrapLogMessage: true,
|
||||||
sortOrder: SortOrder.Descending,
|
sortOrder: SortOrder.Descending,
|
||||||
|
Loading…
Reference in New Issue
Block a user