New Logs Panel: Extend line wrapping to control JSON formatting (#110224)
* LogListControls: add third state to line wrapping * LogListContext: decouple wrapLogMessage and prettifyJSON from state object * Processing: reformat JSON according to prettifyJSON * LogListControls: update class names and colors * onLogOptionsChangeType: update type signature * Update type * Update tests * Dont translate the plus sign * Comments
This commit is contained in:
@@ -703,7 +703,7 @@ const UnthemedLogs: React.FunctionComponent<Props> = (props: Props) => {
|
||||
|
||||
const visibilityChangedRef = useRef(true);
|
||||
const onLogOptionsChange = useCallback(
|
||||
(option: keyof LogListControlOptions, value: string | string[] | boolean) => {
|
||||
(option: LogListControlOptions, value: string | string[] | boolean) => {
|
||||
if (option === 'sortOrder' && isLogsSortOrder(value)) {
|
||||
sortOrderChanged(value);
|
||||
} else if (option === 'dedupStrategy' && isDedupStrategy(value)) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface ControlledLogRowsProps extends Omit<Props, 'scrollElement'> {
|
||||
logsMeta?: LogsMetaItem[];
|
||||
loadMoreLogs?: (range: AbsoluteTimeRange) => void;
|
||||
logOptionsStorageKey?: string;
|
||||
onLogOptionsChange?: (option: keyof LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
onLogOptionsChange?: (option: LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
range: TimeRange;
|
||||
filterLevels?: LogLevel[];
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ export interface Props {
|
||||
onClickFilterOutString?: (value: string, refId?: string) => void;
|
||||
onClickShowField?: (key: string) => void;
|
||||
onClickHideField?: (key: string) => void;
|
||||
onLogOptionsChange?: (option: keyof LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
onLogOptionsChange?: (option: LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
onLogLineHover?: (row?: LogRowModel) => void;
|
||||
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
|
||||
onPinLine?: (row: LogRowModel) => void;
|
||||
@@ -88,7 +88,7 @@ export interface Props {
|
||||
|
||||
export type LogListFontSize = 'default' | 'small';
|
||||
|
||||
export type LogListControlOptions = LogListState;
|
||||
export type LogListControlOptions = keyof LogListState | 'wrapLogMessage' | 'prettifyJSON';
|
||||
|
||||
type LogListComponentProps = Omit<
|
||||
Props,
|
||||
@@ -241,6 +241,7 @@ const LogListComponent = ({
|
||||
onClickFilterString,
|
||||
onClickFilterOutString,
|
||||
permalinkedLogId,
|
||||
prettifyJSON,
|
||||
showDetails,
|
||||
showTime,
|
||||
sortOrder,
|
||||
@@ -315,13 +316,21 @@ const LogListComponent = ({
|
||||
setProcessedLogs(
|
||||
preProcessLogs(
|
||||
logs,
|
||||
{ getFieldLinks, escape: forceEscape ?? false, order: sortOrder, timeZone, virtualization, wrapLogMessage },
|
||||
{
|
||||
getFieldLinks,
|
||||
escape: forceEscape ?? false,
|
||||
prettifyJSON,
|
||||
order: sortOrder,
|
||||
timeZone,
|
||||
virtualization,
|
||||
wrapLogMessage,
|
||||
},
|
||||
grammar
|
||||
)
|
||||
);
|
||||
virtualization.resetLogLineSizes();
|
||||
listRef.current?.resetAfterIndex(0);
|
||||
}, [forceEscape, getFieldLinks, grammar, logs, sortOrder, timeZone, virtualization, wrapLogMessage]);
|
||||
}, [forceEscape, getFieldLinks, grammar, logs, prettifyJSON, sortOrder, timeZone, virtualization, wrapLogMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
listRef.current?.resetAfterIndex(0);
|
||||
|
||||
@@ -33,7 +33,7 @@ import { getDisplayedFieldsForLogs } from '../otel/formats';
|
||||
import { LogLineTimestampResolution } from './LogLine';
|
||||
import { LogLineDetailsMode } from './LogLineDetails';
|
||||
import { GetRowContextQueryFn, LogLineMenuCustomItem } from './LogLineMenu';
|
||||
import { LogListFontSize } from './LogList';
|
||||
import { LogListControlOptions, LogListFontSize } from './LogList';
|
||||
import { reportInteractionOnce } from './analytics';
|
||||
import { LogListModel } from './processing';
|
||||
import { getScrollbarWidth, LOG_LIST_CONTROLS_WIDTH, LOG_LIST_MIN_WIDTH } from './virtualization';
|
||||
@@ -140,15 +140,15 @@ export type LogListState = Pick<
|
||||
| 'forceEscape'
|
||||
| 'filterLevels'
|
||||
| 'pinnedLogs'
|
||||
| 'prettifyJSON'
|
||||
| 'showUniqueLabels'
|
||||
| 'showTime'
|
||||
| 'sortOrder'
|
||||
| 'syntaxHighlighting'
|
||||
| 'timestampResolution'
|
||||
| 'wrapLogMessage'
|
||||
>;
|
||||
|
||||
export type LogListOption = keyof LogListState | 'wrapLogMessage' | 'prettifyJSON';
|
||||
|
||||
export interface Props {
|
||||
app: CoreApp;
|
||||
children?: ReactNode;
|
||||
@@ -174,7 +174,7 @@ export interface Props {
|
||||
onClickFilterOutString?: (value: string, refId?: string) => void;
|
||||
onClickShowField?: (key: string) => void;
|
||||
onClickHideField?: (key: string) => void;
|
||||
onLogOptionsChange?: (option: keyof LogListState, value: string | boolean | string[]) => void;
|
||||
onLogOptionsChange?: (option: LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
onLogLineHover?: (row?: LogRowModel) => void;
|
||||
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
|
||||
onPinLine?: (row: LogRowModel) => void;
|
||||
@@ -227,7 +227,9 @@ export const LogListContextProvider = ({
|
||||
permalinkedLogId,
|
||||
pinLineButtonTooltipTitle,
|
||||
pinnedLogs,
|
||||
prettifyJSON,
|
||||
prettifyJSON: prettifyJSONProp = logOptionsStorageKey
|
||||
? store.getBool(`${logOptionsStorageKey}.prettifyLogMessage`, true)
|
||||
: true,
|
||||
setDisplayedFields,
|
||||
showControls,
|
||||
showTime,
|
||||
@@ -237,7 +239,7 @@ export const LogListContextProvider = ({
|
||||
timestampResolution = logOptionsStorageKey
|
||||
? (store.get(`${logOptionsStorageKey}.timestampResolution`) ?? 'ms')
|
||||
: 'ms',
|
||||
wrapLogMessage,
|
||||
wrapLogMessage: wrapLogMessageProp,
|
||||
}: Props) => {
|
||||
const [logListState, setLogListState] = useState<LogListState>({
|
||||
dedupStrategy,
|
||||
@@ -246,13 +248,11 @@ export const LogListContextProvider = ({
|
||||
fontSize,
|
||||
forceEscape: logOptionsStorageKey ? store.getBool(`${logOptionsStorageKey}.forceEscape`, false) : false,
|
||||
pinnedLogs,
|
||||
prettifyJSON,
|
||||
showTime,
|
||||
showUniqueLabels,
|
||||
sortOrder,
|
||||
syntaxHighlighting,
|
||||
timestampResolution,
|
||||
wrapLogMessage,
|
||||
});
|
||||
const [showDetails, setShowDetails] = useState<LogListModel[]>([]);
|
||||
const [detailsWidth, setDetailsWidthState] = useState(
|
||||
@@ -260,6 +260,8 @@ export const LogListContextProvider = ({
|
||||
);
|
||||
const [detailsMode, setDetailsMode] = useState<LogLineDetailsMode>(detailsModeProp ?? 'sidebar');
|
||||
const [isAssistantAvailable, openAssistant] = useAssistant();
|
||||
const [prettifyJSON, setPrettifyJSONState] = useState(prettifyJSONProp);
|
||||
const [wrapLogMessage, setWrapLogMessageState] = useState(wrapLogMessageProp);
|
||||
|
||||
useEffect(() => {
|
||||
if (noInteractions) {
|
||||
@@ -406,8 +408,9 @@ export const LogListContextProvider = ({
|
||||
store.set(`${logOptionsStorageKey}.fontSize`, fontSize);
|
||||
}
|
||||
setLogListState((logListState) => ({ ...logListState, fontSize }));
|
||||
onLogOptionsChange?.('fontSize', fontSize);
|
||||
},
|
||||
[logOptionsStorageKey]
|
||||
[logOptionsStorageKey, onLogOptionsChange]
|
||||
);
|
||||
|
||||
const setForceEscape = useCallback(
|
||||
@@ -463,13 +466,13 @@ export const LogListContextProvider = ({
|
||||
|
||||
const setPrettifyJSON = useCallback(
|
||||
(prettifyJSON: boolean) => {
|
||||
setLogListState({ ...logListState, prettifyJSON });
|
||||
onLogOptionsChange?.('prettifyJSON', prettifyJSON);
|
||||
setPrettifyJSONState(prettifyJSON);
|
||||
if (logOptionsStorageKey) {
|
||||
store.set(`${logOptionsStorageKey}.prettifyLogMessage`, prettifyJSON);
|
||||
}
|
||||
onLogOptionsChange?.('prettifyJSON', prettifyJSON);
|
||||
},
|
||||
[logListState, logOptionsStorageKey, onLogOptionsChange]
|
||||
[logOptionsStorageKey, onLogOptionsChange]
|
||||
);
|
||||
|
||||
const setSyntaxHighlighting = useCallback(
|
||||
@@ -496,13 +499,13 @@ export const LogListContextProvider = ({
|
||||
|
||||
const setWrapLogMessage = useCallback(
|
||||
(wrapLogMessage: boolean) => {
|
||||
setLogListState({ ...logListState, wrapLogMessage });
|
||||
onLogOptionsChange?.('wrapLogMessage', wrapLogMessage);
|
||||
setWrapLogMessageState(wrapLogMessage);
|
||||
if (logOptionsStorageKey) {
|
||||
store.set(`${logOptionsStorageKey}.wrapLogMessage`, wrapLogMessage);
|
||||
}
|
||||
onLogOptionsChange?.('wrapLogMessage', wrapLogMessage);
|
||||
},
|
||||
[logListState, logOptionsStorageKey, onLogOptionsChange]
|
||||
[logOptionsStorageKey, onLogOptionsChange]
|
||||
);
|
||||
|
||||
const downloadLogs = useCallback(
|
||||
@@ -620,7 +623,7 @@ export const LogListContextProvider = ({
|
||||
permalinkedLogId,
|
||||
pinLineButtonTooltipTitle,
|
||||
pinnedLogs: logListState.pinnedLogs,
|
||||
prettifyJSON: logListState.prettifyJSON,
|
||||
prettifyJSON,
|
||||
setDedupStrategy,
|
||||
setDetailsMode,
|
||||
setDetailsWidth,
|
||||
@@ -644,7 +647,7 @@ export const LogListContextProvider = ({
|
||||
syntaxHighlighting: logListState.syntaxHighlighting,
|
||||
timestampResolution: logListState.timestampResolution,
|
||||
toggleDetails,
|
||||
wrapLogMessage: logListState.wrapLogMessage,
|
||||
wrapLogMessage,
|
||||
isAssistantAvailable,
|
||||
openAssistantByLog,
|
||||
}}
|
||||
|
||||
@@ -211,6 +211,42 @@ describe('LogListControls', () => {
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', true);
|
||||
});
|
||||
|
||||
test('Controls line wrapping and prettify JSON', async () => {
|
||||
const originalFlagState = config.featureToggles.newLogsPanel;
|
||||
config.featureToggles.newLogsPanel = true;
|
||||
|
||||
const onLogOptionsChange = jest.fn();
|
||||
render(
|
||||
<LogListContextProvider
|
||||
{...contextProps}
|
||||
wrapLogMessage={false}
|
||||
onLogOptionsChange={onLogOptionsChange}
|
||||
prettifyJSON={false}
|
||||
>
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Wrap lines'));
|
||||
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(2);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', true);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('prettifyJSON', false);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Wrap lines and expand JSON'));
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(3);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('prettifyJSON', true);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Unwrap lines'));
|
||||
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', false);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('prettifyJSON', false);
|
||||
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(5);
|
||||
|
||||
config.featureToggles.newLogsPanel = originalFlagState;
|
||||
});
|
||||
|
||||
test('Controls syntax highlighting', async () => {
|
||||
const onLogOptionsChange = jest.fn();
|
||||
render(
|
||||
|
||||
@@ -312,19 +312,23 @@ export const LogListControls = ({ eventBus, visualisationType = 'logs' }: Props)
|
||||
size="lg"
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
name="wrap-text"
|
||||
className={wrapLogMessage ? styles.controlButtonActive : styles.controlButton}
|
||||
aria-pressed={wrapLogMessage}
|
||||
onClick={onWrapLogMessageClick}
|
||||
tooltip={
|
||||
wrapLogMessage
|
||||
? t('logs.logs-controls.unwrap-lines', 'Unwrap lines')
|
||||
: t('logs.logs-controls.wrap-lines', 'Wrap lines')
|
||||
}
|
||||
size="lg"
|
||||
/>
|
||||
{prettifyJSON !== undefined && (
|
||||
{config.featureToggles.newLogsPanel ? (
|
||||
<WrapLogMessageButton />
|
||||
) : (
|
||||
<IconButton
|
||||
name="wrap-text"
|
||||
className={wrapLogMessage ? styles.controlButtonActive : styles.controlButton}
|
||||
aria-pressed={wrapLogMessage}
|
||||
onClick={onWrapLogMessageClick}
|
||||
tooltip={
|
||||
wrapLogMessage
|
||||
? t('logs.logs-controls.unwrap-lines', 'Unwrap lines')
|
||||
: t('logs.logs-controls.wrap-lines', 'Wrap lines')
|
||||
}
|
||||
size="lg"
|
||||
/>
|
||||
)}
|
||||
{prettifyJSON !== undefined && !config.featureToggles.newLogsPanel && (
|
||||
<IconButton
|
||||
name="brackets-curly"
|
||||
aria-pressed={prettifyJSON}
|
||||
@@ -480,13 +484,13 @@ const TimestampResolutionButton = () => {
|
||||
<button
|
||||
aria-label={getTimestampTooltip(showTime, timestampResolution)}
|
||||
aria-pressed={showTime}
|
||||
className={`${styles.timestampResolutionButton} ${showTime ? styles.controlButtonActive : styles.controlButton}`}
|
||||
className={`${styles.customControlButton} ${showTime ? styles.controlButtonActive : styles.controlButton}`}
|
||||
type="button"
|
||||
onClick={onShowTimestampsClick}
|
||||
>
|
||||
<Icon name="clock-nine" size="lg" className={styles.timestampResolutionIcon} />
|
||||
<Icon name="clock-nine" size="lg" className={styles.customControlIcon} />
|
||||
{showTime && (
|
||||
<span className={styles.resolutionText}>
|
||||
<span className={styles.customControlTag}>
|
||||
{timestampResolution === 'ms'
|
||||
? t('logs.logs-controls.resolution-ms', 'ms')
|
||||
: t('logs.logs-controls.resolution-ns', 'ns')}
|
||||
@@ -497,6 +501,51 @@ const TimestampResolutionButton = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const WrapLogMessageButton = () => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { prettifyJSON, setPrettifyJSON, setWrapLogMessage, wrapLogMessage } = useLogListContext();
|
||||
|
||||
/**
|
||||
* This component currently controls two internal states: line wrapping and JSON formatting.
|
||||
* The state transition is as follows:
|
||||
* - Line wrapping and JSON formatting disabled.
|
||||
* - Line wrapping enabled.
|
||||
* - Line wrapping and JSON formatting enabled.
|
||||
*
|
||||
* Line wrapping also controls JSON formatting, because with line wrapping disabled,
|
||||
* JSON formatting has no effect, so one is related with the other.
|
||||
*/
|
||||
const onWrapLogMessageClick = useCallback(() => {
|
||||
if (!wrapLogMessage) {
|
||||
setWrapLogMessage(true);
|
||||
setPrettifyJSON(false);
|
||||
} else if (!prettifyJSON) {
|
||||
setPrettifyJSON(true);
|
||||
} else {
|
||||
setWrapLogMessage(false);
|
||||
setPrettifyJSON(false);
|
||||
}
|
||||
reportInteraction('logs_log_list_controls_wrap_clicked', {
|
||||
state: !wrapLogMessage,
|
||||
});
|
||||
}, [prettifyJSON, setPrettifyJSON, setWrapLogMessage, wrapLogMessage]);
|
||||
|
||||
return (
|
||||
<Tooltip content={getWrapLogMessageTooltip(wrapLogMessage, prettifyJSON)}>
|
||||
<button
|
||||
aria-label={getWrapLogMessageTooltip(wrapLogMessage, prettifyJSON)}
|
||||
aria-pressed={wrapLogMessage}
|
||||
className={`${styles.customControlButton} ${wrapLogMessage ? styles.controlButtonActive : styles.controlButton}`}
|
||||
type="button"
|
||||
onClick={onWrapLogMessageClick}
|
||||
>
|
||||
<Icon name="wrap-text" size="lg" className={styles.customControlIcon} />
|
||||
{prettifyJSON && <span className={styles.customControlTag}>+</span>}
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
navContainer: css({
|
||||
@@ -556,7 +605,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
backgroundColor: theme.colors.warning.main,
|
||||
},
|
||||
}),
|
||||
timestampResolutionButton: css({
|
||||
customControlButton: css({
|
||||
position: 'relative',
|
||||
zIndex: 0,
|
||||
margin: 0,
|
||||
@@ -569,17 +618,17 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
padding: 0,
|
||||
overflow: 'visible',
|
||||
}),
|
||||
timestampResolutionIcon: css({
|
||||
customControlIcon: css({
|
||||
verticalAlign: 'baseline',
|
||||
}),
|
||||
resolutionText: css({
|
||||
color: theme.colors.text.primary,
|
||||
customControlTag: css({
|
||||
color: theme.colors.primary.text,
|
||||
fontSize: 10,
|
||||
position: 'absolute',
|
||||
bottom: -4,
|
||||
right: 0,
|
||||
right: 1,
|
||||
lineHeight: '10px',
|
||||
backgroundColor: theme.colors.background.elevated,
|
||||
backgroundColor: theme.colors.background.primary,
|
||||
paddingLeft: 2,
|
||||
}),
|
||||
};
|
||||
@@ -594,3 +643,12 @@ function getTimestampTooltip(showTime: boolean, timestampResolution: LogLineTime
|
||||
}
|
||||
return t('logs.logs-controls.hide-timestamps', 'Hide timestamps');
|
||||
}
|
||||
|
||||
function getWrapLogMessageTooltip(wrapLogMessage: boolean, prettifyJSON: boolean | undefined) {
|
||||
if (!wrapLogMessage) {
|
||||
return t('logs.logs-controls.wrap-lines', 'Wrap lines');
|
||||
}
|
||||
return prettifyJSON
|
||||
? t('logs.logs-controls.unwrap-lines', 'Unwrap lines')
|
||||
: t('logs.logs-controls.wrap-json-lines', 'Wrap lines and expand JSON');
|
||||
}
|
||||
|
||||
@@ -157,6 +157,22 @@ describe('preProcessLogs', () => {
|
||||
expect(logListModel.body).toBe(entry);
|
||||
});
|
||||
|
||||
test('Does not modify wrapped JSON', () => {
|
||||
const entry = '{"key": "value", "otherKey": "otherValue"}';
|
||||
const logListModel = createLogLine(
|
||||
{ entry },
|
||||
{
|
||||
escape: false,
|
||||
order: LogsSortOrder.Descending,
|
||||
timeZone: 'browser',
|
||||
wrapLogMessage: false, // unwrapped
|
||||
prettifyJSON: false,
|
||||
}
|
||||
);
|
||||
expect(logListModel.entry).toBe(entry);
|
||||
expect(logListModel.body).toBe(entry);
|
||||
});
|
||||
|
||||
test('Prettifies wrapped JSON', () => {
|
||||
const entry = '{"key": "value", "otherKey": "otherValue"}';
|
||||
const logListModel = createLogLine(
|
||||
@@ -166,6 +182,7 @@ describe('preProcessLogs', () => {
|
||||
order: LogsSortOrder.Descending,
|
||||
timeZone: 'browser',
|
||||
wrapLogMessage: true, // wrapped
|
||||
prettifyJSON: true,
|
||||
}
|
||||
);
|
||||
expect(logListModel.entry).toBe(entry);
|
||||
|
||||
@@ -61,13 +61,14 @@ export class LogListModel implements LogRowModel {
|
||||
private _highlightedBody: string | undefined = undefined;
|
||||
private _fields: FieldDef[] | undefined = undefined;
|
||||
private _getFieldLinks: GetFieldLinksFn | undefined = undefined;
|
||||
private _prettifyJSON: boolean;
|
||||
private _virtualization?: LogLineVirtualization;
|
||||
private _wrapLogMessage: boolean;
|
||||
private _json = false;
|
||||
|
||||
constructor(
|
||||
log: LogRowModel,
|
||||
{ escape, getFieldLinks, grammar, timeZone, virtualization, wrapLogMessage }: PreProcessLogOptions
|
||||
{ escape, getFieldLinks, grammar, prettifyJSON, timeZone, virtualization, wrapLogMessage }: PreProcessLogOptions
|
||||
) {
|
||||
// LogRowModel
|
||||
this.datasourceType = log.datasourceType;
|
||||
@@ -98,6 +99,7 @@ export class LogListModel implements LogRowModel {
|
||||
this.displayLevel = logLevelToDisplayLevel(log.logLevel);
|
||||
this._getFieldLinks = getFieldLinks;
|
||||
this._grammar = grammar;
|
||||
this._prettifyJSON = Boolean(prettifyJSON);
|
||||
this.timestamp = dateTimeFormat(log.timeEpochMs, {
|
||||
timeZone,
|
||||
// YYYY-MM-DD HH:mm:ss.SSS
|
||||
@@ -129,7 +131,7 @@ export class LogListModel implements LogRowModel {
|
||||
if (typeof parsed === 'object' && parsed !== null && !(parsed instanceof LosslessNumber)) {
|
||||
this._json = true;
|
||||
}
|
||||
const reStringified = this._wrapLogMessage ? stringify(parsed, undefined, 2) : this.raw;
|
||||
const reStringified = this._wrapLogMessage && this._prettifyJSON ? stringify(parsed, undefined, 2) : this.raw;
|
||||
if (reStringified) {
|
||||
this.raw = reStringified;
|
||||
}
|
||||
@@ -243,6 +245,7 @@ export interface PreProcessOptions {
|
||||
escape: boolean;
|
||||
getFieldLinks?: GetFieldLinksFn;
|
||||
order: LogsSortOrder;
|
||||
prettifyJSON?: boolean;
|
||||
timeZone: string;
|
||||
virtualization?: LogLineVirtualization;
|
||||
wrapLogMessage: boolean;
|
||||
@@ -250,7 +253,7 @@ export interface PreProcessOptions {
|
||||
|
||||
export const preProcessLogs = (
|
||||
logs: LogRowModel[],
|
||||
{ escape, getFieldLinks, order, timeZone, virtualization, wrapLogMessage }: PreProcessOptions,
|
||||
{ escape, getFieldLinks, order, prettifyJSON, timeZone, virtualization, wrapLogMessage }: PreProcessOptions,
|
||||
grammar?: Grammar
|
||||
): LogListModel[] => {
|
||||
const orderedLogs = sortLogRows(logs, order);
|
||||
@@ -259,6 +262,7 @@ export const preProcessLogs = (
|
||||
escape,
|
||||
getFieldLinks,
|
||||
grammar,
|
||||
prettifyJSON,
|
||||
timeZone,
|
||||
virtualization,
|
||||
wrapLogMessage,
|
||||
@@ -270,6 +274,7 @@ interface PreProcessLogOptions {
|
||||
escape: boolean;
|
||||
getFieldLinks?: GetFieldLinksFn;
|
||||
grammar?: Grammar;
|
||||
prettifyJSON?: boolean;
|
||||
timeZone: string;
|
||||
virtualization?: LogLineVirtualization;
|
||||
wrapLogMessage: boolean;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CoreApp, DataFrame } from '@grafana/data';
|
||||
import { LogListControlOptions } from 'app/features/logs/components/panel/LogList';
|
||||
|
||||
type onNewLogsReceivedType = (allLogs: DataFrame[], newLogs: DataFrame[]) => void;
|
||||
type onLogOptionsChangeType = (option: keyof LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
type onLogOptionsChangeType = (option: LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
|
||||
export function isOnNewLogsReceivedType(callback: unknown): callback is onNewLogsReceivedType {
|
||||
return typeof callback === 'function';
|
||||
|
||||
@@ -114,7 +114,7 @@ interface LogsPanelProps extends PanelProps<Options> {
|
||||
* controlsStorageKey?: string
|
||||
*
|
||||
* If controls are enabled, this function is called when a change is made in one of the options from the controls.
|
||||
* onLogOptionsChange?: (option: keyof LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
* onLogOptionsChange?: (option: LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
*
|
||||
* When the feature toggle newLogsPanel is enabled, you can pass extra options to the LogLineMenu component.
|
||||
* These options are an array of items with { label, onClick } or { divider: true } for dividers.
|
||||
|
||||
@@ -14,7 +14,7 @@ type filterLabelActiveType = (key: string, value: string, refId?: string) => Pro
|
||||
type onClickShowFieldType = (value: string) => void;
|
||||
type onClickHideFieldType = (value: string) => void;
|
||||
export type onNewLogsReceivedType = (allLogs: DataFrame[], newLogs: DataFrame[]) => void;
|
||||
type onLogOptionsChangeType = (option: keyof LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
type onLogOptionsChangeType = (option: LogListControlOptions, value: string | boolean | string[]) => void;
|
||||
type setDisplayedFieldsType = (fields: string[]) => void;
|
||||
|
||||
export type GetFieldLinksFn = (
|
||||
|
||||
@@ -9644,6 +9644,7 @@
|
||||
"show-timestamps": "Show timestamps",
|
||||
"show-unique-labels": "Show unique labels",
|
||||
"unwrap-lines": "Unwrap lines",
|
||||
"wrap-json-lines": "Wrap lines and expand JSON",
|
||||
"wrap-lines": "Wrap lines"
|
||||
},
|
||||
"logs-navigation": {
|
||||
|
||||
Reference in New Issue
Block a user