Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metering connector actions request body bytes #186804

Merged
merged 41 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2234e3a
Metering connector actions request body bytes
ersin-erdal Jun 24, 2024
1981342
WIP
ersin-erdal Jul 5, 2024
da28c23
merge main
ersin-erdal Jul 7, 2024
4d180d6
Collect request body bytes of the outgoing requests from the connectors
ersin-erdal Jul 7, 2024
ffcda46
merge main
ersin-erdal Jul 9, 2024
c6dff6c
fix unit test
ersin-erdal Jul 9, 2024
8fdd8fc
fix bedrock functional test
ersin-erdal Jul 9, 2024
ce5158c
Add comments to trigger security solutions unit tests
ersin-erdal Jul 11, 2024
f0c8def
Merge branch 'main' into 209-meter-request-bytes
ersin-erdal Jul 11, 2024
7bd6af0
try-catch stringify in collector
ersin-erdal Jul 13, 2024
6c4e455
Merge remote-tracking branch 'origin/209-meter-request-bytes' into 20…
ersin-erdal Jul 13, 2024
a50e49e
Add logger to tests
ersin-erdal Jul 13, 2024
f519b99
Merge branch 'main' into 209-meter-request-bytes
ersin-erdal Jul 13, 2024
7f12521
Merge branch 'main' into 209-meter-request-bytes
ersin-erdal Jul 14, 2024
38d3d54
merge main
ersin-erdal Aug 6, 2024
a4cb0f4
Merge remote-tracking branch 'origin/209-meter-request-bytes' into 20…
ersin-erdal Aug 6, 2024
159143d
add connector id to log
ersin-erdal Aug 7, 2024
229de0c
add connector id to log
ersin-erdal Aug 7, 2024
6a09ee2
parse header
ersin-erdal Aug 7, 2024
0b18887
parse header
ersin-erdal Aug 7, 2024
83bf59a
fix undefined getHeader
ersin-erdal Aug 7, 2024
f9ef8e1
test action executor with a non-zero value
ersin-erdal Aug 7, 2024
6ba986a
fix unit tests
ersin-erdal Aug 7, 2024
1d580c1
fix unit tests
ersin-erdal Aug 7, 2024
a9f69bb
fix unit tests
ersin-erdal Aug 8, 2024
2d9317e
Merge branch 'refs/heads/main' into 209-meter-request-bytes
ersin-erdal Aug 8, 2024
bc45b7c
merge main
ersin-erdal Aug 12, 2024
06abcd4
Rename ConnectorMetricCollector to ConnectorUsageCollector
ersin-erdal Aug 13, 2024
e77bb10
revert eslint changes
ersin-erdal Aug 13, 2024
9af645e
fix snapshot
ersin-erdal Aug 13, 2024
5a4a1e5
fix snapshot
ersin-erdal Aug 14, 2024
7023229
remove snapshot
ersin-erdal Aug 14, 2024
c3e053c
Merge branch 'main' into 209-meter-request-bytes
ersin-erdal Aug 19, 2024
8ab0f53
merge main
ersin-erdal Aug 21, 2024
050e9a9
Merge remote-tracking branch 'origin/209-meter-request-bytes' into 20…
ersin-erdal Aug 21, 2024
f1ab8c8
revert change on security solutions
ersin-erdal Aug 21, 2024
56c8e95
Merge branch 'main' into 209-meter-request-bytes
ersin-erdal Aug 26, 2024
c6fe41a
Merge branch 'main' into 209-meter-request-bytes
ersin-erdal Aug 26, 2024
a1152cf
remove temporary comments
ersin-erdal Aug 26, 2024
5cc8053
Merge remote-tracking branch 'origin/209-meter-request-bytes' into 20…
ersin-erdal Aug 26, 2024
23b4921
fix executor test
ersin-erdal Aug 26, 2024
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
65 changes: 61 additions & 4 deletions x-pack/plugins/actions/server/lib/action_executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { KibanaRequest } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { ActionExecutor } from './action_executor';
Expand All @@ -18,7 +17,7 @@ import {
} from '@kbn/core/server/mocks';
import { eventLoggerMock } from '@kbn/event-log-plugin/server/mocks';
import { spacesServiceMock } from '@kbn/spaces-plugin/server/spaces_service/spaces_service.mock';
import { ActionType as ConnectorType } from '../types';
import { ActionType as ConnectorType, ConnectorUsageCollector } from '../types';
import { actionsAuthorizationMock, actionsMock } from '../mocks';
import {
asBackgroundTaskExecutionSource,
Expand Down Expand Up @@ -150,6 +149,10 @@ const connectorSavedObject = {
references: [],
};

interface ActionUsage {
request_body_bytes: number;
}

const getBaseExecuteStartEventLogDoc = (unsecured: boolean) => {
return {
event: {
Expand All @@ -163,6 +166,7 @@ const getBaseExecuteStartEventLogDoc = (unsecured: boolean) => {
},
id: CONNECTOR_ID,
name: '1',
type_id: 'test',
},
...(unsecured
? {}
Expand Down Expand Up @@ -190,10 +194,23 @@ const getBaseExecuteStartEventLogDoc = (unsecured: boolean) => {
};
};

const getBaseExecuteEventLogDoc = (unsecured: boolean) => {
const getBaseExecuteEventLogDoc = (
unsecured: boolean,
actionUsage: ActionUsage = { request_body_bytes: 0 }
) => {
const base = getBaseExecuteStartEventLogDoc(unsecured);
return {
...base,
kibana: {
...base.kibana,
action: {
...base.kibana.action,
execution: {
...base.kibana.action.execution,
usage: actionUsage,
},
},
},
event: {
...base.event,
action: 'execute',
Expand All @@ -211,9 +228,12 @@ const getBaseExecuteEventLogDoc = (unsecured: boolean) => {
};
};

const mockGetRequestBodyByte = jest.spyOn(ConnectorUsageCollector.prototype, 'getRequestBodyByte');

beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
mockGetRequestBodyByte.mockReturnValue(0);
spacesMock.getSpaceId.mockReturnValue('some-namespace');
loggerMock.get.mockImplementation(() => loggerMock);
const mockRealm = { name: 'default_native', type: 'native' };
Expand All @@ -237,6 +257,7 @@ describe('Action Executor', () => {
const label = executeUnsecure ? 'executes unsecured' : 'executes';

test(`successfully ${label}`, async () => {
mockGetRequestBodyByte.mockReturnValue(300);
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(
connectorSavedObject
);
Expand Down Expand Up @@ -280,13 +301,15 @@ describe('Action Executor', () => {
},
params: { foo: true },
logger: loggerMock,
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});

expect(loggerMock.debug).toBeCalledWith('executing action test:1: 1');
expect(eventLogger.logEvent).toHaveBeenCalledTimes(2);

const execStartDoc = getBaseExecuteStartEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure, { request_body_bytes: 300 });

expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, execStartDoc);
expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, execDoc);
});
Expand Down Expand Up @@ -353,13 +376,15 @@ describe('Action Executor', () => {
params: { foo: true },
logger: loggerMock,
source: executionSource.source,
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});

expect(loggerMock.debug).toBeCalledWith('executing action test:1: 1');
expect(eventLogger.logEvent).toHaveBeenCalledTimes(2);

const execStartDoc = getBaseExecuteStartEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure);

expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, {
...execStartDoc,
kibana: {
Expand Down Expand Up @@ -431,13 +456,15 @@ describe('Action Executor', () => {
},
params: { foo: true },
logger: loggerMock,
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});

expect(loggerMock.debug).toBeCalledWith('executing action test:preconfigured: Preconfigured');
expect(eventLogger.logEvent).toHaveBeenCalledTimes(2);

const execStartDoc = getBaseExecuteStartEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure);

expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, {
...execStartDoc,
kibana: {
Expand Down Expand Up @@ -513,6 +540,7 @@ describe('Action Executor', () => {
params: { foo: true },
logger: loggerMock,
request: {},
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});
}

Expand All @@ -532,6 +560,7 @@ describe('Action Executor', () => {

const execStartDoc = getBaseExecuteStartEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure);

expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, {
...execStartDoc,
kibana: {
Expand All @@ -540,6 +569,7 @@ describe('Action Executor', () => {
...execStartDoc.kibana.action,
id: 'system-connector-.cases',
name: 'System action: .cases',
type_id: '.cases',
},
saved_objects: [
{
Expand Down Expand Up @@ -569,6 +599,7 @@ describe('Action Executor', () => {
...execDoc.kibana.action,
id: 'system-connector-.cases',
name: 'System action: .cases',
type_id: '.cases',
},
saved_objects: [
{
Expand Down Expand Up @@ -890,6 +921,7 @@ describe('Action Executor', () => {
},
params: { foo: true },
logger: loggerMock,
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});
});

Expand Down Expand Up @@ -921,6 +953,7 @@ describe('Action Executor', () => {
params: { foo: true },
logger: loggerMock,
request: {},
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});
});

Expand Down Expand Up @@ -989,13 +1022,15 @@ describe('Action Executor', () => {
},
params: { foo: true },
logger: loggerMock,
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});

expect(loggerMock.debug).toBeCalledWith('executing action test:preconfigured: Preconfigured');
expect(eventLogger.logEvent).toHaveBeenCalledTimes(2);

const execStartDoc = getBaseExecuteStartEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure);

expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, {
...execStartDoc,
kibana: {
Expand Down Expand Up @@ -1026,6 +1061,12 @@ describe('Action Executor', () => {
...execDoc.kibana.action,
id: 'preconfigured',
name: 'Preconfigured',
execution: {
...execStartDoc.kibana.action.execution,
usage: {
request_body_bytes: 0,
},
},
},
saved_objects: [
{
Expand Down Expand Up @@ -1074,6 +1115,7 @@ describe('Action Executor', () => {
params: { foo: true },
logger: loggerMock,
request: {},
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});

expect(loggerMock.debug).toBeCalledWith(
Expand All @@ -1083,6 +1125,7 @@ describe('Action Executor', () => {

const execStartDoc = getBaseExecuteStartEventLogDoc(executeUnsecure);
const execDoc = getBaseExecuteEventLogDoc(executeUnsecure);

expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, {
...execStartDoc,
kibana: {
Expand All @@ -1091,6 +1134,7 @@ describe('Action Executor', () => {
...execStartDoc.kibana.action,
id: 'system-connector-.cases',
name: 'System action: .cases',
type_id: '.cases',
},
saved_objects: [
{
Expand Down Expand Up @@ -1120,6 +1164,7 @@ describe('Action Executor', () => {
...execDoc.kibana.action,
id: 'system-connector-.cases',
name: 'System action: .cases',
type_id: '.cases',
},
saved_objects: [
{
Expand Down Expand Up @@ -1290,6 +1335,7 @@ describe('Action Executor', () => {
},
params: { foo: true },
logger: loggerMock,
connectorUsageCollector: expect.any(ConnectorUsageCollector),
});
}
});
Expand Down Expand Up @@ -1385,6 +1431,7 @@ describe('Event log', () => {
},
name: undefined,
id: 'action1',
type_id: 'test',
},
alert: {
rule: {
Expand Down Expand Up @@ -1430,6 +1477,7 @@ describe('Event log', () => {
},
name: 'action-1',
id: '1',
type_id: 'test',
},
alert: {
rule: {
Expand Down Expand Up @@ -1483,6 +1531,7 @@ describe('Event log', () => {
},
name: 'action-1',
id: '1',
type_id: 'test',
},
alert: {
rule: {
Expand Down Expand Up @@ -1559,9 +1608,13 @@ describe('Event log', () => {
gen_ai: {
usage: mockGenAi.usage,
},
usage: {
request_body_bytes: 0,
},
},
name: 'action-1',
id: '1',
type_id: '.gen-ai',
},
alert: {
rule: {
Expand Down Expand Up @@ -1655,9 +1708,13 @@ describe('Event log', () => {
total_tokens: 35,
},
},
usage: {
request_body_bytes: 0,
},
},
name: 'action-1',
id: '1',
type_id: '.gen-ai',
},
alert: {
rule: {
Expand Down
20 changes: 17 additions & 3 deletions x-pack/plugins/actions/server/lib/action_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/se
import { createTaskRunError, TaskErrorSource } from '@kbn/task-manager-plugin/server';
import { getErrorSource } from '@kbn/task-manager-plugin/server/task_running';
import { GEN_AI_TOKEN_COUNT_EVENT } from './event_based_telemetry';
import { ConnectorUsageCollector } from '../usage/connector_usage_collector';
import { getGenAiTokenTracking, shouldTrackGenAiToken } from './gen_ai_token_tracking';
import {
validateConfig,
Expand Down Expand Up @@ -293,6 +294,7 @@ export class ActionExecutor {
actionExecutionId,
isInMemory: this.actionInfo.isInMemory,
...(source ? { source } : {}),
actionTypeId: this.actionInfo.actionTypeId,
});

eventLogger.logEvent(event);
Expand Down Expand Up @@ -394,6 +396,14 @@ export class ActionExecutor {

const { actionTypeId, name, config, secrets } = actionInfo;

const loggerId = actionTypeId.startsWith('.') ? actionTypeId.substring(1) : actionTypeId;
const logger = this.actionExecutorContext!.logger.get(loggerId);

const connectorUsageCollector = new ConnectorUsageCollector({
logger,
connectorId: actionId,
});

if (!this.actionInfo || this.actionInfo.actionId !== actionId) {
this.actionInfo = actionInfo;
}
Expand Down Expand Up @@ -434,9 +444,6 @@ export class ActionExecutor {
return err.result;
}

const loggerId = actionTypeId.startsWith('.') ? actionTypeId.substring(1) : actionTypeId;
const logger = this.actionExecutorContext!.logger.get(loggerId);

if (span) {
span.name = `${executeLabel} ${actionTypeId}`;
span.addLabels({
Expand Down Expand Up @@ -477,6 +484,7 @@ export class ActionExecutor {
actionExecutionId,
isInMemory: this.actionInfo.isInMemory,
...(source ? { source } : {}),
actionTypeId,
});

eventLogger.startTiming(event);
Expand Down Expand Up @@ -510,6 +518,7 @@ export class ActionExecutor {
logger,
source,
...(actionType.isSystemActionType ? { request } : {}),
connectorUsageCollector,
});

if (rawResult && rawResult.status === 'error') {
Expand Down Expand Up @@ -548,6 +557,11 @@ export class ActionExecutor {
event.user = event.user || {};
event.user.name = currentUser?.username;
event.user.id = currentUser?.profile_uid;
set(
event,
'kibana.action.execution.usage.request_body_bytes',
connectorUsageCollector.getRequestBodyByte()
);

if (result.status === 'ok') {
span?.setOutcome('success');
Expand Down
Loading
Loading