Logs Panel: Add unique labels support for dashboards (#111124)
* New logs panel: add basic show unique labels support
* LogList: switch to useMeasure to detect overflow
* LogList: remove debounce from size calculations reset
* Update test
* LogLine: keep unique labels expanded state between re-renders
* Remove hardcoded true
* LogListModel: add new property
* LogLine: switch to custom resize observer
* Revert removed changes
* Imports order
* Add missing function call in effect
* LogList: use improved debouncing for overflow
* LogLine: refactor resize listeners
* LogLine: mix observer with animation frame
* Prettier
* LogLabels: make button smaller
* LogLine: fix unwrapped unique labels
* Prettier
* LogListContext: sync mode only if not empty
* Revert "LogListContext: sync mode only if not empty"
This reverts commit 2b78249b35.
* module: remove default value
This commit is contained in:
@@ -68,6 +68,7 @@ export const LogLabels = memo(
|
||||
size="sm"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
className={styles.button}
|
||||
aria-label={t('logs.log-labels.expand', 'Expand labels')}
|
||||
onClick={() => {
|
||||
setDisplayAll(true);
|
||||
@@ -83,6 +84,7 @@ export const LogLabels = memo(
|
||||
size="sm"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
className={styles.button}
|
||||
aria-label={t('logs.log-labels.collapse', 'Collapse labels')}
|
||||
onClick={() => {
|
||||
setDisplayAll(false);
|
||||
@@ -137,7 +139,7 @@ LogLabel.displayName = 'LogLabel';
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
logsLabels: css({
|
||||
display: 'flex',
|
||||
display: 'inline-flex',
|
||||
flexWrap: 'wrap',
|
||||
fontSize: theme.typography.size.xs,
|
||||
alignItems: 'center',
|
||||
@@ -161,5 +163,8 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
button: css({
|
||||
height: theme.spacing(2.75),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -613,4 +613,34 @@ describe('getGridTemplateColumns', () => {
|
||||
)
|
||||
).toBe('23px 4px 4px 20px');
|
||||
});
|
||||
|
||||
test('Gets the template columns with unique labels', () => {
|
||||
expect(
|
||||
getGridTemplateColumns(
|
||||
[
|
||||
{
|
||||
field: 'timestamp',
|
||||
width: 23,
|
||||
},
|
||||
{
|
||||
field: 'level',
|
||||
width: 4,
|
||||
},
|
||||
{
|
||||
field: 'unique-labels',
|
||||
width: 0,
|
||||
},
|
||||
{
|
||||
field: 'field',
|
||||
width: 4,
|
||||
},
|
||||
{
|
||||
field: LOG_LINE_BODY_FIELD_NAME,
|
||||
width: 20,
|
||||
},
|
||||
],
|
||||
['field']
|
||||
)
|
||||
).toBe('23px 4px max-content 4px 20px');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { CSSProperties, memo, useCallback, useEffect, useMemo, useRef, useState, MouseEvent } from 'react';
|
||||
import {
|
||||
CSSProperties,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
MouseEvent,
|
||||
useLayoutEffect,
|
||||
} from 'react';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
@@ -8,6 +18,7 @@ import { t } from '@grafana/i18n';
|
||||
import { Button, Icon, Tooltip } from '@grafana/ui';
|
||||
|
||||
import { LOG_LINE_BODY_FIELD_NAME } from '../LogDetailsBody';
|
||||
import { LogLabels } from '../LogLabels';
|
||||
import { LogMessageAnsi } from '../LogMessageAnsi';
|
||||
|
||||
import { HighlightedLogRenderer } from './HighlightedLogRenderer';
|
||||
@@ -18,7 +29,7 @@ import { useLogListSearchContext } from './LogListSearchContext';
|
||||
import { LogListModel } from './processing';
|
||||
import {
|
||||
FIELD_GAP_MULTIPLIER,
|
||||
hasUnderOrOverflow,
|
||||
getLogLineDOMHeight,
|
||||
LogFieldDimension,
|
||||
LogLineVirtualization,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
@@ -108,6 +119,7 @@ const LogLineComponent = memo(
|
||||
fontSize,
|
||||
hasLogsWithErrors,
|
||||
hasSampledLogs,
|
||||
showUniqueLabels,
|
||||
timestampResolution,
|
||||
onLogLineHover,
|
||||
} = useLogListContext();
|
||||
@@ -118,16 +130,41 @@ const LogLineComponent = memo(
|
||||
const pinned = useLogIsPinned(log);
|
||||
const permalinked = useLogIsPermalinked(log);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onOverflow || !logLineRef.current || !virtualization || !height || !wrapLogMessage) {
|
||||
const handleLogLineResize = useCallback(() => {
|
||||
if (!onOverflow || !logLineRef.current || !virtualization || !height) {
|
||||
return;
|
||||
}
|
||||
const calculatedHeight = typeof height === 'number' ? height : undefined;
|
||||
const actualHeight = hasUnderOrOverflow(virtualization, logLineRef.current, calculatedHeight, log.collapsed);
|
||||
const actualHeight = getLogLineDOMHeight(virtualization, logLineRef.current, calculatedHeight, log.collapsed);
|
||||
if (actualHeight) {
|
||||
onOverflow(index, log.uid, actualHeight);
|
||||
}
|
||||
});
|
||||
}, [height, index, log.collapsed, log.uid, onOverflow, virtualization]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
handleLogLineResize();
|
||||
}, [handleLogLineResize, detailsMode]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!logLineRef.current) {
|
||||
return;
|
||||
}
|
||||
let frameId: number;
|
||||
const handleResize = () => {
|
||||
if (frameId) {
|
||||
cancelAnimationFrame(frameId);
|
||||
}
|
||||
frameId = requestAnimationFrame(() => handleLogLineResize());
|
||||
};
|
||||
const observer = new ResizeObserver(handleResize);
|
||||
observer.observe(logLineRef.current);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
if (frameId) {
|
||||
cancelAnimationFrame(frameId);
|
||||
}
|
||||
};
|
||||
}, [handleLogLineResize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wrapLogMessage) {
|
||||
@@ -157,16 +194,6 @@ const LogLineComponent = memo(
|
||||
[log, onClick]
|
||||
);
|
||||
|
||||
const handleLogDetailsResize = useCallback(() => {
|
||||
if (!onOverflow || !logLineRef.current || !virtualization) {
|
||||
return;
|
||||
}
|
||||
const actualHeight = hasUnderOrOverflow(virtualization, logLineRef.current, undefined, log.collapsed);
|
||||
if (actualHeight) {
|
||||
onOverflow(index, log.uid, actualHeight);
|
||||
}
|
||||
}, [index, log.collapsed, log.uid, onOverflow, virtualization]);
|
||||
|
||||
const detailsShown = detailsDisplayed(log);
|
||||
|
||||
return (
|
||||
@@ -233,6 +260,7 @@ const LogLineComponent = memo(
|
||||
displayedFields={displayedFields}
|
||||
log={log}
|
||||
showTime={showTime}
|
||||
showUniqueLabels={showUniqueLabels}
|
||||
styles={styles}
|
||||
timestampResolution={timestampResolution}
|
||||
wrapLogMessage={wrapLogMessage}
|
||||
@@ -269,7 +297,7 @@ const LogLineComponent = memo(
|
||||
<InlineLogLineDetails
|
||||
logs={logs}
|
||||
log={log}
|
||||
onResize={handleLogDetailsResize}
|
||||
onResize={handleLogLineResize}
|
||||
timeRange={timeRange}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
@@ -287,33 +315,53 @@ interface LogProps {
|
||||
displayedFields: string[];
|
||||
log: LogListModel;
|
||||
showTime: boolean;
|
||||
showUniqueLabels?: boolean;
|
||||
styles: LogLineStyles;
|
||||
timestampResolution: LogLineTimestampResolution;
|
||||
wrapLogMessage: boolean;
|
||||
}
|
||||
|
||||
const Log = memo(({ displayedFields, log, showTime, styles, timestampResolution, wrapLogMessage }: LogProps) => {
|
||||
return (
|
||||
<>
|
||||
{showTime && (
|
||||
<span className={`${styles.timestamp} level-${log.logLevel} field`}>
|
||||
{timestampResolution === 'ms' ? log.timestamp : log.timestampNs}
|
||||
</span>
|
||||
)}
|
||||
{
|
||||
// When logs are unwrapped, we want an empty column space to align with other log lines.
|
||||
}
|
||||
{(log.displayLevel || !wrapLogMessage) && (
|
||||
<span className={`${styles.level} level-${log.logLevel} field`}>{log.displayLevel}</span>
|
||||
)}
|
||||
{displayedFields.length > 0 ? (
|
||||
<DisplayedFields displayedFields={displayedFields} log={log} styles={styles} />
|
||||
) : (
|
||||
<LogLineBody log={log} styles={styles} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
const Log = memo(
|
||||
({ displayedFields, log, showTime, showUniqueLabels, styles, timestampResolution, wrapLogMessage }: LogProps) => {
|
||||
const handleLabelsToggle = useCallback(
|
||||
(expanded: boolean) => {
|
||||
log.uniqueLabelsExpanded = expanded;
|
||||
},
|
||||
[log]
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{showTime && (
|
||||
<span className={`${styles.timestamp} level-${log.logLevel} field`}>
|
||||
{timestampResolution === 'ms' ? log.timestamp : log.timestampNs}
|
||||
</span>
|
||||
)}
|
||||
{
|
||||
// When logs are unwrapped, we want an empty column space to align with other log lines.
|
||||
}
|
||||
{(log.displayLevel || !wrapLogMessage) && (
|
||||
<span className={`${styles.level} level-${log.logLevel} field`}>{log.displayLevel}</span>
|
||||
)}
|
||||
{showUniqueLabels && log.uniqueLabels && (
|
||||
<span className="field">
|
||||
<LogLabels
|
||||
addTooltip={true}
|
||||
displayAll={log.uniqueLabelsExpanded}
|
||||
displayMax={5}
|
||||
labels={log.uniqueLabels}
|
||||
onDisplayMaxToggle={handleLabelsToggle}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{displayedFields.length > 0 ? (
|
||||
<DisplayedFields displayedFields={displayedFields} log={log} styles={styles} />
|
||||
) : (
|
||||
<LogLineBody log={log} styles={styles} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
Log.displayName = 'Log';
|
||||
|
||||
const DisplayedFields = ({
|
||||
@@ -402,9 +450,11 @@ const LogLineBody = ({ log, styles }: { log: LogListModel; styles: LogLineStyles
|
||||
};
|
||||
|
||||
export function getGridTemplateColumns(dimensions: LogFieldDimension[], displayedFields: string[]) {
|
||||
const columns = dimensions.map((dimension) => dimension.width).join('px ');
|
||||
const columns = dimensions
|
||||
.map((dimension) => (dimension.width > 0 ? `${dimension.width}px` : 'max-content'))
|
||||
.join(' ');
|
||||
const logLineWidth = displayedFields.length > 0 ? '' : ' 1fr';
|
||||
return `${columns}px${logLineWidth}`;
|
||||
return `${columns}${logLineWidth}`;
|
||||
}
|
||||
|
||||
export type LogLineStyles = ReturnType<typeof getStyles>;
|
||||
|
||||
@@ -64,7 +64,6 @@ const setup = (
|
||||
containerElement: document.createElement('div'),
|
||||
focusLogLine: jest.fn(),
|
||||
logs,
|
||||
onResize: jest.fn(),
|
||||
timeRange: getDefaultTimeRange(),
|
||||
timeZone: 'browser',
|
||||
showControls: true,
|
||||
@@ -613,7 +612,6 @@ describe('LogLineDetails', () => {
|
||||
timeRange: getDefaultTimeRange(),
|
||||
timeZone: 'browser',
|
||||
showControls: true,
|
||||
onResize: jest.fn(),
|
||||
};
|
||||
|
||||
const contextData: LogListContextData = {
|
||||
|
||||
@@ -19,14 +19,13 @@ export interface Props {
|
||||
logs: LogListModel[];
|
||||
timeRange: TimeRange;
|
||||
timeZone: string;
|
||||
onResize(): void;
|
||||
showControls: boolean;
|
||||
}
|
||||
|
||||
export type LogLineDetailsMode = 'inline' | 'sidebar';
|
||||
|
||||
export const LogLineDetails = memo(
|
||||
({ containerElement, focusLogLine, logs, timeRange, timeZone, onResize, showControls }: Props) => {
|
||||
({ containerElement, focusLogLine, logs, timeRange, timeZone, showControls }: Props) => {
|
||||
const { detailsWidth, noInteractions, setDetailsWidth } = useLogListContext();
|
||||
const styles = useStyles2(getStyles, 'sidebar', showControls);
|
||||
const dragStyles = useStyles2(getDragStyles);
|
||||
@@ -36,8 +35,7 @@ export const LogLineDetails = memo(
|
||||
if (containerRef.current) {
|
||||
setDetailsWidth(containerRef.current.clientWidth);
|
||||
}
|
||||
onResize();
|
||||
}, [onResize, setDetailsWidth]);
|
||||
}, [setDetailsWidth]);
|
||||
|
||||
const reportResize = useCallback(() => {
|
||||
if (containerRef.current && !noInteractions) {
|
||||
@@ -71,13 +69,17 @@ LogLineDetails.displayName = 'LogLineDetails';
|
||||
|
||||
const LogLineDetailsTabs = memo(
|
||||
({ focusLogLine, logs, timeRange, timeZone }: Pick<Props, 'focusLogLine' | 'logs' | 'timeRange' | 'timeZone'>) => {
|
||||
const { app, closeDetails, noInteractions, showDetails, toggleDetails } = useLogListContext();
|
||||
const { app, closeDetails, noInteractions, showDetails, toggleDetails, wrapLogMessage } = useLogListContext();
|
||||
const [currentLog, setCurrentLog] = useState(showDetails[0]);
|
||||
const previousShowDetails = usePrevious(showDetails);
|
||||
const styles = useStyles2(getStyles, 'sidebar');
|
||||
|
||||
useEffect(() => {
|
||||
focusLogLine(currentLog);
|
||||
// When wrapping is enabled and details is in sidebar mode, the logs panel width changes and the
|
||||
// user may lose focus of the log line, so we scroll to it.
|
||||
if (wrapLogMessage) {
|
||||
focusLogLine(currentLog);
|
||||
}
|
||||
if (!noInteractions) {
|
||||
reportInteraction('logs_log_line_details_displayed', {
|
||||
mode: 'sidebar',
|
||||
@@ -164,11 +166,8 @@ export const InlineLogLineDetails = memo(({ logs, log, onResize, timeRange, time
|
||||
}, [app, noInteractions]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleResize() {
|
||||
onResize();
|
||||
}
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
window.addEventListener('resize', onResize);
|
||||
return () => window.removeEventListener('resize', onResize);
|
||||
}, [onResize]);
|
||||
|
||||
const saveScroll = useCallback(() => {
|
||||
|
||||
@@ -79,6 +79,7 @@ export interface Props {
|
||||
setDisplayedFields?: (displayedFields: string[]) => void;
|
||||
showControls: boolean;
|
||||
showTime: boolean;
|
||||
showUniqueLabels?: boolean;
|
||||
sortOrder: LogsSortOrder;
|
||||
timeRange: TimeRange;
|
||||
timestampResolution?: LogLineTimestampResolution;
|
||||
@@ -148,6 +149,7 @@ export const LogList = ({
|
||||
setDisplayedFields,
|
||||
showControls,
|
||||
showTime,
|
||||
showUniqueLabels,
|
||||
sortOrder,
|
||||
syntaxHighlighting = logOptionsStorageKey ? store.getBool(`${logOptionsStorageKey}.syntaxHighlighting`, true) : true,
|
||||
timeRange,
|
||||
@@ -192,6 +194,7 @@ export const LogList = ({
|
||||
setDisplayedFields={setDisplayedFields}
|
||||
showControls={showControls}
|
||||
showTime={showTime}
|
||||
showUniqueLabels={showUniqueLabels}
|
||||
sortOrder={sortOrder}
|
||||
syntaxHighlighting={syntaxHighlighting}
|
||||
timestampResolution={timestampResolution}
|
||||
@@ -247,6 +250,7 @@ const LogListComponent = ({
|
||||
prettifyJSON,
|
||||
showDetails,
|
||||
showTime,
|
||||
showUniqueLabels,
|
||||
sortOrder,
|
||||
timestampResolution,
|
||||
toggleDetails,
|
||||
@@ -264,8 +268,13 @@ const LogListComponent = ({
|
||||
() =>
|
||||
wrapLogMessage
|
||||
? []
|
||||
: virtualization.calculateFieldDimensions(processedLogs, displayedFields, timestampResolution),
|
||||
[displayedFields, processedLogs, timestampResolution, virtualization, wrapLogMessage]
|
||||
: virtualization.calculateFieldDimensions(
|
||||
processedLogs,
|
||||
displayedFields,
|
||||
timestampResolution,
|
||||
showUniqueLabels
|
||||
),
|
||||
[displayedFields, processedLogs, showUniqueLabels, timestampResolution, virtualization, wrapLogMessage]
|
||||
);
|
||||
const styles = useStyles2(getStyles, dimensions, displayedFields, { showTime });
|
||||
const widthContainer = wrapperRef.current ?? containerElement;
|
||||
@@ -295,11 +304,13 @@ const LogListComponent = ({
|
||||
[filterLogs, levelFilteredLogs, matchingUids]
|
||||
);
|
||||
|
||||
// When log lines report size discrepancies, we debounce the calculation reset to give time to
|
||||
// use the smallest log index to reset the heights.
|
||||
const debouncedResetAfterIndex = useMemo(() => {
|
||||
return debounce((index: number) => {
|
||||
listRef.current?.resetAfterIndex(index);
|
||||
overflowIndexRef.current = Infinity;
|
||||
}, 25);
|
||||
}, 0);
|
||||
}, []);
|
||||
|
||||
const debouncedScrollToItem = useMemo(() => {
|
||||
@@ -340,17 +351,17 @@ const LogListComponent = ({
|
||||
}, [wrapLogMessage, showDetails, displayedFields, dedupStrategy]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (widthRef.current !== widthContainer.clientWidth) {
|
||||
widthRef.current = widthContainer.clientWidth;
|
||||
debouncedResetAfterIndex(0);
|
||||
}
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const handleResize = debounce(() => {
|
||||
const handleResize = (entry: ResizeObserverEntry) => {
|
||||
setListHeight(getListHeight(containerElement, app, searchVisible));
|
||||
}, 50);
|
||||
const observer = new ResizeObserver(() => handleResize());
|
||||
if (widthRef.current !== entry.contentRect.width) {
|
||||
widthRef.current = entry.contentRect.width;
|
||||
}
|
||||
};
|
||||
const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
||||
if (entries.length) {
|
||||
handleResize(entries[0]);
|
||||
}
|
||||
});
|
||||
observer.observe(containerElement);
|
||||
return () => observer.disconnect();
|
||||
}, [app, containerElement, searchVisible]);
|
||||
@@ -396,10 +407,6 @@ const LogListComponent = ({
|
||||
[handleTextSelection, toggleDetails]
|
||||
);
|
||||
|
||||
const handleLogDetailsResize = useCallback(() => {
|
||||
debouncedResetAfterIndex(0);
|
||||
}, [debouncedResetAfterIndex]);
|
||||
|
||||
const focusLogLine = useCallback(
|
||||
(log: LogListModel) => {
|
||||
const index = filteredLogs.findIndex((filteredLog) => filteredLog.uid === log.uid);
|
||||
@@ -425,7 +432,6 @@ const LogListComponent = ({
|
||||
logs={filteredLogs}
|
||||
timeRange={timeRange}
|
||||
timeZone={timeZone}
|
||||
onResize={handleLogDetailsResize}
|
||||
showControls={showControls}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -53,6 +53,7 @@ export class LogListModel implements LogRowModel {
|
||||
timeUtc: string;
|
||||
uid: string;
|
||||
uniqueLabels: Labels | undefined;
|
||||
uniqueLabelsExpanded = false;
|
||||
|
||||
private _body: string | undefined = undefined;
|
||||
private _currentSearch: string | undefined = undefined;
|
||||
|
||||
@@ -182,7 +182,8 @@ export class LogLineVirtualization {
|
||||
calculateFieldDimensions = (
|
||||
logs: LogListModel[],
|
||||
displayedFields: string[] = [],
|
||||
timestampResolution: LogLineTimestampResolution
|
||||
timestampResolution: LogLineTimestampResolution,
|
||||
showUniqueLabels?: boolean
|
||||
) => {
|
||||
if (!logs.length) {
|
||||
return [];
|
||||
@@ -214,6 +215,12 @@ export class LogLineVirtualization {
|
||||
width: levelWidth,
|
||||
},
|
||||
];
|
||||
if (showUniqueLabels) {
|
||||
dimensions.push({
|
||||
field: 'unique-labels',
|
||||
width: 0,
|
||||
});
|
||||
}
|
||||
for (const field in fieldWidths) {
|
||||
dimensions.push({
|
||||
field,
|
||||
@@ -319,7 +326,7 @@ export interface LogFieldDimension {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export function hasUnderOrOverflow(
|
||||
export function getLogLineDOMHeight(
|
||||
virtualization: LogLineVirtualization,
|
||||
element: HTMLDivElement,
|
||||
calculatedHeight?: number,
|
||||
|
||||
@@ -610,6 +610,7 @@ export const LogsPanel = ({
|
||||
setDisplayedFields={setDisplayedFieldsFn}
|
||||
showControls={Boolean(showControls)}
|
||||
showTime={showTime}
|
||||
showUniqueLabels={showLabels}
|
||||
sortOrder={sortOrder}
|
||||
logOptionsStorageKey={storageKey}
|
||||
syntaxHighlighting={syntaxHighlighting}
|
||||
|
||||
@@ -9,30 +9,29 @@ import { LogsPanelSuggestionsSupplier } from './suggestions';
|
||||
export const plugin = new PanelPlugin<Options>(LogsPanel)
|
||||
.setPanelOptions((builder, context) => {
|
||||
const category = [t('logs.category-logs', 'Logs')];
|
||||
builder.addBooleanSwitch({
|
||||
path: 'showTime',
|
||||
name: t('logs.name-time', 'Show timestamps'),
|
||||
category,
|
||||
description: '',
|
||||
defaultValue: false,
|
||||
});
|
||||
builder
|
||||
.addBooleanSwitch({
|
||||
path: 'showTime',
|
||||
name: t('logs.name-time', 'Show timestamps'),
|
||||
category,
|
||||
description: '',
|
||||
defaultValue: false,
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
path: 'showLabels',
|
||||
name: t('logs.name-unique-labels', 'Unique labels'),
|
||||
category,
|
||||
description: '',
|
||||
});
|
||||
|
||||
if (!config.featureToggles.newLogsPanel) {
|
||||
builder
|
||||
.addBooleanSwitch({
|
||||
path: 'showLabels',
|
||||
name: t('logs.name-unique-labels', 'Unique labels'),
|
||||
category,
|
||||
description: '',
|
||||
defaultValue: false,
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
path: 'showCommonLabels',
|
||||
name: t('logs.name-common-labels', 'Common labels'),
|
||||
category,
|
||||
description: '',
|
||||
defaultValue: false,
|
||||
});
|
||||
builder.addBooleanSwitch({
|
||||
path: 'showCommonLabels',
|
||||
name: t('logs.name-common-labels', 'Common labels'),
|
||||
category,
|
||||
description: '',
|
||||
defaultValue: false,
|
||||
});
|
||||
} else if (context.options?.showTime) {
|
||||
builder.addRadio({
|
||||
path: 'timestampResolution',
|
||||
|
||||
Reference in New Issue
Block a user