Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
efbcf07
extend event log mapping to accommodate security rule execution metrics
maximpn Mar 13, 2026
b1e2004
pass consumer execution metrics to the AF
maximpn Mar 13, 2026
232831d
get rid of unknowns: 'allow' in the metrics schema
maximpn Mar 13, 2026
b5da131
add errors and warnings to the execute event
maximpn Mar 15, 2026
3c6aebb
relocate alerts related counts to alert_counts object
maximpn Mar 17, 2026
78e3924
avoid writing errors and warnings to the execute event for now
maximpn Mar 17, 2026
be60fa9
refactor rule monitoring service
maximpn Mar 17, 2026
06e41fe
simplify event setter implementation
maximpn Mar 17, 2026
6480379
remove metrics we don't have clear explanation for
maximpn Mar 17, 2026
170a05c
revert RuleExecutionLogClientForExecutors message logging
maximpn Mar 17, 2026
d679082
add functionality to pass consumer context data to the event logger
maximpn Mar 17, 2026
27fd7fb
log zero rule revision
maximpn Mar 18, 2026
79e7063
set rule signature id at the framework level
maximpn Mar 18, 2026
2f1c575
add unit tests for the RuleMonitoringService
maximpn Mar 18, 2026
bd2ba31
postpone writing new fields until the next Serverless release
maximpn Mar 18, 2026
fb4c3eb
roll back interface changes
maximpn Mar 18, 2026
b23aedb
fix type checking issues
maximpn Mar 18, 2026
01bf0f5
use @kbn/safer-lodash-set
maximpn Mar 18, 2026
7915b3c
remove framework level metrics from consumer metrics
maximpn Mar 18, 2026
e744d00
extend execution status APM logging
maximpn Mar 18, 2026
46fcdf0
fix failed unit tests
maximpn Mar 18, 2026
42702e0
fix failed functional tests
maximpn Mar 19, 2026
377cdcb
return missing total_enrichment_duration_ms metric
maximpn Mar 19, 2026
2acba73
apply auto fixes
maximpn Mar 19, 2026
d2fe81d
move out alert metrics to the metrics object
maximpn Mar 19, 2026
f6884c7
improve alerts metrics naming
maximpn Mar 19, 2026
95495dd
check for the rule's solution before reading ruleId
maximpn Mar 19, 2026
e43dd73
fix failed functional tests
maximpn Mar 20, 2026
64f21fa
check for the rule's solution before capturing uuid
maximpn Mar 20, 2026
8d388f4
add tests
maximpn Mar 21, 2026
f67cb59
allow logging false evaluatable values
maximpn Mar 23, 2026
1ee0789
log wrapper errors and warnings
maximpn Mar 23, 2026
6047b96
prevent logging previously stored metrics
maximpn Mar 24, 2026
a4a69ab
remove a not required and added earlier test
maximpn Mar 24, 2026
afe0f75
move rule outcome status determination to rule execution logger
maximpn Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .buildkite/ftr_platform_stateful_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ enabled:
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/alerting/group3/config.ts
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/alerting/group4/config.ts
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/alerting/group4/config_with_schedule_circuit_breaker.ts
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/alerting/group5/config.ts
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/config.ts
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/action_task_params/config.ts
- x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/email_recipient_allowlist/config.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ describe('create()', () => {
"metrics": Object {
"duration": 0,
"gap_duration_s": null,
"gap_range": null,
"total_alerts_created": null,
"total_alerts_detected": null,
"total_indexing_duration_ms": null,
Expand Down Expand Up @@ -754,6 +755,7 @@ describe('create()', () => {
"metrics": Object {
"duration": 0,
"gap_duration_s": null,
"gap_range": null,
"total_alerts_created": null,
"total_alerts_detected": null,
"total_indexing_duration_ms": null,
Expand Down Expand Up @@ -1489,8 +1491,7 @@ describe('create()', () => {
metrics: {
duration: 0,
gap_duration_s: null,
// TODO: uncomment after intermidiate release
// gap_range: null,
gap_range: null,
total_alerts_created: null,
total_alerts_detected: null,
total_indexing_duration_ms: null,
Expand Down Expand Up @@ -2642,8 +2643,7 @@ describe('create()', () => {
metrics: {
duration: 0,
gap_duration_s: null,
// TODO: uncomment after intermidiate release
// gap_range: null,
gap_range: null,
total_alerts_created: null,
total_alerts_detected: null,
total_indexing_duration_ms: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@
*/

import * as uuid from 'uuid';
import { set } from '@kbn/safer-lodash-set';
import type { IEvent, IEventLogger, InternalFields } from '@kbn/event-log-plugin/server';
import { millisToNanos, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server';
import type { BulkResponse } from '@elastic/elasticsearch/lib/api/types';
import { EVENT_LOG_ACTIONS } from '../../plugin';
import type { UntypedNormalizedRuleType } from '../../rule_type_registry';
import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects';
import type { TaskRunnerTimings } from '../../task_runner/task_runner_timer';
import type { AlertInstanceState, RuleExecutionStatus } from '../../types';
import type {
AlertInstanceState,
ConsumerExecutionMetrics,
RuleExecutionStatus,
} from '../../types';
import { createAlertEventLogRecordObject } from '../create_alert_event_log_record_object';
import type { RuleRunMetrics } from '../rule_run_metrics_store';
import { Gap } from '../rule_gaps/gap';
Expand All @@ -24,6 +29,7 @@ const Millis2Nanos = 1000 * 1000;

export interface RuleContext {
id: string;
uuid?: string;
type: UntypedNormalizedRuleType;
consumer?: string;
name?: string;
Expand Down Expand Up @@ -58,6 +64,7 @@ interface DoneOpts {
timings?: TaskRunnerTimings;
status?: RuleExecutionStatus;
metrics?: RuleRunMetrics | null;
consumerMetrics?: Partial<ConsumerExecutionMetrics> | null;
backfill?: BackfillOpts;
}

Expand Down Expand Up @@ -214,12 +221,14 @@ export class AlertingEventLogger {
public addOrUpdateRuleData({
name,
id,
uuid: ruleUuid,
consumer,
type,
revision,
}: {
name?: string;
id?: string;
uuid?: string;
consumer?: string;
revision?: number;
type?: UntypedNormalizedRuleType;
Expand All @@ -236,6 +245,10 @@ export class AlertingEventLogger {
};
}

if (ruleUuid) {
this.ruleData.uuid = ruleUuid;
}

if (name) {
this.ruleData.name = name;
}
Expand Down Expand Up @@ -266,6 +279,7 @@ export class AlertingEventLogger {
updateEventWithRuleData(this.event, {
ruleName: name,
ruleId: id,
ruleUuid,
ruleType: type,
consumer,
revision,
Expand Down Expand Up @@ -345,7 +359,7 @@ export class AlertingEventLogger {
);
}

public done({ status, metrics, timings, backfill }: DoneOpts) {
public done({ status, metrics, consumerMetrics, timings, backfill }: DoneOpts) {
if (!this.isInitialized || !this.event || !this.context) {
throw new Error('AlertingEventLogger not initialized');
}
Expand Down Expand Up @@ -385,6 +399,10 @@ export class AlertingEventLogger {
updateEvent(this.event, { metrics });
}

if (consumerMetrics) {
updateEvent(this.event, { consumerMetrics });
}

if (timings) {
updateEvent(this.event, { timings });
}
Expand Down Expand Up @@ -607,6 +625,7 @@ interface UpdateEventOpts {
status?: string;
reason?: string;
metrics?: RuleRunMetrics;
consumerMetrics?: Partial<ConsumerExecutionMetrics>;
timings?: TaskRunnerTimings;
backfill?: BackfillOpts;
maintenanceWindowIds?: string[];
Expand All @@ -615,14 +634,15 @@ interface UpdateEventOpts {
interface UpdateRuleOpts {
ruleName?: string;
ruleId?: string;
ruleUuid?: string;
consumer?: string;
ruleType?: UntypedNormalizedRuleType;
revision?: number;
savedObjects?: SavedObjects[];
}

export function updateEventWithRuleData(event: IEvent, opts: UpdateRuleOpts) {
const { ruleName, ruleId, consumer, ruleType, revision, savedObjects } = opts;
const { ruleName, ruleId, ruleUuid, consumer, ruleType, revision, savedObjects } = opts;
if (!event) {
throw new Error('Cannot update event because it is not initialized.');
}
Expand All @@ -641,6 +661,13 @@ export function updateEventWithRuleData(event: IEvent, opts: UpdateRuleOpts) {
};
}

if (ruleUuid) {
event.rule = {
...event.rule,
uuid: ruleUuid,
};
}

if (consumer) {
event.kibana = event.kibana || {};
event.kibana.alert = event.kibana.alert || {};
Expand Down Expand Up @@ -673,7 +700,8 @@ export function updateEventWithRuleData(event: IEvent, opts: UpdateRuleOpts) {
}
}

if (revision) {
// revision is a non-negative integer. We'd like to capture 0 as well.
if (revision !== undefined) {
event.kibana = event.kibana || {};
event.kibana.alert = event.kibana.alert || {};
event.kibana.alert.rule = event.kibana.alert.rule || {};
Expand All @@ -700,6 +728,7 @@ export function updateEvent(event: IEvent, opts: UpdateEventOpts) {
status,
reason,
metrics,
consumerMetrics,
timings,
alertingOutcome,
backfill,
Expand Down Expand Up @@ -764,6 +793,19 @@ export function updateEvent(event: IEvent, opts: UpdateEventOpts) {
};
}

if (consumerMetrics) {
set(event, 'kibana.alert.rule.execution.metrics', {
...event.kibana?.alert?.rule?.execution?.metrics,
alerts_candidate_count: consumerMetrics.alerts_candidate_count,
alerts_suppressed_count: consumerMetrics.alerts_suppressed_count,
frozen_indices_queried_count: consumerMetrics.frozen_indices_queried_count,
total_indexing_duration_ms: consumerMetrics.total_indexing_duration_ms,
total_enrichment_duration_ms: consumerMetrics.total_enrichment_duration_ms,
execution_gap_duration_s: consumerMetrics.gap_duration_s,
gap_range: consumerMetrics.gap_range,
});
}

if (backfill) {
event.kibana = event.kibana || {};
event.kibana.alert = event.kibana.alert || {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ describe('resetMonitoringLastRun', () => {
total_alerts_detected: null,
total_alerts_created: null,
gap_duration_s: null,
// TODO: uncomment after intermidiate release
// gap_range: null,
gap_range: null,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ const INITIAL_LAST_RUN_METRICS: RuleMonitoringLastRunMetrics = {
total_alerts_detected: null,
total_alerts_created: null,
gap_duration_s: null,
// TODO: uncomment after intermidiate release
// gap_range: null,
gap_range: null,
};

export const getDefaultMonitoring = (timestamp: string): RawRuleMonitoring => {
Expand Down
8 changes: 3 additions & 5 deletions x-pack/platform/plugins/shared/alerting/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,9 @@ const createAbortableSearchServiceMock = () => {

const createRuleMonitoringServiceMock = () => {
const mock = lazyObject({
setLastRunMetricsTotalSearchDurationMs: jest.fn(),
setLastRunMetricsTotalIndexingDurationMs: jest.fn(),
setLastRunMetricsTotalAlertsDetected: jest.fn(),
setLastRunMetricsTotalAlertsCreated: jest.fn(),
setLastRunMetricsGapDurationS: jest.fn(),
setMetric: jest.fn(),
setMetrics: jest.fn(),
clearGapRange: jest.fn(),
}) as unknown as jest.Mocked<PublicRuleMonitoringService>;

return mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
function createRuleMonitoringServiceMock() {
return jest.fn().mockImplementation(() => {
return {
addFrameworkMetrics: jest.fn(),
addHistory: jest.fn(),
getLastRunMetricsSetters: jest.fn(),
getSetters: jest.fn(),
getMonitoring: jest.fn(),
setLastRunMetricsDuration: jest.fn(),
setMonitoring: jest.fn(),
Expand All @@ -20,12 +21,9 @@ function createRuleMonitoringServiceMock() {
function createPublicRuleMonitoringServiceMock() {
return jest.fn().mockImplementation(() => {
return {
setLastRunMetricsGapDurationS: jest.fn(),
setLastRunMetricsTotalAlertsCreated: jest.fn(),
setLastRunMetricsTotalAlertsDetected: jest.fn(),
setLastRunMetricsTotalIndexingDurationMs: jest.fn(),
setLastRunMetricsTotalSearchDurationMs: jest.fn(),
setLastRunMetricsGapRange: jest.fn(),
setMetric: jest.fn(),
setMetrics: jest.fn(),
clearGapRange: jest.fn(),
};
});
}
Expand Down
Loading
Loading