diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md
index 7536cd2b07ae6..16fbc8f4eaea3 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-error: (msg: string, meta: LogMeta) => void;
+error: (msg: string, meta: Meta) => void;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md
index 1b691ee8cb16d..697f8823c4966 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md
@@ -16,7 +16,7 @@ export interface SavedObjectsMigrationLogger
| Property | Type | Description |
| --- | --- | --- |
| [debug](./kibana-plugin-core-server.savedobjectsmigrationlogger.debug.md) | (msg: string) => void
| |
-| [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) | (msg: string, meta: LogMeta) => void
| |
+| [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) | <Meta extends LogMeta = LogMeta>(msg: string, meta: Meta) => void
| |
| [info](./kibana-plugin-core-server.savedobjectsmigrationlogger.info.md) | (msg: string) => void
| |
| [warn](./kibana-plugin-core-server.savedobjectsmigrationlogger.warn.md) | (msg: string) => void
| |
| [warning](./kibana-plugin-core-server.savedobjectsmigrationlogger.warning.md) | (msg: string) => void
| |
diff --git a/packages/kbn-logging/src/ecs/agent.ts b/packages/kbn-logging/src/ecs/agent.ts
new file mode 100644
index 0000000000000..0c2e7f7bbe44f
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/agent.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-agent.html
+ *
+ * @internal
+ */
+export interface EcsAgent {
+ build?: { original: string };
+ ephemeral_id?: string;
+ id?: string;
+ name?: string;
+ type?: string;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/autonomous_system.ts b/packages/kbn-logging/src/ecs/autonomous_system.ts
new file mode 100644
index 0000000000000..85569b7dbabe1
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/autonomous_system.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-as.html
+ *
+ * @internal
+ */
+export interface EcsAutonomousSystem {
+ number?: number;
+ organization?: { name: string };
+}
diff --git a/packages/kbn-logging/src/ecs/base.ts b/packages/kbn-logging/src/ecs/base.ts
new file mode 100644
index 0000000000000..cf12cf0ea6e53
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/base.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-base.html
+ *
+ * @internal
+ */
+export interface EcsBase {
+ ['@timestamp']: string;
+ labels?: Record;
+ message?: string;
+ tags?: string[];
+}
diff --git a/packages/kbn-logging/src/ecs/client.ts b/packages/kbn-logging/src/ecs/client.ts
new file mode 100644
index 0000000000000..ebee7826104a5
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/client.ts
@@ -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
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsAutonomousSystem } from './autonomous_system';
+import { EcsGeo } from './geo';
+import { EcsNestedUser } from './user';
+
+interface NestedFields {
+ as?: EcsAutonomousSystem;
+ geo?: EcsGeo;
+ user?: EcsNestedUser;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-client.html
+ *
+ * @internal
+ */
+export interface EcsClient extends NestedFields {
+ address?: string;
+ bytes?: number;
+ domain?: string;
+ ip?: string;
+ mac?: string;
+ nat?: { ip?: string; port?: number };
+ packets?: number;
+ port?: number;
+ registered_domain?: string;
+ subdomain?: string;
+ top_level_domain?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/cloud.ts b/packages/kbn-logging/src/ecs/cloud.ts
new file mode 100644
index 0000000000000..8ef15d40f5529
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/cloud.ts
@@ -0,0 +1,23 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-cloud.html
+ *
+ * @internal
+ */
+export interface EcsCloud {
+ account?: { id?: string; name?: string };
+ availability_zone?: string;
+ instance?: { id?: string; name?: string };
+ machine?: { type: string };
+ project?: { id?: string; name?: string };
+ provider?: string;
+ region?: string;
+ service?: { name: string };
+}
diff --git a/packages/kbn-logging/src/ecs/code_signature.ts b/packages/kbn-logging/src/ecs/code_signature.ts
new file mode 100644
index 0000000000000..277c3901a4f8b
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/code_signature.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-code_signature.html
+ *
+ * @internal
+ */
+export interface EcsCodeSignature {
+ exists?: boolean;
+ signing_id?: string;
+ status?: string;
+ subject_name?: string;
+ team_id?: string;
+ trusted?: boolean;
+ valid?: boolean;
+}
diff --git a/packages/kbn-logging/src/ecs/container.ts b/packages/kbn-logging/src/ecs/container.ts
new file mode 100644
index 0000000000000..6c5c85e7107e3
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/container.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-container.html
+ *
+ * @internal
+ */
+export interface EcsContainer {
+ id?: string;
+ image?: { name?: string; tag?: string[] };
+ labels?: Record;
+ name?: string;
+ runtime?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/destination.ts b/packages/kbn-logging/src/ecs/destination.ts
new file mode 100644
index 0000000000000..6d2dbc8f431c9
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/destination.ts
@@ -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
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsAutonomousSystem } from './autonomous_system';
+import { EcsGeo } from './geo';
+import { EcsNestedUser } from './user';
+
+interface NestedFields {
+ as?: EcsAutonomousSystem;
+ geo?: EcsGeo;
+ user?: EcsNestedUser;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-destination.html
+ *
+ * @internal
+ */
+export interface EcsDestination extends NestedFields {
+ address?: string;
+ bytes?: number;
+ domain?: string;
+ ip?: string;
+ mac?: string;
+ nat?: { ip?: string; port?: number };
+ packets?: number;
+ port?: number;
+ registered_domain?: string;
+ subdomain?: string;
+ top_level_domain?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/dll.ts b/packages/kbn-logging/src/ecs/dll.ts
new file mode 100644
index 0000000000000..d9ffa68b3f1a5
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/dll.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsCodeSignature } from './code_signature';
+import { EcsHash } from './hash';
+import { EcsPe } from './pe';
+
+interface NestedFields {
+ code_signature?: EcsCodeSignature;
+ hash?: EcsHash;
+ pe?: EcsPe;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-dll.html
+ *
+ * @internal
+ */
+export interface EcsDll extends NestedFields {
+ name?: string;
+ path?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/dns.ts b/packages/kbn-logging/src/ecs/dns.ts
new file mode 100644
index 0000000000000..c7a0e7983376c
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/dns.ts
@@ -0,0 +1,40 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-dns.html
+ *
+ * @internal
+ */
+export interface EcsDns {
+ answers?: Answer[];
+ header_flags?: string[];
+ id?: number;
+ op_code?: string;
+ question?: Question;
+ resolved_ip?: string[];
+ response_code?: string;
+ type?: string;
+}
+
+interface Answer {
+ data: string;
+ class?: string;
+ name?: string;
+ ttl?: number;
+ type?: string;
+}
+
+interface Question {
+ class?: string;
+ name?: string;
+ registered_domain?: string;
+ subdomain?: string;
+ top_level_domain?: string;
+ type?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/error.ts b/packages/kbn-logging/src/ecs/error.ts
new file mode 100644
index 0000000000000..aee010748ddf2
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/error.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-error.html
+ *
+ * @internal
+ */
+export interface EcsError {
+ code?: string;
+ id?: string;
+ message?: string;
+ stack_trace?: string;
+ type?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/event.ts b/packages/kbn-logging/src/ecs/event.ts
new file mode 100644
index 0000000000000..bf711410a9dd7
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/event.ts
@@ -0,0 +1,91 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-event.html
+ *
+ * @internal
+ */
+export interface EcsEvent {
+ action?: string;
+ category?: EcsEventCategory[];
+ code?: string;
+ created?: string;
+ dataset?: string;
+ duration?: number;
+ end?: string;
+ hash?: string;
+ id?: string;
+ ingested?: string;
+ kind?: EcsEventKind;
+ module?: string;
+ original?: string;
+ outcome?: EcsEventOutcome;
+ provider?: string;
+ reason?: string;
+ reference?: string;
+ risk_score?: number;
+ risk_score_norm?: number;
+ sequence?: number;
+ severity?: number;
+ start?: string;
+ timezone?: string;
+ type?: EcsEventType[];
+ url?: string;
+}
+
+/**
+ * @public
+ */
+export type EcsEventCategory =
+ | 'authentication'
+ | 'configuration'
+ | 'database'
+ | 'driver'
+ | 'file'
+ | 'host'
+ | 'iam'
+ | 'intrusion_detection'
+ | 'malware'
+ | 'network'
+ | 'package'
+ | 'process'
+ | 'registry'
+ | 'session'
+ | 'web';
+
+/**
+ * @public
+ */
+export type EcsEventKind = 'alert' | 'event' | 'metric' | 'state' | 'pipeline_error' | 'signal';
+
+/**
+ * @public
+ */
+export type EcsEventOutcome = 'failure' | 'success' | 'unknown';
+
+/**
+ * @public
+ */
+export type EcsEventType =
+ | 'access'
+ | 'admin'
+ | 'allowed'
+ | 'change'
+ | 'connection'
+ | 'creation'
+ | 'deletion'
+ | 'denied'
+ | 'end'
+ | 'error'
+ | 'group'
+ | 'info'
+ | 'installation'
+ | 'protocol'
+ | 'start'
+ | 'user';
diff --git a/packages/kbn-logging/src/ecs/file.ts b/packages/kbn-logging/src/ecs/file.ts
new file mode 100644
index 0000000000000..c09121607e0a4
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/file.ts
@@ -0,0 +1,52 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsCodeSignature } from './code_signature';
+import { EcsHash } from './hash';
+import { EcsPe } from './pe';
+import { EcsX509 } from './x509';
+
+interface NestedFields {
+ code_signature?: EcsCodeSignature;
+ hash?: EcsHash;
+ pe?: EcsPe;
+ x509?: EcsX509;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-file.html
+ *
+ * @internal
+ */
+export interface EcsFile extends NestedFields {
+ accessed?: string;
+ attributes?: string[];
+ created?: string;
+ ctime?: string;
+ device?: string;
+ directory?: string;
+ drive_letter?: string;
+ extension?: string;
+ gid?: string;
+ group?: string;
+ inode?: string;
+ // Technically this is a known list, but it's massive, so we'll just accept a string for now :)
+ // https://www.iana.org/assignments/media-types/media-types.xhtml
+ mime_type?: string;
+ mode?: string;
+ mtime?: string;
+ name?: string;
+ owner?: string;
+ path?: string;
+ 'path.text'?: string;
+ size?: number;
+ target_path?: string;
+ 'target_path.text'?: string;
+ type?: string;
+ uid?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/geo.ts b/packages/kbn-logging/src/ecs/geo.ts
new file mode 100644
index 0000000000000..85d45ca803aee
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/geo.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-geo.html
+ *
+ * @internal
+ */
+export interface EcsGeo {
+ city_name?: string;
+ continent_code?: string;
+ continent_name?: string;
+ country_iso_code?: string;
+ country_name?: string;
+ location?: GeoPoint;
+ name?: string;
+ postal_code?: string;
+ region_iso_code?: string;
+ region_name?: string;
+ timezone?: string;
+}
+
+interface GeoPoint {
+ lat: number;
+ lon: number;
+}
diff --git a/packages/kbn-logging/src/ecs/group.ts b/packages/kbn-logging/src/ecs/group.ts
new file mode 100644
index 0000000000000..e1bc339964fc0
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/group.ts
@@ -0,0 +1,18 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-group.html
+ *
+ * @internal
+ */
+export interface EcsGroup {
+ domain?: string;
+ id?: string;
+ name?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/hash.ts b/packages/kbn-logging/src/ecs/hash.ts
new file mode 100644
index 0000000000000..2ecd49f1ca092
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/hash.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-hash.html
+ *
+ * @internal
+ */
+export interface EcsHash {
+ md5?: string;
+ sha1?: string;
+ sha256?: string;
+ sha512?: string;
+ ssdeep?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/host.ts b/packages/kbn-logging/src/ecs/host.ts
new file mode 100644
index 0000000000000..085db30e13e7e
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/host.ts
@@ -0,0 +1,48 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsGeo } from './geo';
+import { EcsOs } from './os';
+import { EcsNestedUser } from './user';
+
+interface NestedFields {
+ geo?: EcsGeo;
+ os?: EcsOs;
+ /** @deprecated */
+ user?: EcsNestedUser;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-host.html
+ *
+ * @internal
+ */
+export interface EcsHost extends NestedFields {
+ architecture?: string;
+ cpu?: { usage: number };
+ disk?: Disk;
+ domain?: string;
+ hostname?: string;
+ id?: string;
+ ip?: string[];
+ mac?: string[];
+ name?: string;
+ network?: Network;
+ type?: string;
+ uptime?: number;
+}
+
+interface Disk {
+ read?: { bytes: number };
+ write?: { bytes: number };
+}
+
+interface Network {
+ egress?: { bytes?: number; packets?: number };
+ ingress?: { bytes?: number; packets?: number };
+}
diff --git a/packages/kbn-logging/src/ecs/http.ts b/packages/kbn-logging/src/ecs/http.ts
new file mode 100644
index 0000000000000..c734c93318f5c
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/http.ts
@@ -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
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-http.html
+ *
+ * @internal
+ */
+export interface EcsHttp {
+ request?: Request;
+ response?: Response;
+ version?: string;
+}
+
+interface Request {
+ body?: { bytes?: number; content?: string };
+ bytes?: number;
+ id?: string;
+ // We can't provide predefined values here because ECS requires preserving the
+ // original casing for anomaly detection use cases.
+ method?: string;
+ mime_type?: string;
+ referrer?: string;
+}
+
+interface Response {
+ body?: { bytes?: number; content?: string };
+ bytes?: number;
+ mime_type?: string;
+ status_code?: number;
+}
diff --git a/packages/kbn-logging/src/ecs/index.ts b/packages/kbn-logging/src/ecs/index.ts
new file mode 100644
index 0000000000000..30da3baa43b72
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/index.ts
@@ -0,0 +1,97 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsBase } from './base';
+
+import { EcsAgent } from './agent';
+import { EcsAutonomousSystem } from './autonomous_system';
+import { EcsClient } from './client';
+import { EcsCloud } from './cloud';
+import { EcsContainer } from './container';
+import { EcsDestination } from './destination';
+import { EcsDns } from './dns';
+import { EcsError } from './error';
+import { EcsEvent } from './event';
+import { EcsFile } from './file';
+import { EcsGroup } from './group';
+import { EcsHost } from './host';
+import { EcsHttp } from './http';
+import { EcsLog } from './log';
+import { EcsNetwork } from './network';
+import { EcsObserver } from './observer';
+import { EcsOrganization } from './organization';
+import { EcsPackage } from './package';
+import { EcsProcess } from './process';
+import { EcsRegistry } from './registry';
+import { EcsRelated } from './related';
+import { EcsRule } from './rule';
+import { EcsServer } from './server';
+import { EcsService } from './service';
+import { EcsSource } from './source';
+import { EcsThreat } from './threat';
+import { EcsTls } from './tls';
+import { EcsTracing } from './tracing';
+import { EcsUrl } from './url';
+import { EcsUser } from './user';
+import { EcsUserAgent } from './user_agent';
+import { EcsVulnerability } from './vulnerability';
+
+export { EcsEventCategory, EcsEventKind, EcsEventOutcome, EcsEventType } from './event';
+
+interface EcsField {
+ /**
+ * These typings were written as of ECS 1.9.0.
+ * Don't change this value without checking the rest
+ * of the types to conform to that ECS version.
+ *
+ * https://www.elastic.co/guide/en/ecs/1.9/index.html
+ */
+ version: '1.9.0';
+}
+
+/**
+ * Represents the full ECS schema.
+ *
+ * @public
+ */
+export type Ecs = EcsBase &
+ EcsTracing & {
+ ecs: EcsField;
+
+ agent?: EcsAgent;
+ as?: EcsAutonomousSystem;
+ client?: EcsClient;
+ cloud?: EcsCloud;
+ container?: EcsContainer;
+ destination?: EcsDestination;
+ dns?: EcsDns;
+ error?: EcsError;
+ event?: EcsEvent;
+ file?: EcsFile;
+ group?: EcsGroup;
+ host?: EcsHost;
+ http?: EcsHttp;
+ log?: EcsLog;
+ network?: EcsNetwork;
+ observer?: EcsObserver;
+ organization?: EcsOrganization;
+ package?: EcsPackage;
+ process?: EcsProcess;
+ registry?: EcsRegistry;
+ related?: EcsRelated;
+ rule?: EcsRule;
+ server?: EcsServer;
+ service?: EcsService;
+ source?: EcsSource;
+ threat?: EcsThreat;
+ tls?: EcsTls;
+ url?: EcsUrl;
+ user?: EcsUser;
+ user_agent?: EcsUserAgent;
+ vulnerability?: EcsVulnerability;
+ };
diff --git a/packages/kbn-logging/src/ecs/interface.ts b/packages/kbn-logging/src/ecs/interface.ts
new file mode 100644
index 0000000000000..49b33e8338184
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/interface.ts
@@ -0,0 +1,18 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-interface.html
+ *
+ * @internal
+ */
+export interface EcsInterface {
+ alias?: string;
+ id?: string;
+ name?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/log.ts b/packages/kbn-logging/src/ecs/log.ts
new file mode 100644
index 0000000000000..8bc2e4982e96c
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/log.ts
@@ -0,0 +1,32 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-log.html
+ *
+ * @internal
+ */
+export interface EcsLog {
+ file?: { path: string };
+ level?: string;
+ logger?: string;
+ origin?: Origin;
+ original?: string;
+ syslog?: Syslog;
+}
+
+interface Origin {
+ file?: { line?: number; name?: string };
+ function?: string;
+}
+
+interface Syslog {
+ facility?: { code?: number; name?: string };
+ priority?: number;
+ severity?: { code?: number; name?: string };
+}
diff --git a/packages/kbn-logging/src/ecs/network.ts b/packages/kbn-logging/src/ecs/network.ts
new file mode 100644
index 0000000000000..912427b6cdb7e
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/network.ts
@@ -0,0 +1,33 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsVlan } from './vlan';
+
+interface NestedFields {
+ inner?: { vlan?: EcsVlan };
+ vlan?: EcsVlan;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-network.html
+ *
+ * @internal
+ */
+export interface EcsNetwork extends NestedFields {
+ application?: string;
+ bytes?: number;
+ community_id?: string;
+ direction?: string;
+ forwarded_ip?: string;
+ iana_number?: string;
+ name?: string;
+ packets?: number;
+ protocol?: string;
+ transport?: string;
+ type?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/observer.ts b/packages/kbn-logging/src/ecs/observer.ts
new file mode 100644
index 0000000000000..be2636d15dcdf
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/observer.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsGeo } from './geo';
+import { EcsInterface } from './interface';
+import { EcsOs } from './os';
+import { EcsVlan } from './vlan';
+
+interface NestedFields {
+ egress?: NestedEgressFields;
+ geo?: EcsGeo;
+ ingress?: NestedIngressFields;
+ os?: EcsOs;
+}
+
+interface NestedEgressFields {
+ interface?: EcsInterface;
+ vlan?: EcsVlan;
+}
+
+interface NestedIngressFields {
+ interface?: EcsInterface;
+ vlan?: EcsVlan;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-observer.html
+ *
+ * @internal
+ */
+export interface EcsObserver extends NestedFields {
+ egress?: Egress;
+ hostname?: string;
+ ingress?: Ingress;
+ ip?: string[];
+ mac?: string[];
+ name?: string;
+ product?: string;
+ serial_number?: string;
+ type?: string;
+ vendor?: string;
+ version?: string;
+}
+
+interface Egress extends NestedEgressFields {
+ zone?: string;
+}
+
+interface Ingress extends NestedIngressFields {
+ zone?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/organization.ts b/packages/kbn-logging/src/ecs/organization.ts
new file mode 100644
index 0000000000000..370e6b2646a2f
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/organization.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-organization.html
+ *
+ * @internal
+ */
+export interface EcsOrganization {
+ id?: string;
+ name?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/os.ts b/packages/kbn-logging/src/ecs/os.ts
new file mode 100644
index 0000000000000..342eb14264fd3
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/os.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-os.html
+ *
+ * @internal
+ */
+export interface EcsOs {
+ family?: string;
+ full?: string;
+ kernel?: string;
+ name?: string;
+ platform?: string;
+ type?: string;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/package.ts b/packages/kbn-logging/src/ecs/package.ts
new file mode 100644
index 0000000000000..10528066f3f29
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/package.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-package.html
+ *
+ * @internal
+ */
+export interface EcsPackage {
+ architecture?: string;
+ build_version?: string;
+ checksum?: string;
+ description?: string;
+ install_scope?: string;
+ installed?: string;
+ license?: string;
+ name?: string;
+ path?: string;
+ reference?: string;
+ size?: number;
+ type?: string;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/pe.ts b/packages/kbn-logging/src/ecs/pe.ts
new file mode 100644
index 0000000000000..bd53b7048a50d
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/pe.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-pe.html
+ *
+ * @internal
+ */
+export interface EcsPe {
+ architecture?: string;
+ company?: string;
+ description?: string;
+ file_version?: string;
+ imphash?: string;
+ original_file_name?: string;
+ product?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/process.ts b/packages/kbn-logging/src/ecs/process.ts
new file mode 100644
index 0000000000000..9a034c30fd531
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/process.ts
@@ -0,0 +1,41 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsCodeSignature } from './code_signature';
+import { EcsHash } from './hash';
+import { EcsPe } from './pe';
+
+interface NestedFields {
+ code_signature?: EcsCodeSignature;
+ hash?: EcsHash;
+ parent?: EcsProcess;
+ pe?: EcsPe;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-process.html
+ *
+ * @internal
+ */
+export interface EcsProcess extends NestedFields {
+ args?: string[];
+ args_count?: number;
+ command_line?: string;
+ entity_id?: string;
+ executable?: string;
+ exit_code?: number;
+ name?: string;
+ pgid?: number;
+ pid?: number;
+ ppid?: number;
+ start?: string;
+ thread?: { id?: number; name?: string };
+ title?: string;
+ uptime?: number;
+ working_directory?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/registry.ts b/packages/kbn-logging/src/ecs/registry.ts
new file mode 100644
index 0000000000000..ba7ef699e2cdb
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/registry.ts
@@ -0,0 +1,26 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-registry.html
+ *
+ * @internal
+ */
+export interface EcsRegistry {
+ data?: Data;
+ hive?: string;
+ key?: string;
+ path?: string;
+ value?: string;
+}
+
+interface Data {
+ bytes?: string;
+ strings?: string[];
+ type?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/related.ts b/packages/kbn-logging/src/ecs/related.ts
new file mode 100644
index 0000000000000..33c3ff50540ce
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/related.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-related.html
+ *
+ * @internal
+ */
+export interface EcsRelated {
+ hash?: string[];
+ hosts?: string[];
+ ip?: string[];
+ user?: string[];
+}
diff --git a/packages/kbn-logging/src/ecs/rule.ts b/packages/kbn-logging/src/ecs/rule.ts
new file mode 100644
index 0000000000000..c6bf1ce96552a
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/rule.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-rule.html
+ *
+ * @internal
+ */
+export interface EcsRule {
+ author?: string[];
+ category?: string;
+ description?: string;
+ id?: string;
+ license?: string;
+ name?: string;
+ reference?: string;
+ ruleset?: string;
+ uuid?: string;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/server.ts b/packages/kbn-logging/src/ecs/server.ts
new file mode 100644
index 0000000000000..9b2a9b1a11b42
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/server.ts
@@ -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
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsAutonomousSystem } from './autonomous_system';
+import { EcsGeo } from './geo';
+import { EcsNestedUser } from './user';
+
+interface NestedFields {
+ as?: EcsAutonomousSystem;
+ geo?: EcsGeo;
+ user?: EcsNestedUser;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-server.html
+ *
+ * @internal
+ */
+export interface EcsServer extends NestedFields {
+ address?: string;
+ bytes?: number;
+ domain?: string;
+ ip?: string;
+ mac?: string;
+ nat?: { ip?: string; port?: number };
+ packets?: number;
+ port?: number;
+ registered_domain?: string;
+ subdomain?: string;
+ top_level_domain?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/service.ts b/packages/kbn-logging/src/ecs/service.ts
new file mode 100644
index 0000000000000..4cd79e928c076
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/service.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-service.html
+ *
+ * @internal
+ */
+export interface EcsService {
+ ephemeral_id?: string;
+ id?: string;
+ name?: string;
+ node?: { name: string };
+ state?: string;
+ type?: string;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/source.ts b/packages/kbn-logging/src/ecs/source.ts
new file mode 100644
index 0000000000000..9ec7e2521d0b9
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/source.ts
@@ -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
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsAutonomousSystem } from './autonomous_system';
+import { EcsGeo } from './geo';
+import { EcsNestedUser } from './user';
+
+interface NestedFields {
+ as?: EcsAutonomousSystem;
+ geo?: EcsGeo;
+ user?: EcsNestedUser;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-source.html
+ *
+ * @internal
+ */
+export interface EcsSource extends NestedFields {
+ address?: string;
+ bytes?: number;
+ domain?: string;
+ ip?: string;
+ mac?: string;
+ nat?: { ip?: string; port?: number };
+ packets?: number;
+ port?: number;
+ registered_domain?: string;
+ subdomain?: string;
+ top_level_domain?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/threat.ts b/packages/kbn-logging/src/ecs/threat.ts
new file mode 100644
index 0000000000000..ac6033949fccd
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/threat.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-threat.html
+ *
+ * @internal
+ */
+export interface EcsThreat {
+ framework?: string;
+ tactic?: Tactic;
+ technique?: Technique;
+}
+
+interface Tactic {
+ id?: string[];
+ name?: string[];
+ reference?: string[];
+}
+
+interface Technique {
+ id?: string[];
+ name?: string[];
+ reference?: string[];
+ subtechnique?: Technique;
+}
diff --git a/packages/kbn-logging/src/ecs/tls.ts b/packages/kbn-logging/src/ecs/tls.ts
new file mode 100644
index 0000000000000..b04d03d650908
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/tls.ts
@@ -0,0 +1,64 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsX509 } from './x509';
+
+interface NestedClientFields {
+ x509?: EcsX509;
+}
+
+interface NestedServerFields {
+ x509?: EcsX509;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-tls.html
+ *
+ * @internal
+ */
+export interface EcsTls {
+ cipher?: string;
+ client?: Client;
+ curve?: string;
+ established?: boolean;
+ next_protocol?: string;
+ resumed?: boolean;
+ server?: Server;
+ version?: string;
+ version_protocol?: string;
+}
+
+interface Client extends NestedClientFields {
+ certificate?: string;
+ certificate_chain?: string[];
+ hash?: Hash;
+ issuer?: string;
+ ja3?: string;
+ not_after?: string;
+ not_before?: string;
+ server_name?: string;
+ subject?: string;
+ supported_ciphers?: string[];
+}
+
+interface Server extends NestedServerFields {
+ certificate?: string;
+ certificate_chain?: string[];
+ hash?: Hash;
+ issuer?: string;
+ ja3s?: string;
+ not_after?: string;
+ not_before?: string;
+ subject?: string;
+}
+
+interface Hash {
+ md5?: string;
+ sha1?: string;
+ sha256?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/tracing.ts b/packages/kbn-logging/src/ecs/tracing.ts
new file mode 100644
index 0000000000000..1abbbd4b4c8a2
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/tracing.ts
@@ -0,0 +1,23 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * Unlike other ECS field sets, tracing fields are not nested under the field
+ * set name (i.e. `trace.id` is valid, `tracing.trace.id` is not). So, like
+ * the base fields, we will need to do an intersection with these types at
+ * the root level.
+ *
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-tracing.html
+ *
+ * @internal
+ */
+export interface EcsTracing {
+ span?: { id?: string };
+ trace?: { id?: string };
+ transaction?: { id?: string };
+}
diff --git a/packages/kbn-logging/src/ecs/url.ts b/packages/kbn-logging/src/ecs/url.ts
new file mode 100644
index 0000000000000..5985b28a4f6c3
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/url.ts
@@ -0,0 +1,29 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-url.html
+ *
+ * @internal
+ */
+export interface EcsUrl {
+ domain?: string;
+ extension?: string;
+ fragment?: string;
+ full?: string;
+ original?: string;
+ password?: string;
+ path?: string;
+ port?: number;
+ query?: string;
+ registered_domain?: string;
+ scheme?: string;
+ subdomain?: string;
+ top_level_domain?: string;
+ username?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/user.ts b/packages/kbn-logging/src/ecs/user.ts
new file mode 100644
index 0000000000000..3ab0c946b49b7
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/user.ts
@@ -0,0 +1,48 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsGroup } from './group';
+
+interface NestedFields {
+ group?: EcsGroup;
+}
+
+/**
+ * `User` is unlike most other fields which can be reused in multiple places
+ * in that ECS places restrictions on which individual properties can be reused;
+ *
+ * Specifically, `changes`, `effective`, and `target` may be used if `user` is
+ * placed at the root level, but not if it is nested inside another field like
+ * `destination`. A more detailed explanation of these nuances can be found at:
+ *
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-user-usage.html
+ *
+ * As a result, we need to export a separate `NestedUser` type to import into
+ * other interfaces internally. This contains the reusable subset of properties
+ * from `User`.
+ *
+ * @internal
+ */
+export interface EcsNestedUser extends NestedFields {
+ domain?: string;
+ email?: string;
+ full_name?: string;
+ hash?: string;
+ id?: string;
+ name?: string;
+ roles?: string[];
+}
+
+/**
+ * @internal
+ */
+export interface EcsUser extends EcsNestedUser {
+ changes?: EcsNestedUser;
+ effective?: EcsNestedUser;
+ target?: EcsNestedUser;
+}
diff --git a/packages/kbn-logging/src/ecs/user_agent.ts b/packages/kbn-logging/src/ecs/user_agent.ts
new file mode 100644
index 0000000000000..f77b3ba9e1f0f
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/user_agent.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsOs } from './os';
+
+interface NestedFields {
+ os?: EcsOs;
+}
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-user_agent.html
+ *
+ * @internal
+ */
+export interface EcsUserAgent extends NestedFields {
+ device?: { name: string };
+ name?: string;
+ original?: string;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/vlan.ts b/packages/kbn-logging/src/ecs/vlan.ts
new file mode 100644
index 0000000000000..646f8ee17fd03
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/vlan.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-vlan.html
+ *
+ * @internal
+ */
+export interface EcsVlan {
+ id?: string;
+ name?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/vulnerability.ts b/packages/kbn-logging/src/ecs/vulnerability.ts
new file mode 100644
index 0000000000000..2c26d557d2ba9
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/vulnerability.ts
@@ -0,0 +1,32 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-vulnerability.html
+ *
+ * @internal
+ */
+export interface EcsVulnerability {
+ category?: string[];
+ classification?: string;
+ description?: string;
+ enumeration?: string;
+ id?: string;
+ reference?: string;
+ report_id?: string;
+ scanner?: { vendor: string };
+ score?: Score;
+ severity?: string;
+}
+
+interface Score {
+ base?: number;
+ environmental?: number;
+ temporal?: number;
+ version?: string;
+}
diff --git a/packages/kbn-logging/src/ecs/x509.ts b/packages/kbn-logging/src/ecs/x509.ts
new file mode 100644
index 0000000000000..35bc1b458579a
--- /dev/null
+++ b/packages/kbn-logging/src/ecs/x509.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+/**
+ * https://www.elastic.co/guide/en/ecs/1.9/ecs-x509.html
+ *
+ * @internal
+ */
+export interface EcsX509 {
+ alternative_names?: string[];
+ issuer?: Issuer;
+ not_after?: string;
+ not_before?: string;
+ public_key_algorithm?: string;
+ public_key_curve?: string;
+ public_key_exponent?: number;
+ public_key_size?: number;
+ serial_number?: string;
+ signature_algorithm?: string;
+ subject?: Subject;
+ version_number?: string;
+}
+
+interface Issuer {
+ common_name?: string[];
+ country?: string[];
+ distinguished_name?: string;
+ locality?: string[];
+ organization?: string[];
+ organizational_unit?: string[];
+ state_or_province?: string[];
+}
+
+interface Subject {
+ common_name?: string[];
+ country?: string[];
+ distinguished_name?: string;
+ locality?: string[];
+ organization?: string[];
+ organizational_unit?: string[];
+ state_or_province?: string[];
+}
diff --git a/packages/kbn-logging/src/index.ts b/packages/kbn-logging/src/index.ts
index 048a95395e5c6..075e18f99afe3 100644
--- a/packages/kbn-logging/src/index.ts
+++ b/packages/kbn-logging/src/index.ts
@@ -8,7 +8,9 @@
export { LogLevel, LogLevelId } from './log_level';
export { LogRecord } from './log_record';
-export { Logger, LogMeta } from './logger';
+export { Logger } from './logger';
+export { LogMeta } from './log_meta';
export { LoggerFactory } from './logger_factory';
export { Layout } from './layout';
export { Appender, DisposableAppender } from './appenders';
+export { Ecs, EcsEventCategory, EcsEventKind, EcsEventOutcome, EcsEventType } from './ecs';
diff --git a/packages/kbn-logging/src/log_meta.ts b/packages/kbn-logging/src/log_meta.ts
new file mode 100644
index 0000000000000..7822792c7fbeb
--- /dev/null
+++ b/packages/kbn-logging/src/log_meta.ts
@@ -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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EcsBase } from './ecs/base';
+
+import { EcsAgent } from './ecs/agent';
+import { EcsAutonomousSystem } from './ecs/autonomous_system';
+import { EcsClient } from './ecs/client';
+import { EcsCloud } from './ecs/cloud';
+import { EcsContainer } from './ecs/container';
+import { EcsDestination } from './ecs/destination';
+import { EcsDns } from './ecs/dns';
+import { EcsError } from './ecs/error';
+import { EcsEvent } from './ecs/event';
+import { EcsFile } from './ecs/file';
+import { EcsGroup } from './ecs/group';
+import { EcsHost } from './ecs/host';
+import { EcsHttp } from './ecs/http';
+import { EcsLog } from './ecs/log';
+import { EcsNetwork } from './ecs/network';
+import { EcsObserver } from './ecs/observer';
+import { EcsOrganization } from './ecs/organization';
+import { EcsPackage } from './ecs/package';
+import { EcsProcess } from './ecs/process';
+import { EcsRegistry } from './ecs/registry';
+import { EcsRelated } from './ecs/related';
+import { EcsRule } from './ecs/rule';
+import { EcsServer } from './ecs/server';
+import { EcsService } from './ecs/service';
+import { EcsSource } from './ecs/source';
+import { EcsThreat } from './ecs/threat';
+import { EcsTls } from './ecs/tls';
+import { EcsTracing } from './ecs/tracing';
+import { EcsUrl } from './ecs/url';
+import { EcsUser } from './ecs/user';
+import { EcsUserAgent } from './ecs/user_agent';
+import { EcsVulnerability } from './ecs/vulnerability';
+
+/**
+ * Represents the ECS schema with the following reserved keys excluded:
+ * - `ecs`
+ * - `@timestamp`
+ * - `message`
+ * - `log.level`
+ * - `log.logger`
+ *
+ * @public
+ */
+export type LogMeta = Omit &
+ EcsTracing & {
+ agent?: EcsAgent;
+ as?: EcsAutonomousSystem;
+ client?: EcsClient;
+ cloud?: EcsCloud;
+ container?: EcsContainer;
+ destination?: EcsDestination;
+ dns?: EcsDns;
+ error?: EcsError;
+ event?: EcsEvent;
+ file?: EcsFile;
+ group?: EcsGroup;
+ host?: EcsHost;
+ http?: EcsHttp;
+ log?: Omit;
+ network?: EcsNetwork;
+ observer?: EcsObserver;
+ organization?: EcsOrganization;
+ package?: EcsPackage;
+ process?: EcsProcess;
+ registry?: EcsRegistry;
+ related?: EcsRelated;
+ rule?: EcsRule;
+ server?: EcsServer;
+ service?: EcsService;
+ source?: EcsSource;
+ threat?: EcsThreat;
+ tls?: EcsTls;
+ url?: EcsUrl;
+ user?: EcsUser;
+ user_agent?: EcsUserAgent;
+ vulnerability?: EcsVulnerability;
+ };
diff --git a/packages/kbn-logging/src/logger.ts b/packages/kbn-logging/src/logger.ts
index dad4fb07c6cfa..fda3cf45b9d79 100644
--- a/packages/kbn-logging/src/logger.ts
+++ b/packages/kbn-logging/src/logger.ts
@@ -6,17 +6,9 @@
* Side Public License, v 1.
*/
+import { LogMeta } from './log_meta';
import { LogRecord } from './log_record';
-/**
- * Contextual metadata
- *
- * @public
- */
-export interface LogMeta {
- [key: string]: any;
-}
-
/**
* Logger exposes all the necessary methods to log any type of information and
* this is the interface used by the logging consumers including plugins.
@@ -30,28 +22,28 @@ export interface Logger {
* @param message - The log message
* @param meta -
*/
- trace(message: string, meta?: LogMeta): void;
+ trace(message: string, meta?: Meta): void;
/**
* Log messages useful for debugging and interactive investigation
* @param message - The log message
* @param meta -
*/
- debug(message: string, meta?: LogMeta): void;
+ debug(message: string, meta?: Meta): void;
/**
* Logs messages related to general application flow
* @param message - The log message
* @param meta -
*/
- info(message: string, meta?: LogMeta): void;
+ info(message: string, meta?: Meta): void;
/**
* Logs abnormal or unexpected errors or messages
* @param errorOrMessage - An Error object or message string to log
* @param meta -
*/
- warn(errorOrMessage: string | Error, meta?: LogMeta): void;
+ warn(errorOrMessage: string | Error, meta?: Meta): void;
/**
* Logs abnormal or unexpected errors or messages that caused a failure in the application flow
@@ -59,7 +51,7 @@ export interface Logger {
* @param errorOrMessage - An Error object or message string to log
* @param meta -
*/
- error(errorOrMessage: string | Error, meta?: LogMeta): void;
+ error(errorOrMessage: string | Error, meta?: Meta): void;
/**
* Logs abnormal or unexpected errors or messages that caused an unrecoverable failure
@@ -67,7 +59,7 @@ export interface Logger {
* @param errorOrMessage - An Error object or message string to log
* @param meta -
*/
- fatal(errorOrMessage: string | Error, meta?: LogMeta): void;
+ fatal(errorOrMessage: string | Error, meta?: Meta): void;
/** @internal */
log(record: LogRecord): void;
diff --git a/src/core/server/environment/write_pid_file.ts b/src/core/server/environment/write_pid_file.ts
index b7d47111a4d53..46096ca347e8a 100644
--- a/src/core/server/environment/write_pid_file.ts
+++ b/src/core/server/environment/write_pid_file.ts
@@ -31,13 +31,23 @@ export const writePidFile = async ({
if (pidConfig.exclusive) {
throw new Error(message);
} else {
- logger.warn(message, { path, pid });
+ logger.warn(message, {
+ process: {
+ pid: process.pid,
+ path,
+ },
+ });
}
}
await writeFile(path, pid);
- logger.debug(`wrote pid file to ${path}`, { path, pid });
+ logger.debug(`wrote pid file to ${path}`, {
+ process: {
+ pid: process.pid,
+ path,
+ },
+ });
const clean = once(() => {
unlink(path);
diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts
index cd7d7ccc5aeff..62a4217da6d13 100644
--- a/src/core/server/http/http_server.ts
+++ b/src/core/server/http/http_server.ts
@@ -303,7 +303,7 @@ export class HttpServer {
const log = this.logger.get('http', 'server', 'response');
this.handleServerResponseEvent = (request) => {
- const { message, ...meta } = getEcsResponseLog(request, this.log);
+ const { message, meta } = getEcsResponseLog(request, this.log);
log.debug(message!, meta);
};
diff --git a/src/core/server/http/logging/get_response_log.test.ts b/src/core/server/http/logging/get_response_log.test.ts
index 64241ff44fc6b..5f749220138d7 100644
--- a/src/core/server/http/logging/get_response_log.test.ts
+++ b/src/core/server/http/logging/get_response_log.test.ts
@@ -81,7 +81,8 @@ describe('getEcsResponseLog', () => {
},
});
const result = getEcsResponseLog(req, logger);
- expect(result.http.response.responseTime).toBe(1000);
+ // @ts-expect-error ECS custom field
+ expect(result.meta.http.response.responseTime).toBe(1000);
});
test('with response.info.responded', () => {
@@ -92,14 +93,16 @@ describe('getEcsResponseLog', () => {
},
});
const result = getEcsResponseLog(req, logger);
- expect(result.http.response.responseTime).toBe(500);
+ // @ts-expect-error ECS custom field
+ expect(result.meta.http.response.responseTime).toBe(500);
});
test('excludes responseTime from message if none is provided', () => {
const req = createMockHapiRequest();
const result = getEcsResponseLog(req, logger);
expect(result.message).toMatchInlineSnapshot(`"GET /path 200 - 1.2KB"`);
- expect(result.http.response.responseTime).toBeUndefined();
+ // @ts-expect-error ECS custom field
+ expect(result.meta.http.response.responseTime).toBeUndefined();
});
});
@@ -112,7 +115,7 @@ describe('getEcsResponseLog', () => {
},
});
const result = getEcsResponseLog(req, logger);
- expect(result.url.query).toMatchInlineSnapshot(`"a=hello&b=world"`);
+ expect(result.meta.url!.query).toMatchInlineSnapshot(`"a=hello&b=world"`);
expect(result.message).toMatchInlineSnapshot(`"GET /path?a=hello&b=world 200 - 1.2KB"`);
});
@@ -121,7 +124,7 @@ describe('getEcsResponseLog', () => {
query: { a: '¡hola!' },
});
const result = getEcsResponseLog(req, logger);
- expect(result.url.query).toMatchInlineSnapshot(`"a=%C2%A1hola!"`);
+ expect(result.meta.url!.query).toMatchInlineSnapshot(`"a=%C2%A1hola!"`);
expect(result.message).toMatchInlineSnapshot(`"GET /path?a=%C2%A1hola! 200 - 1.2KB"`);
});
});
@@ -145,7 +148,7 @@ describe('getEcsResponseLog', () => {
response: Boom.badRequest(),
});
const result = getEcsResponseLog(req, logger);
- expect(result.http.response.status_code).toBe(400);
+ expect(result.meta.http!.response!.status_code).toBe(400);
});
describe('filters sensitive headers', () => {
@@ -155,14 +158,16 @@ describe('getEcsResponseLog', () => {
response: { headers: { 'content-length': 123, 'set-cookie': 'c' } },
});
const result = getEcsResponseLog(req, logger);
- expect(result.http.request.headers).toMatchInlineSnapshot(`
+ // @ts-expect-error ECS custom field
+ expect(result.meta.http.request.headers).toMatchInlineSnapshot(`
Object {
"authorization": "[REDACTED]",
"cookie": "[REDACTED]",
"user-agent": "hi",
}
`);
- expect(result.http.response.headers).toMatchInlineSnapshot(`
+ // @ts-expect-error ECS custom field
+ expect(result.meta.http.response.headers).toMatchInlineSnapshot(`
Object {
"content-length": 123,
"set-cookie": "[REDACTED]",
@@ -196,9 +201,12 @@ describe('getEcsResponseLog', () => {
}
`);
- responseLog.http.request.headers.a = 'testA';
- responseLog.http.request.headers.b[1] = 'testB';
- responseLog.http.request.headers.c = 'testC';
+ // @ts-expect-error ECS custom field
+ responseLog.meta.http.request.headers.a = 'testA';
+ // @ts-expect-error ECS custom field
+ responseLog.meta.http.request.headers.b[1] = 'testB';
+ // @ts-expect-error ECS custom field
+ responseLog.meta.http.request.headers.c = 'testC';
expect(reqHeaders).toMatchInlineSnapshot(`
Object {
"a": "foo",
@@ -244,48 +252,41 @@ describe('getEcsResponseLog', () => {
});
describe('ecs', () => {
- test('specifies correct ECS version', () => {
- const req = createMockHapiRequest();
- const result = getEcsResponseLog(req, logger);
- expect(result.ecs.version).toBe('1.7.0');
- });
-
test('provides an ECS-compatible response', () => {
const req = createMockHapiRequest();
const result = getEcsResponseLog(req, logger);
expect(result).toMatchInlineSnapshot(`
Object {
- "client": Object {
- "ip": undefined,
- },
- "ecs": Object {
- "version": "1.7.0",
- },
- "http": Object {
- "request": Object {
- "headers": Object {
- "user-agent": "",
- },
- "method": "GET",
- "mime_type": "application/json",
- "referrer": "localhost:5601/app/home",
+ "message": "GET /path 200 - 1.2KB",
+ "meta": Object {
+ "client": Object {
+ "ip": undefined,
},
- "response": Object {
- "body": Object {
- "bytes": 1234,
+ "http": Object {
+ "request": Object {
+ "headers": Object {
+ "user-agent": "",
+ },
+ "method": "GET",
+ "mime_type": "application/json",
+ "referrer": "localhost:5601/app/home",
+ },
+ "response": Object {
+ "body": Object {
+ "bytes": 1234,
+ },
+ "headers": Object {},
+ "responseTime": undefined,
+ "status_code": 200,
},
- "headers": Object {},
- "responseTime": undefined,
- "status_code": 200,
},
- },
- "message": "GET /path 200 - 1.2KB",
- "url": Object {
- "path": "/path",
- "query": "",
- },
- "user_agent": Object {
- "original": "",
+ "url": Object {
+ "path": "/path",
+ "query": "",
+ },
+ "user_agent": Object {
+ "original": "",
+ },
},
}
`);
diff --git a/src/core/server/http/logging/get_response_log.ts b/src/core/server/http/logging/get_response_log.ts
index 57c02e05bebff..37ee618e43395 100644
--- a/src/core/server/http/logging/get_response_log.ts
+++ b/src/core/server/http/logging/get_response_log.ts
@@ -11,10 +11,9 @@ import { isBoom } from '@hapi/boom';
import type { Request } from '@hapi/hapi';
import numeral from '@elastic/numeral';
import { LogMeta } from '@kbn/logging';
-import { EcsEvent, Logger } from '../../logging';
+import { Logger } from '../../logging';
import { getResponsePayloadBytes } from './get_payload_size';
-const ECS_VERSION = '1.7.0';
const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie'];
const REDACTED_HEADER_TEXT = '[REDACTED]';
@@ -44,7 +43,7 @@ function cloneAndFilterHeaders(headers?: HapiHeaders) {
*
* @internal
*/
-export function getEcsResponseLog(request: Request, log: Logger): LogMeta {
+export function getEcsResponseLog(request: Request, log: Logger) {
const { path, response } = request;
const method = request.method.toUpperCase();
@@ -66,9 +65,7 @@ export function getEcsResponseLog(request: Request, log: Logger): LogMeta {
const bytes = getResponsePayloadBytes(response, log);
const bytesMsg = bytes ? ` - ${numeral(bytes).format('0.0b')}` : '';
- const meta: EcsEvent = {
- ecs: { version: ECS_VERSION },
- message: `${method} ${pathWithQuery} ${status_code}${responseTimeMsg}${bytesMsg}`,
+ const meta: LogMeta = {
client: {
ip: request.info.remoteAddress,
},
@@ -77,7 +74,7 @@ export function getEcsResponseLog(request: Request, log: Logger): LogMeta {
method,
mime_type: request.mime,
referrer: request.info.referrer,
- // @ts-expect-error Headers are not yet part of ECS: https://github.com/elastic/ecs/issues/232.
+ // @ts-expect-error ECS custom field: https://github.com/elastic/ecs/issues/232.
headers: requestHeaders,
},
response: {
@@ -85,7 +82,7 @@ export function getEcsResponseLog(request: Request, log: Logger): LogMeta {
bytes,
},
status_code,
- // @ts-expect-error Headers are not yet part of ECS: https://github.com/elastic/ecs/issues/232.
+ // @ts-expect-error ECS custom field: https://github.com/elastic/ecs/issues/232.
headers: responseHeaders,
// responseTime is a custom non-ECS field
responseTime: !isNaN(responseTime) ? responseTime : undefined,
@@ -100,5 +97,8 @@ export function getEcsResponseLog(request: Request, log: Logger): LogMeta {
},
};
- return meta;
+ return {
+ message: `${method} ${pathWithQuery} ${status_code}${responseTimeMsg}${bytesMsg}`,
+ meta,
+ };
}
diff --git a/src/core/server/index.ts b/src/core/server/index.ts
index 2c6fa74cb54a0..31508e075297b 100644
--- a/src/core/server/index.ts
+++ b/src/core/server/index.ts
@@ -236,6 +236,11 @@ export type { IRenderOptions } from './rendering';
export type {
Logger,
LoggerFactory,
+ Ecs,
+ EcsEventCategory,
+ EcsEventKind,
+ EcsEventOutcome,
+ EcsEventType,
LogMeta,
LogRecord,
LogLevel,
diff --git a/src/core/server/logging/__snapshots__/logging_system.test.ts.snap b/src/core/server/logging/__snapshots__/logging_system.test.ts.snap
index 81321a3b1fe44..d74317203d78e 100644
--- a/src/core/server/logging/__snapshots__/logging_system.test.ts.snap
+++ b/src/core/server/logging/__snapshots__/logging_system.test.ts.snap
@@ -15,6 +15,9 @@ exports[`appends records via multiple appenders.: file logs 2`] = `
exports[`asLoggerFactory() only allows to create new loggers. 1`] = `
Object {
"@timestamp": "2012-01-30T22:33:22.011-05:00",
+ "ecs": Object {
+ "version": "1.9.0",
+ },
"log": Object {
"level": "TRACE",
"logger": "test.context",
@@ -29,6 +32,9 @@ Object {
exports[`asLoggerFactory() only allows to create new loggers. 2`] = `
Object {
"@timestamp": "2012-01-30T17:33:22.011-05:00",
+ "ecs": Object {
+ "version": "1.9.0",
+ },
"log": Object {
"level": "INFO",
"logger": "test.context",
@@ -44,6 +50,9 @@ Object {
exports[`asLoggerFactory() only allows to create new loggers. 3`] = `
Object {
"@timestamp": "2012-01-30T12:33:22.011-05:00",
+ "ecs": Object {
+ "version": "1.9.0",
+ },
"log": Object {
"level": "FATAL",
"logger": "test.context",
@@ -58,6 +67,9 @@ Object {
exports[`flushes memory buffer logger and switches to real logger once config is provided: buffered messages 1`] = `
Object {
"@timestamp": "2012-02-01T09:33:22.011-05:00",
+ "ecs": Object {
+ "version": "1.9.0",
+ },
"log": Object {
"level": "INFO",
"logger": "test.context",
@@ -73,6 +85,9 @@ Object {
exports[`flushes memory buffer logger and switches to real logger once config is provided: new messages 1`] = `
Object {
"@timestamp": "2012-01-31T23:33:22.011-05:00",
+ "ecs": Object {
+ "version": "1.9.0",
+ },
"log": Object {
"level": "INFO",
"logger": "test.context",
diff --git a/src/core/server/logging/appenders/rewrite/policies/meta/meta_policy.test.ts b/src/core/server/logging/appenders/rewrite/policies/meta/meta_policy.test.ts
index 52b88331a75be..faa026363ed40 100644
--- a/src/core/server/logging/appenders/rewrite/policies/meta/meta_policy.test.ts
+++ b/src/core/server/logging/appenders/rewrite/policies/meta/meta_policy.test.ts
@@ -26,12 +26,14 @@ describe('MetaRewritePolicy', () => {
describe('mode: update', () => {
it('updates existing properties in LogMeta', () => {
+ // @ts-expect-error ECS custom meta
const log = createLogRecord({ a: 'before' });
const policy = createPolicy('update', [{ path: 'a', value: 'after' }]);
expect(policy.rewrite(log).meta!.a).toBe('after');
});
it('updates nested properties in LogMeta', () => {
+ // @ts-expect-error ECS custom meta
const log = createLogRecord({ a: 'before a', b: { c: 'before b.c' }, d: [0, 1] });
const policy = createPolicy('update', [
{ path: 'a', value: 'after a' },
@@ -60,6 +62,7 @@ describe('MetaRewritePolicy', () => {
{ path: 'd', value: 'hi' },
]);
const log = createLogRecord({
+ // @ts-expect-error ECS custom meta
a: 'a',
b: 'b',
c: 'c',
@@ -80,6 +83,7 @@ describe('MetaRewritePolicy', () => {
{ path: 'a.b', value: 'foo' },
{ path: 'a.c', value: 'bar' },
]);
+ // @ts-expect-error ECS custom meta
const log = createLogRecord({ a: { b: 'existing meta' } });
const { meta } = policy.rewrite(log);
expect(meta!.a.b).toBe('foo');
@@ -106,12 +110,14 @@ describe('MetaRewritePolicy', () => {
describe('mode: remove', () => {
it('removes existing properties in LogMeta', () => {
+ // @ts-expect-error ECS custom meta
const log = createLogRecord({ a: 'goodbye' });
const policy = createPolicy('remove', [{ path: 'a' }]);
expect(policy.rewrite(log).meta!.a).toBeUndefined();
});
it('removes nested properties in LogMeta', () => {
+ // @ts-expect-error ECS custom meta
const log = createLogRecord({ a: 'a', b: { c: 'b.c' }, d: [0, 1] });
const policy = createPolicy('remove', [{ path: 'b.c' }, { path: 'd[1]' }]);
expect(policy.rewrite(log).meta).toMatchInlineSnapshot(`
@@ -127,6 +133,7 @@ describe('MetaRewritePolicy', () => {
});
it('has no effect if property does not exist', () => {
+ // @ts-expect-error ECS custom meta
const log = createLogRecord({ a: 'a' });
const policy = createPolicy('remove', [{ path: 'b' }]);
expect(policy.rewrite(log).meta).toMatchInlineSnapshot(`
diff --git a/src/core/server/logging/appenders/rewrite/rewrite_appender.test.ts b/src/core/server/logging/appenders/rewrite/rewrite_appender.test.ts
index 72a54b5012ce5..f4ce64ee65075 100644
--- a/src/core/server/logging/appenders/rewrite/rewrite_appender.test.ts
+++ b/src/core/server/logging/appenders/rewrite/rewrite_appender.test.ts
@@ -85,8 +85,8 @@ describe('RewriteAppender', () => {
const appender = new RewriteAppender(config);
appenderMocks.forEach((mock) => appender.addAppender(...mock));
- const log1 = createLogRecord({ a: 'b' });
- const log2 = createLogRecord({ c: 'd' });
+ const log1 = createLogRecord({ user_agent: { name: 'a' } });
+ const log2 = createLogRecord({ user_agent: { name: 'b' } });
appender.append(log1);
@@ -109,8 +109,8 @@ describe('RewriteAppender', () => {
const appender = new RewriteAppender(config);
appender.addAppender(...createAppenderMock('mock1'));
- const log1 = createLogRecord({ a: 'b' });
- const log2 = createLogRecord({ c: 'd' });
+ const log1 = createLogRecord({ user_agent: { name: 'a' } });
+ const log2 = createLogRecord({ user_agent: { name: 'b' } });
appender.append(log1);
diff --git a/src/core/server/logging/ecs.ts b/src/core/server/logging/ecs.ts
deleted file mode 100644
index f6db79819d819..0000000000000
--- a/src/core/server/logging/ecs.ts
+++ /dev/null
@@ -1,129 +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
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-/**
- * Typings for some ECS fields which core uses internally.
- * These are not a complete set of ECS typings and should not
- * be used externally; the only types included here are ones
- * currently used in core.
- *
- * @internal
- */
-export interface EcsEvent {
- /**
- * These typings were written as of ECS 1.7.0.
- * Don't change this value without checking the rest
- * of the types to conform to that ECS version.
- *
- * https://www.elastic.co/guide/en/ecs/1.7/index.html
- */
- ecs: { version: '1.7.0' };
-
- // base fields
- ['@timestamp']?: string;
- labels?: Record;
- message?: string;
- tags?: string[];
-
- // other fields
- client?: EcsClientField;
- event?: EcsEventField;
- http?: EcsHttpField;
- process?: EcsProcessField;
- url?: EcsUrlField;
- user_agent?: EcsUserAgentField;
-}
-
-/** @internal */
-export enum EcsEventKind {
- ALERT = 'alert',
- EVENT = 'event',
- METRIC = 'metric',
- STATE = 'state',
- PIPELINE_ERROR = 'pipeline_error',
- SIGNAL = 'signal',
-}
-
-/** @internal */
-export enum EcsEventCategory {
- AUTHENTICATION = 'authentication',
- CONFIGURATION = 'configuration',
- DATABASE = 'database',
- DRIVER = 'driver',
- FILE = 'file',
- HOST = 'host',
- IAM = 'iam',
- INTRUSION_DETECTION = 'intrusion_detection',
- MALWARE = 'malware',
- NETWORK = 'network',
- PACKAGE = 'package',
- PROCESS = 'process',
- WEB = 'web',
-}
-
-/** @internal */
-export enum EcsEventType {
- ACCESS = 'access',
- ADMIN = 'admin',
- ALLOWED = 'allowed',
- CHANGE = 'change',
- CONNECTION = 'connection',
- CREATION = 'creation',
- DELETION = 'deletion',
- DENIED = 'denied',
- END = 'end',
- ERROR = 'error',
- GROUP = 'group',
- INFO = 'info',
- INSTALLATION = 'installation',
- PROTOCOL = 'protocol',
- START = 'start',
- USER = 'user',
-}
-
-interface EcsEventField {
- kind?: EcsEventKind;
- category?: EcsEventCategory[];
- type?: EcsEventType;
-}
-
-interface EcsProcessField {
- uptime?: number;
-}
-
-interface EcsClientField {
- ip?: string;
-}
-
-interface EcsHttpFieldRequest {
- body?: { bytes?: number; content?: string };
- method?: string;
- mime_type?: string;
- referrer?: string;
-}
-
-interface EcsHttpFieldResponse {
- body?: { bytes?: number; content?: string };
- bytes?: number;
- status_code?: number;
-}
-
-interface EcsHttpField {
- version?: string;
- request?: EcsHttpFieldRequest;
- response?: EcsHttpFieldResponse;
-}
-
-interface EcsUrlField {
- path?: string;
- query?: string;
-}
-
-interface EcsUserAgentField {
- original?: string;
-}
diff --git a/src/core/server/logging/index.ts b/src/core/server/logging/index.ts
index cef96be54870e..9d17b289bfa4c 100644
--- a/src/core/server/logging/index.ts
+++ b/src/core/server/logging/index.ts
@@ -9,6 +9,11 @@ export { LogLevel } from '@kbn/logging';
export type {
DisposableAppender,
Appender,
+ Ecs,
+ EcsEventCategory,
+ EcsEventKind,
+ EcsEventOutcome,
+ EcsEventType,
LogRecord,
Layout,
LoggerFactory,
@@ -16,8 +21,6 @@ export type {
Logger,
LogLevelId,
} from '@kbn/logging';
-export { EcsEventType, EcsEventCategory, EcsEventKind } from './ecs';
-export type { EcsEvent } from './ecs';
export { config } from './logging_config';
export type {
LoggingConfigType,
diff --git a/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap b/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap
index 0e7ce8d0b2f3c..a131d5c8a9248 100644
--- a/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap
+++ b/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap
@@ -1,13 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`\`format()\` correctly formats record. 1`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-1\\",\\"error\\":{\\"message\\":\\"Some error message\\",\\"type\\":\\"Some error name\\",\\"stack_trace\\":\\"Some error stack\\"},\\"log\\":{\\"level\\":\\"FATAL\\",\\"logger\\":\\"context-1\\"},\\"process\\":{\\"pid\\":5355}}"`;
+exports[`\`format()\` correctly formats record. 1`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-1\\",\\"error\\":{\\"message\\":\\"Some error message\\",\\"type\\":\\"Some error name\\",\\"stack_trace\\":\\"Some error stack\\"},\\"log\\":{\\"level\\":\\"FATAL\\",\\"logger\\":\\"context-1\\"},\\"process\\":{\\"pid\\":5355}}"`;
-exports[`\`format()\` correctly formats record. 2`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-2\\",\\"log\\":{\\"level\\":\\"ERROR\\",\\"logger\\":\\"context-2\\"},\\"process\\":{\\"pid\\":5355}}"`;
+exports[`\`format()\` correctly formats record. 2`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-2\\",\\"log\\":{\\"level\\":\\"ERROR\\",\\"logger\\":\\"context-2\\"},\\"process\\":{\\"pid\\":5355}}"`;
-exports[`\`format()\` correctly formats record. 3`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-3\\",\\"log\\":{\\"level\\":\\"WARN\\",\\"logger\\":\\"context-3\\"},\\"process\\":{\\"pid\\":5355}}"`;
+exports[`\`format()\` correctly formats record. 3`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-3\\",\\"log\\":{\\"level\\":\\"WARN\\",\\"logger\\":\\"context-3\\"},\\"process\\":{\\"pid\\":5355}}"`;
-exports[`\`format()\` correctly formats record. 4`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-4\\",\\"log\\":{\\"level\\":\\"DEBUG\\",\\"logger\\":\\"context-4\\"},\\"process\\":{\\"pid\\":5355}}"`;
+exports[`\`format()\` correctly formats record. 4`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-4\\",\\"log\\":{\\"level\\":\\"DEBUG\\",\\"logger\\":\\"context-4\\"},\\"process\\":{\\"pid\\":5355}}"`;
-exports[`\`format()\` correctly formats record. 5`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-5\\",\\"log\\":{\\"level\\":\\"INFO\\",\\"logger\\":\\"context-5\\"},\\"process\\":{\\"pid\\":5355}}"`;
+exports[`\`format()\` correctly formats record. 5`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-5\\",\\"log\\":{\\"level\\":\\"INFO\\",\\"logger\\":\\"context-5\\"},\\"process\\":{\\"pid\\":5355}}"`;
-exports[`\`format()\` correctly formats record. 6`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-6\\",\\"log\\":{\\"level\\":\\"TRACE\\",\\"logger\\":\\"context-6\\"},\\"process\\":{\\"pid\\":5355}}"`;
+exports[`\`format()\` correctly formats record. 6`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-6\\",\\"log\\":{\\"level\\":\\"TRACE\\",\\"logger\\":\\"context-6\\"},\\"process\\":{\\"pid\\":5355}}"`;
diff --git a/src/core/server/logging/layouts/json_layout.test.ts b/src/core/server/logging/layouts/json_layout.test.ts
index e55f69daab110..e76e3fb4402bb 100644
--- a/src/core/server/logging/layouts/json_layout.test.ts
+++ b/src/core/server/logging/layouts/json_layout.test.ts
@@ -94,6 +94,7 @@ test('`format()` correctly formats record with meta-data', () => {
})
)
).toStrictEqual({
+ ecs: { version: '1.9.0' },
'@timestamp': '2012-02-01T09:30:22.011-05:00',
log: {
level: 'DEBUG',
@@ -135,6 +136,7 @@ test('`format()` correctly formats error record with meta-data', () => {
})
)
).toStrictEqual({
+ ecs: { version: '1.9.0' },
'@timestamp': '2012-02-01T09:30:22.011-05:00',
log: {
level: 'DEBUG',
@@ -156,7 +158,39 @@ test('`format()` correctly formats error record with meta-data', () => {
});
});
-test('format() meta can override @timestamp', () => {
+test('format() meta can merge override logs', () => {
+ const layout = new JsonLayout();
+ expect(
+ JSON.parse(
+ layout.format({
+ timestamp,
+ message: 'foo',
+ level: LogLevel.Error,
+ context: 'bar',
+ pid: 3,
+ meta: {
+ log: {
+ kbn_custom_field: 'hello',
+ },
+ },
+ })
+ )
+ ).toStrictEqual({
+ ecs: { version: '1.9.0' },
+ '@timestamp': '2012-02-01T09:30:22.011-05:00',
+ message: 'foo',
+ log: {
+ level: 'ERROR',
+ logger: 'bar',
+ kbn_custom_field: 'hello',
+ },
+ process: {
+ pid: 3,
+ },
+ });
+});
+
+test('format() meta can not override message', () => {
const layout = new JsonLayout();
expect(
JSON.parse(
@@ -167,12 +201,13 @@ test('format() meta can override @timestamp', () => {
context: 'bar',
pid: 3,
meta: {
- '@timestamp': '2099-05-01T09:30:22.011-05:00',
+ message: 'baz',
},
})
)
).toStrictEqual({
- '@timestamp': '2099-05-01T09:30:22.011-05:00',
+ ecs: { version: '1.9.0' },
+ '@timestamp': '2012-02-01T09:30:22.011-05:00',
message: 'foo',
log: {
level: 'DEBUG',
@@ -184,30 +219,60 @@ test('format() meta can override @timestamp', () => {
});
});
-test('format() meta can merge override logs', () => {
+test('format() meta can not override ecs version', () => {
const layout = new JsonLayout();
expect(
JSON.parse(
layout.format({
+ message: 'foo',
timestamp,
+ level: LogLevel.Debug,
+ context: 'bar',
+ pid: 3,
+ meta: {
+ message: 'baz',
+ },
+ })
+ )
+ ).toStrictEqual({
+ ecs: { version: '1.9.0' },
+ '@timestamp': '2012-02-01T09:30:22.011-05:00',
+ message: 'foo',
+ log: {
+ level: 'DEBUG',
+ logger: 'bar',
+ },
+ process: {
+ pid: 3,
+ },
+ });
+});
+
+test('format() meta can not override logger or level', () => {
+ const layout = new JsonLayout();
+ expect(
+ JSON.parse(
+ layout.format({
message: 'foo',
- level: LogLevel.Error,
+ timestamp,
+ level: LogLevel.Debug,
context: 'bar',
pid: 3,
meta: {
log: {
- kbn_custom_field: 'hello',
+ level: 'IGNORE',
+ logger: 'me',
},
},
})
)
).toStrictEqual({
+ ecs: { version: '1.9.0' },
'@timestamp': '2012-02-01T09:30:22.011-05:00',
message: 'foo',
log: {
- level: 'ERROR',
+ level: 'DEBUG',
logger: 'bar',
- kbn_custom_field: 'hello',
},
process: {
pid: 3,
@@ -215,29 +280,28 @@ test('format() meta can merge override logs', () => {
});
});
-test('format() meta can override log level objects', () => {
+test('format() meta can not override timestamp', () => {
const layout = new JsonLayout();
expect(
JSON.parse(
layout.format({
- timestamp,
- context: '123',
message: 'foo',
- level: LogLevel.Error,
+ timestamp,
+ level: LogLevel.Debug,
+ context: 'bar',
pid: 3,
meta: {
- log: {
- level: 'FATAL',
- },
+ '@timestamp': '2099-02-01T09:30:22.011-05:00',
},
})
)
).toStrictEqual({
+ ecs: { version: '1.9.0' },
'@timestamp': '2012-02-01T09:30:22.011-05:00',
message: 'foo',
log: {
- level: 'FATAL',
- logger: '123',
+ level: 'DEBUG',
+ logger: 'bar',
},
process: {
pid: 3,
diff --git a/src/core/server/logging/layouts/json_layout.ts b/src/core/server/logging/layouts/json_layout.ts
index bb8423f8240af..add88cc01b6d2 100644
--- a/src/core/server/logging/layouts/json_layout.ts
+++ b/src/core/server/logging/layouts/json_layout.ts
@@ -9,7 +9,7 @@
import moment from 'moment-timezone';
import { merge } from '@kbn/std';
import { schema } from '@kbn/config-schema';
-import { LogRecord, Layout } from '@kbn/logging';
+import { Ecs, LogRecord, Layout } from '@kbn/logging';
const { literal, object } = schema;
@@ -42,7 +42,8 @@ export class JsonLayout implements Layout {
}
public format(record: LogRecord): string {
- const log = {
+ const log: Ecs = {
+ ecs: { version: '1.9.0' },
'@timestamp': moment(record.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
message: record.message,
error: JsonLayout.errorToSerializableObject(record.error),
@@ -54,7 +55,8 @@ export class JsonLayout implements Layout {
pid: record.pid,
},
};
- const output = record.meta ? merge(log, record.meta) : log;
+ const output = record.meta ? merge({ ...record.meta }, log) : log;
+
return JSON.stringify(output);
}
}
diff --git a/src/core/server/logging/logger.test.ts b/src/core/server/logging/logger.test.ts
index b7f224e73cb8b..c57ce2563ca3d 100644
--- a/src/core/server/logging/logger.test.ts
+++ b/src/core/server/logging/logger.test.ts
@@ -45,6 +45,7 @@ test('`trace()` correctly forms `LogRecord` and passes it to all appenders.', ()
});
}
+ // @ts-expect-error ECS custom meta
logger.trace('message-2', { trace: true });
for (const appenderMock of appenderMocks) {
expect(appenderMock.append).toHaveBeenCalledTimes(2);
@@ -75,6 +76,7 @@ test('`debug()` correctly forms `LogRecord` and passes it to all appenders.', ()
});
}
+ // @ts-expect-error ECS custom meta
logger.debug('message-2', { debug: true });
for (const appenderMock of appenderMocks) {
expect(appenderMock.append).toHaveBeenCalledTimes(2);
@@ -105,6 +107,7 @@ test('`info()` correctly forms `LogRecord` and passes it to all appenders.', ()
});
}
+ // @ts-expect-error ECS custom meta
logger.info('message-2', { info: true });
for (const appenderMock of appenderMocks) {
expect(appenderMock.append).toHaveBeenCalledTimes(2);
@@ -150,6 +153,7 @@ test('`warn()` correctly forms `LogRecord` and passes it to all appenders.', ()
});
}
+ // @ts-expect-error ECS custom meta
logger.warn('message-3', { warn: true });
for (const appenderMock of appenderMocks) {
expect(appenderMock.append).toHaveBeenCalledTimes(3);
@@ -195,6 +199,7 @@ test('`error()` correctly forms `LogRecord` and passes it to all appenders.', ()
});
}
+ // @ts-expect-error ECS custom meta
logger.error('message-3', { error: true });
for (const appenderMock of appenderMocks) {
expect(appenderMock.append).toHaveBeenCalledTimes(3);
@@ -240,6 +245,7 @@ test('`fatal()` correctly forms `LogRecord` and passes it to all appenders.', ()
});
}
+ // @ts-expect-error ECS custom meta
logger.fatal('message-3', { fatal: true });
for (const appenderMock of appenderMocks) {
expect(appenderMock.append).toHaveBeenCalledTimes(3);
diff --git a/src/core/server/logging/logger.ts b/src/core/server/logging/logger.ts
index 4ba334cec2fb9..e025c28a88f0e 100644
--- a/src/core/server/logging/logger.ts
+++ b/src/core/server/logging/logger.ts
@@ -21,28 +21,28 @@ export class BaseLogger implements Logger {
private readonly factory: LoggerFactory
) {}
- public trace(message: string, meta?: LogMeta): void {
- this.log(this.createLogRecord(LogLevel.Trace, message, meta));
+ public trace(message: string, meta?: Meta): void {
+ this.log(this.createLogRecord(LogLevel.Trace, message, meta));
}
- public debug(message: string, meta?: LogMeta): void {
- this.log(this.createLogRecord(LogLevel.Debug, message, meta));
+ public debug(message: string, meta?: Meta): void {
+ this.log(this.createLogRecord(LogLevel.Debug, message, meta));
}
- public info(message: string, meta?: LogMeta): void {
- this.log(this.createLogRecord(LogLevel.Info, message, meta));
+ public info(message: string, meta?: Meta): void {
+ this.log(this.createLogRecord(LogLevel.Info, message, meta));
}
- public warn(errorOrMessage: string | Error, meta?: LogMeta): void {
- this.log(this.createLogRecord(LogLevel.Warn, errorOrMessage, meta));
+ public warn(errorOrMessage: string | Error, meta?: Meta): void {
+ this.log(this.createLogRecord(LogLevel.Warn, errorOrMessage, meta));
}
- public error(errorOrMessage: string | Error, meta?: LogMeta): void {
- this.log(this.createLogRecord(LogLevel.Error, errorOrMessage, meta));
+ public error(errorOrMessage: string | Error, meta?: Meta): void {
+ this.log(this.createLogRecord(LogLevel.Error, errorOrMessage, meta));
}
- public fatal(errorOrMessage: string | Error, meta?: LogMeta): void {
- this.log(this.createLogRecord(LogLevel.Fatal, errorOrMessage, meta));
+ public fatal(errorOrMessage: string | Error, meta?: Meta): void {
+ this.log(this.createLogRecord(LogLevel.Fatal, errorOrMessage, meta));
}
public log(record: LogRecord) {
@@ -59,10 +59,10 @@ export class BaseLogger implements Logger {
return this.factory.get(...[this.context, ...childContextPaths]);
}
- private createLogRecord(
+ private createLogRecord(
level: LogLevel,
errorOrMessage: string | Error,
- meta?: LogMeta
+ meta?: Meta
): LogRecord {
if (isError(errorOrMessage)) {
return {
diff --git a/src/core/server/logging/logging_system.test.ts b/src/core/server/logging/logging_system.test.ts
index b67be384732cb..9c4313bc0c49d 100644
--- a/src/core/server/logging/logging_system.test.ts
+++ b/src/core/server/logging/logging_system.test.ts
@@ -49,6 +49,7 @@ test('uses default memory buffer logger until config is provided', () => {
// We shouldn't create new buffer appender for another context name.
const anotherLogger = system.get('test', 'context2');
+ // @ts-expect-error ECS custom meta
anotherLogger.fatal('fatal message', { some: 'value' });
expect(bufferAppendSpy).toHaveBeenCalledTimes(2);
@@ -62,6 +63,7 @@ test('flushes memory buffer logger and switches to real logger once config is pr
const logger = system.get('test', 'context');
logger.trace('buffered trace message');
+ // @ts-expect-error ECS custom meta
logger.info('buffered info message', { some: 'value' });
logger.fatal('buffered fatal message');
@@ -159,6 +161,7 @@ test('attaches appenders to appenders that declare refs', async () => {
);
const testLogger = system.get('tests');
+ // @ts-expect-error ECS custom meta
testLogger.warn('This message goes to a test context.', { a: 'hi', b: 'remove me' });
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
@@ -233,6 +236,7 @@ test('asLoggerFactory() only allows to create new loggers.', async () => {
);
logger.trace('buffered trace message');
+ // @ts-expect-error ECS custom meta
logger.info('buffered info message', { some: 'value' });
logger.fatal('buffered fatal message');
diff --git a/src/core/server/metrics/logging/get_ops_metrics_log.test.ts b/src/core/server/metrics/logging/get_ops_metrics_log.test.ts
index 014d3ae258823..e535b9babf92b 100644
--- a/src/core/server/metrics/logging/get_ops_metrics_log.test.ts
+++ b/src/core/server/metrics/logging/get_ops_metrics_log.test.ts
@@ -66,7 +66,7 @@ describe('getEcsOpsMetricsLog', () => {
it('correctly formats process uptime', () => {
const logMeta = getEcsOpsMetricsLog(createMockOpsMetrics(testMetrics));
- expect(logMeta.process!.uptime).toEqual(1);
+ expect(logMeta.meta.process!.uptime).toEqual(1);
});
it('excludes values from the message if unavailable', () => {
@@ -80,44 +80,40 @@ describe('getEcsOpsMetricsLog', () => {
expect(logMeta.message).toMatchInlineSnapshot(`""`);
});
- it('specifies correct ECS version', () => {
- const logMeta = getEcsOpsMetricsLog(createBaseOpsMetrics());
- expect(logMeta.ecs.version).toBe('1.7.0');
- });
-
it('provides an ECS-compatible response', () => {
const logMeta = getEcsOpsMetricsLog(createBaseOpsMetrics());
expect(logMeta).toMatchInlineSnapshot(`
Object {
- "ecs": Object {
- "version": "1.7.0",
- },
- "event": Object {
- "category": Array [
- "process",
- "host",
- ],
- "kind": "metric",
- "type": "info",
- },
- "host": Object {
- "os": Object {
- "load": Object {
- "15m": 1,
- "1m": 1,
- "5m": 1,
+ "message": "memory: 1.0B load: [1.00,1.00,1.00] delay: 1.000",
+ "meta": Object {
+ "event": Object {
+ "category": Array [
+ "process",
+ "host",
+ ],
+ "kind": "metric",
+ "type": Array [
+ "info",
+ ],
+ },
+ "host": Object {
+ "os": Object {
+ "load": Object {
+ "15m": 1,
+ "1m": 1,
+ "5m": 1,
+ },
},
},
- },
- "message": "memory: 1.0B load: [1.00,1.00,1.00] delay: 1.000",
- "process": Object {
- "eventLoopDelay": 1,
- "memory": Object {
- "heap": Object {
- "usedInBytes": 1,
+ "process": Object {
+ "eventLoopDelay": 1,
+ "memory": Object {
+ "heap": Object {
+ "usedInBytes": 1,
+ },
},
+ "uptime": 0,
},
- "uptime": 0,
},
}
`);
@@ -125,8 +121,8 @@ describe('getEcsOpsMetricsLog', () => {
it('logs ECS fields in the log meta', () => {
const logMeta = getEcsOpsMetricsLog(createBaseOpsMetrics());
- expect(logMeta.event!.kind).toBe('metric');
- expect(logMeta.event!.category).toEqual(expect.arrayContaining(['process', 'host']));
- expect(logMeta.event!.type).toBe('info');
+ expect(logMeta.meta.event!.kind).toBe('metric');
+ expect(logMeta.meta.event!.category).toEqual(expect.arrayContaining(['process', 'host']));
+ expect(logMeta.meta.event!.type).toEqual(expect.arrayContaining(['info']));
});
});
diff --git a/src/core/server/metrics/logging/get_ops_metrics_log.ts b/src/core/server/metrics/logging/get_ops_metrics_log.ts
index 02c3ad312c7dd..7e13f35889ec7 100644
--- a/src/core/server/metrics/logging/get_ops_metrics_log.ts
+++ b/src/core/server/metrics/logging/get_ops_metrics_log.ts
@@ -7,16 +7,15 @@
*/
import numeral from '@elastic/numeral';
-import { EcsEvent, EcsEventKind, EcsEventCategory, EcsEventType } from '../../logging';
+import { LogMeta } from '@kbn/logging';
import { OpsMetrics } from '..';
-const ECS_VERSION = '1.7.0';
/**
* Converts ops metrics into ECS-compliant `LogMeta` for logging
*
* @internal
*/
-export function getEcsOpsMetricsLog(metrics: OpsMetrics): EcsEvent {
+export function getEcsOpsMetricsLog(metrics: OpsMetrics) {
const { process, os } = metrics;
const processMemoryUsedInBytes = process?.memory?.heap?.used_in_bytes;
const processMemoryUsedInBytesMsg = processMemoryUsedInBytes
@@ -51,13 +50,11 @@ export function getEcsOpsMetricsLog(metrics: OpsMetrics): EcsEvent {
})}] `
: '';
- return {
- ecs: { version: ECS_VERSION },
- message: `${processMemoryUsedInBytesMsg}${uptimeValMsg}${loadValsMsg}${eventLoopDelayValMsg}`,
+ const meta: LogMeta = {
event: {
- kind: EcsEventKind.METRIC,
- category: [EcsEventCategory.PROCESS, EcsEventCategory.HOST],
- type: EcsEventType.INFO,
+ kind: 'metric',
+ category: ['process', 'host'],
+ type: ['info'],
},
process: {
uptime: uptimeVal,
@@ -71,8 +68,14 @@ export function getEcsOpsMetricsLog(metrics: OpsMetrics): EcsEvent {
},
host: {
os: {
+ // @ts-expect-error custom fields not yet part of ECS
load: loadEntries,
},
},
};
+
+ return {
+ message: `${processMemoryUsedInBytesMsg}${uptimeValMsg}${loadValsMsg}${eventLoopDelayValMsg}`,
+ meta,
+ };
}
diff --git a/src/core/server/metrics/metrics_service.test.ts b/src/core/server/metrics/metrics_service.test.ts
index 4fbca5addda11..d7de41fd7ccf7 100644
--- a/src/core/server/metrics/metrics_service.test.ts
+++ b/src/core/server/metrics/metrics_service.test.ts
@@ -182,16 +182,15 @@ describe('MetricsService', () => {
Array [
"",
Object {
- "ecs": Object {
- "version": "1.7.0",
- },
"event": Object {
"category": Array [
"process",
"host",
],
"kind": "metric",
- "type": "info",
+ "type": Array [
+ "info",
+ ],
},
"host": Object {
"os": Object {
diff --git a/src/core/server/metrics/metrics_service.ts b/src/core/server/metrics/metrics_service.ts
index 382848e0a80c3..78e4dd98f93d6 100644
--- a/src/core/server/metrics/metrics_service.ts
+++ b/src/core/server/metrics/metrics_service.ts
@@ -73,7 +73,7 @@ export class MetricsService
private async refreshMetrics() {
const metrics = await this.metricsCollector!.collect();
- const { message, ...meta } = getEcsOpsMetricsLog(metrics);
+ const { message, meta } = getEcsOpsMetricsLog(metrics);
this.opsMetricsLogger.debug(message!, meta);
this.metricsCollector!.reset();
this.metrics$.next(metrics);
diff --git a/src/core/server/saved_objects/migrations/core/migration_logger.ts b/src/core/server/saved_objects/migrations/core/migration_logger.ts
index e8cb6352195de..6c935b915ce68 100644
--- a/src/core/server/saved_objects/migrations/core/migration_logger.ts
+++ b/src/core/server/saved_objects/migrations/core/migration_logger.ts
@@ -24,7 +24,7 @@ export interface SavedObjectsMigrationLogger {
*/
warning: (msg: string) => void;
warn: (msg: string) => void;
- error: (msg: string, meta: LogMeta) => void;
+ error: (msg: string, meta: Meta) => void;
}
export class MigrationLogger implements SavedObjectsMigrationLogger {
diff --git a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts
index fa2e65f16bb2d..a6617fc2fb7f4 100644
--- a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts
+++ b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts
@@ -211,86 +211,90 @@ describe('migrationsStateActionMachine', () => {
Array [
"[.my-so-index] INIT -> LEGACY_DELETE",
Object {
- "batchSize": 1000,
- "controlState": "LEGACY_DELETE",
- "currentAlias": ".my-so-index",
- "indexPrefix": ".my-so-index",
- "kibanaVersion": "7.11.0",
- "legacyIndex": ".my-so-index",
- "logs": Array [
- Object {
- "level": "info",
- "message": "Log from LEGACY_DELETE control state",
- },
- ],
- "outdatedDocuments": Array [
- "1234",
- ],
- "outdatedDocumentsQuery": Object {
- "bool": Object {
- "should": Array [],
- },
- },
- "preMigrationScript": Object {
- "_tag": "None",
- },
- "reason": "the fatal reason",
- "retryAttempts": 5,
- "retryCount": 0,
- "retryDelay": 0,
- "targetIndexMappings": Object {
- "properties": Object {},
- },
- "tempIndex": ".my-so-index_7.11.0_reindex_temp",
- "tempIndexMappings": Object {
- "dynamic": false,
- "properties": Object {
- "migrationVersion": Object {
- "dynamic": "true",
- "type": "object",
+ "kibana": Object {
+ "migrationState": Object {
+ "batchSize": 1000,
+ "controlState": "LEGACY_DELETE",
+ "currentAlias": ".my-so-index",
+ "indexPrefix": ".my-so-index",
+ "kibanaVersion": "7.11.0",
+ "legacyIndex": ".my-so-index",
+ "logs": Array [
+ Object {
+ "level": "info",
+ "message": "Log from LEGACY_DELETE control state",
+ },
+ ],
+ "outdatedDocuments": Array [
+ "1234",
+ ],
+ "outdatedDocumentsQuery": Object {
+ "bool": Object {
+ "should": Array [],
+ },
},
- "type": Object {
- "type": "keyword",
+ "preMigrationScript": Object {
+ "_tag": "None",
},
- },
- },
- "unusedTypesQuery": Object {
- "_tag": "Some",
- "value": Object {
- "bool": Object {
- "must_not": Array [
- Object {
- "term": Object {
- "type": "fleet-agent-events",
- },
+ "reason": "the fatal reason",
+ "retryAttempts": 5,
+ "retryCount": 0,
+ "retryDelay": 0,
+ "targetIndexMappings": Object {
+ "properties": Object {},
+ },
+ "tempIndex": ".my-so-index_7.11.0_reindex_temp",
+ "tempIndexMappings": Object {
+ "dynamic": false,
+ "properties": Object {
+ "migrationVersion": Object {
+ "dynamic": "true",
+ "type": "object",
},
- Object {
- "term": Object {
- "type": "tsvb-validation-telemetry",
- },
+ "type": Object {
+ "type": "keyword",
},
- Object {
- "bool": Object {
- "must": Array [
- Object {
- "match": Object {
- "type": "search-session",
- },
+ },
+ },
+ "unusedTypesQuery": Object {
+ "_tag": "Some",
+ "value": Object {
+ "bool": Object {
+ "must_not": Array [
+ Object {
+ "term": Object {
+ "type": "fleet-agent-events",
},
- Object {
- "match": Object {
- "search-session.persisted": false,
- },
+ },
+ Object {
+ "term": Object {
+ "type": "tsvb-validation-telemetry",
},
- ],
- },
+ },
+ Object {
+ "bool": Object {
+ "must": Array [
+ Object {
+ "match": Object {
+ "type": "search-session",
+ },
+ },
+ Object {
+ "match": Object {
+ "search-session.persisted": false,
+ },
+ },
+ ],
+ },
+ },
+ ],
},
- ],
+ },
},
+ "versionAlias": ".my-so-index_7.11.0",
+ "versionIndex": ".my-so-index_7.11.0_001",
},
},
- "versionAlias": ".my-so-index_7.11.0",
- "versionIndex": ".my-so-index_7.11.0_001",
},
],
Array [
@@ -303,90 +307,94 @@ describe('migrationsStateActionMachine', () => {
Array [
"[.my-so-index] LEGACY_DELETE -> FATAL",
Object {
- "batchSize": 1000,
- "controlState": "FATAL",
- "currentAlias": ".my-so-index",
- "indexPrefix": ".my-so-index",
- "kibanaVersion": "7.11.0",
- "legacyIndex": ".my-so-index",
- "logs": Array [
- Object {
- "level": "info",
- "message": "Log from LEGACY_DELETE control state",
- },
- Object {
- "level": "info",
- "message": "Log from FATAL control state",
- },
- ],
- "outdatedDocuments": Array [
- "1234",
- ],
- "outdatedDocumentsQuery": Object {
- "bool": Object {
- "should": Array [],
- },
- },
- "preMigrationScript": Object {
- "_tag": "None",
- },
- "reason": "the fatal reason",
- "retryAttempts": 5,
- "retryCount": 0,
- "retryDelay": 0,
- "targetIndexMappings": Object {
- "properties": Object {},
- },
- "tempIndex": ".my-so-index_7.11.0_reindex_temp",
- "tempIndexMappings": Object {
- "dynamic": false,
- "properties": Object {
- "migrationVersion": Object {
- "dynamic": "true",
- "type": "object",
+ "kibana": Object {
+ "migrationState": Object {
+ "batchSize": 1000,
+ "controlState": "FATAL",
+ "currentAlias": ".my-so-index",
+ "indexPrefix": ".my-so-index",
+ "kibanaVersion": "7.11.0",
+ "legacyIndex": ".my-so-index",
+ "logs": Array [
+ Object {
+ "level": "info",
+ "message": "Log from LEGACY_DELETE control state",
+ },
+ Object {
+ "level": "info",
+ "message": "Log from FATAL control state",
+ },
+ ],
+ "outdatedDocuments": Array [
+ "1234",
+ ],
+ "outdatedDocumentsQuery": Object {
+ "bool": Object {
+ "should": Array [],
+ },
},
- "type": Object {
- "type": "keyword",
+ "preMigrationScript": Object {
+ "_tag": "None",
},
- },
- },
- "unusedTypesQuery": Object {
- "_tag": "Some",
- "value": Object {
- "bool": Object {
- "must_not": Array [
- Object {
- "term": Object {
- "type": "fleet-agent-events",
- },
+ "reason": "the fatal reason",
+ "retryAttempts": 5,
+ "retryCount": 0,
+ "retryDelay": 0,
+ "targetIndexMappings": Object {
+ "properties": Object {},
+ },
+ "tempIndex": ".my-so-index_7.11.0_reindex_temp",
+ "tempIndexMappings": Object {
+ "dynamic": false,
+ "properties": Object {
+ "migrationVersion": Object {
+ "dynamic": "true",
+ "type": "object",
},
- Object {
- "term": Object {
- "type": "tsvb-validation-telemetry",
- },
+ "type": Object {
+ "type": "keyword",
},
- Object {
- "bool": Object {
- "must": Array [
- Object {
- "match": Object {
- "type": "search-session",
- },
+ },
+ },
+ "unusedTypesQuery": Object {
+ "_tag": "Some",
+ "value": Object {
+ "bool": Object {
+ "must_not": Array [
+ Object {
+ "term": Object {
+ "type": "fleet-agent-events",
+ },
+ },
+ Object {
+ "term": Object {
+ "type": "tsvb-validation-telemetry",
},
- Object {
- "match": Object {
- "search-session.persisted": false,
- },
+ },
+ Object {
+ "bool": Object {
+ "must": Array [
+ Object {
+ "match": Object {
+ "type": "search-session",
+ },
+ },
+ Object {
+ "match": Object {
+ "search-session.persisted": false,
+ },
+ },
+ ],
},
- ],
- },
+ },
+ ],
},
- ],
+ },
},
+ "versionAlias": ".my-so-index_7.11.0",
+ "versionIndex": ".my-so-index_7.11.0_001",
},
},
- "versionAlias": ".my-so-index_7.11.0",
- "versionIndex": ".my-so-index_7.11.0_001",
},
],
]
@@ -490,84 +498,88 @@ describe('migrationsStateActionMachine', () => {
Array [
"[.my-so-index] INIT -> LEGACY_REINDEX",
Object {
- "batchSize": 1000,
- "controlState": "LEGACY_REINDEX",
- "currentAlias": ".my-so-index",
- "indexPrefix": ".my-so-index",
- "kibanaVersion": "7.11.0",
- "legacyIndex": ".my-so-index",
- "logs": Array [
- Object {
- "level": "info",
- "message": "Log from LEGACY_REINDEX control state",
- },
- ],
- "outdatedDocuments": Array [],
- "outdatedDocumentsQuery": Object {
- "bool": Object {
- "should": Array [],
- },
- },
- "preMigrationScript": Object {
- "_tag": "None",
- },
- "reason": "the fatal reason",
- "retryAttempts": 5,
- "retryCount": 0,
- "retryDelay": 0,
- "targetIndexMappings": Object {
- "properties": Object {},
- },
- "tempIndex": ".my-so-index_7.11.0_reindex_temp",
- "tempIndexMappings": Object {
- "dynamic": false,
- "properties": Object {
- "migrationVersion": Object {
- "dynamic": "true",
- "type": "object",
+ "kibana": Object {
+ "migrationState": Object {
+ "batchSize": 1000,
+ "controlState": "LEGACY_REINDEX",
+ "currentAlias": ".my-so-index",
+ "indexPrefix": ".my-so-index",
+ "kibanaVersion": "7.11.0",
+ "legacyIndex": ".my-so-index",
+ "logs": Array [
+ Object {
+ "level": "info",
+ "message": "Log from LEGACY_REINDEX control state",
+ },
+ ],
+ "outdatedDocuments": Array [],
+ "outdatedDocumentsQuery": Object {
+ "bool": Object {
+ "should": Array [],
+ },
},
- "type": Object {
- "type": "keyword",
+ "preMigrationScript": Object {
+ "_tag": "None",
},
- },
- },
- "unusedTypesQuery": Object {
- "_tag": "Some",
- "value": Object {
- "bool": Object {
- "must_not": Array [
- Object {
- "term": Object {
- "type": "fleet-agent-events",
- },
+ "reason": "the fatal reason",
+ "retryAttempts": 5,
+ "retryCount": 0,
+ "retryDelay": 0,
+ "targetIndexMappings": Object {
+ "properties": Object {},
+ },
+ "tempIndex": ".my-so-index_7.11.0_reindex_temp",
+ "tempIndexMappings": Object {
+ "dynamic": false,
+ "properties": Object {
+ "migrationVersion": Object {
+ "dynamic": "true",
+ "type": "object",
},
- Object {
- "term": Object {
- "type": "tsvb-validation-telemetry",
- },
+ "type": Object {
+ "type": "keyword",
},
- Object {
- "bool": Object {
- "must": Array [
- Object {
- "match": Object {
- "type": "search-session",
- },
+ },
+ },
+ "unusedTypesQuery": Object {
+ "_tag": "Some",
+ "value": Object {
+ "bool": Object {
+ "must_not": Array [
+ Object {
+ "term": Object {
+ "type": "fleet-agent-events",
},
- Object {
- "match": Object {
- "search-session.persisted": false,
- },
+ },
+ Object {
+ "term": Object {
+ "type": "tsvb-validation-telemetry",
},
- ],
- },
+ },
+ Object {
+ "bool": Object {
+ "must": Array [
+ Object {
+ "match": Object {
+ "type": "search-session",
+ },
+ },
+ Object {
+ "match": Object {
+ "search-session.persisted": false,
+ },
+ },
+ ],
+ },
+ },
+ ],
},
- ],
+ },
},
+ "versionAlias": ".my-so-index_7.11.0",
+ "versionIndex": ".my-so-index_7.11.0_001",
},
},
- "versionAlias": ".my-so-index_7.11.0",
- "versionIndex": ".my-so-index_7.11.0_001",
},
],
Array [
@@ -577,88 +589,92 @@ describe('migrationsStateActionMachine', () => {
Array [
"[.my-so-index] LEGACY_REINDEX -> LEGACY_DELETE",
Object {
- "batchSize": 1000,
- "controlState": "LEGACY_DELETE",
- "currentAlias": ".my-so-index",
- "indexPrefix": ".my-so-index",
- "kibanaVersion": "7.11.0",
- "legacyIndex": ".my-so-index",
- "logs": Array [
- Object {
- "level": "info",
- "message": "Log from LEGACY_REINDEX control state",
- },
- Object {
- "level": "info",
- "message": "Log from LEGACY_DELETE control state",
- },
- ],
- "outdatedDocuments": Array [],
- "outdatedDocumentsQuery": Object {
- "bool": Object {
- "should": Array [],
- },
- },
- "preMigrationScript": Object {
- "_tag": "None",
- },
- "reason": "the fatal reason",
- "retryAttempts": 5,
- "retryCount": 0,
- "retryDelay": 0,
- "targetIndexMappings": Object {
- "properties": Object {},
- },
- "tempIndex": ".my-so-index_7.11.0_reindex_temp",
- "tempIndexMappings": Object {
- "dynamic": false,
- "properties": Object {
- "migrationVersion": Object {
- "dynamic": "true",
- "type": "object",
+ "kibana": Object {
+ "migrationState": Object {
+ "batchSize": 1000,
+ "controlState": "LEGACY_DELETE",
+ "currentAlias": ".my-so-index",
+ "indexPrefix": ".my-so-index",
+ "kibanaVersion": "7.11.0",
+ "legacyIndex": ".my-so-index",
+ "logs": Array [
+ Object {
+ "level": "info",
+ "message": "Log from LEGACY_REINDEX control state",
+ },
+ Object {
+ "level": "info",
+ "message": "Log from LEGACY_DELETE control state",
+ },
+ ],
+ "outdatedDocuments": Array [],
+ "outdatedDocumentsQuery": Object {
+ "bool": Object {
+ "should": Array [],
+ },
},
- "type": Object {
- "type": "keyword",
+ "preMigrationScript": Object {
+ "_tag": "None",
},
- },
- },
- "unusedTypesQuery": Object {
- "_tag": "Some",
- "value": Object {
- "bool": Object {
- "must_not": Array [
- Object {
- "term": Object {
- "type": "fleet-agent-events",
- },
+ "reason": "the fatal reason",
+ "retryAttempts": 5,
+ "retryCount": 0,
+ "retryDelay": 0,
+ "targetIndexMappings": Object {
+ "properties": Object {},
+ },
+ "tempIndex": ".my-so-index_7.11.0_reindex_temp",
+ "tempIndexMappings": Object {
+ "dynamic": false,
+ "properties": Object {
+ "migrationVersion": Object {
+ "dynamic": "true",
+ "type": "object",
},
- Object {
- "term": Object {
- "type": "tsvb-validation-telemetry",
- },
+ "type": Object {
+ "type": "keyword",
},
- Object {
- "bool": Object {
- "must": Array [
- Object {
- "match": Object {
- "type": "search-session",
- },
+ },
+ },
+ "unusedTypesQuery": Object {
+ "_tag": "Some",
+ "value": Object {
+ "bool": Object {
+ "must_not": Array [
+ Object {
+ "term": Object {
+ "type": "fleet-agent-events",
+ },
+ },
+ Object {
+ "term": Object {
+ "type": "tsvb-validation-telemetry",
},
- Object {
- "match": Object {
- "search-session.persisted": false,
- },
+ },
+ Object {
+ "bool": Object {
+ "must": Array [
+ Object {
+ "match": Object {
+ "type": "search-session",
+ },
+ },
+ Object {
+ "match": Object {
+ "search-session.persisted": false,
+ },
+ },
+ ],
},
- ],
- },
+ },
+ ],
},
- ],
+ },
},
+ "versionAlias": ".my-so-index_7.11.0",
+ "versionIndex": ".my-so-index_7.11.0_001",
},
},
- "versionAlias": ".my-so-index_7.11.0",
- "versionIndex": ".my-so-index_7.11.0_001",
},
],
]
diff --git a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts
index e35e21421ac1f..20177dda63b3b 100644
--- a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts
+++ b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts
@@ -13,6 +13,12 @@ import { CorruptSavedObjectError } from '../migrations/core/migrate_raw_docs';
import { Model, Next, stateActionMachine } from './state_action_machine';
import { State } from './types';
+interface StateLogMeta extends LogMeta {
+ kibana: {
+ migrationState: State;
+ };
+}
+
type ExecutionLog = Array<
| {
type: 'transition';
@@ -35,9 +41,15 @@ const logStateTransition = (
tookMs: number
) => {
if (newState.logs.length > oldState.logs.length) {
- newState.logs
- .slice(oldState.logs.length)
- .forEach((log) => logger[log.level](logMessagePrefix + log.message));
+ newState.logs.slice(oldState.logs.length).forEach((log) => {
+ const getLogger = (level: keyof Logger) => {
+ if (level === 'error') {
+ return logger[level] as Logger['error'];
+ }
+ return logger[level] as Logger['info'];
+ };
+ getLogger(log.level)(logMessagePrefix + log.message);
+ });
}
logger.info(
@@ -58,7 +70,14 @@ const dumpExecutionLog = (logger: Logger, logMessagePrefix: string, executionLog
logger.error(logMessagePrefix + 'migration failed, dumping execution log:');
executionLog.forEach((log) => {
if (log.type === 'transition') {
- logger.info(logMessagePrefix + `${log.prevControlState} -> ${log.controlState}`, log.state);
+ logger.info(
+ logMessagePrefix + `${log.prevControlState} -> ${log.controlState}`,
+ {
+ kibana: {
+ migrationState: log.state,
+ },
+ }
+ );
}
if (log.type === 'response') {
logger.info(logMessagePrefix + `${log.controlState} RESPONSE`, log.res as LogMeta);
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index e5804b3c9fc58..ec447f636f9c3 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -49,6 +49,11 @@ import { DeleteTemplateParams } from 'elasticsearch';
import { DetailedPeerCertificate } from 'tls';
import { Duration } from 'moment';
import { Duration as Duration_2 } from 'moment-timezone';
+import { Ecs } from '@kbn/logging';
+import { EcsEventCategory } from '@kbn/logging';
+import { EcsEventKind } from '@kbn/logging';
+import { EcsEventOutcome } from '@kbn/logging';
+import { EcsEventType } from '@kbn/logging';
import { EnvironmentMode } from '@kbn/config';
import { estypes } from '@elastic/elasticsearch';
import { ExistsParams } from 'elasticsearch';
@@ -886,6 +891,16 @@ export interface DiscoveredPlugin {
readonly requiredPlugins: readonly PluginName[];
}
+export { Ecs }
+
+export { EcsEventCategory }
+
+export { EcsEventKind }
+
+export { EcsEventOutcome }
+
+export { EcsEventType }
+
// @public
export type ElasticsearchClient = Omit & {
transport: {
@@ -2779,7 +2794,7 @@ export interface SavedObjectsMigrationLogger {
// (undocumented)
debug: (msg: string) => void;
// (undocumented)
- error: (msg: string, meta: LogMeta) => void;
+ error: (msg: string, meta: Meta) => void;
// (undocumented)
info: (msg: string) => void;
// (undocumented)
diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts
index 09cf5b92b2b8a..7724e7a5e44b4 100644
--- a/src/core/server/status/status_service.ts
+++ b/src/core/server/status/status_service.ts
@@ -12,7 +12,7 @@ import { isDeepStrictEqual } from 'util';
import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
-import { Logger } from '../logging';
+import { Logger, LogMeta } from '../logging';
import { InternalElasticsearchServiceSetup } from '../elasticsearch';
import { InternalHttpServiceSetup } from '../http';
import { InternalSavedObjectsServiceSetup } from '../saved_objects';
@@ -26,6 +26,10 @@ import { ServiceStatus, CoreStatus, InternalStatusServiceSetup } from './types';
import { getSummaryStatus } from './get_summary_status';
import { PluginsStatusService } from './plugins_status';
+interface StatusLogMeta extends LogMeta {
+ kibana: { status: ServiceStatus };
+}
+
interface SetupDeps {
elasticsearch: Pick;
environment: InternalEnvironmentServiceSetup;
@@ -70,7 +74,11 @@ export class StatusService implements CoreService {
...Object.entries(coreStatus),
...Object.entries(pluginsStatus),
]);
- this.logger.debug(`Recalculated overall status`, { status: summary });
+ this.logger.debug(`Recalculated overall status`, {
+ kibana: {
+ status: summary,
+ },
+ });
return summary;
}),
distinctUntilChanged(isDeepStrictEqual),
diff --git a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts
index b169c715b9b95..669849dcd8d9b 100644
--- a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts
+++ b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts
@@ -131,8 +131,12 @@ describe('uiSettings/createOrUpgradeSavedConfig', function () {
Array [
"Upgrade config from 4.0.0 to 4.0.1",
Object {
- "newVersion": "4.0.1",
- "prevVersion": "4.0.0",
+ "kibana": Object {
+ "config": Object {
+ "newVersion": "4.0.1",
+ "prevVersion": "4.0.0",
+ },
+ },
},
],
]
diff --git a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts
index a32556d1aef6f..d015f506df6e3 100644
--- a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts
+++ b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts
@@ -10,10 +10,16 @@ import { defaults } from 'lodash';
import { SavedObjectsClientContract } from '../../saved_objects/types';
import { SavedObjectsErrorHelpers } from '../../saved_objects/';
-import { Logger } from '../../logging';
+import { Logger, LogMeta } from '../../logging';
import { getUpgradeableConfig } from './get_upgradeable_config';
+interface ConfigLogMeta extends LogMeta {
+ kibana: {
+ config: { prevVersion: string; newVersion: string };
+ };
+}
+
interface Options {
savedObjectsClient: SavedObjectsClientContract;
version: string;
@@ -60,9 +66,13 @@ export async function createOrUpgradeSavedConfig(
}
if (upgradeableConfig) {
- log.debug(`Upgrade config from ${upgradeableConfig.id} to ${version}`, {
- prevVersion: upgradeableConfig.id,
- newVersion: version,
+ log.debug(`Upgrade config from ${upgradeableConfig.id} to ${version}`, {
+ kibana: {
+ config: {
+ prevVersion: upgradeableConfig.id,
+ newVersion: version,
+ },
+ },
});
}
}
diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.test.ts b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.test.ts
index c800bce6390c9..8a76368c8cd9d 100644
--- a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.test.ts
+++ b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.test.ts
@@ -187,10 +187,13 @@ describe('UsageCountersService', () => {
await tick();
// number of incrementCounter calls + number of retries
expect(mockIncrementCounter).toBeCalledTimes(2 + 1);
- expect(logger.debug).toHaveBeenNthCalledWith(1, 'Store counters into savedObjects', [
- mockError,
- 'pass',
- ]);
+ expect(logger.debug).toHaveBeenNthCalledWith(1, 'Store counters into savedObjects', {
+ kibana: {
+ usageCounters: {
+ results: [mockError, 'pass'],
+ },
+ },
+ });
});
it('buffers counters within `bufferDurationMs` time', async () => {
diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts
index 88ca9f6358926..a698ea3db5bad 100644
--- a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts
+++ b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts
@@ -13,7 +13,7 @@ import {
SavedObjectsServiceSetup,
SavedObjectsServiceStart,
} from 'src/core/server';
-import type { Logger } from 'src/core/server';
+import type { Logger, LogMeta } from 'src/core/server';
import moment from 'moment';
import { CounterMetric, UsageCounter } from './usage_counter';
@@ -23,6 +23,10 @@ import {
serializeCounterKey,
} from './saved_objects';
+interface UsageCountersLogMeta extends LogMeta {
+ kibana: { usageCounters: { results: unknown[] } };
+}
+
export interface UsageCountersServiceDeps {
logger: Logger;
retryCount: number;
@@ -116,7 +120,11 @@ export class UsageCountersService {
rxOp.concatMap((counters) => this.storeDate$(counters, internalRepository))
)
.subscribe((results) => {
- this.logger.debug('Store counters into savedObjects', results);
+ this.logger.debug('Store counters into savedObjects', {
+ kibana: {
+ usageCounters: { results },
+ },
+ });
});
this.flushCache$.next();
diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts
index d8dcde2fab103..9f87de5f686cc 100644
--- a/x-pack/plugins/actions/server/actions_client.ts
+++ b/x-pack/plugins/actions/server/actions_client.ts
@@ -18,7 +18,7 @@ import {
KibanaRequest,
SavedObjectsUtils,
} from '../../../../src/core/server';
-import { AuditLogger, EventOutcome } from '../../security/server';
+import { AuditLogger } from '../../security/server';
import { ActionType } from '../common';
import { ActionTypeRegistry } from './action_type_registry';
import { validateConfig, validateSecrets, ActionExecutorContract } from './lib';
@@ -146,7 +146,7 @@ export class ActionsClient {
connectorAuditEvent({
action: ConnectorAuditAction.CREATE,
savedObject: { type: 'action', id },
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
})
);
@@ -218,7 +218,7 @@ export class ActionsClient {
connectorAuditEvent({
action: ConnectorAuditAction.UPDATE,
savedObject: { type: 'action', id },
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
})
);
@@ -452,7 +452,7 @@ export class ActionsClient {
this.auditLogger?.log(
connectorAuditEvent({
action: ConnectorAuditAction.DELETE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'action', id },
})
);
diff --git a/x-pack/plugins/actions/server/builtin_action_types/server_log.ts b/x-pack/plugins/actions/server/builtin_action_types/server_log.ts
index ac9c4211f07cc..6c54c1b9f2ff1 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/server_log.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/server_log.ts
@@ -9,7 +9,7 @@ import { curry } from 'lodash';
import { i18n } from '@kbn/i18n';
import { schema, TypeOf } from '@kbn/config-schema';
-import { Logger } from '../../../../../src/core/server';
+import { Logger, LogMeta } from '../../../../../src/core/server';
import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../types';
import { withoutControlCharacters } from './lib/string_utils';
@@ -66,7 +66,7 @@ async function executor(
const sanitizedMessage = withoutControlCharacters(params.message);
try {
- logger[params.level](`Server log: ${sanitizedMessage}`);
+ (logger[params.level] as Logger['info'])(`Server log: ${sanitizedMessage}`);
} catch (err) {
const message = i18n.translate('xpack.actions.builtin.serverLog.errorLoggingErrorMessage', {
defaultMessage: 'error logging message',
diff --git a/x-pack/plugins/actions/server/lib/audit_events.test.ts b/x-pack/plugins/actions/server/lib/audit_events.test.ts
index 6047a97b63c54..b30ccc1fb372b 100644
--- a/x-pack/plugins/actions/server/lib/audit_events.test.ts
+++ b/x-pack/plugins/actions/server/lib/audit_events.test.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { EventOutcome } from '../../../security/server/audit';
import { ConnectorAuditAction, connectorAuditEvent } from './audit_events';
describe('#connectorAuditEvent', () => {
@@ -13,7 +12,7 @@ describe('#connectorAuditEvent', () => {
expect(
connectorAuditEvent({
action: ConnectorAuditAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'action', id: 'ACTION_ID' },
})
).toMatchInlineSnapshot(`
@@ -21,9 +20,13 @@ describe('#connectorAuditEvent', () => {
"error": undefined,
"event": Object {
"action": "connector_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "unknown",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
@@ -47,9 +50,13 @@ describe('#connectorAuditEvent', () => {
"error": undefined,
"event": Object {
"action": "connector_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "success",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
@@ -77,9 +84,13 @@ describe('#connectorAuditEvent', () => {
},
"event": Object {
"action": "connector_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "failure",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
diff --git a/x-pack/plugins/actions/server/lib/audit_events.ts b/x-pack/plugins/actions/server/lib/audit_events.ts
index f80fa00e11641..5231c9bab7c37 100644
--- a/x-pack/plugins/actions/server/lib/audit_events.ts
+++ b/x-pack/plugins/actions/server/lib/audit_events.ts
@@ -5,7 +5,8 @@
* 2.0.
*/
-import { AuditEvent, EventOutcome, EventCategory, EventType } from '../../../security/server';
+import type { EcsEventOutcome, EcsEventType } from 'src/core/server';
+import { AuditEvent } from '../../../security/server';
export enum ConnectorAuditAction {
CREATE = 'connector_create',
@@ -27,18 +28,18 @@ const eventVerbs: Record = {
connector_execute: ['execute', 'executing', 'executed'],
};
-const eventTypes: Record = {
- connector_create: EventType.CREATION,
- connector_get: EventType.ACCESS,
- connector_update: EventType.CHANGE,
- connector_delete: EventType.DELETION,
- connector_find: EventType.ACCESS,
+const eventTypes: Record = {
+ connector_create: 'creation',
+ connector_get: 'access',
+ connector_update: 'change',
+ connector_delete: 'deletion',
+ connector_find: 'access',
connector_execute: undefined,
};
export interface ConnectorAuditEventParams {
action: ConnectorAuditAction;
- outcome?: EventOutcome;
+ outcome?: EcsEventOutcome;
savedObject?: NonNullable['saved_object'];
error?: Error;
}
@@ -53,7 +54,7 @@ export function connectorAuditEvent({
const [present, progressive, past] = eventVerbs[action];
const message = error
? `Failed attempt to ${present} ${doc}`
- : outcome === EventOutcome.UNKNOWN
+ : outcome === 'unknown'
? `User is ${progressive} ${doc}`
: `User has ${past} ${doc}`;
const type = eventTypes[action];
@@ -62,9 +63,9 @@ export function connectorAuditEvent({
message,
event: {
action,
- category: EventCategory.DATABASE,
- type,
- outcome: outcome ?? (error ? EventOutcome.FAILURE : EventOutcome.SUCCESS),
+ category: ['database'],
+ type: type ? [type] : undefined,
+ outcome: outcome ?? (error ? 'failure' : 'success'),
},
kibana: {
saved_object: savedObject,
diff --git a/x-pack/plugins/actions/server/saved_objects/migrations.ts b/x-pack/plugins/actions/server/saved_objects/migrations.ts
index 9b8b887fbec28..9bd54330f5d05 100644
--- a/x-pack/plugins/actions/server/saved_objects/migrations.ts
+++ b/x-pack/plugins/actions/server/saved_objects/migrations.ts
@@ -6,6 +6,7 @@
*/
import {
+ LogMeta,
SavedObjectMigrationMap,
SavedObjectUnsanitizedDoc,
SavedObjectMigrationFn,
@@ -14,6 +15,10 @@ import {
import { RawAction } from '../types';
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
+interface ActionsLogMeta extends LogMeta {
+ migrations: { actionDocument: SavedObjectUnsanitizedDoc };
+}
+
type ActionMigration = (
doc: SavedObjectUnsanitizedDoc
) => SavedObjectUnsanitizedDoc;
@@ -50,9 +55,13 @@ function executeMigrationWithErrorHandling(
try {
return migrationFunc(doc, context);
} catch (ex) {
- context.log.error(
+ context.log.error(
`encryptedSavedObject ${version} migration failed for action ${doc.id} with error: ${ex.message}`,
- { actionDocument: doc }
+ {
+ migrations: {
+ actionDocument: doc,
+ },
+ }
);
}
return doc;
diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts
index e316ecd3c6fec..210bdf954ada4 100644
--- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts
+++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts
@@ -51,7 +51,7 @@ import { IEventLogClient } from '../../../../plugins/event_log/server';
import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date';
import { alertInstanceSummaryFromEventLog } from '../lib/alert_instance_summary_from_event_log';
import { IEvent } from '../../../event_log/server';
-import { AuditLogger, EventOutcome } from '../../../security/server';
+import { AuditLogger } from '../../../security/server';
import { parseDuration } from '../../common/parse_duration';
import { retryIfConflicts } from '../lib/retry_if_conflicts';
import { partiallyUpdateAlert } from '../saved_objects';
@@ -293,7 +293,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -598,7 +598,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.DELETE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -671,7 +671,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.UPDATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -850,7 +850,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.UPDATE_API_KEY,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -935,7 +935,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.ENABLE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -1036,7 +1036,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.DISABLE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -1112,7 +1112,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.MUTE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -1173,7 +1173,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.UNMUTE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id },
})
);
@@ -1234,7 +1234,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.MUTE_INSTANCE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id: alertId },
})
);
@@ -1300,7 +1300,7 @@ export class AlertsClient {
this.auditLogger?.log(
alertAuditEvent({
action: AlertAuditAction.UNMUTE_INSTANCE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id: alertId },
})
);
diff --git a/x-pack/plugins/alerting/server/alerts_client/audit_events.test.ts b/x-pack/plugins/alerting/server/alerts_client/audit_events.test.ts
index fd79e9fac4fd1..4ccb69832cd26 100644
--- a/x-pack/plugins/alerting/server/alerts_client/audit_events.test.ts
+++ b/x-pack/plugins/alerting/server/alerts_client/audit_events.test.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { EventOutcome } from '../../../security/server/audit';
import { AlertAuditAction, alertAuditEvent } from './audit_events';
describe('#alertAuditEvent', () => {
@@ -13,7 +12,7 @@ describe('#alertAuditEvent', () => {
expect(
alertAuditEvent({
action: AlertAuditAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'alert', id: 'ALERT_ID' },
})
).toMatchInlineSnapshot(`
@@ -21,9 +20,13 @@ describe('#alertAuditEvent', () => {
"error": undefined,
"event": Object {
"action": "alert_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "unknown",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
@@ -47,9 +50,13 @@ describe('#alertAuditEvent', () => {
"error": undefined,
"event": Object {
"action": "alert_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "success",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
@@ -77,9 +84,13 @@ describe('#alertAuditEvent', () => {
},
"event": Object {
"action": "alert_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "failure",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
diff --git a/x-pack/plugins/alerting/server/alerts_client/audit_events.ts b/x-pack/plugins/alerting/server/alerts_client/audit_events.ts
index 354f58bafd888..93cca255d6ebc 100644
--- a/x-pack/plugins/alerting/server/alerts_client/audit_events.ts
+++ b/x-pack/plugins/alerting/server/alerts_client/audit_events.ts
@@ -5,7 +5,8 @@
* 2.0.
*/
-import { AuditEvent, EventOutcome, EventCategory, EventType } from '../../../security/server';
+import { EcsEventOutcome, EcsEventType } from 'src/core/server';
+import { AuditEvent } from '../../../security/server';
export enum AlertAuditAction {
CREATE = 'alert_create',
@@ -39,24 +40,24 @@ const eventVerbs: Record = {
alert_instance_unmute: ['unmute instance of', 'unmuting instance of', 'unmuted instance of'],
};
-const eventTypes: Record = {
- alert_create: EventType.CREATION,
- alert_get: EventType.ACCESS,
- alert_update: EventType.CHANGE,
- alert_update_api_key: EventType.CHANGE,
- alert_enable: EventType.CHANGE,
- alert_disable: EventType.CHANGE,
- alert_delete: EventType.DELETION,
- alert_find: EventType.ACCESS,
- alert_mute: EventType.CHANGE,
- alert_unmute: EventType.CHANGE,
- alert_instance_mute: EventType.CHANGE,
- alert_instance_unmute: EventType.CHANGE,
+const eventTypes: Record = {
+ alert_create: 'creation',
+ alert_get: 'access',
+ alert_update: 'change',
+ alert_update_api_key: 'change',
+ alert_enable: 'change',
+ alert_disable: 'change',
+ alert_delete: 'deletion',
+ alert_find: 'access',
+ alert_mute: 'change',
+ alert_unmute: 'change',
+ alert_instance_mute: 'change',
+ alert_instance_unmute: 'change',
};
export interface AlertAuditEventParams {
action: AlertAuditAction;
- outcome?: EventOutcome;
+ outcome?: EcsEventOutcome;
savedObject?: NonNullable['saved_object'];
error?: Error;
}
@@ -71,7 +72,7 @@ export function alertAuditEvent({
const [present, progressive, past] = eventVerbs[action];
const message = error
? `Failed attempt to ${present} ${doc}`
- : outcome === EventOutcome.UNKNOWN
+ : outcome === 'unknown'
? `User is ${progressive} ${doc}`
: `User has ${past} ${doc}`;
const type = eventTypes[action];
@@ -80,9 +81,9 @@ export function alertAuditEvent({
message,
event: {
action,
- category: EventCategory.DATABASE,
- type,
- outcome: outcome ?? (error ? EventOutcome.FAILURE : EventOutcome.SUCCESS),
+ category: ['database'],
+ type: type ? [type] : undefined,
+ outcome: outcome ?? (error ? 'failure' : 'success'),
},
kibana: {
saved_object: savedObject,
diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts
index a080809bbc968..4888116e43602 100644
--- a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts
+++ b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts
@@ -252,10 +252,12 @@ describe('7.10.0 migrates with failure', () => {
expect(migrationContext.log.error).toHaveBeenCalledWith(
`encryptedSavedObject 7.10.0 migration failed for alert ${alert.id} with error: Can't migrate!`,
{
- alertDocument: {
- ...alert,
- attributes: {
- ...alert.attributes,
+ migrations: {
+ alertDocument: {
+ ...alert,
+ attributes: {
+ ...alert.attributes,
+ },
},
},
}
diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.ts
index c9327ed8f186a..8969e3ad0fdef 100644
--- a/x-pack/plugins/alerting/server/saved_objects/migrations.ts
+++ b/x-pack/plugins/alerting/server/saved_objects/migrations.ts
@@ -6,6 +6,7 @@
*/
import {
+ LogMeta,
SavedObjectMigrationMap,
SavedObjectUnsanitizedDoc,
SavedObjectMigrationFn,
@@ -20,6 +21,10 @@ const SIEM_APP_ID = 'securitySolution';
const SIEM_SERVER_APP_ID = 'siem';
export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0';
+interface AlertLogMeta extends LogMeta {
+ migrations: { alertDocument: SavedObjectUnsanitizedDoc };
+}
+
type AlertMigration = (
doc: SavedObjectUnsanitizedDoc
) => SavedObjectUnsanitizedDoc;
@@ -84,9 +89,13 @@ function executeMigrationWithErrorHandling(
try {
return migrationFunc(doc, context);
} catch (ex) {
- context.log.error(
+ context.log.error(
`encryptedSavedObject ${version} migration failed for alert ${doc.id} with error: ${ex.message}`,
- { alertDocument: doc }
+ {
+ migrations: {
+ alertDocument: doc,
+ },
+ }
);
}
return doc;
diff --git a/x-pack/plugins/security/README.md b/x-pack/plugins/security/README.md
index b93be0269536b..cc817b50fa442 100644
--- a/x-pack/plugins/security/README.md
+++ b/x-pack/plugins/security/README.md
@@ -13,9 +13,9 @@ auditLogger.log({
message: 'User is updating dashboard [id=123]',
event: {
action: 'saved_object_update',
- category: EventCategory.DATABASE,
- type: EventType.CHANGE,
- outcome: EventOutcome.UNKNOWN,
+ category: ['database'],
+ type: ['change'],
+ outcome: 'unknown',
},
kibana: {
saved_object: { type: 'dashboard', id: '123' },
diff --git a/x-pack/plugins/security/server/audit/audit_events.test.ts b/x-pack/plugins/security/server/audit/audit_events.test.ts
index f986c57987022..779463aaaf794 100644
--- a/x-pack/plugins/security/server/audit/audit_events.test.ts
+++ b/x-pack/plugins/security/server/audit/audit_events.test.ts
@@ -12,7 +12,6 @@ import { httpServerMock } from 'src/core/server/mocks';
import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock';
import { AuthenticationResult } from '../authentication';
import {
- EventOutcome,
httpRequestEvent,
SavedObjectAction,
savedObjectEvent,
@@ -26,7 +25,7 @@ describe('#savedObjectEvent', () => {
expect(
savedObjectEvent({
action: SavedObjectAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'dashboard', id: 'SAVED_OBJECT_ID' },
})
).toMatchInlineSnapshot(`
@@ -34,9 +33,13 @@ describe('#savedObjectEvent', () => {
"error": undefined,
"event": Object {
"action": "saved_object_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "unknown",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"add_to_spaces": undefined,
@@ -62,9 +65,13 @@ describe('#savedObjectEvent', () => {
"error": undefined,
"event": Object {
"action": "saved_object_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "success",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"add_to_spaces": undefined,
@@ -94,9 +101,13 @@ describe('#savedObjectEvent', () => {
},
"event": Object {
"action": "saved_object_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "failure",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"add_to_spaces": undefined,
@@ -197,9 +208,13 @@ describe('#savedObjectEvent', () => {
"error": undefined,
"event": Object {
"action": "saved_object_remove_references",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "success",
- "type": "change",
+ "type": Array [
+ "change",
+ ],
},
"kibana": Object {
"add_to_spaces": undefined,
@@ -228,7 +243,9 @@ describe('#userLoginEvent', () => {
"error": undefined,
"event": Object {
"action": "user_login",
- "category": "authentication",
+ "category": Array [
+ "authentication",
+ ],
"outcome": "success",
},
"kibana": Object {
@@ -264,7 +281,9 @@ describe('#userLoginEvent', () => {
},
"event": Object {
"action": "user_login",
- "category": "authentication",
+ "category": Array [
+ "authentication",
+ ],
"outcome": "failure",
},
"kibana": Object {
@@ -291,7 +310,9 @@ describe('#httpRequestEvent', () => {
Object {
"event": Object {
"action": "http_request",
- "category": "web",
+ "category": Array [
+ "web",
+ ],
"outcome": "unknown",
},
"http": Object {
@@ -328,7 +349,9 @@ describe('#httpRequestEvent', () => {
Object {
"event": Object {
"action": "http_request",
- "category": "web",
+ "category": Array [
+ "web",
+ ],
"outcome": "unknown",
},
"http": Object {
@@ -354,7 +377,7 @@ describe('#spaceAuditEvent', () => {
expect(
spaceAuditEvent({
action: SpaceAuditAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'space', id: 'SPACE_ID' },
})
).toMatchInlineSnapshot(`
@@ -362,9 +385,13 @@ describe('#spaceAuditEvent', () => {
"error": undefined,
"event": Object {
"action": "space_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "unknown",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
@@ -388,9 +415,13 @@ describe('#spaceAuditEvent', () => {
"error": undefined,
"event": Object {
"action": "space_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "success",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
@@ -418,9 +449,13 @@ describe('#spaceAuditEvent', () => {
},
"event": Object {
"action": "space_create",
- "category": "database",
+ "category": Array [
+ "database",
+ ],
"outcome": "failure",
- "type": "creation",
+ "type": Array [
+ "creation",
+ ],
},
"kibana": Object {
"saved_object": Object {
diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts
index 00f77ff2bc5fd..70d8149682370 100644
--- a/x-pack/plugins/security/server/audit/audit_events.ts
+++ b/x-pack/plugins/security/server/audit/audit_events.ts
@@ -5,36 +5,20 @@
* 2.0.
*/
-import type { KibanaRequest } from 'src/core/server';
+import type { EcsEventOutcome, EcsEventType, KibanaRequest, LogMeta } from 'src/core/server';
import type { AuthenticationResult } from '../authentication/authentication_result';
/**
- * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.6/index.html
+ * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.9/index.html
*
* If you add additional fields to the schema ensure you update the Kibana Filebeat module:
* https://github.com/elastic/beats/tree/master/filebeat/module/kibana
*
* @public
*/
-export interface AuditEvent {
- /**
- * Human readable message describing action, outcome and user.
- *
- * @example
- * Failed attempt to login using basic provider [name=basic1]
- */
+export interface AuditEvent extends LogMeta {
message: string;
- event: {
- action: string;
- category?: EventCategory;
- type?: EventType;
- outcome?: EventOutcome;
- };
- user?: {
- name: string;
- roles?: readonly string[];
- };
kibana?: {
/**
* The ID of the space associated with this event.
@@ -77,41 +61,6 @@ export interface AuditEvent {
*/
delete_from_spaces?: readonly string[];
};
- error?: {
- code?: string;
- message?: string;
- };
- http?: {
- request?: {
- method?: string;
- };
- };
- url?: {
- domain?: string;
- path?: string;
- port?: number;
- query?: string;
- scheme?: string;
- };
-}
-
-export enum EventCategory {
- DATABASE = 'database',
- WEB = 'web',
- AUTHENTICATION = 'authentication',
-}
-
-export enum EventType {
- CREATION = 'creation',
- ACCESS = 'access',
- CHANGE = 'change',
- DELETION = 'deletion',
-}
-
-export enum EventOutcome {
- SUCCESS = 'success',
- FAILURE = 'failure',
- UNKNOWN = 'unknown',
}
export interface HttpRequestParams {
@@ -125,8 +74,8 @@ export function httpRequestEvent({ request }: HttpRequestParams): AuditEvent {
message: `User is requesting [${url.pathname}] endpoint`,
event: {
action: 'http_request',
- category: EventCategory.WEB,
- outcome: EventOutcome.UNKNOWN,
+ category: ['web'],
+ outcome: 'unknown',
},
http: {
request: {
@@ -160,12 +109,12 @@ export function userLoginEvent({
: `Failed attempt to login using ${authenticationType} provider [name=${authenticationProvider}]`,
event: {
action: 'user_login',
- category: EventCategory.AUTHENTICATION,
- outcome: authenticationResult.user ? EventOutcome.SUCCESS : EventOutcome.FAILURE,
+ category: ['authentication'],
+ outcome: authenticationResult.user ? 'success' : 'failure',
},
user: authenticationResult.user && {
name: authenticationResult.user.username,
- roles: authenticationResult.user.roles,
+ roles: authenticationResult.user.roles as string[],
},
kibana: {
space_id: undefined, // Ensure this does not get populated by audit service
@@ -223,23 +172,23 @@ const savedObjectAuditVerbs: Record = {
],
};
-const savedObjectAuditTypes: Record = {
- saved_object_create: EventType.CREATION,
- saved_object_get: EventType.ACCESS,
- saved_object_resolve: EventType.ACCESS,
- saved_object_update: EventType.CHANGE,
- saved_object_delete: EventType.DELETION,
- saved_object_find: EventType.ACCESS,
- saved_object_add_to_spaces: EventType.CHANGE,
- saved_object_delete_from_spaces: EventType.CHANGE,
- saved_object_open_point_in_time: EventType.CREATION,
- saved_object_close_point_in_time: EventType.DELETION,
- saved_object_remove_references: EventType.CHANGE,
+const savedObjectAuditTypes: Record = {
+ saved_object_create: 'creation',
+ saved_object_get: 'access',
+ saved_object_resolve: 'access',
+ saved_object_update: 'change',
+ saved_object_delete: 'deletion',
+ saved_object_find: 'access',
+ saved_object_add_to_spaces: 'change',
+ saved_object_delete_from_spaces: 'change',
+ saved_object_open_point_in_time: 'creation',
+ saved_object_close_point_in_time: 'deletion',
+ saved_object_remove_references: 'change',
};
export interface SavedObjectEventParams {
action: SavedObjectAction;
- outcome?: EventOutcome;
+ outcome?: EcsEventOutcome;
savedObject?: NonNullable['saved_object'];
addToSpaces?: readonly string[];
deleteFromSpaces?: readonly string[];
@@ -258,13 +207,13 @@ export function savedObjectEvent({
const [present, progressive, past] = savedObjectAuditVerbs[action];
const message = error
? `Failed attempt to ${present} ${doc}`
- : outcome === EventOutcome.UNKNOWN
+ : outcome === 'unknown'
? `User is ${progressive} ${doc}`
: `User has ${past} ${doc}`;
const type = savedObjectAuditTypes[action];
if (
- type === EventType.ACCESS &&
+ type === 'access' &&
savedObject &&
(savedObject.type === 'config' || savedObject.type === 'telemetry')
) {
@@ -275,9 +224,9 @@ export function savedObjectEvent({
message,
event: {
action,
- category: EventCategory.DATABASE,
- type,
- outcome: outcome ?? (error ? EventOutcome.FAILURE : EventOutcome.SUCCESS),
+ category: ['database'],
+ type: [type],
+ outcome: outcome ?? (error ? 'failure' : 'success'),
},
kibana: {
saved_object: savedObject,
@@ -307,17 +256,17 @@ const spaceAuditVerbs: Record = {
space_find: ['access', 'accessing', 'accessed'],
};
-const spaceAuditTypes: Record = {
- space_create: EventType.CREATION,
- space_get: EventType.ACCESS,
- space_update: EventType.CHANGE,
- space_delete: EventType.DELETION,
- space_find: EventType.ACCESS,
+const spaceAuditTypes: Record = {
+ space_create: 'creation',
+ space_get: 'access',
+ space_update: 'change',
+ space_delete: 'deletion',
+ space_find: 'access',
};
export interface SpacesAuditEventParams {
action: SpaceAuditAction;
- outcome?: EventOutcome;
+ outcome?: EcsEventOutcome;
savedObject?: NonNullable['saved_object'];
error?: Error;
}
@@ -332,7 +281,7 @@ export function spaceAuditEvent({
const [present, progressive, past] = spaceAuditVerbs[action];
const message = error
? `Failed attempt to ${present} ${doc}`
- : outcome === EventOutcome.UNKNOWN
+ : outcome === 'unknown'
? `User is ${progressive} ${doc}`
: `User has ${past} ${doc}`;
const type = spaceAuditTypes[action];
@@ -341,9 +290,9 @@ export function spaceAuditEvent({
message,
event: {
action,
- category: EventCategory.DATABASE,
- type,
- outcome: outcome ?? (error ? EventOutcome.FAILURE : EventOutcome.SUCCESS),
+ category: ['database'],
+ type: [type],
+ outcome: outcome ?? (error ? 'failure' : 'success'),
},
kibana: {
saved_object: savedObject,
diff --git a/x-pack/plugins/security/server/audit/audit_service.test.ts b/x-pack/plugins/security/server/audit/audit_service.test.ts
index ffacaff7237c5..7c7bc4f031793 100644
--- a/x-pack/plugins/security/server/audit/audit_service.test.ts
+++ b/x-pack/plugins/security/server/audit/audit_service.test.ts
@@ -19,7 +19,6 @@ import { licenseMock } from '../../common/licensing/index.mock';
import type { ConfigType } from '../config';
import { ConfigSchema } from '../config';
import type { AuditEvent } from './audit_events';
-import { EventCategory, EventOutcome, EventType } from './audit_events';
import {
AuditService,
createLoggingConfig,
@@ -185,10 +184,8 @@ describe('#asScoped', () => {
await auditSetup.asScoped(request).log({ message: 'MESSAGE', event: { action: 'ACTION' } });
expect(logger.info).toHaveBeenCalledWith('MESSAGE', {
- ecs: { version: '1.6.0' },
event: { action: 'ACTION' },
kibana: { space_id: 'default', session_id: 'SESSION_ID' },
- message: 'MESSAGE',
trace: { id: 'REQUEST_ID' },
user: { name: 'jdoe', roles: ['admin'] },
});
@@ -349,21 +346,25 @@ describe('#createLoggingConfig', () => {
});
describe('#filterEvent', () => {
- const event: AuditEvent = {
- message: 'this is my audit message',
- event: {
- action: 'http_request',
- category: EventCategory.WEB,
- type: EventType.ACCESS,
- outcome: EventOutcome.SUCCESS,
- },
- user: {
- name: 'jdoe',
- },
- kibana: {
- space_id: 'default',
- },
- };
+ let event: AuditEvent;
+
+ beforeEach(() => {
+ event = {
+ message: 'this is my audit message',
+ event: {
+ action: 'http_request',
+ category: ['web'],
+ type: ['access'],
+ outcome: 'success',
+ },
+ user: {
+ name: 'jdoe',
+ },
+ kibana: {
+ space_id: 'default',
+ },
+ };
+ });
test('keeps event when ignore filters are undefined or empty', () => {
expect(filterEvent(event, undefined)).toBeTruthy();
@@ -421,6 +422,66 @@ describe('#filterEvent', () => {
).toBeTruthy();
});
+ test('keeps event when one item per category does not match', () => {
+ event = {
+ message: 'this is my audit message',
+ event: {
+ action: 'http_request',
+ category: ['authentication', 'web'],
+ type: ['access'],
+ outcome: 'success',
+ },
+ user: {
+ name: 'jdoe',
+ },
+ kibana: {
+ space_id: 'default',
+ },
+ };
+
+ expect(
+ filterEvent(event, [
+ {
+ actions: ['http_request'],
+ categories: ['web', 'NO_MATCH'],
+ types: ['access'],
+ outcomes: ['success'],
+ spaces: ['default'],
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ test('keeps event when one item per type does not match', () => {
+ event = {
+ message: 'this is my audit message',
+ event: {
+ action: 'http_request',
+ category: ['web'],
+ type: ['access', 'user'],
+ outcome: 'success',
+ },
+ user: {
+ name: 'jdoe',
+ },
+ kibana: {
+ space_id: 'default',
+ },
+ };
+
+ expect(
+ filterEvent(event, [
+ {
+ actions: ['http_request'],
+ categories: ['web'],
+ types: ['access', 'NO_MATCH'],
+ outcomes: ['success'],
+ spaces: ['default'],
+ },
+ ])
+ ).toBeTruthy();
+ });
+
test('filters out event when all criteria in a single rule match', () => {
expect(
filterEvent(event, [
@@ -441,6 +502,66 @@ describe('#filterEvent', () => {
])
).toBeFalsy();
});
+
+ test('filters out event when all categories match', () => {
+ event = {
+ message: 'this is my audit message',
+ event: {
+ action: 'http_request',
+ category: ['authentication', 'web'],
+ type: ['access'],
+ outcome: 'success',
+ },
+ user: {
+ name: 'jdoe',
+ },
+ kibana: {
+ space_id: 'default',
+ },
+ };
+
+ expect(
+ filterEvent(event, [
+ {
+ actions: ['http_request'],
+ categories: ['authentication', 'web'],
+ types: ['access'],
+ outcomes: ['success'],
+ spaces: ['default'],
+ },
+ ])
+ ).toBeFalsy();
+ });
+
+ test('filters out event when all types match', () => {
+ event = {
+ message: 'this is my audit message',
+ event: {
+ action: 'http_request',
+ category: ['web'],
+ type: ['access', 'user'],
+ outcome: 'success',
+ },
+ user: {
+ name: 'jdoe',
+ },
+ kibana: {
+ space_id: 'default',
+ },
+ };
+
+ expect(
+ filterEvent(event, [
+ {
+ actions: ['http_request'],
+ categories: ['web'],
+ types: ['access', 'user'],
+ outcomes: ['success'],
+ spaces: ['default'],
+ },
+ ])
+ ).toBeFalsy();
+ });
});
describe('#getLogger', () => {
diff --git a/x-pack/plugins/security/server/audit/audit_service.ts b/x-pack/plugins/security/server/audit/audit_service.ts
index 7511e079b9adb..a6205ff196537 100644
--- a/x-pack/plugins/security/server/audit/audit_service.ts
+++ b/x-pack/plugins/security/server/audit/audit_service.ts
@@ -37,15 +37,6 @@ export interface AuditLogger {
log: (event: AuditEvent | undefined) => void;
}
-interface AuditLogMeta extends AuditEvent {
- ecs: {
- version: string;
- };
- trace: {
- id: string;
- };
-}
-
export interface AuditServiceSetup {
asScoped: (request: KibanaRequest) => AuditLogger;
getLogger: (id?: string) => LegacyAuditLogger;
@@ -146,7 +137,7 @@ export class AuditService {
* message: 'User is updating dashboard [id=123]',
* event: {
* action: 'saved_object_update',
- * outcome: EventOutcome.UNKNOWN
+ * outcome: 'unknown'
* },
* kibana: {
* saved_object: { type: 'dashboard', id: '123' }
@@ -161,13 +152,12 @@ export class AuditService {
const spaceId = getSpaceId(request);
const user = getCurrentUser(request);
const sessionId = await getSID(request);
- const meta: AuditLogMeta = {
- ecs: { version: ECS_VERSION },
+ const meta: AuditEvent = {
...event,
user:
(user && {
name: user.username,
- roles: user.roles,
+ roles: user.roles as string[],
}) ||
event.user,
kibana: {
@@ -178,7 +168,8 @@ export class AuditService {
trace: { id: request.id },
};
if (filterEvent(meta, config.ignore_filters)) {
- this.ecsLogger.info(event.message!, meta);
+ const { message, ...eventMeta } = meta;
+ this.ecsLogger.info(message, eventMeta);
}
};
return { log };
@@ -243,6 +234,13 @@ export const createLoggingConfig = (config: ConfigType['audit']) =>
],
}));
+/**
+ * Evaluates the list of provided ignore rules, and filters out events only
+ * if *all* rules match the event.
+ *
+ * For event fields that can contain an array of multiple values, every value
+ * must be matched by an ignore rule for the event to be excluded.
+ */
export function filterEvent(
event: AuditEvent,
ignoreFilters: ConfigType['audit']['ignore_filters']
@@ -250,10 +248,10 @@ export function filterEvent(
if (ignoreFilters) {
return !ignoreFilters.some(
(rule) =>
- (!rule.actions || rule.actions.includes(event.event.action)) &&
- (!rule.categories || rule.categories.includes(event.event.category!)) &&
- (!rule.types || rule.types.includes(event.event.type!)) &&
- (!rule.outcomes || rule.outcomes.includes(event.event.outcome!)) &&
+ (!rule.actions || rule.actions.includes(event.event?.action!)) &&
+ (!rule.categories || event.event?.category?.every((c) => rule.categories?.includes(c))) &&
+ (!rule.types || event.event?.type?.every((t) => rule.types?.includes(t))) &&
+ (!rule.outcomes || rule.outcomes.includes(event.event?.outcome!)) &&
(!rule.spaces || rule.spaces.includes(event.kibana?.space_id!))
);
}
diff --git a/x-pack/plugins/security/server/audit/index.ts b/x-pack/plugins/security/server/audit/index.ts
index ebf1e9bed5df6..c42022bc76aa9 100644
--- a/x-pack/plugins/security/server/audit/index.ts
+++ b/x-pack/plugins/security/server/audit/index.ts
@@ -8,9 +8,6 @@
export { AuditService, AuditServiceSetup, AuditLogger, LegacyAuditLogger } from './audit_service';
export {
AuditEvent,
- EventCategory,
- EventType,
- EventOutcome,
userLoginEvent,
httpRequestEvent,
savedObjectEvent,
diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts
index be53caffc066d..1bd430d0c5c98 100644
--- a/x-pack/plugins/security/server/authentication/authenticator.test.ts
+++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts
@@ -337,7 +337,7 @@ describe('Authenticator', () => {
expect(auditLogger.log).toHaveBeenCalledTimes(1);
expect(auditLogger.log).toHaveBeenCalledWith(
expect.objectContaining({
- event: { action: 'user_login', category: 'authentication', outcome: 'success' },
+ event: { action: 'user_login', category: ['authentication'], outcome: 'success' },
})
);
});
@@ -353,7 +353,7 @@ describe('Authenticator', () => {
expect(auditLogger.log).toHaveBeenCalledTimes(1);
expect(auditLogger.log).toHaveBeenCalledWith(
expect.objectContaining({
- event: { action: 'user_login', category: 'authentication', outcome: 'failure' },
+ event: { action: 'user_login', category: ['authentication'], outcome: 'failure' },
})
);
});
diff --git a/x-pack/plugins/security/server/index.ts b/x-pack/plugins/security/server/index.ts
index 6412562af8a41..b66ed6e9eb7ca 100644
--- a/x-pack/plugins/security/server/index.ts
+++ b/x-pack/plugins/security/server/index.ts
@@ -27,14 +27,7 @@ export type {
GrantAPIKeyResult,
} from './authentication';
export type { CheckPrivilegesPayload } from './authorization';
-export {
- LegacyAuditLogger,
- AuditLogger,
- AuditEvent,
- EventCategory,
- EventType,
- EventOutcome,
-} from './audit';
+export { LegacyAuditLogger, AuditLogger, AuditEvent } from './audit';
export type { SecurityPluginSetup, SecurityPluginStart };
export type { AuthenticatedUser } from '../common/model';
diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts
index 554244dc98be9..2658f4edec5ac 100644
--- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts
+++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts
@@ -5,11 +5,10 @@
* 2.0.
*/
-import type { SavedObjectsClientContract } from 'src/core/server';
+import type { EcsEventOutcome, SavedObjectsClientContract } from 'src/core/server';
import { httpServerMock, savedObjectsClientMock } from 'src/core/server/mocks';
import type { AuditEvent } from '../audit';
-import { EventOutcome } from '../audit';
import { auditServiceMock, securityAuditLoggerMock } from '../audit/index.mock';
import { Actions } from '../authorization';
import type { SavedObjectActions } from '../authorization/actions/saved_object';
@@ -199,8 +198,8 @@ const expectObjectNamespaceFiltering = async (
};
const expectAuditEvent = (
- action: AuditEvent['event']['action'],
- outcome: AuditEvent['event']['outcome'],
+ action: string,
+ outcome: EcsEventOutcome,
savedObject?: Required['kibana']['saved_object']
) => {
expect(clientOpts.auditLogger.log).toHaveBeenCalledWith(
@@ -445,14 +444,14 @@ describe('#addToNamespaces', () => {
await client.addToNamespaces(type, id, namespaces);
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_add_to_spaces', EventOutcome.UNKNOWN, { type, id });
+ expectAuditEvent('saved_object_add_to_spaces', 'unknown', { type, id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.addToNamespaces(type, id, namespaces)).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_add_to_spaces', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_add_to_spaces', 'failure', { type, id });
});
});
@@ -515,16 +514,16 @@ describe('#bulkCreate', () => {
const options = { namespace };
await expectSuccess(client.bulkCreate, { objects, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_create', EventOutcome.UNKNOWN, { type: obj1.type, id: obj1.id });
- expectAuditEvent('saved_object_create', EventOutcome.UNKNOWN, { type: obj2.type, id: obj2.id });
+ expectAuditEvent('saved_object_create', 'unknown', { type: obj1.type, id: obj1.id });
+ expectAuditEvent('saved_object_create', 'unknown', { type: obj2.type, id: obj2.id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.bulkCreate([obj1, obj2], { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_create', EventOutcome.FAILURE, { type: obj1.type, id: obj1.id });
- expectAuditEvent('saved_object_create', EventOutcome.FAILURE, { type: obj2.type, id: obj2.id });
+ expectAuditEvent('saved_object_create', 'failure', { type: obj1.type, id: obj1.id });
+ expectAuditEvent('saved_object_create', 'failure', { type: obj2.type, id: obj2.id });
});
});
@@ -573,16 +572,16 @@ describe('#bulkGet', () => {
const options = { namespace };
await expectSuccess(client.bulkGet, { objects, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_get', EventOutcome.SUCCESS, obj1);
- expectAuditEvent('saved_object_get', EventOutcome.SUCCESS, obj2);
+ expectAuditEvent('saved_object_get', 'success', obj1);
+ expectAuditEvent('saved_object_get', 'success', obj2);
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.bulkGet([obj1, obj2], { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_get', EventOutcome.FAILURE, obj1);
- expectAuditEvent('saved_object_get', EventOutcome.FAILURE, obj2);
+ expectAuditEvent('saved_object_get', 'failure', obj1);
+ expectAuditEvent('saved_object_get', 'failure', obj2);
});
});
@@ -642,16 +641,16 @@ describe('#bulkUpdate', () => {
const options = { namespace };
await expectSuccess(client.bulkUpdate, { objects, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_update', EventOutcome.UNKNOWN, { type: obj1.type, id: obj1.id });
- expectAuditEvent('saved_object_update', EventOutcome.UNKNOWN, { type: obj2.type, id: obj2.id });
+ expectAuditEvent('saved_object_update', 'unknown', { type: obj1.type, id: obj1.id });
+ expectAuditEvent('saved_object_update', 'unknown', { type: obj2.type, id: obj2.id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.bulkUpdate([obj1, obj2], { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_update', EventOutcome.FAILURE, { type: obj1.type, id: obj1.id });
- expectAuditEvent('saved_object_update', EventOutcome.FAILURE, { type: obj2.type, id: obj2.id });
+ expectAuditEvent('saved_object_update', 'failure', { type: obj1.type, id: obj1.id });
+ expectAuditEvent('saved_object_update', 'failure', { type: obj2.type, id: obj2.id });
});
});
@@ -744,14 +743,14 @@ describe('#create', () => {
const options = { id: 'mock-saved-object-id', namespace };
await expectSuccess(client.create, { type, attributes, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_create', EventOutcome.UNKNOWN, { type, id: expect.any(String) });
+ expectAuditEvent('saved_object_create', 'unknown', { type, id: expect.any(String) });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.create(type, attributes, { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_create', EventOutcome.FAILURE, { type, id: expect.any(String) });
+ expectAuditEvent('saved_object_create', 'failure', { type, id: expect.any(String) });
});
});
@@ -789,14 +788,14 @@ describe('#delete', () => {
const options = { namespace };
await expectSuccess(client.delete, { type, id, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_delete', EventOutcome.UNKNOWN, { type, id });
+ expectAuditEvent('saved_object_delete', 'unknown', { type, id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.delete(type, id)).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_delete', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_delete', 'failure', { type, id });
});
});
@@ -936,8 +935,8 @@ describe('#find', () => {
const options = Object.freeze({ type: type1, namespaces: ['some-ns'] });
await expectSuccess(client.find, { options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(2);
- expectAuditEvent('saved_object_find', EventOutcome.SUCCESS, obj1);
- expectAuditEvent('saved_object_find', EventOutcome.SUCCESS, obj2);
+ expectAuditEvent('saved_object_find', 'success', obj1);
+ expectAuditEvent('saved_object_find', 'success', obj2);
});
test(`adds audit event when not successful`, async () => {
@@ -946,7 +945,7 @@ describe('#find', () => {
);
await client.find({ type: type1 });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_find', EventOutcome.FAILURE);
+ expectAuditEvent('saved_object_find', 'failure');
});
});
@@ -989,14 +988,14 @@ describe('#get', () => {
const options = { namespace };
await expectSuccess(client.get, { type, id, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_get', EventOutcome.SUCCESS, { type, id });
+ expectAuditEvent('saved_object_get', 'success', { type, id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.get(type, id, { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_get', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_get', 'failure', { type, id });
});
});
@@ -1023,14 +1022,14 @@ describe('#openPointInTimeForType', () => {
const options = { namespace };
await expectSuccess(client.openPointInTimeForType, { type, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_open_point_in_time', EventOutcome.UNKNOWN);
+ expectAuditEvent('saved_object_open_point_in_time', 'unknown');
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.openPointInTimeForType(type, { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_open_point_in_time', EventOutcome.FAILURE);
+ expectAuditEvent('saved_object_open_point_in_time', 'failure');
});
});
@@ -1054,7 +1053,7 @@ describe('#closePointInTime', () => {
const options = { namespace };
await client.closePointInTime(id, options);
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_close_point_in_time', EventOutcome.UNKNOWN);
+ expectAuditEvent('saved_object_close_point_in_time', 'unknown');
});
});
@@ -1153,14 +1152,14 @@ describe('#resolve', () => {
const options = { namespace };
await expectSuccess(client.resolve, { type, id, options }, 'resolve');
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_resolve', EventOutcome.SUCCESS, { type, id: resolvedId });
+ expectAuditEvent('saved_object_resolve', 'success', { type, id: resolvedId });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.resolve(type, id, { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_resolve', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_resolve', 'failure', { type, id });
});
});
@@ -1239,14 +1238,14 @@ describe('#deleteFromNamespaces', () => {
clientOpts.baseClient.deleteFromNamespaces.mockReturnValue(apiCallReturnValue as any);
await client.deleteFromNamespaces(type, id, namespaces);
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_delete_from_spaces', EventOutcome.UNKNOWN, { type, id });
+ expectAuditEvent('saved_object_delete_from_spaces', 'unknown', { type, id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.deleteFromNamespaces(type, id, namespaces)).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_delete_from_spaces', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_delete_from_spaces', 'failure', { type, id });
});
});
@@ -1290,14 +1289,14 @@ describe('#update', () => {
const options = { namespace };
await expectSuccess(client.update, { type, id, attributes, options });
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_update', EventOutcome.UNKNOWN, { type, id });
+ expectAuditEvent('saved_object_update', 'unknown', { type, id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.update(type, id, attributes, { namespace })).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_update', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_update', 'failure', { type, id });
});
});
@@ -1341,14 +1340,14 @@ describe('#removeReferencesTo', () => {
await client.removeReferencesTo(type, id);
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_remove_references', EventOutcome.UNKNOWN, { type, id });
+ expectAuditEvent('saved_object_remove_references', 'unknown', { type, id });
});
test(`adds audit event when not successful`, async () => {
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error());
await expect(() => client.removeReferencesTo(type, id)).rejects.toThrow();
expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1);
- expectAuditEvent('saved_object_remove_references', EventOutcome.FAILURE, { type, id });
+ expectAuditEvent('saved_object_remove_references', 'failure', { type, id });
});
});
diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
index d876175a05fe8..066a720f70721 100644
--- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
+++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
@@ -28,7 +28,7 @@ import type {
import { SavedObjectsUtils } from '../../../../../src/core/server';
import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants';
import type { AuditLogger, SecurityAuditLogger } from '../audit';
-import { EventOutcome, SavedObjectAction, savedObjectEvent } from '../audit';
+import { SavedObjectAction, savedObjectEvent } from '../audit';
import type { Actions, CheckSavedObjectsPrivileges } from '../authorization';
import type { CheckPrivilegesResponse } from '../authorization/types';
import type { SpacesService } from '../plugin';
@@ -116,7 +116,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id: optionsWithId.id },
})
);
@@ -178,7 +178,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id },
})
)
@@ -205,7 +205,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.DELETE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id },
})
);
@@ -400,7 +400,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.UPDATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id },
})
);
@@ -446,7 +446,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.ADD_TO_SPACES,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id },
addToSpaces: namespaces,
})
@@ -483,7 +483,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.DELETE_FROM_SPACES,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id },
deleteFromSpaces: namespaces,
})
@@ -524,7 +524,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.UPDATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type, id },
})
)
@@ -560,7 +560,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
savedObjectEvent({
action: SavedObjectAction.REMOVE_REFERENCES,
savedObject: { type, id },
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
})
);
@@ -592,7 +592,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.OPEN_POINT_IN_TIME,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
})
);
@@ -611,7 +611,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
this.auditLogger.log(
savedObjectEvent({
action: SavedObjectAction.CLOSE_POINT_IN_TIME,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
})
);
diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts
index 3f17d18bbe5f7..0b8a7abab2382 100644
--- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts
+++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts
@@ -6,13 +6,14 @@
*/
import { deepFreeze } from '@kbn/std';
+import type { EcsEventOutcome } from 'src/core/server';
import { SavedObjectsErrorHelpers } from 'src/core/server';
import { httpServerMock } from 'src/core/server/mocks';
import type { GetAllSpacesPurpose, Space } from '../../../spaces/server';
import { spacesClientMock } from '../../../spaces/server/mocks';
import type { AuditEvent, AuditLogger } from '../audit';
-import { EventOutcome, SpaceAuditAction } from '../audit';
+import { SpaceAuditAction } from '../audit';
import { auditServiceMock } from '../audit/index.mock';
import type { AuthorizationServiceSetup } from '../authorization';
import { authorizationMock } from '../authorization/index.mock';
@@ -135,8 +136,8 @@ const expectSuccessAuditLogging = (
const expectAuditEvent = (
auditLogger: AuditLogger,
- action: AuditEvent['event']['action'],
- outcome: AuditEvent['event']['outcome'],
+ action: string,
+ outcome: EcsEventOutcome,
savedObject?: Required['kibana']['saved_object']
) => {
expect(auditLogger.log).toHaveBeenCalledWith(
@@ -194,15 +195,15 @@ describe('SecureSpacesClientWrapper', () => {
expect(response).toEqual(spaces);
expectNoAuthorizationCheck(authorization);
expectNoAuditLogging(legacyAuditLogger);
- expectAuditEvent(auditLogger, SpaceAuditAction.FIND, EventOutcome.SUCCESS, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.FIND, 'success', {
type: 'space',
id: spaces[0].id,
});
- expectAuditEvent(auditLogger, SpaceAuditAction.FIND, EventOutcome.SUCCESS, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.FIND, 'success', {
type: 'space',
id: spaces[1].id,
});
- expectAuditEvent(auditLogger, SpaceAuditAction.FIND, EventOutcome.SUCCESS, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.FIND, 'success', {
type: 'space',
id: spaces[2].id,
});
@@ -285,7 +286,7 @@ describe('SecureSpacesClientWrapper', () => {
);
expectForbiddenAuditLogging(legacyAuditLogger, username, 'getAll');
- expectAuditEvent(auditLogger, SpaceAuditAction.FIND, EventOutcome.FAILURE);
+ expectAuditEvent(auditLogger, SpaceAuditAction.FIND, 'failure');
});
test(`returns spaces that the user is authorized for`, async () => {
@@ -330,7 +331,7 @@ describe('SecureSpacesClientWrapper', () => {
);
expectSuccessAuditLogging(legacyAuditLogger, username, 'getAll', [spaces[0].id]);
- expectAuditEvent(auditLogger, SpaceAuditAction.FIND, EventOutcome.SUCCESS, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.FIND, 'success', {
type: 'space',
id: spaces[0].id,
});
@@ -351,7 +352,7 @@ describe('SecureSpacesClientWrapper', () => {
expect(response).toEqual(spaces[0]);
expectNoAuthorizationCheck(authorization);
expectNoAuditLogging(legacyAuditLogger);
- expectAuditEvent(auditLogger, SpaceAuditAction.GET, EventOutcome.SUCCESS, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.GET, 'success', {
type: 'space',
id: spaces[0].id,
});
@@ -392,7 +393,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectForbiddenAuditLogging(legacyAuditLogger, username, 'get', spaceId);
- expectAuditEvent(auditLogger, SpaceAuditAction.GET, EventOutcome.FAILURE, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.GET, 'failure', {
type: 'space',
id: spaces[0].id,
});
@@ -432,7 +433,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectSuccessAuditLogging(legacyAuditLogger, username, 'get', [spaceId]);
- expectAuditEvent(auditLogger, SpaceAuditAction.GET, EventOutcome.SUCCESS, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.GET, 'success', {
type: 'space',
id: spaceId,
});
@@ -457,7 +458,7 @@ describe('SecureSpacesClientWrapper', () => {
expect(response).toEqual(space);
expectNoAuthorizationCheck(authorization);
expectNoAuditLogging(legacyAuditLogger);
- expectAuditEvent(auditLogger, SpaceAuditAction.CREATE, EventOutcome.UNKNOWN, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.CREATE, 'unknown', {
type: 'space',
id: space.id,
});
@@ -495,7 +496,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectForbiddenAuditLogging(legacyAuditLogger, username, 'create');
- expectAuditEvent(auditLogger, SpaceAuditAction.CREATE, EventOutcome.FAILURE, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.CREATE, 'failure', {
type: 'space',
id: space.id,
});
@@ -534,7 +535,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectSuccessAuditLogging(legacyAuditLogger, username, 'create');
- expectAuditEvent(auditLogger, SpaceAuditAction.CREATE, EventOutcome.UNKNOWN, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.CREATE, 'unknown', {
type: 'space',
id: space.id,
});
@@ -559,7 +560,7 @@ describe('SecureSpacesClientWrapper', () => {
expect(response).toEqual(space.id);
expectNoAuthorizationCheck(authorization);
expectNoAuditLogging(legacyAuditLogger);
- expectAuditEvent(auditLogger, SpaceAuditAction.UPDATE, EventOutcome.UNKNOWN, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.UPDATE, 'unknown', {
type: 'space',
id: space.id,
});
@@ -597,7 +598,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectForbiddenAuditLogging(legacyAuditLogger, username, 'update');
- expectAuditEvent(auditLogger, SpaceAuditAction.UPDATE, EventOutcome.FAILURE, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.UPDATE, 'failure', {
type: 'space',
id: space.id,
});
@@ -636,7 +637,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectSuccessAuditLogging(legacyAuditLogger, username, 'update');
- expectAuditEvent(auditLogger, SpaceAuditAction.UPDATE, EventOutcome.UNKNOWN, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.UPDATE, 'unknown', {
type: 'space',
id: space.id,
});
@@ -660,7 +661,7 @@ describe('SecureSpacesClientWrapper', () => {
expect(baseClient.delete).toHaveBeenCalledWith(space.id);
expectNoAuthorizationCheck(authorization);
expectNoAuditLogging(legacyAuditLogger);
- expectAuditEvent(auditLogger, SpaceAuditAction.DELETE, EventOutcome.UNKNOWN, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.DELETE, 'unknown', {
type: 'space',
id: space.id,
});
@@ -698,7 +699,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectForbiddenAuditLogging(legacyAuditLogger, username, 'delete');
- expectAuditEvent(auditLogger, SpaceAuditAction.DELETE, EventOutcome.FAILURE, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.DELETE, 'failure', {
type: 'space',
id: space.id,
});
@@ -735,7 +736,7 @@ describe('SecureSpacesClientWrapper', () => {
});
expectSuccessAuditLogging(legacyAuditLogger, username, 'delete');
- expectAuditEvent(auditLogger, SpaceAuditAction.DELETE, EventOutcome.UNKNOWN, {
+ expectAuditEvent(auditLogger, SpaceAuditAction.DELETE, 'unknown', {
type: 'space',
id: space.id,
});
diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts
index 7257dc625d4b4..ab882570ac630 100644
--- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts
+++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts
@@ -17,7 +17,7 @@ import type {
Space,
} from '../../../spaces/server';
import type { AuditLogger } from '../audit';
-import { EventOutcome, SpaceAuditAction, spaceAuditEvent } from '../audit';
+import { SpaceAuditAction, spaceAuditEvent } from '../audit';
import type { AuthorizationServiceSetup } from '../authorization';
import type { SecurityPluginSetup } from '../plugin';
import type { LegacySpacesAuditLogger } from './legacy_audit_logger';
@@ -207,7 +207,7 @@ export class SecureSpacesClientWrapper implements ISpacesClient {
this.auditLogger.log(
spaceAuditEvent({
action: SpaceAuditAction.CREATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'space', id: space.id },
})
);
@@ -238,7 +238,7 @@ export class SecureSpacesClientWrapper implements ISpacesClient {
this.auditLogger.log(
spaceAuditEvent({
action: SpaceAuditAction.UPDATE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'space', id },
})
);
@@ -269,7 +269,7 @@ export class SecureSpacesClientWrapper implements ISpacesClient {
this.auditLogger.log(
spaceAuditEvent({
action: SpaceAuditAction.DELETE,
- outcome: EventOutcome.UNKNOWN,
+ outcome: 'unknown',
savedObject: { type: 'space', id },
})
);