Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -34554,9 +34554,6 @@
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.eventTypeColumn": "Typ",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.fetchErrorDescription": "Fehler beim Abrufen der Regelausführungsereignisse",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.logLevelColumn": "Kategorie",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.messageColumn": "Nachricht",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.jsonTitle": "Vollständiges JSON",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.messageTitle": "Nachricht",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableSubtitle": "Ein detailliertes Log der Regelausführungsereignisse",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableTitle": "Ausführungslog",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.timestampColumn": "Zeitstempel",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34910,9 +34910,6 @@
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.eventTypeColumn": "Type",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.fetchErrorDescription": "Impossible de récupérer les événements d'exécution de règle",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.logLevelColumn": "Niveau",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.messageColumn": "Message",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.jsonTitle": "Full JSON",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.messageTitle": "Message",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableSubtitle": "Un log détaillé des événements d’exécution de la règle",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableTitle": "Log d'exécution",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.timestampColumn": "Horodatage",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34962,9 +34962,6 @@
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.eventTypeColumn": "型",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.fetchErrorDescription": "ルール実行イベントを取得できませんでした",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.logLevelColumn": "レベル",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.messageColumn": "メッセージ",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.jsonTitle": "JSON全体",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.messageTitle": "メッセージ",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableSubtitle": "ルール実行イベントの詳細ログ",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableTitle": "実行ログ",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.timestampColumn": "タイムスタンプ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34946,9 +34946,6 @@
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.eventTypeColumn": "类型",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.fetchErrorDescription": "无法提取规则执行事件",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.logLevelColumn": "级别",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.messageColumn": "消息",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.jsonTitle": "完整 JSON",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.rowDetails.messageTitle": "消息",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableSubtitle": "规则执行事件的详细日志",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.tableTitle": "执行日志",
"xpack.securitySolution.detectionEngine.ruleMonitoring.executionEventsTable.timestampColumn": "时间戳",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ export const RuleExecutionEvent = z.object({
type: RuleExecutionEventType,
execution_id: z.string().min(1),
message: z.string(),
/**
* Event details. The details vary per event type.
*/
details: z.object({}).catchall(z.unknown()).optional(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ components:
minLength: 1
message:
type: string
details:
type: object
additionalProperties: true
description: Event details. The details vary per event type.
required:
- timestamp
- sequence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ export const HealthTruncateText: React.FC<PropsWithChildren<HealthTruncateTextPr
children,
healthColor,
dataTestSubj,
}) => (
<EuiToolTip content={tooltipContent}>
}) => {
const content = (
<EuiHealth color={healthColor} data-test-subj={dataTestSubj}>
<StatusTextWrapper tabIndex={0}>
<span className="eui-textTruncate">{children}</span>
</StatusTextWrapper>
</EuiHealth>
</EuiToolTip>
);
);
if (!tooltipContent) {
return content;
}
return <EuiToolTip content={tooltipContent}>{content}</EuiToolTip>;
};

HealthTruncateText.displayName = 'HealthTruncateText';
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import * as i18n from './translations';
export const getBadgeIcon = (type: RuleExecutionEventType): IconType => {
switch (type) {
case RuleExecutionEventTypeEnum.message:
return 'console';
return 'comment';
case RuleExecutionEventTypeEnum['status-change']:
return 'dot';
case RuleExecutionEventTypeEnum['execution-metrics']:
return 'gear';
return 'stats';
default:
return assertUnreachable(type, 'Unknown rule execution event type');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,22 @@
* 2.0.
*/

import type { ChangeEvent } from 'react';
import React, { useCallback } from 'react';
import React from 'react';
import { EuiFieldSearch } from '@elastic/eui';
import * as i18n from './translations';

interface EventMessageFilterProps {
value: string;
onChange: (value: string) => void;
onSearch: (value: string) => void;
}

export function EventMessageFilter({ value, onChange }: EventMessageFilterProps): JSX.Element {
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value),
[onChange]
);

export function EventMessageFilter({ onSearch }: EventMessageFilterProps): JSX.Element {
return (
<EuiFieldSearch
aria-label={i18n.SEARCH_BY_EVENT_MESSAGE_ARIA_LABEL}
fullWidth
incremental={false}
placeholder={i18n.SEARCH_BY_EVENT_MESSAGE_PLACEHOLDER}
value={value}
onChange={handleChange}
onSearch={onSearch}
data-test-subj="ruleEventLogMessageSearchField"
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { RuleExecutionEvent } from '../../../../../common/api/detection_eng
import { HeaderSection } from '../../../../common/components/header_section';
import { EventTypeFilter } from '../basic/filters/event_type_filter';
import { LogLevelFilter } from '../basic/filters/log_level_filter';
import { ExecutionEventsTableRowDetails } from './execution_events_table_row_details';
import { ExecutionEventsTableDetailsCell } from './execution_events_table_row_details';

import { useFilters } from './use_filters';
import { useSorting } from '../basic/tables/use_sorting';
Expand All @@ -34,6 +34,10 @@ import * as i18n from './translations';

const PAGE_SIZE_OPTIONS = [10, 20, 50, 100, 200];

const tableRowProps = {
'data-test-subj': 'executionEventsTableRow',
};

interface ExecutionEventsTableProps {
ruleId: string;
}
Expand All @@ -44,7 +48,7 @@ const ExecutionEventsTableComponent: React.FC<ExecutionEventsTableProps> = ({ ru
}, []);

const renderExpandedItem = useCallback((item: RuleExecutionEvent) => {
return <ExecutionEventsTableRowDetails item={item} />;
return <ExecutionEventsTableDetailsCell item={item} />;
}, []);

const rows = useExpandableRows<RuleExecutionEvent>({
Expand Down Expand Up @@ -96,7 +100,7 @@ const ExecutionEventsTableComponent: React.FC<ExecutionEventsTableProps> = ({ ru
<HeaderSection title={i18n.TABLE_TITLE} subtitle={i18n.TABLE_SUBTITLE} />
</EuiFlexItem>
<EuiFlexItem grow>
<EventMessageFilter value={filters.state.searchTerm} onChange={filters.setSearchTerm} />
<EventMessageFilter onSearch={filters.setSearchTerm} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<LogLevelFilter selectedItems={filters.state.logLevels} onChange={filters.setLogLevels} />
Expand All @@ -113,12 +117,14 @@ const ExecutionEventsTableComponent: React.FC<ExecutionEventsTableProps> = ({ ru
end={filters.state.dateRange.end}
onTimeChange={filters.setDateRange}
showUpdateButton={false}
data-test-subj="executionEventsTable-datePicker"
/>
</EuiFlexItem>
</EuiFlexGroup>

{/* Table with items */}
<EuiBasicTable
data-test-subj="executionEventsTable"
columns={columns}
items={items}
itemId={getItemId}
Expand All @@ -127,6 +133,8 @@ const ExecutionEventsTableComponent: React.FC<ExecutionEventsTableProps> = ({ ru
sorting={sorting.state}
pagination={pagination.state}
onChange={handleTableChange}
tableCaption={i18n.TABLE_SUBTITLE}
rowProps={tableRowProps}
/>
</EuiPanel>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,64 @@
*/

import React from 'react';
import { EuiDescriptionList } from '@elastic/eui';
import type { RuleExecutionEvent } from '../../../../../common/api/detection_engine/rule_monitoring';
import { TextBlock } from '../basic/text/text_block';
import { EuiCodeBlock, EuiDescriptionList } from '@elastic/eui';
import { type RuleExecutionEvent } from '../../../../../common/api/detection_engine/rule_monitoring';

import * as i18n from './translations';

interface ExecutionEventsTableRowDetailsProps {
interface ExecutionEventsTableDetailsCellProps {
item: RuleExecutionEvent;
}

const ExecutionEventsTableRowDetailsComponent: React.FC<ExecutionEventsTableRowDetailsProps> = ({
const ExecutionEventsTableDetailsCellComponent: React.FC<ExecutionEventsTableDetailsCellProps> = ({
item,
}) => {
const listItems = [];

if (item.message) {
listItems.push({
title: i18n.EVENT_MESSAGE,
description: (
<EuiCodeBlock
isCopyable={true}
language="text"
overflowHeight={200}
paddingSize="s"
data-test-subj="executionEventsTable-eventMessage"
>
{item.message}
</EuiCodeBlock>
),
});
}

if (item?.details?.metrics) {
listItems.push({
title: i18n.METRICS,
description: (
<EuiCodeBlock isCopyable={true} language="text" paddingSize="s">
{JSON.stringify(item.details.metrics, null, 2)}
</EuiCodeBlock>
),
});
}

listItems.push({
title: i18n.RULE_EXECUTION_ID,
description: (
<EuiCodeBlock isCopyable={true} language="text" paddingSize="s">
{item.execution_id}
</EuiCodeBlock>
),
});
return (
<EuiDescriptionList
data-test-subj="executionEventsTable-eventDetails"
className="eui-fullWidth"
listItems={[
{
title: i18n.ROW_DETAILS_MESSAGE,
description: <TextBlock text={item.message} />,
},
{
title: i18n.ROW_DETAILS_JSON,
description: <TextBlock text={JSON.stringify(item, null, 2)} />,
},
]}
listItems={listItems}
/>
);
};

export const ExecutionEventsTableRowDetails = React.memo(ExecutionEventsTableRowDetailsComponent);
ExecutionEventsTableRowDetails.displayName = 'ExecutionEventsTableRowDetails';
export const ExecutionEventsTableDetailsCell = React.memo(ExecutionEventsTableDetailsCellComponent);
ExecutionEventsTableDetailsCell.displayName = 'ExecutionEventsTableDetailsCell';
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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 React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';

import type {
RuleExecutionEvent,
RuleExecutionMetrics,
RuleExecutionStatus,
} from '../../../../../common/api/detection_engine/rule_monitoring';
import { RuleExecutionEventTypeEnum } from '../../../../../common/api/detection_engine/rule_monitoring';

import { assertUnreachable } from '../../../../../common/utility_types';
import { TruncatedText } from '../basic/text/truncated_text';
import { RuleStatusBadge } from '../../../common/components/rule_execution_status/rule_status_badge';

import * as i18n from './translations';

interface ExecutionEventsTableSummaryCellProps {
event: RuleExecutionEvent;
}

export function ExecutionEventsTableSummaryCell({ event }: ExecutionEventsTableSummaryCellProps) {
switch (event.type) {
case RuleExecutionEventTypeEnum.message:
return <MessageSummary message={event.message} />;
case RuleExecutionEventTypeEnum['status-change']:
return <StatusChangeSummary details={event.details} />;
case RuleExecutionEventTypeEnum['execution-metrics']:
return <ExecutionMetricsSummary details={event.details} />;
default:
assertUnreachable(event.type, 'Unknown rule execution event type');
}
}

function MessageSummary({ message }: { message: string }) {
const firstLineOnly = message.split('\n')[0];
return <TruncatedText text={firstLineOnly} />;
}

interface StatusChangeSummaryProps {
details?: { status?: RuleExecutionStatus };
}

function StatusChangeSummary({ details }: StatusChangeSummaryProps) {
const status = details?.status;

if (!status) {
return null;
}

return (
<EuiFlexGroup alignItems="center" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem grow={false}>{i18n.STATUS_CHANGED_TO}</EuiFlexItem>
<EuiFlexItem grow={false}>
<RuleStatusBadge status={status} showTooltip={false} />
</EuiFlexItem>
</EuiFlexGroup>
);
}

interface ExecutionMetricsSummaryProps {
details?: { metrics?: RuleExecutionMetrics };
}

function ExecutionMetricsSummary({ details }: ExecutionMetricsSummaryProps) {
const metrics = details?.metrics;

const summaryMetrics: string[] = [];

if (metrics?.execution_gap_duration_s) {
summaryMetrics.push(i18n.GAP_DURATION(metrics.execution_gap_duration_s));
}

summaryMetrics.push(i18n.SEARCH_DURATION(metrics?.total_search_duration_ms ?? 0));

if (metrics?.total_indexing_duration_ms) {
summaryMetrics.push(i18n.INDEXING_DURATION(metrics.total_indexing_duration_ms));
}

return <TruncatedText text={summaryMetrics.join(', ')} />;
}
Loading
Loading