mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[core.logging] Ensure LogMeta is ECS-compliant. (#96350)
This commit is contained in:
parent
366691a9c8
commit
12b245c4e5
99 changed files with 2641 additions and 931 deletions
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
error: (msg: string, meta: LogMeta) => void;
|
||||
error: <Meta extends LogMeta = LogMeta>(msg: string, meta: Meta) => void;
|
||||
```
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface SavedObjectsMigrationLogger
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [debug](./kibana-plugin-core-server.savedobjectsmigrationlogger.debug.md) | <code>(msg: string) => void</code> | |
|
||||
| [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) | <code>(msg: string, meta: LogMeta) => void</code> | |
|
||||
| [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) | <code><Meta extends LogMeta = LogMeta>(msg: string, meta: Meta) => void</code> | |
|
||||
| [info](./kibana-plugin-core-server.savedobjectsmigrationlogger.info.md) | <code>(msg: string) => void</code> | |
|
||||
| [warn](./kibana-plugin-core-server.savedobjectsmigrationlogger.warn.md) | <code>(msg: string) => void</code> | |
|
||||
| [warning](./kibana-plugin-core-server.savedobjectsmigrationlogger.warning.md) | <code>(msg: string) => void</code> | |
|
||||
|
|
21
packages/kbn-logging/src/ecs/agent.ts
Normal file
21
packages/kbn-logging/src/ecs/agent.ts
Normal file
|
@ -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;
|
||||
}
|
17
packages/kbn-logging/src/ecs/autonomous_system.ts
Normal file
17
packages/kbn-logging/src/ecs/autonomous_system.ts
Normal file
|
@ -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 };
|
||||
}
|
19
packages/kbn-logging/src/ecs/base.ts
Normal file
19
packages/kbn-logging/src/ecs/base.ts
Normal file
|
@ -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<string, unknown>;
|
||||
message?: string;
|
||||
tags?: string[];
|
||||
}
|
36
packages/kbn-logging/src/ecs/client.ts
Normal file
36
packages/kbn-logging/src/ecs/client.ts
Normal file
|
@ -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;
|
||||
}
|
23
packages/kbn-logging/src/ecs/cloud.ts
Normal file
23
packages/kbn-logging/src/ecs/cloud.ts
Normal file
|
@ -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 };
|
||||
}
|
22
packages/kbn-logging/src/ecs/code_signature.ts
Normal file
22
packages/kbn-logging/src/ecs/code_signature.ts
Normal file
|
@ -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;
|
||||
}
|
20
packages/kbn-logging/src/ecs/container.ts
Normal file
20
packages/kbn-logging/src/ecs/container.ts
Normal file
|
@ -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<string, unknown>;
|
||||
name?: string;
|
||||
runtime?: string;
|
||||
}
|
36
packages/kbn-logging/src/ecs/destination.ts
Normal file
36
packages/kbn-logging/src/ecs/destination.ts
Normal file
|
@ -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;
|
||||
}
|
27
packages/kbn-logging/src/ecs/dll.ts
Normal file
27
packages/kbn-logging/src/ecs/dll.ts
Normal file
|
@ -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;
|
||||
}
|
40
packages/kbn-logging/src/ecs/dns.ts
Normal file
40
packages/kbn-logging/src/ecs/dns.ts
Normal file
|
@ -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;
|
||||
}
|
20
packages/kbn-logging/src/ecs/error.ts
Normal file
20
packages/kbn-logging/src/ecs/error.ts
Normal file
|
@ -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;
|
||||
}
|
91
packages/kbn-logging/src/ecs/event.ts
Normal file
91
packages/kbn-logging/src/ecs/event.ts
Normal file
|
@ -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';
|
52
packages/kbn-logging/src/ecs/file.ts
Normal file
52
packages/kbn-logging/src/ecs/file.ts
Normal file
|
@ -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;
|
||||
}
|
31
packages/kbn-logging/src/ecs/geo.ts
Normal file
31
packages/kbn-logging/src/ecs/geo.ts
Normal file
|
@ -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;
|
||||
}
|
18
packages/kbn-logging/src/ecs/group.ts
Normal file
18
packages/kbn-logging/src/ecs/group.ts
Normal file
|
@ -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;
|
||||
}
|
20
packages/kbn-logging/src/ecs/hash.ts
Normal file
20
packages/kbn-logging/src/ecs/hash.ts
Normal file
|
@ -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;
|
||||
}
|
48
packages/kbn-logging/src/ecs/host.ts
Normal file
48
packages/kbn-logging/src/ecs/host.ts
Normal file
|
@ -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 };
|
||||
}
|
36
packages/kbn-logging/src/ecs/http.ts
Normal file
36
packages/kbn-logging/src/ecs/http.ts
Normal file
|
@ -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;
|
||||
}
|
97
packages/kbn-logging/src/ecs/index.ts
Normal file
97
packages/kbn-logging/src/ecs/index.ts
Normal file
|
@ -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;
|
||||
};
|
18
packages/kbn-logging/src/ecs/interface.ts
Normal file
18
packages/kbn-logging/src/ecs/interface.ts
Normal file
|
@ -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;
|
||||
}
|
32
packages/kbn-logging/src/ecs/log.ts
Normal file
32
packages/kbn-logging/src/ecs/log.ts
Normal file
|
@ -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 };
|
||||
}
|
33
packages/kbn-logging/src/ecs/network.ts
Normal file
33
packages/kbn-logging/src/ecs/network.ts
Normal file
|
@ -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;
|
||||
}
|
56
packages/kbn-logging/src/ecs/observer.ts
Normal file
56
packages/kbn-logging/src/ecs/observer.ts
Normal file
|
@ -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;
|
||||
}
|
17
packages/kbn-logging/src/ecs/organization.ts
Normal file
17
packages/kbn-logging/src/ecs/organization.ts
Normal file
|
@ -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;
|
||||
}
|
22
packages/kbn-logging/src/ecs/os.ts
Normal file
22
packages/kbn-logging/src/ecs/os.ts
Normal file
|
@ -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;
|
||||
}
|
28
packages/kbn-logging/src/ecs/package.ts
Normal file
28
packages/kbn-logging/src/ecs/package.ts
Normal file
|
@ -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;
|
||||
}
|
22
packages/kbn-logging/src/ecs/pe.ts
Normal file
22
packages/kbn-logging/src/ecs/pe.ts
Normal file
|
@ -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;
|
||||
}
|
41
packages/kbn-logging/src/ecs/process.ts
Normal file
41
packages/kbn-logging/src/ecs/process.ts
Normal file
|
@ -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;
|
||||
}
|
26
packages/kbn-logging/src/ecs/registry.ts
Normal file
26
packages/kbn-logging/src/ecs/registry.ts
Normal file
|
@ -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;
|
||||
}
|
19
packages/kbn-logging/src/ecs/related.ts
Normal file
19
packages/kbn-logging/src/ecs/related.ts
Normal file
|
@ -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[];
|
||||
}
|
25
packages/kbn-logging/src/ecs/rule.ts
Normal file
25
packages/kbn-logging/src/ecs/rule.ts
Normal file
|
@ -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;
|
||||
}
|
36
packages/kbn-logging/src/ecs/server.ts
Normal file
36
packages/kbn-logging/src/ecs/server.ts
Normal file
|
@ -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;
|
||||
}
|
22
packages/kbn-logging/src/ecs/service.ts
Normal file
22
packages/kbn-logging/src/ecs/service.ts
Normal file
|
@ -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;
|
||||
}
|
36
packages/kbn-logging/src/ecs/source.ts
Normal file
36
packages/kbn-logging/src/ecs/source.ts
Normal file
|
@ -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;
|
||||
}
|
31
packages/kbn-logging/src/ecs/threat.ts
Normal file
31
packages/kbn-logging/src/ecs/threat.ts
Normal file
|
@ -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;
|
||||
}
|
64
packages/kbn-logging/src/ecs/tls.ts
Normal file
64
packages/kbn-logging/src/ecs/tls.ts
Normal file
|
@ -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;
|
||||
}
|
23
packages/kbn-logging/src/ecs/tracing.ts
Normal file
23
packages/kbn-logging/src/ecs/tracing.ts
Normal file
|
@ -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 };
|
||||
}
|
29
packages/kbn-logging/src/ecs/url.ts
Normal file
29
packages/kbn-logging/src/ecs/url.ts
Normal file
|
@ -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;
|
||||
}
|
48
packages/kbn-logging/src/ecs/user.ts
Normal file
48
packages/kbn-logging/src/ecs/user.ts
Normal file
|
@ -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;
|
||||
}
|
25
packages/kbn-logging/src/ecs/user_agent.ts
Normal file
25
packages/kbn-logging/src/ecs/user_agent.ts
Normal file
|
@ -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;
|
||||
}
|
17
packages/kbn-logging/src/ecs/vlan.ts
Normal file
17
packages/kbn-logging/src/ecs/vlan.ts
Normal file
|
@ -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;
|
||||
}
|
32
packages/kbn-logging/src/ecs/vulnerability.ts
Normal file
32
packages/kbn-logging/src/ecs/vulnerability.ts
Normal file
|
@ -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;
|
||||
}
|
47
packages/kbn-logging/src/ecs/x509.ts
Normal file
47
packages/kbn-logging/src/ecs/x509.ts
Normal file
|
@ -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[];
|
||||
}
|
|
@ -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';
|
||||
|
|
87
packages/kbn-logging/src/log_meta.ts
Normal file
87
packages/kbn-logging/src/log_meta.ts
Normal file
|
@ -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<EcsBase, '@timestamp' | 'message'> &
|
||||
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<EcsLog, 'level' | 'logger'>;
|
||||
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;
|
||||
};
|
|
@ -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<Meta extends LogMeta = LogMeta>(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<Meta extends LogMeta = LogMeta>(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<Meta extends LogMeta = LogMeta>(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<Meta extends LogMeta = LogMeta>(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<Meta extends LogMeta = LogMeta>(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<Meta extends LogMeta = LogMeta>(errorOrMessage: string | Error, meta?: Meta): void;
|
||||
|
||||
/** @internal */
|
||||
log(record: LogRecord): void;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -334,7 +334,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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
"response": Object {
|
||||
"body": Object {
|
||||
"bytes": 1234,
|
||||
},
|
||||
"headers": Object {},
|
||||
"responseTime": undefined,
|
||||
"status_code": 200,
|
||||
},
|
||||
},
|
||||
"message": "GET /path 200 - 1.2KB",
|
||||
"url": Object {
|
||||
"path": "/path",
|
||||
"query": "",
|
||||
},
|
||||
"user_agent": Object {
|
||||
"original": "",
|
||||
"meta": Object {
|
||||
"client": Object {
|
||||
"ip": undefined,
|
||||
},
|
||||
"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,
|
||||
},
|
||||
},
|
||||
"url": Object {
|
||||
"path": "/path",
|
||||
"query": "",
|
||||
},
|
||||
"user_agent": Object {
|
||||
"original": "",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -238,6 +238,11 @@ export type { IRenderOptions } from './rendering';
|
|||
export type {
|
||||
Logger,
|
||||
LoggerFactory,
|
||||
Ecs,
|
||||
EcsEventCategory,
|
||||
EcsEventKind,
|
||||
EcsEventOutcome,
|
||||
EcsEventType,
|
||||
LogMeta,
|
||||
LogRecord,
|
||||
LogLevel,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(`
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<string, unknown>;
|
||||
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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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}}"`;
|
||||
|
|
|
@ -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,34 +158,6 @@ test('`format()` correctly formats error record with meta-data', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('format() meta can override @timestamp', () => {
|
||||
const layout = new JsonLayout();
|
||||
expect(
|
||||
JSON.parse(
|
||||
layout.format({
|
||||
message: 'foo',
|
||||
timestamp,
|
||||
level: LogLevel.Debug,
|
||||
context: 'bar',
|
||||
pid: 3,
|
||||
meta: {
|
||||
'@timestamp': '2099-05-01T09:30:22.011-05:00',
|
||||
},
|
||||
})
|
||||
)
|
||||
).toStrictEqual({
|
||||
'@timestamp': '2099-05-01T09:30:22.011-05:00',
|
||||
message: 'foo',
|
||||
log: {
|
||||
level: 'DEBUG',
|
||||
logger: 'bar',
|
||||
},
|
||||
process: {
|
||||
pid: 3,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('format() meta can merge override logs', () => {
|
||||
const layout = new JsonLayout();
|
||||
expect(
|
||||
|
@ -202,6 +176,7 @@ test('format() meta can merge override logs', () => {
|
|||
})
|
||||
)
|
||||
).toStrictEqual({
|
||||
ecs: { version: '1.9.0' },
|
||||
'@timestamp': '2012-02-01T09:30:22.011-05:00',
|
||||
message: 'foo',
|
||||
log: {
|
||||
|
@ -215,29 +190,118 @@ test('format() meta can merge override logs', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('format() meta can override log level objects', () => {
|
||||
test('format() meta can not override message', () => {
|
||||
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',
|
||||
},
|
||||
message: 'baz',
|
||||
},
|
||||
})
|
||||
)
|
||||
).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,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
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',
|
||||
timestamp,
|
||||
level: LogLevel.Debug,
|
||||
context: 'bar',
|
||||
pid: 3,
|
||||
meta: {
|
||||
log: {
|
||||
level: 'IGNORE',
|
||||
logger: 'me',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
).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 timestamp', () => {
|
||||
const layout = new JsonLayout();
|
||||
expect(
|
||||
JSON.parse(
|
||||
layout.format({
|
||||
message: 'foo',
|
||||
timestamp,
|
||||
level: LogLevel.Debug,
|
||||
context: 'bar',
|
||||
pid: 3,
|
||||
meta: {
|
||||
'@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: 'DEBUG',
|
||||
logger: 'bar',
|
||||
},
|
||||
process: {
|
||||
pid: 3,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Meta extends LogMeta = LogMeta>(message: string, meta?: Meta): void {
|
||||
this.log(this.createLogRecord<Meta>(LogLevel.Trace, message, meta));
|
||||
}
|
||||
|
||||
public debug(message: string, meta?: LogMeta): void {
|
||||
this.log(this.createLogRecord(LogLevel.Debug, message, meta));
|
||||
public debug<Meta extends LogMeta = LogMeta>(message: string, meta?: Meta): void {
|
||||
this.log(this.createLogRecord<Meta>(LogLevel.Debug, message, meta));
|
||||
}
|
||||
|
||||
public info(message: string, meta?: LogMeta): void {
|
||||
this.log(this.createLogRecord(LogLevel.Info, message, meta));
|
||||
public info<Meta extends LogMeta = LogMeta>(message: string, meta?: Meta): void {
|
||||
this.log(this.createLogRecord<Meta>(LogLevel.Info, message, meta));
|
||||
}
|
||||
|
||||
public warn(errorOrMessage: string | Error, meta?: LogMeta): void {
|
||||
this.log(this.createLogRecord(LogLevel.Warn, errorOrMessage, meta));
|
||||
public warn<Meta extends LogMeta = LogMeta>(errorOrMessage: string | Error, meta?: Meta): void {
|
||||
this.log(this.createLogRecord<Meta>(LogLevel.Warn, errorOrMessage, meta));
|
||||
}
|
||||
|
||||
public error(errorOrMessage: string | Error, meta?: LogMeta): void {
|
||||
this.log(this.createLogRecord(LogLevel.Error, errorOrMessage, meta));
|
||||
public error<Meta extends LogMeta = LogMeta>(errorOrMessage: string | Error, meta?: Meta): void {
|
||||
this.log(this.createLogRecord<Meta>(LogLevel.Error, errorOrMessage, meta));
|
||||
}
|
||||
|
||||
public fatal(errorOrMessage: string | Error, meta?: LogMeta): void {
|
||||
this.log(this.createLogRecord(LogLevel.Fatal, errorOrMessage, meta));
|
||||
public fatal<Meta extends LogMeta = LogMeta>(errorOrMessage: string | Error, meta?: Meta): void {
|
||||
this.log(this.createLogRecord<Meta>(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<Meta extends LogMeta>(
|
||||
level: LogLevel,
|
||||
errorOrMessage: string | Error,
|
||||
meta?: LogMeta
|
||||
meta?: Meta
|
||||
): LogRecord {
|
||||
if (isError(errorOrMessage)) {
|
||||
return {
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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",
|
||||
"process": Object {
|
||||
"eventLoopDelay": 1,
|
||||
"memory": Object {
|
||||
"heap": Object {
|
||||
"usedInBytes": 1,
|
||||
"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,
|
||||
},
|
||||
},
|
||||
},
|
||||
"uptime": 0,
|
||||
"process": Object {
|
||||
"eventLoopDelay": 1,
|
||||
"memory": Object {
|
||||
"heap": Object {
|
||||
"usedInBytes": 1,
|
||||
},
|
||||
},
|
||||
"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']));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -24,7 +24,7 @@ export interface SavedObjectsMigrationLogger {
|
|||
*/
|
||||
warning: (msg: string) => void;
|
||||
warn: (msg: string) => void;
|
||||
error: (msg: string, meta: LogMeta) => void;
|
||||
error: <Meta extends LogMeta = LogMeta>(msg: string, meta: Meta) => void;
|
||||
}
|
||||
|
||||
export class MigrationLogger implements SavedObjectsMigrationLogger {
|
||||
|
|
|
@ -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 {
|
||||
"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",
|
||||
},
|
||||
],
|
||||
]
|
||||
|
@ -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 {
|
||||
"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",
|
||||
},
|
||||
],
|
||||
]
|
||||
|
|
|
@ -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<StateLogMeta>(
|
||||
logMessagePrefix + `${log.prevControlState} -> ${log.controlState}`,
|
||||
{
|
||||
kibana: {
|
||||
migrationState: log.state,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
if (log.type === 'response') {
|
||||
logger.info(logMessagePrefix + `${log.controlState} RESPONSE`, log.res as LogMeta);
|
||||
|
|
|
@ -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';
|
||||
|
@ -891,6 +896,16 @@ export interface DiscoveredPlugin {
|
|||
readonly requiredPlugins: readonly PluginName[];
|
||||
}
|
||||
|
||||
export { Ecs }
|
||||
|
||||
export { EcsEventCategory }
|
||||
|
||||
export { EcsEventKind }
|
||||
|
||||
export { EcsEventOutcome }
|
||||
|
||||
export { EcsEventType }
|
||||
|
||||
// @public
|
||||
export type ElasticsearchClient = Omit<KibanaClient, 'connectionPool' | 'transport' | 'serializer' | 'extend' | 'child' | 'close'> & {
|
||||
transport: {
|
||||
|
@ -2792,7 +2807,7 @@ export interface SavedObjectsMigrationLogger {
|
|||
// (undocumented)
|
||||
debug: (msg: string) => void;
|
||||
// (undocumented)
|
||||
error: (msg: string, meta: LogMeta) => void;
|
||||
error: <Meta extends LogMeta = LogMeta>(msg: string, meta: Meta) => void;
|
||||
// (undocumented)
|
||||
info: (msg: string) => void;
|
||||
// (undocumented)
|
||||
|
|
|
@ -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<InternalElasticsearchServiceSetup, 'status$'>;
|
||||
environment: InternalEnvironmentServiceSetup;
|
||||
|
@ -70,7 +74,11 @@ export class StatusService implements CoreService<InternalStatusServiceSetup> {
|
|||
...Object.entries(coreStatus),
|
||||
...Object.entries(pluginsStatus),
|
||||
]);
|
||||
this.logger.debug(`Recalculated overall status`, { status: summary });
|
||||
this.logger.debug<StatusLogMeta>(`Recalculated overall status`, {
|
||||
kibana: {
|
||||
status: summary,
|
||||
},
|
||||
});
|
||||
return summary;
|
||||
}),
|
||||
distinctUntilChanged(isDeepStrictEqual),
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
]
|
||||
|
|
|
@ -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<ConfigLogMeta>(`Upgrade config from ${upgradeableConfig.id} to ${version}`, {
|
||||
kibana: {
|
||||
config: {
|
||||
prevVersion: upgradeableConfig.id,
|
||||
newVersion: version,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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<UsageCountersLogMeta>('Store counters into savedObjects', {
|
||||
kibana: {
|
||||
usageCounters: { results },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
this.flushCache$.next();
|
||||
|
|
|
@ -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 },
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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'])<LogMeta>(`Server log: ${sanitizedMessage}`);
|
||||
} catch (err) {
|
||||
const message = i18n.translate('xpack.actions.builtin.serverLog.errorLoggingErrorMessage', {
|
||||
defaultMessage: 'error logging message',
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<ConnectorAuditAction, VerbsTuple> = {
|
|||
connector_execute: ['execute', 'executing', 'executed'],
|
||||
};
|
||||
|
||||
const eventTypes: Record<ConnectorAuditAction, EventType | undefined> = {
|
||||
connector_create: EventType.CREATION,
|
||||
connector_get: EventType.ACCESS,
|
||||
connector_update: EventType.CHANGE,
|
||||
connector_delete: EventType.DELETION,
|
||||
connector_find: EventType.ACCESS,
|
||||
const eventTypes: Record<ConnectorAuditAction, EcsEventType | undefined> = {
|
||||
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<AuditEvent['kibana']>['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,
|
||||
|
|
|
@ -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<RawAction> };
|
||||
}
|
||||
|
||||
type ActionMigration = (
|
||||
doc: SavedObjectUnsanitizedDoc<RawAction>
|
||||
) => SavedObjectUnsanitizedDoc<RawAction>;
|
||||
|
@ -50,9 +55,13 @@ function executeMigrationWithErrorHandling(
|
|||
try {
|
||||
return migrationFunc(doc, context);
|
||||
} catch (ex) {
|
||||
context.log.error(
|
||||
context.log.error<ActionsLogMeta>(
|
||||
`encryptedSavedObject ${version} migration failed for action ${doc.id} with error: ${ex.message}`,
|
||||
{ actionDocument: doc }
|
||||
{
|
||||
migrations: {
|
||||
actionDocument: doc,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return doc;
|
||||
|
|
|
@ -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 },
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<AlertAuditAction, VerbsTuple> = {
|
|||
alert_instance_unmute: ['unmute instance of', 'unmuting instance of', 'unmuted instance of'],
|
||||
};
|
||||
|
||||
const eventTypes: Record<AlertAuditAction, EventType> = {
|
||||
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<AlertAuditAction, EcsEventType> = {
|
||||
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<AuditEvent['kibana']>['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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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<RawAlert> };
|
||||
}
|
||||
|
||||
type AlertMigration = (
|
||||
doc: SavedObjectUnsanitizedDoc<RawAlert>
|
||||
) => SavedObjectUnsanitizedDoc<RawAlert>;
|
||||
|
@ -84,9 +89,13 @@ function executeMigrationWithErrorHandling(
|
|||
try {
|
||||
return migrationFunc(doc, context);
|
||||
} catch (ex) {
|
||||
context.log.error(
|
||||
context.log.error<AlertLogMeta>(
|
||||
`encryptedSavedObject ${version} migration failed for alert ${doc.id} with error: ${ex.message}`,
|
||||
{ alertDocument: doc }
|
||||
{
|
||||
migrations: {
|
||||
alertDocument: doc,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return doc;
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<SavedObjectAction, VerbsTuple> = {
|
|||
],
|
||||
};
|
||||
|
||||
const savedObjectAuditTypes: Record<SavedObjectAction, EventType> = {
|
||||
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<SavedObjectAction, EcsEventType> = {
|
||||
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<AuditEvent['kibana']>['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<SpaceAuditAction, VerbsTuple> = {
|
|||
space_find: ['access', 'accessing', 'accessed'],
|
||||
};
|
||||
|
||||
const spaceAuditTypes: Record<SpaceAuditAction, EventType> = {
|
||||
space_create: EventType.CREATION,
|
||||
space_get: EventType.ACCESS,
|
||||
space_update: EventType.CHANGE,
|
||||
space_delete: EventType.DELETION,
|
||||
space_find: EventType.ACCESS,
|
||||
const spaceAuditTypes: Record<SpaceAuditAction, EcsEventType> = {
|
||||
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<AuditEvent['kibana']>['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,
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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!))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
export { AuditService, AuditServiceSetup, AuditLogger, LegacyAuditLogger } from './audit_service';
|
||||
export {
|
||||
AuditEvent,
|
||||
EventCategory,
|
||||
EventType,
|
||||
EventOutcome,
|
||||
userLoginEvent,
|
||||
httpRequestEvent,
|
||||
savedObjectEvent,
|
||||
|
|
|
@ -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' },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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<AuditEvent>['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<any>([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 });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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',
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -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<AuditEvent>['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,
|
||||
});
|
||||
|
|
|
@ -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 },
|
||||
})
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue