[Logs + Metrics UI] Add index names for the new indexing strategy (#70245)

This add support for the new index name patterns `logs-*` and `metrics-*` of the new indexing strategy to the Logs and Metrics UI source configurations in the form of a migration and changed defaults.
This commit is contained in:
Felix Stürmer 2020-07-06 17:37:28 +02:00 committed by GitHub
parent 3250816d02
commit 2399780d99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 358 additions and 767 deletions

View file

@ -1,161 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiBadge, EuiButton, EuiPopover, EuiPopoverTitle, EuiSelectable } from '@elastic/eui';
import { EuiSelectableOption } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useCallback, useMemo } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { LogColumnConfiguration } from '../../utils/source_configuration';
import { useVisibilityState } from '../../utils/use_visibility_state';
import { euiStyled } from '../../../../observability/public';
interface SelectableColumnOption {
optionProps: EuiSelectableOption;
columnConfiguration: LogColumnConfiguration;
}
export const AddLogColumnButtonAndPopover: React.FunctionComponent<{
addLogColumn: (logColumnConfiguration: LogColumnConfiguration) => void;
availableFields: string[];
isDisabled?: boolean;
}> = ({ addLogColumn, availableFields, isDisabled }) => {
const { isVisible: isOpen, show: openPopover, hide: closePopover } = useVisibilityState(false);
const availableColumnOptions = useMemo<SelectableColumnOption[]>(
() => [
{
optionProps: {
append: <SystemColumnBadge />,
'data-test-subj': 'addTimestampLogColumn',
// this key works around EuiSelectable using a lowercased label as
// key, which leads to conflicts with field names
key: 'timestamp',
label: 'Timestamp',
},
columnConfiguration: {
timestampColumn: {
id: uuidv4(),
},
},
},
{
optionProps: {
'data-test-subj': 'addMessageLogColumn',
append: <SystemColumnBadge />,
// this key works around EuiSelectable using a lowercased label as
// key, which leads to conflicts with field names
key: 'message',
label: 'Message',
},
columnConfiguration: {
messageColumn: {
id: uuidv4(),
},
},
},
...availableFields.map<SelectableColumnOption>((field) => ({
optionProps: {
'data-test-subj': `addFieldLogColumn addFieldLogColumn:${field}`,
// this key works around EuiSelectable using a lowercased label as
// key, which leads to conflicts with fields that only differ in the
// case (e.g. the metricbeat mongodb module)
key: `field-${field}`,
label: field,
},
columnConfiguration: {
fieldColumn: {
id: uuidv4(),
field,
},
},
})),
],
[availableFields]
);
const availableOptions = useMemo<EuiSelectableOption[]>(
() => availableColumnOptions.map((availableColumnOption) => availableColumnOption.optionProps),
[availableColumnOptions]
);
const handleColumnSelection = useCallback(
(selectedOptions: EuiSelectableOption[]) => {
closePopover();
const selectedOptionIndex = selectedOptions.findIndex(
(selectedOption) => selectedOption.checked === 'on'
);
const selectedOption = availableColumnOptions[selectedOptionIndex];
addLogColumn(selectedOption.columnConfiguration);
},
[addLogColumn, availableColumnOptions, closePopover]
);
return (
<EuiPopover
anchorPosition="downRight"
button={
<EuiButton
data-test-subj="addLogColumnButton"
isDisabled={isDisabled}
iconType="plusInCircle"
onClick={openPopover}
>
<FormattedMessage
id="xpack.infra.sourceConfiguration.addLogColumnButtonLabel"
defaultMessage="Add column"
/>
</EuiButton>
}
closePopover={closePopover}
id="addLogColumn"
isOpen={isOpen}
ownFocus
panelPaddingSize="none"
>
<EuiSelectable
height={600}
listProps={selectableListProps}
onChange={handleColumnSelection}
options={availableOptions}
searchable
searchProps={searchProps}
singleSelection
>
{(list, search) => (
<SelectableContent data-test-subj="addLogColumnPopover">
<EuiPopoverTitle>{search}</EuiPopoverTitle>
{list}
</SelectableContent>
)}
</EuiSelectable>
</EuiPopover>
);
};
const searchProps = {
'data-test-subj': 'fieldSearchInput',
};
const selectableListProps = {
showIcons: false,
};
const SystemColumnBadge: React.FunctionComponent = () => (
<EuiBadge>
<FormattedMessage
id="xpack.infra.sourceConfiguration.systemColumnBadgeLabel"
defaultMessage="System"
/>
</EuiBadge>
);
const SelectableContent = euiStyled.div`
width: 400px;
`;

View file

@ -27,9 +27,7 @@ interface FieldsConfigurationPanelProps {
isLoading: boolean;
readOnly: boolean;
podFieldProps: InputFieldProps;
tiebreakerFieldProps: InputFieldProps;
timestampFieldProps: InputFieldProps;
displaySettings: 'metrics' | 'logs';
}
export const FieldsConfigurationPanel = ({
@ -38,15 +36,12 @@ export const FieldsConfigurationPanel = ({
isLoading,
readOnly,
podFieldProps,
tiebreakerFieldProps,
timestampFieldProps,
displaySettings,
}: FieldsConfigurationPanelProps) => {
const isHostValueDefault = hostFieldProps.value === 'host.name';
const isContainerValueDefault = containerFieldProps.value === 'container.id';
const isPodValueDefault = podFieldProps.value === 'kubernetes.pod.uid';
const isTimestampValueDefault = timestampFieldProps.value === '@timestamp';
const isTiebreakerValueDefault = tiebreakerFieldProps.value === '_doc';
return (
<EuiForm>
<EuiTitle size="s">
@ -139,194 +134,141 @@ export const FieldsConfigurationPanel = ({
/>
</EuiFormRow>
</EuiDescribedFormGroup>
{displaySettings === 'logs' && (
<>
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.tiebreakerFieldLabel"
defaultMessage="Tiebreaker"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.tiebreakerFieldDescription"
defaultMessage="Field used to break ties between two entries with the same timestamp"
/>
}
>
<EuiFormRow
error={tiebreakerFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.tiebreakerFieldRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>_doc</EuiCode>,
}}
/>
}
isInvalid={tiebreakerFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.tiebreakerFieldLabel"
defaultMessage="Tiebreaker"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isTiebreakerValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...tiebreakerFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
</>
)}
{displaySettings === 'metrics' && (
<>
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldLabel"
defaultMessage="Container ID"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldDescription"
defaultMessage="Field used to identify Docker containers"
/>
}
>
<EuiFormRow
error={containerFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>container.id</EuiCode>,
}}
/>
}
isInvalid={containerFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldLabel"
defaultMessage="Container ID"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isContainerValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...containerFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostNameFieldLabel"
defaultMessage="Host name"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostNameFieldDescription"
defaultMessage="Field used to identify hosts"
/>
}
>
<EuiFormRow
error={hostFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostFieldDescription"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>host.name</EuiCode>,
}}
/>
}
isInvalid={hostFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostFieldLabel"
defaultMessage="Host name"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isHostValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...hostFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldLabel"
defaultMessage="Pod ID"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldDescription"
defaultMessage="Field used to identify Kubernetes pods"
/>
}
>
<EuiFormRow
error={podFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>kubernetes.pod.uid</EuiCode>,
}}
/>
}
isInvalid={podFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldLabel"
defaultMessage="Pod ID"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isPodValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...podFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
</>
)}
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldLabel"
defaultMessage="Container ID"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldDescription"
defaultMessage="Field used to identify Docker containers"
/>
}
>
<EuiFormRow
error={containerFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>container.id</EuiCode>,
}}
/>
}
isInvalid={containerFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.containerFieldLabel"
defaultMessage="Container ID"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isContainerValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...containerFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostNameFieldLabel"
defaultMessage="Host name"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostNameFieldDescription"
defaultMessage="Field used to identify hosts"
/>
}
>
<EuiFormRow
error={hostFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostFieldDescription"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>host.name</EuiCode>,
}}
/>
}
isInvalid={hostFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.hostFieldLabel"
defaultMessage="Host name"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isHostValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...hostFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldLabel"
defaultMessage="Pod ID"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldDescription"
defaultMessage="Field used to identify Kubernetes pods"
/>
}
>
<EuiFormRow
error={podFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>kubernetes.pod.uid</EuiCode>,
}}
/>
}
isInvalid={podFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.podFieldLabel"
defaultMessage="Pod ID"
/>
}
>
<EuiFieldText
fullWidth
disabled={isLoading || isPodValueDefault}
readOnly={readOnly}
isLoading={isLoading}
{...podFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
</EuiForm>
);
};

View file

@ -21,17 +21,13 @@ import { InputFieldProps } from './input_fields';
interface IndicesConfigurationPanelProps {
isLoading: boolean;
readOnly: boolean;
logAliasFieldProps: InputFieldProps;
metricAliasFieldProps: InputFieldProps;
displaySettings: 'metrics' | 'logs';
}
export const IndicesConfigurationPanel = ({
isLoading,
readOnly,
logAliasFieldProps,
metricAliasFieldProps,
displaySettings,
}: IndicesConfigurationPanelProps) => (
<EuiForm>
<EuiTitle size="s">
@ -43,101 +39,51 @@ export const IndicesConfigurationPanel = ({
</h3>
</EuiTitle>
<EuiSpacer size="m" />
{displaySettings === 'metrics' && (
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesTitle"
defaultMessage="Metric indices"
/>
</h4>
}
description={
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesDescription"
defaultMessage="Index pattern for matching indices that contain Metricbeat data"
id="xpack.infra.sourceConfiguration.metricIndicesTitle"
defaultMessage="Metrics indices"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesDescription"
defaultMessage="Index pattern for matching indices that contain metrics data"
/>
}
>
<EuiFormRow
error={metricAliasFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>metrics-*,metricbeat-*</EuiCode>,
}}
/>
}
isInvalid={metricAliasFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesLabel"
defaultMessage="Metrics indices"
/>
}
>
<EuiFormRow
error={metricAliasFieldProps.error}
<EuiFieldText
data-test-subj="metricIndicesInput"
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>metricbeat-*</EuiCode>,
}}
/>
}
isInvalid={metricAliasFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.metricIndicesLabel"
defaultMessage="Metric indices"
/>
}
>
<EuiFieldText
data-test-subj="metricIndicesInput"
fullWidth
disabled={isLoading}
readOnly={readOnly}
isLoading={isLoading}
{...metricAliasFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
)}
{displaySettings === 'logs' && (
<EuiDescribedFormGroup
title={
<h4>
<FormattedMessage
id="xpack.infra.sourceConfiguration.logIndicesTitle"
defaultMessage="Log indices"
/>
</h4>
}
description={
<FormattedMessage
id="xpack.infra.sourceConfiguration.logIndicesDescription"
defaultMessage="Index pattern for matching indices that contain log data"
/>
}
>
<EuiFormRow
error={logAliasFieldProps.error}
fullWidth
helpText={
<FormattedMessage
id="xpack.infra.sourceConfiguration.logIndicesRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>filebeat-*</EuiCode>,
}}
/>
}
isInvalid={logAliasFieldProps.isInvalid}
label={
<FormattedMessage
id="xpack.infra.sourceConfiguration.logIndicesLabel"
defaultMessage="Log indices"
/>
}
>
<EuiFieldText
data-test-subj="logIndicesInput"
fullWidth
disabled={isLoading}
isLoading={isLoading}
readOnly={readOnly}
{...logAliasFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
)}
disabled={isLoading}
readOnly={readOnly}
isLoading={isLoading}
{...metricAliasFieldProps}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
</EuiForm>
);

View file

@ -1,279 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import {
EuiButtonIcon,
EuiEmptyPrompt,
EuiForm,
EuiPanel,
EuiSpacer,
EuiText,
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiDragDropContext,
EuiDraggable,
EuiDroppable,
EuiIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useCallback } from 'react';
import { DragHandleProps, DropResult } from '../../../../observability/public';
import { AddLogColumnButtonAndPopover } from './add_log_column_popover';
import {
FieldLogColumnConfigurationProps,
LogColumnConfigurationProps,
} from './log_columns_configuration_form_state';
import { LogColumnConfiguration } from '../../utils/source_configuration';
interface LogColumnsConfigurationPanelProps {
availableFields: string[];
isLoading: boolean;
logColumnConfiguration: LogColumnConfigurationProps[];
addLogColumn: (logColumn: LogColumnConfiguration) => void;
moveLogColumn: (sourceIndex: number, destinationIndex: number) => void;
}
export const LogColumnsConfigurationPanel: React.FunctionComponent<LogColumnsConfigurationPanelProps> = ({
addLogColumn,
moveLogColumn,
availableFields,
isLoading,
logColumnConfiguration,
}) => {
const onDragEnd = useCallback(
({ source, destination }: DropResult) =>
destination && moveLogColumn(source.index, destination.index),
[moveLogColumn]
);
return (
<EuiForm>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="s" data-test-subj="sourceConfigurationLogColumnsSectionTitle">
<h3>
<FormattedMessage
id="xpack.infra.sourceConfiguration.logColumnsSectionTitle"
defaultMessage="Log Columns"
/>
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AddLogColumnButtonAndPopover
addLogColumn={addLogColumn}
availableFields={availableFields}
isDisabled={isLoading}
/>
</EuiFlexItem>
</EuiFlexGroup>
{logColumnConfiguration.length > 0 ? (
<EuiDragDropContext onDragEnd={onDragEnd}>
<EuiDroppable droppableId="COLUMN_CONFIG_DROPPABLE_AREA">
<>
{/* Fragment here necessary for typechecking */}
{logColumnConfiguration.map((column, index) => (
<EuiDraggable
key={`logColumnConfigurationPanel-${column.logColumnConfiguration.id}`}
index={index}
draggableId={column.logColumnConfiguration.id}
customDragHandle
>
{(provided) => (
<LogColumnConfigurationPanel
dragHandleProps={provided.dragHandleProps}
logColumnConfigurationProps={column}
/>
)}
</EuiDraggable>
))}
</>
</EuiDroppable>
</EuiDragDropContext>
) : (
<LogColumnConfigurationEmptyPrompt />
)}
</EuiForm>
);
};
interface LogColumnConfigurationPanelProps {
logColumnConfigurationProps: LogColumnConfigurationProps;
dragHandleProps: DragHandleProps;
}
const LogColumnConfigurationPanel: React.FunctionComponent<LogColumnConfigurationPanelProps> = (
props
) => (
<>
<EuiSpacer size="m" />
{props.logColumnConfigurationProps.type === 'timestamp' ? (
<TimestampLogColumnConfigurationPanel {...props} />
) : props.logColumnConfigurationProps.type === 'message' ? (
<MessageLogColumnConfigurationPanel {...props} />
) : (
<FieldLogColumnConfigurationPanel
logColumnConfigurationProps={props.logColumnConfigurationProps}
dragHandleProps={props.dragHandleProps}
/>
)}
</>
);
const TimestampLogColumnConfigurationPanel: React.FunctionComponent<LogColumnConfigurationPanelProps> = ({
logColumnConfigurationProps,
dragHandleProps,
}) => (
<ExplainedLogColumnConfigurationPanel
fieldName="Timestamp"
helpText={
<FormattedMessage
tagName="span"
id="xpack.infra.sourceConfiguration.timestampLogColumnDescription"
defaultMessage="This system field shows the log entry's time as determined by the {timestampSetting} field setting."
values={{
timestampSetting: <code>timestamp</code>,
}}
/>
}
removeColumn={logColumnConfigurationProps.remove}
dragHandleProps={dragHandleProps}
/>
);
const MessageLogColumnConfigurationPanel: React.FunctionComponent<LogColumnConfigurationPanelProps> = ({
logColumnConfigurationProps,
dragHandleProps,
}) => (
<ExplainedLogColumnConfigurationPanel
fieldName="Message"
helpText={
<FormattedMessage
tagName="span"
id="xpack.infra.sourceConfiguration.messageLogColumnDescription"
defaultMessage="This system field shows the log entry message as derived from the document fields."
/>
}
removeColumn={logColumnConfigurationProps.remove}
dragHandleProps={dragHandleProps}
/>
);
const FieldLogColumnConfigurationPanel: React.FunctionComponent<{
logColumnConfigurationProps: FieldLogColumnConfigurationProps;
dragHandleProps: DragHandleProps;
}> = ({
logColumnConfigurationProps: {
logColumnConfiguration: { field },
remove,
},
dragHandleProps,
}) => {
const fieldLogColumnTitle = i18n.translate(
'xpack.infra.sourceConfiguration.fieldLogColumnTitle',
{
defaultMessage: 'Field',
}
);
return (
<EuiPanel data-test-subj={`logColumnPanel fieldLogColumnPanel fieldLogColumnPanel:${field}`}>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<div data-test-subj="moveLogColumnHandle" {...dragHandleProps}>
<EuiIcon type="grab" />
</div>
</EuiFlexItem>
<EuiFlexItem grow={1}>{fieldLogColumnTitle}</EuiFlexItem>
<EuiFlexItem grow={3}>
<code>{field}</code>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<RemoveLogColumnButton
onClick={remove}
columnDescription={`${fieldLogColumnTitle} - ${field}`}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
};
const ExplainedLogColumnConfigurationPanel: React.FunctionComponent<{
fieldName: React.ReactNode;
helpText: React.ReactNode;
removeColumn: () => void;
dragHandleProps: DragHandleProps;
}> = ({ fieldName, helpText, removeColumn, dragHandleProps }) => (
<EuiPanel
data-test-subj={`logColumnPanel systemLogColumnPanel systemLogColumnPanel:${fieldName}`}
>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<div data-test-subj="moveLogColumnHandle" {...dragHandleProps}>
<EuiIcon type="grab" />
</div>
</EuiFlexItem>
<EuiFlexItem grow={1}>{fieldName}</EuiFlexItem>
<EuiFlexItem grow={3}>
<EuiText size="s" color="subdued">
{helpText}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<RemoveLogColumnButton onClick={removeColumn} columnDescription={String(fieldName)} />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
const RemoveLogColumnButton: React.FunctionComponent<{
onClick?: () => void;
columnDescription: string;
}> = ({ onClick, columnDescription }) => {
const removeColumnLabel = i18n.translate(
'xpack.infra.sourceConfiguration.removeLogColumnButtonLabel',
{
defaultMessage: 'Remove {columnDescription} column',
values: { columnDescription },
}
);
return (
<EuiButtonIcon
color="danger"
data-test-subj="removeLogColumnButton"
iconType="trash"
onClick={onClick}
title={removeColumnLabel}
aria-label={removeColumnLabel}
/>
);
};
const LogColumnConfigurationEmptyPrompt: React.FunctionComponent = () => (
<EuiEmptyPrompt
iconType="list"
title={
<h2>
<FormattedMessage
id="xpack.infra.sourceConfiguration.noLogColumnsTitle"
defaultMessage="No columns"
/>
</h2>
}
body={
<p>
<FormattedMessage
id="xpack.infra.sourceConfiguration.noLogColumnsDescription"
defaultMessage="Add a column to this list using the button above."
/>
</p>
}
/>
);

View file

@ -22,19 +22,16 @@ import { Source } from '../../containers/source';
import { FieldsConfigurationPanel } from './fields_configuration_panel';
import { IndicesConfigurationPanel } from './indices_configuration_panel';
import { NameConfigurationPanel } from './name_configuration_panel';
import { LogColumnsConfigurationPanel } from './log_columns_configuration_panel';
import { useSourceConfigurationFormState } from './source_configuration_form_state';
import { SourceLoadingPage } from '../source_loading_page';
import { Prompt } from '../../utils/navigation_warning_prompt';
interface SourceConfigurationSettingsProps {
shouldAllowEdit: boolean;
displaySettings: 'metrics' | 'logs';
}
export const SourceConfigurationSettings = ({
shouldAllowEdit,
displaySettings,
}: SourceConfigurationSettingsProps) => {
const {
createSourceConfiguration,
@ -45,16 +42,8 @@ export const SourceConfigurationSettings = ({
updateSourceConfiguration,
} = useContext(Source.Context);
const availableFields = useMemo(
() => (source && source.status ? source.status.indexFields.map((field) => field.name) : []),
[source]
);
const {
addLogColumn,
moveLogColumn,
indicesConfigurationProps,
logColumnConfigurationProps,
errors,
resetForm,
isFormDirty,
@ -119,10 +108,8 @@ export const SourceConfigurationSettings = ({
<EuiPanel paddingSize="l">
<IndicesConfigurationPanel
isLoading={isLoading}
logAliasFieldProps={indicesConfigurationProps.logAlias}
metricAliasFieldProps={indicesConfigurationProps.metricAlias}
readOnly={!isWriteable}
displaySettings={displaySettings}
/>
</EuiPanel>
<EuiSpacer />
@ -133,23 +120,10 @@ export const SourceConfigurationSettings = ({
isLoading={isLoading}
podFieldProps={indicesConfigurationProps.podField}
readOnly={!isWriteable}
tiebreakerFieldProps={indicesConfigurationProps.tiebreakerField}
timestampFieldProps={indicesConfigurationProps.timestampField}
displaySettings={displaySettings}
/>
</EuiPanel>
<EuiSpacer />
{displaySettings === 'logs' && (
<EuiPanel paddingSize="l">
<LogColumnsConfigurationPanel
addLogColumn={addLogColumn}
moveLogColumn={moveLogColumn}
availableFields={availableFields}
isLoading={isLoading}
logColumnConfiguration={logColumnConfigurationProps}
/>
</EuiPanel>
)}
{errors.length > 0 ? (
<>
<EuiCallOut color="danger">

View file

@ -62,7 +62,7 @@ export const IndicesConfigurationPanel = ({
id="xpack.infra.sourceConfiguration.logIndicesRecommendedValue"
defaultMessage="The recommended value is {defaultValue}"
values={{
defaultValue: <EuiCode>filebeat-*</EuiCode>,
defaultValue: <EuiCode>logs-*,filebeat-*</EuiCode>,
}}
/>
}

View file

@ -15,7 +15,6 @@ export const MetricsSettingsPage = () => {
<EuiErrorBoundary>
<SourceConfigurationSettings
shouldAllowEdit={uiCapabilities?.infrastructure?.configureSource as boolean}
displaySettings="metrics"
/>
</EuiErrorBoundary>
);

View file

@ -9,8 +9,8 @@ import { InfraSourceConfiguration } from '../../../common/http_api/source_api';
export const defaultSourceConfiguration: InfraSourceConfiguration = {
name: 'Default',
description: '',
metricAlias: 'metricbeat-*',
logAlias: 'filebeat-*,kibana_sample_data_logs*',
metricAlias: 'metrics-*,metricbeat-*',
logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs*',
fields: {
container: 'container.id',
host: 'host.name',

View file

@ -0,0 +1,131 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { migrationMocks } from 'src/core/server/mocks';
import { addNewIndexingStrategyIndexNames } from './7_9_0_add_new_indexing_strategy_index_names';
import { infraSourceConfigurationSavedObjectName } from '../saved_object_type';
describe('infra source configuration migration function for 7.9.0', () => {
test('adds "logs-*" when the logAlias contains "filebeat-*"', () => {
const unmigratedConfiguration = createTestSourceConfiguration(
'filebeat-*,custom-log-index-*',
'custom-metric-index-*'
);
const migratedConfiguration = addNewIndexingStrategyIndexNames(
unmigratedConfiguration,
migrationMocks.createContext()
);
expect(migratedConfiguration).toStrictEqual(
createTestSourceConfiguration('filebeat-*,custom-log-index-*,logs-*', 'custom-metric-index-*')
);
});
test('doesn\'t add "logs-*" when the logAlias doesn\'t contain "filebeat-*"', () => {
const unmigratedConfiguration = createTestSourceConfiguration(
'custom-log-index-*',
'custom-metric-index-*'
);
const migratedConfiguration = addNewIndexingStrategyIndexNames(
unmigratedConfiguration,
migrationMocks.createContext()
);
expect(migratedConfiguration).toStrictEqual(unmigratedConfiguration);
});
test('doesn\'t add "logs-*" when the logAlias already contains it', () => {
const unmigratedConfiguration = createTestSourceConfiguration(
'filebeat-*,logs-*,custom-log-index-*',
'custom-metric-index-*'
);
const migratedConfiguration = addNewIndexingStrategyIndexNames(
unmigratedConfiguration,
migrationMocks.createContext()
);
expect(migratedConfiguration).toStrictEqual(unmigratedConfiguration);
});
test('adds "metrics-*" when the logAlias contains "metricbeat-*"', () => {
const unmigratedConfiguration = createTestSourceConfiguration(
'custom-log-index-*',
'metricbeat-*,custom-metric-index-*'
);
const migratedConfiguration = addNewIndexingStrategyIndexNames(
unmigratedConfiguration,
migrationMocks.createContext()
);
expect(migratedConfiguration).toStrictEqual(
createTestSourceConfiguration(
'custom-log-index-*',
'metricbeat-*,custom-metric-index-*,metrics-*'
)
);
});
test('doesn\'t add "metrics-*" when the logAlias doesn\'t contain "metricbeat-*"', () => {
const unmigratedConfiguration = createTestSourceConfiguration(
'custom-log-index-*',
'custom-metric-index-*'
);
const migratedConfiguration = addNewIndexingStrategyIndexNames(
unmigratedConfiguration,
migrationMocks.createContext()
);
expect(migratedConfiguration).toStrictEqual(unmigratedConfiguration);
});
test('doesn\'t add "metrics-*" when the metricAlias already contains it', () => {
const unmigratedConfiguration = createTestSourceConfiguration(
'custom-log-index-*',
'metrics-*,metricbeat-*,custom-metric-index-*'
);
const migratedConfiguration = addNewIndexingStrategyIndexNames(
unmigratedConfiguration,
migrationMocks.createContext()
);
expect(migratedConfiguration).toStrictEqual(unmigratedConfiguration);
});
});
const createTestSourceConfiguration = (logAlias: string, metricAlias: string) => ({
attributes: {
name: 'TEST CONFIGURATION',
description: '',
fields: {
pod: 'TEST POD FIELD',
host: 'TEST HOST FIELD',
message: ['TEST MESSAGE FIELD'],
container: 'TEST CONTAINER FIELD',
timestamp: 'TEST TIMESTAMP FIELD',
tiebreaker: 'TEST TIEBREAKER FIELD',
},
inventoryDefaultView: '0',
metricsExplorerDefaultView: '0',
logColumns: [
{
fieldColumn: {
id: 'TEST FIELD COLUMN ID',
field: 'TEST FIELD COLUMN FIELD',
},
},
],
logAlias,
metricAlias,
},
id: 'TEST_ID',
type: infraSourceConfigurationSavedObjectName,
});

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObjectMigrationFn } from 'src/core/server';
import { InfraSourceConfiguration } from '../../../../common/http_api/source_api';
export const addNewIndexingStrategyIndexNames: SavedObjectMigrationFn<
InfraSourceConfiguration,
InfraSourceConfiguration
> = (sourceConfigurationDocument) => {
const oldLogAliasSegments = sourceConfigurationDocument.attributes.logAlias.split(',');
const oldMetricAliasSegments = sourceConfigurationDocument.attributes.metricAlias.split(',');
const newLogAliasSegment = 'logs-*';
const newMetricAliasSegment = 'metrics-*';
return {
...sourceConfigurationDocument,
attributes: {
...sourceConfigurationDocument.attributes,
logAlias:
oldLogAliasSegments.includes('filebeat-*') &&
!oldLogAliasSegments.includes(newLogAliasSegment)
? [...oldLogAliasSegments, newLogAliasSegment].join(',')
: sourceConfigurationDocument.attributes.logAlias,
metricAlias:
oldMetricAliasSegments.includes('metricbeat-*') &&
!oldMetricAliasSegments.includes(newMetricAliasSegment)
? [...oldMetricAliasSegments, newMetricAliasSegment].join(',')
: sourceConfigurationDocument.attributes.metricAlias,
},
};
};

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { SavedObjectsType } from 'src/core/server';
import { addNewIndexingStrategyIndexNames } from './migrations/7_9_0_add_new_indexing_strategy_index_names';
export const infraSourceConfigurationSavedObjectName = 'infrastructure-ui-source';
@ -86,4 +86,7 @@ export const infraSourceConfigurationSavedObjectType: SavedObjectsType = {
},
},
},
migrations: {
'7.9.0': addNewIndexingStrategyIndexNames,
},
};

View file

@ -35,7 +35,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(origin).to.be('fallback');
expect(configuration.name).to.be('Default');
expect(configuration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
expect(configuration.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(configuration.fields.timestamp).to.be('@timestamp');
expect(configuration.fields.tiebreaker).to.be('_doc');
expect(configuration.logColumns[0]).to.have.key('timestampColumn');
@ -97,7 +97,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(configuration.name).to.be('Default');
expect(origin).to.be('stored');
expect(configuration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
expect(configuration.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(configuration.fields.timestamp).to.be('@timestamp');
expect(configuration.fields.tiebreaker).to.be('_doc');
expect(configuration.logColumns).to.have.length(3);
@ -166,7 +166,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(configuration.name).to.be('NAME');
expect(origin).to.be('stored');
expect(configuration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
expect(configuration.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(configuration.fields.timestamp).to.be('@timestamp');
expect(configuration.fields.tiebreaker).to.be('_doc');
expect(configuration.logColumns).to.have.length(3);

View file

@ -40,8 +40,8 @@ export default function ({ getService }: FtrProviderContext) {
// shipped default values
expect(sourceConfiguration.name).to.be('Default');
expect(sourceConfiguration.metricAlias).to.be('metricbeat-*');
expect(sourceConfiguration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
expect(sourceConfiguration.metricAlias).to.be('metrics-*,metricbeat-*');
expect(sourceConfiguration.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(sourceConfiguration.fields.container).to.be('container.id');
expect(sourceConfiguration.fields.host).to.be('host.name');
expect(sourceConfiguration.fields.pod).to.be('kubernetes.pod.uid');
@ -125,8 +125,8 @@ export default function ({ getService }: FtrProviderContext) {
expect(updatedAt).to.be.greaterThan(0);
expect(configuration.name).to.be('NAME');
expect(configuration.description).to.be('');
expect(configuration.metricAlias).to.be('metricbeat-*');
expect(configuration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
expect(configuration.metricAlias).to.be('metrics-*,metricbeat-*');
expect(configuration.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(configuration.fields.container).to.be('container.id');
expect(configuration.fields.host).to.be('host.name');
expect(configuration.fields.pod).to.be('kubernetes.pod.uid');
@ -283,7 +283,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(version).to.not.be(initialVersion);
expect(updatedAt).to.be.greaterThan(createdAt);
expect(configuration.metricAlias).to.be('metricbeat-**');
expect(configuration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
expect(configuration.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(status.logIndicesExist).to.be(true);
expect(status.metricIndicesExist).to.be(true);
});