diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts index f622ea9248b24..d88f4119b691f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts @@ -79,7 +79,32 @@ export class EventLogAdapter implements IRuleExecutionLogClient { // EventLog execution events are immutable, nothing to do here } - private async logExecutionMetrics(args: LogExecutionMetricsArgs) { + public async logStatusChange(args: LogStatusChangeArgs): Promise { + await Promise.all([ + this.logStatusChangeToSavedObjects(args), + this.logStatusChangeToEventLog(args), + ]); + } + + private async logStatusChangeToSavedObjects(args: LogStatusChangeArgs): Promise { + await this.savedObjectsAdapter.logStatusChange(args); + } + + private async logStatusChangeToEventLog(args: LogStatusChangeArgs): Promise { + if (args.metrics) { + this.logExecutionMetrics({ + ruleId: args.ruleId, + ruleName: args.ruleName, + ruleType: args.ruleType, + spaceId: args.spaceId, + metrics: args.metrics, + }); + } + + this.eventLogClient.logStatusChange(args); + } + + private logExecutionMetrics(args: LogExecutionMetricsArgs): void { const { ruleId, spaceId, ruleType, ruleName, metrics } = args; this.eventLogClient.logExecutionMetrics({ @@ -98,20 +123,4 @@ export class EventLogAdapter implements IRuleExecutionLogClient { }, }); } - - public async logStatusChange(args: LogStatusChangeArgs) { - await this.savedObjectsAdapter.logStatusChange(args); - - if (args.metrics) { - await this.logExecutionMetrics({ - ruleId: args.ruleId, - ruleName: args.ruleName, - ruleType: args.ruleType, - spaceId: args.spaceId, - metrics: args.metrics, - }); - } - - this.eventLogClient.logStatusChange(args); - } } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts index bc77c6e44fa56..f3321580aa052 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectsClientContract } from '../../../../../../../src/core/server'; +import { Logger, SavedObjectsClientContract } from 'src/core/server'; import { IEventLogClient, IEventLogService } from '../../../../../event_log/server'; import { IRuleStatusSOAttributes } from '../rules/types'; import { EventLogAdapter } from './event_log_adapter/event_log_adapter'; @@ -20,6 +20,7 @@ import { GetCurrentStatusArgs, GetCurrentStatusBulkArgs, GetCurrentStatusBulkResult, + ExtMeta, } from './types'; import { truncateMessage } from './utils/normalization'; @@ -28,13 +29,16 @@ interface ConstructorParams { savedObjectsClient: SavedObjectsClientContract; eventLogService: IEventLogService; eventLogClient?: IEventLogClient; + logger: Logger; } export class RuleExecutionLogClient implements IRuleExecutionLogClient { - private client: IRuleExecutionLogClient; + private readonly client: IRuleExecutionLogClient; + private readonly logger: Logger; constructor(params: ConstructorParams) { - const { underlyingClient, eventLogService, eventLogClient, savedObjectsClient } = params; + const { underlyingClient, eventLogService, eventLogClient, savedObjectsClient, logger } = + params; switch (underlyingClient) { case UnderlyingLogClient.savedObjects: @@ -44,6 +48,10 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient { this.client = new EventLogAdapter(eventLogService, eventLogClient, savedObjectsClient); break; } + + // We write rule execution logs via a child console logger with the context + // "plugins.securitySolution.ruleExecution" + this.logger = logger.get('ruleExecution'); } /** @deprecated */ @@ -74,11 +82,34 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient { return this.client.deleteCurrentStatus(ruleId); } - public async logStatusChange(args: LogStatusChangeArgs) { - const message = args.message ? truncateMessage(args.message) : args.message; - return this.client.logStatusChange({ - ...args, - message, - }); + public async logStatusChange(args: LogStatusChangeArgs): Promise { + const { newStatus, message, ruleId, ruleName, ruleType, spaceId } = args; + + try { + const truncatedMessage = message ? truncateMessage(message) : message; + await this.client.logStatusChange({ + ...args, + message: truncatedMessage, + }); + } catch (e) { + const logMessage = 'Error logging rule execution status change'; + const logAttributes = `status: "${newStatus}", rule id: "${ruleId}", rule name: "${ruleName}"`; + const logReason = e instanceof Error ? `${e.stack}` : `${e}`; + const logMeta: ExtMeta = { + rule: { + id: ruleId, + name: ruleName, + type: ruleType, + execution: { + status: newStatus, + }, + }, + kibana: { + spaceId, + }, + }; + + this.logger.error(`${logMessage}; ${logAttributes}; ${logReason}`, logMeta); + } } } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts index 06a0b90985af7..1fa256b0f088c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts @@ -6,7 +6,7 @@ */ import { Duration } from 'moment'; -import { SavedObjectsFindResult } from '../../../../../../../src/core/server'; +import { LogMeta, SavedObjectsFindResult } from 'src/core/server'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { IRuleStatusSOAttributes } from '../rules/types'; @@ -103,3 +103,18 @@ export interface ExecutionMetrics { lastLookBackDate?: string; executionGap?: Duration; } + +/** + * Custom extended log metadata that rule execution logger can attach to every log record. + */ +export type ExtMeta = LogMeta & { + rule?: LogMeta['rule'] & { + type?: string; + execution?: { + status?: RuleExecutionStatus; + }; + }; + kibana?: { + spaceId?: string; + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index bc13a12e01ca4..daab0540c23af 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -70,6 +70,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = underlyingClient: config.ruleExecutionLog.underlyingClient, savedObjectsClient, eventLogService, + logger, }); const completeRule = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 85285eed2817a..f49c6327483e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -149,6 +149,7 @@ export const signalRulesAlertType = ({ underlyingClient: config.ruleExecutionLog.underlyingClient, savedObjectsClient: services.savedObjectsClient, eventLogService, + logger, }); const completeRule: CompleteRule = { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 5e7bf0659947c..dfff89a90c732 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -140,7 +140,7 @@ export class Plugin implements ISecuritySolutionPlugin { const eventLogService = plugins.eventLog; registerEventLogProvider(eventLogService); - const requestContextFactory = new RequestContextFactory({ config, core, plugins }); + const requestContextFactory = new RequestContextFactory({ config, logger, core, plugins }); const router = core.http.createRouter(); core.http.registerRouteHandlerContext( APP_ID, diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index b7586ee959652..f6c1d6b44eca6 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { Logger, KibanaRequest, RequestHandlerContext } from 'kibana/server'; import { ExceptionListClient } from '../../lists/server'; import { DEFAULT_SPACE_ID } from '../common/constants'; @@ -28,6 +28,7 @@ export interface IRequestContextFactory { interface ConstructorOptions { config: ConfigType; + logger: Logger; core: SecuritySolutionPluginCoreSetupDependencies; plugins: SecuritySolutionPluginSetupDependencies; } @@ -44,7 +45,7 @@ export class RequestContextFactory implements IRequestContextFactory { request: KibanaRequest ): Promise { const { options, appClientFactory } = this; - const { config, core, plugins } = options; + const { config, logger, core, plugins } = options; const { lists, ruleRegistry, security } = plugins; const [, startPlugins] = await core.getStartServices(); @@ -73,6 +74,7 @@ export class RequestContextFactory implements IRequestContextFactory { savedObjectsClient: context.core.savedObjects.client, eventLogService: plugins.eventLog, eventLogClient: startPlugins.eventLog.getClient(request), + logger, }), getExceptionListClient: () => {