-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Obs utils] Add Observability utils package (#189712)
Adds a `@kbn/observability-utils` package. ```md # @kbn/observability-utils This package contains utilities for Observability plugins. It's a separate package to get out of dependency hell. You can put anything in here that is stateless and has no dependency on other plugins (either directly or via other packages). The utility functions should be used via direct imports to minimize impact on bundle size and limit the risk on importing browser code to the server and vice versa. ``` Co-authored-by: Elastic Machine <[email protected]>
- Loading branch information
1 parent
eeed6c8
commit ecec57c
Showing
21 changed files
with
524 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# @kbn/observability-utils | ||
|
||
This package contains utilities for Observability plugins. It's a separate package to get out of dependency hell. You can put anything in here that is stateless and has no dependency on other plugins (either directly or via other packages). | ||
|
||
The utility functions should be used via direct imports to minimize impact on bundle size and limit the risk on importing browser code to the server and vice versa. |
66 changes: 66 additions & 0 deletions
66
...ck/packages/observability/observability_utils/es/client/create_observability_es_client.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import type { ElasticsearchClient, Logger } from '@kbn/core/server'; | ||
import type { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types'; | ||
import { withSpan } from '@kbn/apm-utils'; | ||
|
||
type SearchRequest = ESSearchRequest & { | ||
index: string | string[]; | ||
track_total_hits: number | boolean; | ||
size: number | boolean; | ||
}; | ||
|
||
/** | ||
* An Elasticsearch Client with a fully typed `search` method and built-in | ||
* APM instrumentation. | ||
*/ | ||
export interface ObservabilityElasticsearchClient { | ||
search<TDocument = unknown, TSearchRequest extends SearchRequest = SearchRequest>( | ||
operationName: string, | ||
parameters: TSearchRequest | ||
): Promise<InferSearchResponseOf<TDocument, TSearchRequest>>; | ||
client: ElasticsearchClient; | ||
} | ||
|
||
export function createObservabilityEsClient({ | ||
client, | ||
logger, | ||
plugin, | ||
}: { | ||
client: ElasticsearchClient; | ||
logger: Logger; | ||
plugin: string; | ||
}): ObservabilityElasticsearchClient { | ||
return { | ||
client, | ||
search<TDocument = unknown, TSearchRequest extends SearchRequest = SearchRequest>( | ||
operationName: string, | ||
parameters: SearchRequest | ||
) { | ||
logger.trace(() => `Request (${operationName}):\n${JSON.stringify(parameters, null, 2)}`); | ||
// wraps the search operation in a named APM span for better analysis | ||
// (otherwise it would just be a _search span) | ||
return withSpan( | ||
{ | ||
name: operationName, | ||
labels: { | ||
plugin, | ||
}, | ||
}, | ||
() => { | ||
return client.search<TDocument>(parameters) as unknown as Promise< | ||
InferSearchResponseOf<TDocument, TSearchRequest> | ||
>; | ||
} | ||
).then((response) => { | ||
logger.trace(() => `Response (${operationName}):\n${JSON.stringify(response, null, 2)}`); | ||
return response; | ||
}); | ||
}, | ||
}; | ||
} |
17 changes: 17 additions & 0 deletions
17
x-pack/packages/observability/observability_utils/es/queries/kql_query.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import type { estypes } from '@elastic/elasticsearch'; | ||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; | ||
|
||
export function kqlQuery(kql?: string): estypes.QueryDslQueryContainer[] { | ||
if (!kql) { | ||
return []; | ||
} | ||
|
||
const ast = fromKueryExpression(kql); | ||
return [toElasticsearchQuery(ast)]; | ||
} |
25 changes: 25 additions & 0 deletions
25
x-pack/packages/observability/observability_utils/es/queries/range_query.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import type { estypes } from '@elastic/elasticsearch'; | ||
|
||
export function rangeQuery( | ||
start?: number, | ||
end?: number, | ||
field = '@timestamp' | ||
): estypes.QueryDslQueryContainer[] { | ||
return [ | ||
{ | ||
range: { | ||
[field]: { | ||
gte: start, | ||
lte: end, | ||
format: 'epoch_millis', | ||
}, | ||
}, | ||
}, | ||
]; | ||
} |
24 changes: 24 additions & 0 deletions
24
x-pack/packages/observability/observability_utils/es/queries/term_query.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; | ||
|
||
interface TermQueryOpts { | ||
queryEmptyString: boolean; | ||
} | ||
|
||
export function termQuery<T extends string>( | ||
field: T, | ||
value: string | boolean | number | undefined | null, | ||
opts: TermQueryOpts = { queryEmptyString: true } | ||
): QueryDslQueryContainer[] { | ||
if (value === null || value === undefined || (!opts.queryEmptyString && value === '')) { | ||
return []; | ||
} | ||
|
||
return [{ term: { [field]: value } }]; | ||
} |
25 changes: 25 additions & 0 deletions
25
x-pack/packages/observability/observability_utils/hooks/use_abort_controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { useEffect, useState } from 'react'; | ||
|
||
export function useAbortController() { | ||
const [controller, setController] = useState(() => new AbortController()); | ||
|
||
useEffect(() => { | ||
return () => { | ||
controller.abort(); | ||
}; | ||
}, [controller]); | ||
|
||
return { | ||
signal: controller.signal, | ||
refresh: () => { | ||
setController(() => new AbortController()); | ||
}, | ||
}; | ||
} |
87 changes: 87 additions & 0 deletions
87
x-pack/packages/observability/observability_utils/hooks/use_abortable_async.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import { isPromise } from '@kbn/std'; | ||
import { useEffect, useMemo, useRef, useState } from 'react'; | ||
|
||
interface State<T> { | ||
error?: Error; | ||
value?: T; | ||
loading: boolean; | ||
} | ||
|
||
export type AbortableAsyncState<T> = (T extends Promise<infer TReturn> | ||
? State<TReturn> | ||
: State<T>) & { refresh: () => void }; | ||
|
||
export function useAbortableAsync<T>( | ||
fn: ({}: { signal: AbortSignal }) => T | Promise<T>, | ||
deps: any[], | ||
options?: { clearValueOnNext?: boolean; defaultValue?: () => T } | ||
): AbortableAsyncState<T> { | ||
const clearValueOnNext = options?.clearValueOnNext; | ||
|
||
const controllerRef = useRef(new AbortController()); | ||
|
||
const [refreshId, setRefreshId] = useState(0); | ||
|
||
const [error, setError] = useState<Error>(); | ||
const [loading, setLoading] = useState(false); | ||
const [value, setValue] = useState<T | undefined>(options?.defaultValue); | ||
|
||
useEffect(() => { | ||
controllerRef.current.abort(); | ||
|
||
const controller = new AbortController(); | ||
controllerRef.current = controller; | ||
|
||
if (clearValueOnNext) { | ||
setValue(undefined); | ||
setError(undefined); | ||
} | ||
|
||
try { | ||
const response = fn({ signal: controller.signal }); | ||
if (isPromise(response)) { | ||
setLoading(true); | ||
response | ||
.then((nextValue) => { | ||
setError(undefined); | ||
setValue(nextValue); | ||
}) | ||
.catch((err) => { | ||
setValue(undefined); | ||
setError(err); | ||
}) | ||
.finally(() => setLoading(false)); | ||
} else { | ||
setError(undefined); | ||
setValue(response); | ||
setLoading(false); | ||
} | ||
} catch (err) { | ||
setValue(undefined); | ||
setError(err); | ||
setLoading(false); | ||
} | ||
|
||
return () => { | ||
controller.abort(); | ||
}; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps.concat(refreshId, clearValueOnNext)); | ||
|
||
return useMemo<AbortableAsyncState<T>>(() => { | ||
return { | ||
error, | ||
loading, | ||
value, | ||
refresh: () => { | ||
setRefreshId((id) => id + 1); | ||
}, | ||
} as unknown as AbortableAsyncState<T>; | ||
}, [error, value, loading]); | ||
} |
12 changes: 12 additions & 0 deletions
12
x-pack/packages/observability/observability_utils/hooks/use_theme.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { useEuiTheme } from '@elastic/eui'; | ||
|
||
export function useTheme() { | ||
return useEuiTheme().euiTheme; | ||
} |
12 changes: 12 additions & 0 deletions
12
x-pack/packages/observability/observability_utils/jest.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
module.exports = { | ||
preset: '@kbn/test', | ||
rootDir: '../../../..', | ||
roots: ['<rootDir>/x-pack/packages/observability/observability_utils'], | ||
}; |
5 changes: 5 additions & 0 deletions
5
x-pack/packages/observability/observability_utils/kibana.jsonc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"type": "shared-common", | ||
"id": "@kbn/observability-utils", | ||
"owner": "@elastic/observability-ui" | ||
} |
39 changes: 39 additions & 0 deletions
39
x-pack/packages/observability/observability_utils/object/flatten_object.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import { flattenObject } from './flatten_object'; | ||
|
||
describe('flattenObject', () => { | ||
it('flattens deeply nested objects', () => { | ||
expect( | ||
flattenObject({ | ||
first: { | ||
second: { | ||
third: 'third', | ||
}, | ||
}, | ||
}) | ||
).toEqual({ | ||
'first.second.third': 'third', | ||
}); | ||
}); | ||
|
||
it('does not flatten arrays', () => { | ||
expect( | ||
flattenObject({ | ||
simpleArray: ['0', '1', '2'], | ||
complexArray: [{ one: 'one', two: 'two', three: 'three' }], | ||
nested: { | ||
array: [0, 1, 2], | ||
}, | ||
}) | ||
).toEqual({ | ||
simpleArray: ['0', '1', '2'], | ||
complexArray: [{ one: 'one', two: 'two', three: 'three' }], | ||
'nested.array': [0, 1, 2], | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.