mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[One Discover] Add row indicators for log documents (#190676)
## 📓 Summary Closes #190457 This change extracts the logic around the logs document controls/indicator from Logs Explorer and registers the same controls in Discover through the extension point added in #188762. The controls are registered only for the `logs-data-source-profile` contextual profile. https://github.com/user-attachments/assets/40adfb19-357f-46e1-9d69-fc9c0860c832 ## 👣 Next steps - [ ] https://github.com/elastic/kibana/issues/190670 - [ ] https://github.com/elastic/kibana/issues/190460 --------- Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c4dd4d1d99
commit
ac66e7cb44
43 changed files with 406 additions and 226 deletions
|
@ -34,6 +34,8 @@ export {
|
|||
buildDataTableRecord,
|
||||
buildDataTableRecordList,
|
||||
createLogsContextService,
|
||||
createDegradedDocsControl,
|
||||
createStacktraceControl,
|
||||
fieldConstants,
|
||||
formatFieldValue,
|
||||
formatHit,
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { EuiCode, EuiSpacer } from '@elastic/eui';
|
||||
import {
|
||||
RowControlColumn,
|
||||
RowControlComponent,
|
||||
RowControlProps,
|
||||
RowControlRowProps,
|
||||
} from './types';
|
||||
import { DEGRADED_DOCS_FIELDS } from '../../field_constants';
|
||||
|
||||
interface DegradedDocsControlProps extends Partial<RowControlProps> {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Degraded docs control factory function.
|
||||
* @param props Optional props for the generated Control component, useful to override onClick, etc
|
||||
*/
|
||||
export const createDegradedDocsControl = (props?: DegradedDocsControlProps): RowControlColumn => ({
|
||||
id: 'connectedDegradedDocs',
|
||||
headerAriaLabel: actionsHeaderAriaLabelDegradedAction,
|
||||
renderControl: (Control, rowProps) => {
|
||||
return <DegradedDocs Control={Control} rowProps={rowProps} {...props} />;
|
||||
},
|
||||
});
|
||||
|
||||
const actionsHeaderAriaLabelDegradedAction = i18n.translate(
|
||||
'discover.customControl.degradedDocArialLabel',
|
||||
{ defaultMessage: 'Access to degraded docs' }
|
||||
);
|
||||
|
||||
const degradedDocButtonLabelWhenPresent = i18n.translate(
|
||||
'discover.customControl.degradedDocPresent',
|
||||
{
|
||||
defaultMessage:
|
||||
"This document couldn't be parsed correctly. Not all fields are properly populated",
|
||||
}
|
||||
);
|
||||
|
||||
const degradedDocButtonLabelWhenNotPresent = i18n.translate(
|
||||
'discover.customControl.degradedDocNotPresent',
|
||||
{ defaultMessage: 'All fields in this document were parsed correctly' }
|
||||
);
|
||||
|
||||
const degradedDocButtonLabelWhenDisabled = i18n.translate(
|
||||
'discover.customControl.degradedDocDisabled',
|
||||
{
|
||||
defaultMessage:
|
||||
'Degraded document field detection is currently disabled for this search. To enable it, include the METADATA directive for the `_ignored` field in your ES|QL query. For example:',
|
||||
}
|
||||
);
|
||||
|
||||
const DegradedDocs = ({
|
||||
Control,
|
||||
enabled = true,
|
||||
rowProps: { record },
|
||||
...props
|
||||
}: {
|
||||
Control: RowControlComponent;
|
||||
rowProps: RowControlRowProps;
|
||||
} & DegradedDocsControlProps) => {
|
||||
const isDegradedDocumentExists = DEGRADED_DOCS_FIELDS.some(
|
||||
(field) => field in record.raw && record.raw[field] !== null
|
||||
);
|
||||
|
||||
if (!enabled) {
|
||||
const codeSample = 'FROM logs-* METADATA _ignored';
|
||||
|
||||
const tooltipContent = (
|
||||
<div>
|
||||
{degradedDocButtonLabelWhenDisabled}
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCode>{codeSample}</EuiCode>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Control
|
||||
disabled
|
||||
data-test-subj="docTableDegradedDocDisabled"
|
||||
tooltipContent={tooltipContent}
|
||||
label={`${degradedDocButtonLabelWhenDisabled} ${codeSample}`}
|
||||
iconType="indexClose"
|
||||
onClick={undefined}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return isDegradedDocumentExists ? (
|
||||
<Control
|
||||
data-test-subj="docTableDegradedDocExist"
|
||||
color="danger"
|
||||
tooltipContent={degradedDocButtonLabelWhenPresent}
|
||||
label={degradedDocButtonLabelWhenPresent}
|
||||
iconType="indexClose"
|
||||
onClick={undefined}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<Control
|
||||
data-test-subj="docTableDegradedDocDoesNotExist"
|
||||
color="text"
|
||||
tooltipContent={degradedDocButtonLabelWhenNotPresent}
|
||||
label={degradedDocButtonLabelWhenNotPresent}
|
||||
iconType="indexClose"
|
||||
onClick={undefined}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { createDegradedDocsControl } from './degraded_docs_control';
|
||||
export { createStacktraceControl } from './stacktrace_control';
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import {
|
||||
RowControlColumn,
|
||||
RowControlComponent,
|
||||
RowControlProps,
|
||||
RowControlRowProps,
|
||||
} from './types';
|
||||
import { LogDocument } from '../../data_types';
|
||||
import { getStacktraceFields } from '../../utils/get_stack_trace_fields';
|
||||
|
||||
/**
|
||||
* Stacktrace control factory function.
|
||||
* @param props Optional props for the generated Control component, useful to override onClick, etc
|
||||
*/
|
||||
export const createStacktraceControl = (props?: Partial<RowControlProps>): RowControlColumn => ({
|
||||
id: 'connectedStacktraceDocs',
|
||||
headerAriaLabel: actionsHeaderAriaLabelStacktraceAction,
|
||||
renderControl: (Control, rowProps) => {
|
||||
return <Stacktrace Control={Control} rowProps={rowProps} {...props} />;
|
||||
},
|
||||
});
|
||||
|
||||
const actionsHeaderAriaLabelStacktraceAction = i18n.translate(
|
||||
'discover.customControl.stacktraceArialLabel',
|
||||
{ defaultMessage: 'Access to available stacktraces' }
|
||||
);
|
||||
|
||||
const stacktraceAvailableControlButton = i18n.translate(
|
||||
'discover.customControl.stacktrace.available',
|
||||
{ defaultMessage: 'Stacktraces available' }
|
||||
);
|
||||
|
||||
const stacktraceNotAvailableControlButton = i18n.translate(
|
||||
'discover.customControl.stacktrace.notAvailable',
|
||||
{ defaultMessage: 'Stacktraces not available' }
|
||||
);
|
||||
|
||||
const Stacktrace = ({
|
||||
Control,
|
||||
rowProps: { record },
|
||||
...props
|
||||
}: {
|
||||
Control: RowControlComponent;
|
||||
rowProps: RowControlRowProps;
|
||||
} & Partial<RowControlProps>) => {
|
||||
const stacktrace = getStacktraceFields(record as LogDocument);
|
||||
const hasValue = Object.values(stacktrace).some(Boolean);
|
||||
|
||||
return hasValue ? (
|
||||
<Control
|
||||
data-test-subj="docTableStacktraceExist"
|
||||
label={stacktraceAvailableControlButton}
|
||||
tooltipContent={stacktraceAvailableControlButton}
|
||||
iconType="apmTrace"
|
||||
onClick={undefined}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<Control
|
||||
disabled
|
||||
data-test-subj="docTableStacktraceDoesNotExist"
|
||||
label={stacktraceNotAvailableControlButton}
|
||||
tooltipContent={stacktraceNotAvailableControlButton}
|
||||
iconType="apmTrace"
|
||||
onClick={undefined}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiButtonIconProps, EuiDataGridControlColumn, IconType } from '@elastic/eui';
|
||||
import React, { FC, ReactElement } from 'react';
|
||||
import { DataTableRecord } from '../../types';
|
||||
|
||||
export interface RowControlRowProps {
|
||||
rowIndex: number;
|
||||
record: DataTableRecord;
|
||||
}
|
||||
|
||||
export interface RowControlProps {
|
||||
'data-test-subj'?: string;
|
||||
color?: EuiButtonIconProps['color'];
|
||||
disabled?: boolean;
|
||||
label: string;
|
||||
iconType: IconType;
|
||||
onClick: ((props: RowControlRowProps) => void) | undefined;
|
||||
tooltipContent?: React.ReactNode;
|
||||
}
|
||||
|
||||
export type RowControlComponent = FC<RowControlProps>;
|
||||
|
||||
export interface RowControlColumn {
|
||||
id: string;
|
||||
headerAriaLabel: string;
|
||||
headerCellRender?: EuiDataGridControlColumn['headerCellRender'];
|
||||
renderControl: (Control: RowControlComponent, props: RowControlRowProps) => ReactElement;
|
||||
}
|
|
@ -35,7 +35,7 @@ export const CONTAINER_NAME_FIELD = 'container.name';
|
|||
export const CONTAINER_ID_FIELD = 'container.id';
|
||||
|
||||
// Degraded Docs
|
||||
export const DEGRADED_DOCS_FIELD = 'ignored_field_values';
|
||||
export const DEGRADED_DOCS_FIELDS = ['ignored_field_values', '_ignored'] as const;
|
||||
|
||||
// Error Stacktrace
|
||||
export const ERROR_STACK_TRACE = 'error.stack_trace';
|
||||
|
|
|
@ -12,3 +12,5 @@ export * as fieldConstants from './field_constants';
|
|||
export * from './hooks';
|
||||
export * from './utils';
|
||||
export * from './data_types';
|
||||
|
||||
export * from './components/custom_control_columns';
|
||||
|
|
|
@ -11,6 +11,12 @@ import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'
|
|||
import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common';
|
||||
|
||||
export type { IgnoredReason, ShouldShowFieldInTableHandler } from './utils';
|
||||
export type {
|
||||
RowControlColumn,
|
||||
RowControlComponent,
|
||||
RowControlProps,
|
||||
RowControlRowProps,
|
||||
} from './components/custom_control_columns/types';
|
||||
|
||||
type DiscoverSearchHit = SearchHit<Record<string, unknown>>;
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { getFieldFromDoc, LogDocument, StackTraceFields } from '..';
|
||||
import {
|
||||
ERROR_EXCEPTION_STACKTRACE,
|
||||
ERROR_LOG_STACKTRACE,
|
||||
ERROR_STACK_TRACE,
|
||||
} from '../field_constants';
|
||||
|
||||
export const getStacktraceFields = (doc: LogDocument): StackTraceFields => {
|
||||
const errorStackTrace = getFieldFromDoc(doc, ERROR_STACK_TRACE);
|
||||
const errorExceptionStackTrace = getFieldFromDoc(doc, ERROR_EXCEPTION_STACKTRACE);
|
||||
const errorLogStackTrace = getFieldFromDoc(doc, ERROR_LOG_STACKTRACE);
|
||||
|
||||
return {
|
||||
[ERROR_STACK_TRACE]: errorStackTrace,
|
||||
[ERROR_EXCEPTION_STACKTRACE]: errorExceptionStackTrace,
|
||||
[ERROR_LOG_STACKTRACE]: errorLogStackTrace,
|
||||
};
|
||||
};
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
export * from './build_data_record';
|
||||
export * from './calc_field_counts';
|
||||
export * from './format_hit';
|
||||
export * from './format_value';
|
||||
export * from './get_doc_id';
|
||||
|
@ -15,6 +16,7 @@ export * from './get_ignored_reason';
|
|||
export * from './get_log_document_overview';
|
||||
export * from './get_message_field_with_fallbacks';
|
||||
export * from './get_should_show_field_handler';
|
||||
export * from './get_stack_trace_fields';
|
||||
export * from './nested_fields';
|
||||
export * from './get_field_value';
|
||||
export * from './calc_field_counts';
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
|
|
@ -8,4 +8,4 @@ This package contains utilities for ES|QL.
|
|||
- *removeDropCommandsFromESQLQuery*: Use this function to remove all the occurences of the `drop` command from the query.
|
||||
- *appendToESQLQuery*: Use this function to append more pipes in an existing ES|QL query. It adds the additional commands in a new line.
|
||||
- *appendWhereClauseToESQLQuery*: Use this function to append where clause in an existing query.
|
||||
- *retieveMetadataColumns*: Use this function to get if there is a metadata option in the from command, and retrieve the columns if so
|
||||
- *retrieveMetadataColumns*: Use this function to get if there is a metadata option in the from command, and retrieve the columns if so
|
||||
|
|
|
@ -27,7 +27,7 @@ export {
|
|||
hasStartEndParams,
|
||||
prettifyQuery,
|
||||
isQueryWrappedByPipes,
|
||||
retieveMetadataColumns,
|
||||
retrieveMetadataColumns,
|
||||
TextBasedLanguages,
|
||||
} from './src';
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ export {
|
|||
getTimeFieldFromESQLQuery,
|
||||
prettifyQuery,
|
||||
isQueryWrappedByPipes,
|
||||
retieveMetadataColumns,
|
||||
retrieveMetadataColumns,
|
||||
} from './utils/query_parsing_helpers';
|
||||
export { appendToESQLQuery, appendWhereClauseToESQLQuery } from './utils/append_to_query';
|
||||
export {
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
getTimeFieldFromESQLQuery,
|
||||
prettifyQuery,
|
||||
isQueryWrappedByPipes,
|
||||
retieveMetadataColumns,
|
||||
retrieveMetadataColumns,
|
||||
} from './query_parsing_helpers';
|
||||
|
||||
describe('esql query helpers', () => {
|
||||
|
@ -217,16 +217,16 @@ describe('esql query helpers', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('retieveMetadataColumns', () => {
|
||||
describe('retrieveMetadataColumns', () => {
|
||||
it('should return metadata columns if they exist', () => {
|
||||
expect(retieveMetadataColumns('from a metadata _id, _ignored | eval b = 1')).toStrictEqual([
|
||||
expect(retrieveMetadataColumns('from a metadata _id, _ignored | eval b = 1')).toStrictEqual([
|
||||
'_id',
|
||||
'_ignored',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return empty columns if metadata doesnt exist', () => {
|
||||
expect(retieveMetadataColumns('from a | eval b = 1')).toStrictEqual([]);
|
||||
expect(retrieveMetadataColumns('from a | eval b = 1')).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -126,7 +126,7 @@ export const prettifyQuery = (query: string, isWrapped: boolean): string => {
|
|||
return BasicPrettyPrinter.print(ast, { multiline: !isWrapped });
|
||||
};
|
||||
|
||||
export const retieveMetadataColumns = (esql: string): string[] => {
|
||||
export const retrieveMetadataColumns = (esql: string): string[] => {
|
||||
const { ast } = getAstAndSyntaxErrors(esql);
|
||||
const options: ESQLCommandOption[] = [];
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
EuiSpacer,
|
||||
EuiDataGridControlColumn,
|
||||
} from '@elastic/eui';
|
||||
import type { RowControlColumn } from '../src/types';
|
||||
import { RowControlColumn } from '@kbn/discover-utils';
|
||||
|
||||
const SelectionHeaderCell = () => {
|
||||
return (
|
||||
|
@ -128,6 +128,7 @@ export const mockRowAdditionalLeadingControls = ['visBarVerticalStacked', 'heart
|
|||
<Control
|
||||
data-test-subj={`exampleRowControl-${iconType}`}
|
||||
label={`Example ${iconType}`}
|
||||
tooltipContent={`Example ${iconType}`}
|
||||
iconType={iconType}
|
||||
onClick={() => {
|
||||
alert(`Example "${iconType}" control clicked. Row index: ${rowProps.rowIndex}`);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
import type { EuiDataGridControlColumn } from '@elastic/eui';
|
||||
import type { RowControlColumn } from '../../../types';
|
||||
import { RowControlColumn } from '@kbn/discover-utils';
|
||||
import { getRowControlColumn } from './row_control_column';
|
||||
import { getRowMenuControlColumn } from './row_menu_control_column';
|
||||
|
||||
|
|
|
@ -25,7 +25,12 @@ describe('getRowControlColumn', () => {
|
|||
id: 'test_row_control',
|
||||
headerAriaLabel: 'row control',
|
||||
renderControl: jest.fn((Control, rowProps) => (
|
||||
<Control label={`test-${rowProps.rowIndex}`} iconType="heart" onClick={mockClick} />
|
||||
<Control
|
||||
label={`test-${rowProps.rowIndex}`}
|
||||
tooltipContent={`test-${rowProps.rowIndex}`}
|
||||
iconType="heart"
|
||||
onClick={mockClick}
|
||||
/>
|
||||
)),
|
||||
};
|
||||
const rowControlColumn = getRowControlColumn(props);
|
||||
|
|
|
@ -15,8 +15,8 @@ import {
|
|||
EuiScreenReaderOnly,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { RowControlColumn, RowControlProps } from '@kbn/discover-utils';
|
||||
import { DataTableRowControl, Size } from '../../data_table_row_control';
|
||||
import type { RowControlColumn, RowControlProps } from '../../../types';
|
||||
import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants';
|
||||
import { useControlColumn } from '../../../hooks/use_control_column';
|
||||
|
||||
|
@ -30,10 +30,18 @@ export const RowControlCell = ({
|
|||
|
||||
const Control: React.FC<RowControlProps> = useMemo(
|
||||
() =>
|
||||
({ 'data-test-subj': dataTestSubj, color, disabled, label, iconType, onClick }) => {
|
||||
({
|
||||
'data-test-subj': dataTestSubj,
|
||||
color,
|
||||
disabled,
|
||||
iconType,
|
||||
label,
|
||||
onClick,
|
||||
tooltipContent,
|
||||
}) => {
|
||||
return (
|
||||
<DataTableRowControl size={Size.normal}>
|
||||
<EuiToolTip content={label} delay="long">
|
||||
<EuiToolTip content={tooltipContent ?? label} delay="long">
|
||||
<EuiButtonIcon
|
||||
data-test-subj={dataTestSubj ?? `unifiedDataTable_rowControl_${props.columnId}`}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -26,7 +26,12 @@ describe('getRowMenuControlColumn', () => {
|
|||
id: 'test_row_menu_control',
|
||||
headerAriaLabel: 'row control',
|
||||
renderControl: jest.fn((Control, rowProps) => (
|
||||
<Control label={`test-${rowProps.rowIndex}`} iconType="heart" onClick={mockClick} />
|
||||
<Control
|
||||
label={`test-${rowProps.rowIndex}`}
|
||||
tooltipContent={`test-${rowProps.rowIndex}`}
|
||||
iconType="heart"
|
||||
onClick={mockClick}
|
||||
/>
|
||||
)),
|
||||
};
|
||||
const rowMenuControlColumn = getRowMenuControlColumn([
|
||||
|
|
|
@ -20,8 +20,8 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { css } from '@emotion/react';
|
||||
import { RowControlColumn, RowControlProps } from '@kbn/discover-utils';
|
||||
import { DataTableRowControl, Size } from '../../data_table_row_control';
|
||||
import type { RowControlColumn, RowControlProps } from '../../../types';
|
||||
import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants';
|
||||
import { useControlColumn } from '../../../hooks/use_control_column';
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import type { ToastsStart, IUiSettingsClient } from '@kbn/core/public';
|
|||
import type { Serializable } from '@kbn/utility-types';
|
||||
import type { DataTableRecord } from '@kbn/discover-utils/types';
|
||||
import {
|
||||
RowControlColumn,
|
||||
getShouldShowFieldHandler,
|
||||
canPrependTimeFieldColumn,
|
||||
getVisibleColumns,
|
||||
|
@ -57,7 +58,6 @@ import {
|
|||
DataTableColumnsMeta,
|
||||
CustomCellRenderer,
|
||||
CustomGridColumnsConfiguration,
|
||||
RowControlColumn,
|
||||
} from '../types';
|
||||
import { getDisplayedColumns } from '../utils/columns';
|
||||
import { convertValueToString } from '../utils/convert_value_to_string';
|
||||
|
|
|
@ -7,18 +7,11 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { ReactElement, FC } from 'react';
|
||||
import type {
|
||||
EuiDataGridCellValueElementProps,
|
||||
EuiDataGridColumn,
|
||||
IconType,
|
||||
EuiButtonIconProps,
|
||||
} from '@elastic/eui';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { EuiDataGridCellValueElementProps, EuiDataGridColumn } from '@elastic/eui';
|
||||
import type { DataTableRecord } from '@kbn/discover-utils/src/types';
|
||||
import type { DataView } from '@kbn/data-views-plugin/common';
|
||||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import type { EuiDataGridControlColumn } from '@elastic/eui/src/components/datagrid/data_grid_types';
|
||||
|
||||
export type { DataTableColumnsMeta } from '@kbn/discover-utils/src/types';
|
||||
export type { DataGridDensity } from './constants';
|
||||
|
||||
|
@ -67,26 +60,3 @@ export type CustomGridColumnsConfiguration = Record<
|
|||
string,
|
||||
(props: CustomGridColumnProps) => EuiDataGridColumn
|
||||
>;
|
||||
|
||||
export interface RowControlRowProps {
|
||||
rowIndex: number;
|
||||
record: DataTableRecord;
|
||||
}
|
||||
|
||||
export interface RowControlProps {
|
||||
'data-test-subj'?: string;
|
||||
color?: EuiButtonIconProps['color'];
|
||||
disabled?: boolean;
|
||||
label: string;
|
||||
iconType: IconType;
|
||||
onClick: ((props: RowControlRowProps) => void) | undefined;
|
||||
}
|
||||
|
||||
export type RowControlComponent = FC<RowControlProps>;
|
||||
|
||||
export interface RowControlColumn {
|
||||
id: string;
|
||||
headerAriaLabel: string;
|
||||
headerCellRender?: EuiDataGridControlColumn['headerCellRender'];
|
||||
renderControl: (Control: RowControlComponent, props: RowControlRowProps) => ReactElement;
|
||||
}
|
||||
|
|
|
@ -493,6 +493,7 @@ function DiscoverDocumentsComponent({
|
|||
additionalFieldGroups={additionalFieldGroups}
|
||||
dataGridDensityState={density}
|
||||
onUpdateDataGridDensity={onUpdateDensity}
|
||||
query={query}
|
||||
cellActionsTriggerId={DISCOVER_CELL_ACTIONS_TRIGGER.id}
|
||||
cellActionsMetadata={cellActionsMetadata}
|
||||
cellActionsHandling="append"
|
||||
|
|
|
@ -14,13 +14,19 @@ import {
|
|||
type UnifiedDataTableProps,
|
||||
} from '@kbn/unified-data-table';
|
||||
import { useProfileAccessor } from '../../context_awareness';
|
||||
import { DiscoverAppState } from '../../application/main/state_management/discover_app_state_container';
|
||||
|
||||
export interface DiscoverGridProps extends UnifiedDataTableProps {
|
||||
query?: DiscoverAppState['query'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Customized version of the UnifiedDataTable
|
||||
* @constructor
|
||||
*/
|
||||
export const DiscoverGrid: React.FC<UnifiedDataTableProps> = ({
|
||||
export const DiscoverGrid: React.FC<DiscoverGridProps> = ({
|
||||
rowAdditionalLeadingControls: customRowAdditionalLeadingControls,
|
||||
query,
|
||||
...props
|
||||
}) => {
|
||||
const getRowIndicatorProvider = useProfileAccessor('getRowIndicatorProvider');
|
||||
|
@ -34,8 +40,14 @@ export const DiscoverGrid: React.FC<UnifiedDataTableProps> = ({
|
|||
const rowAdditionalLeadingControls = useMemo(() => {
|
||||
return getRowAdditionalLeadingControlsAccessor(() => customRowAdditionalLeadingControls)({
|
||||
dataView: props.dataView,
|
||||
query,
|
||||
});
|
||||
}, [getRowAdditionalLeadingControlsAccessor, props.dataView, customRowAdditionalLeadingControls]);
|
||||
}, [
|
||||
getRowAdditionalLeadingControlsAccessor,
|
||||
props.dataView,
|
||||
query,
|
||||
customRowAdditionalLeadingControls,
|
||||
]);
|
||||
|
||||
return (
|
||||
<UnifiedDataTable
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
*/
|
||||
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
import { getFieldValue } from '@kbn/discover-utils';
|
||||
import type { RowControlColumn } from '@kbn/unified-data-table';
|
||||
import { getFieldValue, RowControlColumn } from '@kbn/discover-utils';
|
||||
import { isOfAggregateQueryType } from '@kbn/es-query';
|
||||
import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
|
@ -88,6 +87,7 @@ export const exampleDataSourceProfileProvider: DataSourceProfileProvider = {
|
|||
<Control
|
||||
data-test-subj={`exampleLogsControl_${iconType}`}
|
||||
label={`Example ${iconType}`}
|
||||
tooltipContent={`Example ${iconType}`}
|
||||
iconType={iconType}
|
||||
onClick={() => {
|
||||
alert(`Example "${iconType}" control clicked. Row index: ${rowProps.rowIndex}`);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { createDegradedDocsControl, createStacktraceControl } from '@kbn/discover-utils';
|
||||
import { retrieveMetadataColumns } from '@kbn/esql-utils';
|
||||
import { AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query';
|
||||
import type { DataSourceProfileProvider } from '../../../profiles';
|
||||
|
||||
export const getRowAdditionalLeadingControls: DataSourceProfileProvider['profile']['getRowAdditionalLeadingControls'] =
|
||||
(prev) => (params) => {
|
||||
const additionalControls = prev(params) || [];
|
||||
const { query } = params;
|
||||
|
||||
const isDegradedDocsControlEnabled = isOfAggregateQueryType(query)
|
||||
? queryContainsMetadataIgnored(query)
|
||||
: true;
|
||||
|
||||
return [
|
||||
...additionalControls,
|
||||
createDegradedDocsControl({ enabled: isDegradedDocsControlEnabled }),
|
||||
createStacktraceControl(),
|
||||
];
|
||||
};
|
||||
|
||||
const queryContainsMetadataIgnored = (query: AggregateQuery) =>
|
||||
retrieveMetadataColumns(query.esql).includes('_ignored');
|
|
@ -160,4 +160,18 @@ describe('logsDataSourceProfileProvider', () => {
|
|||
expect(cellRenderers?.['log_level.keyword']).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRowAdditionalLeadingControls', () => {
|
||||
it('should return the passed additional controls', () => {
|
||||
const getRowAdditionalLeadingControls =
|
||||
logsDataSourceProfileProvider.profile.getRowAdditionalLeadingControls?.(() => undefined);
|
||||
const rowAdditionalLeadingControls = getRowAdditionalLeadingControls?.({
|
||||
dataView: dataViewWithLogLevel,
|
||||
});
|
||||
|
||||
expect(rowAdditionalLeadingControls).toHaveLength(2);
|
||||
expect(rowAdditionalLeadingControls?.[0].id).toBe('connectedDegradedDocs');
|
||||
expect(rowAdditionalLeadingControls?.[1].id).toBe('connectedStacktraceDocs');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ import { ProfileProviderServices } from '../profile_provider_services';
|
|||
import { getRowIndicatorProvider } from './accessors';
|
||||
import { extractIndexPatternFrom } from '../extract_index_pattern_from';
|
||||
import { getCellRenderers } from './accessors';
|
||||
import { getRowAdditionalLeadingControls } from './accessors/get_row_additional_leading_controls';
|
||||
|
||||
export const createLogsDataSourceProfileProvider = (
|
||||
services: ProfileProviderServices
|
||||
|
@ -20,6 +21,7 @@ export const createLogsDataSourceProfileProvider = (
|
|||
profile: {
|
||||
getRowIndicatorProvider,
|
||||
getCellRenderers,
|
||||
getRowAdditionalLeadingControls,
|
||||
},
|
||||
resolve: (params) => {
|
||||
const indexPattern = extractIndexPatternFrom(params);
|
||||
|
|
|
@ -17,6 +17,7 @@ import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query';
|
|||
import type { OmitIndexSignature } from 'type-fest';
|
||||
import type { Trigger } from '@kbn/ui-actions-plugin/public';
|
||||
import type { DiscoverDataSource } from '../../common/data_sources';
|
||||
import { DiscoverAppState } from '../application/main/state_management/discover_app_state_container';
|
||||
|
||||
export interface DocViewerExtension {
|
||||
title: string | undefined;
|
||||
|
@ -47,6 +48,7 @@ export interface DefaultAppStateExtension {
|
|||
|
||||
export interface RowControlsExtensionParams {
|
||||
dataView: DataView;
|
||||
query?: DiscoverAppState['query'];
|
||||
}
|
||||
|
||||
export const DISCOVER_CELL_ACTIONS_TRIGGER: Trigger = { id: 'DISCOVER_CELL_ACTIONS_TRIGGER_ID' };
|
||||
|
|
|
@ -205,7 +205,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
if (isLegacyDefault) {
|
||||
await testSubjects.click('docTableCellFilter');
|
||||
} else {
|
||||
await dataGrid.clickCellFilterForButton(1, 3);
|
||||
await dataGrid.clickCellFilterForButtonExcludingControlColumns(1, 1);
|
||||
}
|
||||
const filterCount = await filterBar.getFilterCount();
|
||||
expect(filterCount).to.equal(1);
|
||||
|
|
|
@ -201,6 +201,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await dataGrid.getHeaders()).to.eql([
|
||||
'Select column',
|
||||
'Control column',
|
||||
'Access to degraded docs',
|
||||
'Access to available stacktraces',
|
||||
'Numberbytes',
|
||||
'machine.ram_range',
|
||||
]);
|
||||
|
|
|
@ -84,7 +84,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
return text === 'Sep 22, 2015 @ 23:50:13.253';
|
||||
});
|
||||
|
||||
await dataGrid.clickCellExpandButton(0, 3);
|
||||
await dataGrid.clickCellExpandButtonExcludingControlColumns(0, 1);
|
||||
|
||||
let expandDocId = '';
|
||||
await retry.waitForWithTimeout('expandDocId to be valid', 5000, async () => {
|
||||
|
@ -127,7 +127,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
log.debug(`row document timestamp: ${text}`);
|
||||
return text === 'Sep 22, 2015 @ 23:50:13.253';
|
||||
});
|
||||
await dataGrid.clickCellExpandButton(0, 3);
|
||||
await dataGrid.clickCellExpandButtonExcludingControlColumns(0, 1);
|
||||
|
||||
let expandDocId = '';
|
||||
await retry.waitForWithTimeout('expandDocId to be valid', 5000, async () => {
|
||||
|
|
|
@ -487,7 +487,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should filter by scripted field value in Discover', async function () {
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await dataGrid.clickCellFilterForButton(0, 3);
|
||||
await dataGrid.clickCellFilterForButtonExcludingControlColumns(0, 1);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await retry.try(async function () {
|
||||
|
|
|
@ -96,46 +96,3 @@ export const resourceHeaderTooltipParagraph = i18n.translate(
|
|||
defaultMessage: "Fields that provide information on the document's source, such as:",
|
||||
}
|
||||
);
|
||||
|
||||
export const actionsHeaderAriaLabelDegradedAction = i18n.translate(
|
||||
'xpack.logsExplorer.dataTable.controlColumnHeader.degradedDocArialLabel',
|
||||
{
|
||||
defaultMessage: 'Access to degraded docs',
|
||||
}
|
||||
);
|
||||
|
||||
export const actionsHeaderAriaLabelStacktraceAction = i18n.translate(
|
||||
'xpack.logsExplorer.dataTable.controlColumnHeader.stacktraceArialLabel',
|
||||
{
|
||||
defaultMessage: 'Access to available stacktraces',
|
||||
}
|
||||
);
|
||||
|
||||
export const degradedDocButtonLabelWhenPresent = i18n.translate(
|
||||
'xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocPresent',
|
||||
{
|
||||
defaultMessage:
|
||||
"This document couldn't be parsed correctly. Not all fields are properly populated",
|
||||
}
|
||||
);
|
||||
|
||||
export const degradedDocButtonLabelWhenNotPresent = i18n.translate(
|
||||
'xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocNotPresent',
|
||||
{
|
||||
defaultMessage: 'All fields in this document were parsed correctly',
|
||||
}
|
||||
);
|
||||
|
||||
export const stacktraceAvailableControlButton = i18n.translate(
|
||||
'xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available',
|
||||
{
|
||||
defaultMessage: 'Stacktraces available',
|
||||
}
|
||||
);
|
||||
|
||||
export const stacktraceNotAvailableControlButton = i18n.translate(
|
||||
'xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable',
|
||||
{
|
||||
defaultMessage: 'Stacktraces not available',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -5,96 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { LogDocument } from '@kbn/discover-utils/src';
|
||||
import type {
|
||||
UnifiedDataTableProps,
|
||||
RowControlComponent,
|
||||
RowControlRowProps,
|
||||
} from '@kbn/unified-data-table';
|
||||
import {
|
||||
actionsHeaderAriaLabelDegradedAction,
|
||||
actionsHeaderAriaLabelStacktraceAction,
|
||||
degradedDocButtonLabelWhenNotPresent,
|
||||
degradedDocButtonLabelWhenPresent,
|
||||
stacktraceAvailableControlButton,
|
||||
stacktraceNotAvailableControlButton,
|
||||
} from '../components/common/translations';
|
||||
import * as constants from '../../common/constants';
|
||||
import { getStacktraceFields } from '../utils/get_stack_trace';
|
||||
|
||||
const DegradedDocs = ({
|
||||
Control,
|
||||
rowProps: { record },
|
||||
}: {
|
||||
Control: RowControlComponent;
|
||||
rowProps: RowControlRowProps;
|
||||
}) => {
|
||||
const isDegradedDocumentExists = constants.DEGRADED_DOCS_FIELD in record.raw;
|
||||
|
||||
return isDegradedDocumentExists ? (
|
||||
<Control
|
||||
data-test-subj="docTableDegradedDocExist"
|
||||
color="danger"
|
||||
label={degradedDocButtonLabelWhenPresent}
|
||||
iconType="indexClose"
|
||||
onClick={undefined}
|
||||
/>
|
||||
) : (
|
||||
<Control
|
||||
data-test-subj="docTableDegradedDocDoesNotExist"
|
||||
color="text"
|
||||
label={degradedDocButtonLabelWhenNotPresent}
|
||||
iconType="indexClose"
|
||||
onClick={undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Stacktrace = ({
|
||||
Control,
|
||||
rowProps: { record },
|
||||
}: {
|
||||
Control: RowControlComponent;
|
||||
rowProps: RowControlRowProps;
|
||||
}) => {
|
||||
const stacktrace = getStacktraceFields(record as LogDocument);
|
||||
const hasValue = Object.values(stacktrace).some((value) => value);
|
||||
|
||||
return hasValue ? (
|
||||
<Control
|
||||
data-test-subj="docTableStacktraceExist"
|
||||
label={stacktraceAvailableControlButton}
|
||||
iconType="apmTrace"
|
||||
onClick={undefined}
|
||||
/>
|
||||
) : (
|
||||
<Control
|
||||
disabled
|
||||
data-test-subj="docTableStacktraceDoesNotExist"
|
||||
label={stacktraceNotAvailableControlButton}
|
||||
iconType="apmTrace"
|
||||
onClick={undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
import { createDegradedDocsControl, createStacktraceControl } from '@kbn/discover-utils';
|
||||
import { type UnifiedDataTableProps } from '@kbn/unified-data-table';
|
||||
|
||||
export const getRowAdditionalControlColumns =
|
||||
(): UnifiedDataTableProps['rowAdditionalLeadingControls'] => {
|
||||
return [
|
||||
{
|
||||
id: 'connectedDegradedDocs',
|
||||
headerAriaLabel: actionsHeaderAriaLabelDegradedAction,
|
||||
renderControl: (Control, rowProps) => {
|
||||
return <DegradedDocs Control={Control} rowProps={rowProps} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'connectedStacktraceDocs',
|
||||
headerAriaLabel: actionsHeaderAriaLabelStacktraceAction,
|
||||
renderControl: (Control, rowProps) => {
|
||||
return <Stacktrace Control={Control} rowProps={rowProps} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
return [createDegradedDocsControl(), createStacktraceControl()];
|
||||
};
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getFieldFromDoc, LogDocument, StackTraceFields } from '@kbn/discover-utils/src';
|
||||
import * as constants from '../../common/constants';
|
||||
|
||||
export const getStacktraceFields = (doc: LogDocument): StackTraceFields => {
|
||||
const errorStackTrace = getFieldFromDoc(doc, constants.ERROR_STACK_TRACE);
|
||||
const errorExceptionStackTrace = getFieldFromDoc(doc, constants.ERROR_EXCEPTION_STACKTRACE);
|
||||
const errorLogStackTrace = getFieldFromDoc(doc, constants.ERROR_LOG_STACKTRACE);
|
||||
|
||||
return {
|
||||
[constants.ERROR_STACK_TRACE]: errorStackTrace,
|
||||
[constants.ERROR_EXCEPTION_STACKTRACE]: errorExceptionStackTrace,
|
||||
[constants.ERROR_LOG_STACKTRACE]: errorLogStackTrace,
|
||||
};
|
||||
};
|
|
@ -26216,10 +26216,6 @@
|
|||
"xpack.logsExplorer.dataSourceSelector.sortOrders": "Sens de tri",
|
||||
"xpack.logsExplorer.dataSourceSelector.TryEsql": "Langue : ES|QL",
|
||||
"xpack.logsExplorer.dataSourceSelector.uncategorized": "Non catégorisé",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocNotPresent": "Tous les champs de ce document ont été analysés correctement",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocPresent": "Ce document n'a pas pu être analysé correctement. Tous les champs n'ont pas été remplis correctement",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available": "Traces d'appel disponibles",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable": "Traces d'appel indisponibles",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "Affiche le {logLevel} du document et les champs {message}.",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph2": "Lorsque le champ de message est vide, l'une des informations suivantes s'affiche :",
|
||||
"xpack.logsExplorer.dataTable.header.popover.content": "Contenu",
|
||||
|
|
|
@ -26205,10 +26205,6 @@
|
|||
"xpack.logsExplorer.dataSourceSelector.sortOrders": "並べ替え方向",
|
||||
"xpack.logsExplorer.dataSourceSelector.TryEsql": "言語:ES|QL",
|
||||
"xpack.logsExplorer.dataSourceSelector.uncategorized": "未分類",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocNotPresent": "このドキュメントのすべてのフィールドは正しく解析されました",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocPresent": "このドキュメントを正しく解析できませんでした。一部のフィールドが正しく入力されていません",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available": "スタックトレースがあります",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable": "スタックトレースがありません",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "ドキュメントの{logLevel}と{message}フィールドを表示します。",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph2": "メッセージフィールドが空のときには、次のいずれかが表示されます。",
|
||||
"xpack.logsExplorer.dataTable.header.popover.content": "コンテンツ",
|
||||
|
|
|
@ -26237,10 +26237,6 @@
|
|||
"xpack.logsExplorer.dataSourceSelector.sortOrders": "排序方向",
|
||||
"xpack.logsExplorer.dataSourceSelector.TryEsql": "语言:ES|QL",
|
||||
"xpack.logsExplorer.dataSourceSelector.uncategorized": "未分类",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocNotPresent": "此文档中的所有字段均进行了正确解析",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDocPresent": "无法正确解析此文档。并非所有字段都进行了正确填充",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available": "堆栈跟踪可用",
|
||||
"xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable": "堆栈跟踪不可用",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "显示该文档的 {logLevel} 和 {message} 字段。",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph2": "消息字段为空时,将显示以下项之一:",
|
||||
"xpack.logsExplorer.dataTable.header.popover.content": "内容",
|
||||
|
|
|
@ -204,6 +204,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await dataGrid.getHeaders()).to.eql([
|
||||
'Select column',
|
||||
'Control column',
|
||||
'Access to degraded docs',
|
||||
'Access to available stacktraces',
|
||||
'Numberbytes',
|
||||
'machine.ram_range',
|
||||
]);
|
||||
|
|
|
@ -84,7 +84,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
return text === 'Sep 22, 2015 @ 23:50:13.253';
|
||||
});
|
||||
|
||||
await dataGrid.clickCellExpandButton(0, 3);
|
||||
await dataGrid.clickCellExpandButtonExcludingControlColumns(0, 1);
|
||||
|
||||
let expandDocId = '';
|
||||
await retry.waitForWithTimeout('expandDocId to be valid', 5000, async () => {
|
||||
|
@ -127,7 +127,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
log.debug(`row document timestamp: ${text}`);
|
||||
return text === 'Sep 22, 2015 @ 23:50:13.253';
|
||||
});
|
||||
await dataGrid.clickCellExpandButton(0, 3);
|
||||
await dataGrid.clickCellExpandButtonExcludingControlColumns(0, 1);
|
||||
|
||||
let expandDocId = '';
|
||||
await retry.waitForWithTimeout('expandDocId to be valid', 5000, async () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue