mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Observability] Split up observability-utils package (#199801)](https://github.com/elastic/kibana/pull/199801) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Dario Gieselaar","email":"dario.gieselaar@elastic.co"},"sourceCommit":{"committedDate":"2024-11-13T18:51:42Z","message":"[Observability] Split up observability-utils package (#199801)\n\nSplit up observability-utils package in browser, common, server. Also\r\nmade a small change to `withSpan` to automatically log operation times\r\nwhen the debug level for the logger is enabled.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"11a752da8751f447a083f050e0c4eeb85073fa56","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport missing","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services","v8.17.0","v8.18.0"],"number":199801,"url":"https://github.com/elastic/kibana/pull/199801","mergeCommit":{"message":"[Observability] Split up observability-utils package (#199801)\n\nSplit up observability-utils package in browser, common, server. Also\r\nmade a small change to `withSpan` to automatically log operation times\r\nwhen the debug level for the logger is enabled.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"11a752da8751f447a083f050e0c4eeb85073fa56"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.18"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/199801","number":199801,"mergeCommit":{"message":"[Observability] Split up observability-utils package (#199801)\n\nSplit up observability-utils package in browser, common, server. Also\r\nmade a small change to `withSpan` to automatically log operation times\r\nwhen the debug level for the logger is enabled.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"11a752da8751f447a083f050e0c4eeb85073fa56"}},{"branch":"8.x","label":"v8.17.0","labelRegex":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT-->
This commit is contained in:
parent
71cf9c8305
commit
b7d06a222e
87 changed files with 1225 additions and 101 deletions
|
@ -704,7 +704,9 @@
|
|||
"@kbn/observability-plugin": "link:x-pack/plugins/observability_solution/observability",
|
||||
"@kbn/observability-shared-plugin": "link:x-pack/plugins/observability_solution/observability_shared",
|
||||
"@kbn/observability-synthetics-test-data": "link:x-pack/packages/observability/synthetics_test_data",
|
||||
"@kbn/observability-utils": "link:x-pack/packages/observability/observability_utils",
|
||||
"@kbn/observability-utils-browser": "link:x-pack/packages/observability/observability_utils/observability_utils_browser",
|
||||
"@kbn/observability-utils-common": "link:x-pack/packages/observability/observability_utils/observability_utils_common",
|
||||
"@kbn/observability-utils-server": "link:x-pack/packages/observability/observability_utils/observability_utils_server",
|
||||
"@kbn/oidc-provider-plugin": "link:x-pack/test/security_api_integration/plugins/oidc_provider",
|
||||
"@kbn/open-telemetry-instrumented-plugin": "link:test/common/plugins/otel_metrics",
|
||||
"@kbn/openapi-common": "link:packages/kbn-openapi-common",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import agent from 'elastic-apm-node';
|
||||
import agent, { Logger } from 'elastic-apm-node';
|
||||
import asyncHooks from 'async_hooks';
|
||||
|
||||
export interface SpanOptions {
|
||||
|
@ -34,14 +34,48 @@ const runInNewContext = <T extends (...args: any[]) => any>(cb: T): ReturnType<T
|
|||
|
||||
export async function withSpan<T>(
|
||||
optionsOrName: SpanOptions | string,
|
||||
cb: (span?: Span) => Promise<T>
|
||||
cb: (span?: Span) => Promise<T>,
|
||||
logger?: Logger
|
||||
): Promise<T> {
|
||||
const options = parseSpanOptions(optionsOrName);
|
||||
|
||||
const { name, type, subtype, labels, intercept } = options;
|
||||
|
||||
let time: number | undefined;
|
||||
if (logger?.isLevelEnabled('debug')) {
|
||||
time = performance.now();
|
||||
}
|
||||
|
||||
function logTook(failed: boolean) {
|
||||
if (time) {
|
||||
logger?.debug(
|
||||
() =>
|
||||
`Operation ${name}${failed ? ` (failed)` : ''} ${
|
||||
Math.round(performance.now() - time!) / 1000
|
||||
}s`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const withLogTook = [
|
||||
<TR>(res: TR): TR | Promise<TR> => {
|
||||
logTook(false);
|
||||
return res;
|
||||
},
|
||||
(err: any): never => {
|
||||
logTook(true);
|
||||
throw err;
|
||||
},
|
||||
];
|
||||
|
||||
if (!agent.isStarted()) {
|
||||
return cb();
|
||||
const promise = cb();
|
||||
// make sure tests that mock out the callback with a sync
|
||||
// function don't fail.
|
||||
if (typeof promise === 'object' && 'then' in promise) {
|
||||
return promise.then(...withLogTook);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
let createdSpan: Span | undefined;
|
||||
|
@ -57,7 +91,7 @@ export async function withSpan<T>(
|
|||
createdSpan = agent.startSpan(name) ?? undefined;
|
||||
|
||||
if (!createdSpan) {
|
||||
return cb();
|
||||
return cb().then(...withLogTook);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +110,7 @@ export async function withSpan<T>(
|
|||
}
|
||||
|
||||
if (!span) {
|
||||
return promise;
|
||||
return promise.then(...withLogTook);
|
||||
}
|
||||
|
||||
const targetedSpan = span;
|
||||
|
@ -98,6 +132,7 @@ export async function withSpan<T>(
|
|||
}
|
||||
|
||||
return promise
|
||||
.then(...withLogTook)
|
||||
.then((res) => {
|
||||
if (!targetedSpan.outcome || targetedSpan.outcome === 'unknown') {
|
||||
targetedSpan.outcome = 'success';
|
||||
|
|
|
@ -1332,8 +1332,12 @@
|
|||
"@kbn/observability-shared-plugin/*": ["x-pack/plugins/observability_solution/observability_shared/*"],
|
||||
"@kbn/observability-synthetics-test-data": ["x-pack/packages/observability/synthetics_test_data"],
|
||||
"@kbn/observability-synthetics-test-data/*": ["x-pack/packages/observability/synthetics_test_data/*"],
|
||||
"@kbn/observability-utils": ["x-pack/packages/observability/observability_utils"],
|
||||
"@kbn/observability-utils/*": ["x-pack/packages/observability/observability_utils/*"],
|
||||
"@kbn/observability-utils-browser": ["x-pack/packages/observability/observability_utils/observability_utils_browser"],
|
||||
"@kbn/observability-utils-browser/*": ["x-pack/packages/observability/observability_utils/observability_utils_browser/*"],
|
||||
"@kbn/observability-utils-common": ["x-pack/packages/observability/observability_utils/observability_utils_common"],
|
||||
"@kbn/observability-utils-common/*": ["x-pack/packages/observability/observability_utils/observability_utils_common/*"],
|
||||
"@kbn/observability-utils-server": ["x-pack/packages/observability/observability_utils/observability_utils_server"],
|
||||
"@kbn/observability-utils-server/*": ["x-pack/packages/observability/observability_utils/observability_utils_server/*"],
|
||||
"@kbn/oidc-provider-plugin": ["x-pack/test/security_api_integration/plugins/oidc_provider"],
|
||||
"@kbn/oidc-provider-plugin/*": ["x-pack/test/security_api_integration/plugins/oidc_provider/*"],
|
||||
"@kbn/open-telemetry-instrumented-plugin": ["test/common/plugins/otel_metrics"],
|
||||
|
|
|
@ -81,3 +81,5 @@ export {
|
|||
isInferenceInternalError,
|
||||
isInferenceRequestError,
|
||||
} from './src/errors';
|
||||
|
||||
export { truncateList } from './src/truncate_list';
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# @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.
|
|
@ -18,6 +18,9 @@ export function useAbortController() {
|
|||
|
||||
return {
|
||||
signal: controller.signal,
|
||||
abort: () => {
|
||||
controller.abort();
|
||||
},
|
||||
refresh: () => {
|
||||
setController(() => new AbortController());
|
||||
},
|
|
@ -17,12 +17,32 @@ export type AbortableAsyncState<T> = (T extends Promise<infer TReturn>
|
|||
? State<TReturn>
|
||||
: State<T>) & { refresh: () => void };
|
||||
|
||||
export type AbortableAsyncStateOf<T extends AbortableAsyncState<any>> =
|
||||
T extends AbortableAsyncState<infer TResponse> ? Awaited<TResponse> : never;
|
||||
|
||||
interface UseAbortableAsyncOptions<T> {
|
||||
clearValueOnNext?: boolean;
|
||||
unsetValueOnError?: boolean;
|
||||
defaultValue?: () => T;
|
||||
onError?: (error: Error) => void;
|
||||
}
|
||||
|
||||
export type UseAbortableAsync<
|
||||
TAdditionalParameters extends Record<string, any> = {},
|
||||
TAdditionalOptions extends Record<string, any> = {}
|
||||
> = <T>(
|
||||
fn: ({}: { signal: AbortSignal } & TAdditionalParameters) => T | Promise<T>,
|
||||
deps: any[],
|
||||
options?: UseAbortableAsyncOptions<T> & TAdditionalOptions
|
||||
) => AbortableAsyncState<T>;
|
||||
|
||||
export function useAbortableAsync<T>(
|
||||
fn: ({}: { signal: AbortSignal }) => T | Promise<T>,
|
||||
deps: any[],
|
||||
options?: { clearValueOnNext?: boolean; defaultValue?: () => T }
|
||||
options?: UseAbortableAsyncOptions<T>
|
||||
): AbortableAsyncState<T> {
|
||||
const clearValueOnNext = options?.clearValueOnNext;
|
||||
const unsetValueOnError = options?.unsetValueOnError;
|
||||
|
||||
const controllerRef = useRef(new AbortController());
|
||||
|
||||
|
@ -43,6 +63,15 @@ export function useAbortableAsync<T>(
|
|||
setError(undefined);
|
||||
}
|
||||
|
||||
function handleError(err: Error) {
|
||||
setError(err);
|
||||
if (unsetValueOnError) {
|
||||
setValue(undefined);
|
||||
}
|
||||
setLoading(false);
|
||||
options?.onError?.(err);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = fn({ signal: controller.signal });
|
||||
if (isPromise(response)) {
|
||||
|
@ -52,12 +81,7 @@ export function useAbortableAsync<T>(
|
|||
setError(undefined);
|
||||
setValue(nextValue);
|
||||
})
|
||||
.catch((err) => {
|
||||
setValue(undefined);
|
||||
if (!controller.signal.aborted) {
|
||||
setError(err);
|
||||
}
|
||||
})
|
||||
.catch(handleError)
|
||||
.finally(() => setLoading(false));
|
||||
} else {
|
||||
setError(undefined);
|
||||
|
@ -65,9 +89,7 @@ export function useAbortableAsync<T>(
|
|||
setLoading(false);
|
||||
}
|
||||
} catch (err) {
|
||||
setValue(undefined);
|
||||
setError(err);
|
||||
setLoading(false);
|
||||
handleError(err);
|
||||
}
|
||||
|
||||
return () => {
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 { TimeRange } from '@kbn/data-plugin/common';
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
export function useDateRange({ data }: { data: DataPublicPluginStart }): {
|
||||
timeRange: TimeRange;
|
||||
absoluteTimeRange: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
setTimeRange: React.Dispatch<React.SetStateAction<TimeRange>>;
|
||||
} {
|
||||
const timefilter = data.query.timefilter.timefilter;
|
||||
|
||||
const [timeRange, setTimeRange] = useState(() => timefilter.getTime());
|
||||
|
||||
const [absoluteTimeRange, setAbsoluteTimeRange] = useState(() => timefilter.getAbsoluteTime());
|
||||
|
||||
useEffect(() => {
|
||||
const timeUpdateSubscription = timefilter.getTimeUpdate$().subscribe({
|
||||
next: () => {
|
||||
setTimeRange(() => timefilter.getTime());
|
||||
setAbsoluteTimeRange(() => timefilter.getAbsoluteTime());
|
||||
},
|
||||
});
|
||||
|
||||
return () => {
|
||||
timeUpdateSubscription.unsubscribe();
|
||||
};
|
||||
}, [timefilter]);
|
||||
|
||||
const setTimeRangeMemoized: React.Dispatch<React.SetStateAction<TimeRange>> = useCallback(
|
||||
(nextOrCallback) => {
|
||||
const val =
|
||||
typeof nextOrCallback === 'function'
|
||||
? nextOrCallback(timefilter.getTime())
|
||||
: nextOrCallback;
|
||||
|
||||
timefilter.setTime(val);
|
||||
},
|
||||
[timefilter]
|
||||
);
|
||||
|
||||
const asEpoch = useMemo(() => {
|
||||
return {
|
||||
start: new Date(absoluteTimeRange.from).getTime(),
|
||||
end: new Date(absoluteTimeRange.to).getTime(),
|
||||
};
|
||||
}, [absoluteTimeRange]);
|
||||
|
||||
return {
|
||||
timeRange,
|
||||
absoluteTimeRange: asEpoch,
|
||||
setTimeRange: setTimeRangeMemoized,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
|
||||
export function useLocalStorage<T>(key: string, defaultValue: T) {
|
||||
// This is necessary to fix a race condition issue.
|
||||
// It guarantees that the latest value will be always returned after the value is updated
|
||||
const [storageUpdate, setStorageUpdate] = useState(0);
|
||||
|
||||
const item = useMemo(() => {
|
||||
return getFromStorage(key, defaultValue);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [key, storageUpdate, defaultValue]);
|
||||
|
||||
const saveToStorage = useCallback(
|
||||
(value: T) => {
|
||||
if (value === undefined) {
|
||||
window.localStorage.removeItem(key);
|
||||
} else {
|
||||
window.localStorage.setItem(key, JSON.stringify(value));
|
||||
setStorageUpdate(storageUpdate + 1);
|
||||
}
|
||||
},
|
||||
[key, storageUpdate]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
function onUpdate(event: StorageEvent) {
|
||||
if (event.key === key) {
|
||||
setStorageUpdate(storageUpdate + 1);
|
||||
}
|
||||
}
|
||||
window.addEventListener('storage', onUpdate);
|
||||
return () => {
|
||||
window.removeEventListener('storage', onUpdate);
|
||||
};
|
||||
}, [key, setStorageUpdate, storageUpdate]);
|
||||
|
||||
return useMemo(() => [item, saveToStorage] as const, [item, saveToStorage]);
|
||||
}
|
||||
|
||||
function getFromStorage<T>(keyName: string, defaultValue: T) {
|
||||
const storedItem = window.localStorage.getItem(keyName);
|
||||
|
||||
if (storedItem !== null) {
|
||||
try {
|
||||
return JSON.parse(storedItem) as T;
|
||||
} catch (err) {
|
||||
window.localStorage.removeItem(keyName);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Unable to decode: ${keyName}`);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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/observability_utils_browser',
|
||||
],
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-browser",
|
||||
"id": "@kbn/observability-utils-browser",
|
||||
"owner": "@elastic/observability-ui"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/observability-utils-browser",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/data-plugin",
|
||||
"@kbn/core-ui-settings-browser",
|
||||
"@kbn/std",
|
||||
]
|
||||
}
|
|
@ -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 { UI_SETTINGS } from '@kbn/data-plugin/public';
|
||||
import { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
|
||||
|
||||
export function getTimeZone(uiSettings?: IUiSettingsClient) {
|
||||
const kibanaTimeZone = uiSettings?.get<'Browser' | string>(UI_SETTINGS.DATEFORMAT_TZ);
|
||||
if (!kibanaTimeZone || kibanaTimeZone === 'Browser') {
|
||||
return 'local';
|
||||
}
|
||||
|
||||
return kibanaTimeZone;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export function getEntityKuery(entity: Record<string, unknown>) {
|
||||
return Object.entries(entity)
|
||||
.map(([name, value]) => {
|
||||
return `(${name}:"${value}")`;
|
||||
})
|
||||
.join(' AND ');
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export function formatValueForKql(value: string) {
|
||||
return `(${value.replaceAll(/((^|[^\\])):/g, '\\:')})`;
|
||||
}
|
|
@ -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';
|
||||
|
||||
export function entityQuery(entity: Record<string, string>): QueryDslQueryContainer[] {
|
||||
return [
|
||||
{
|
||||
bool: {
|
||||
filter: Object.entries(entity).map(([field, value]) => {
|
||||
return {
|
||||
term: {
|
||||
[field]: value,
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 numeral from '@elastic/numeral';
|
||||
|
||||
export function formatInteger(num: number) {
|
||||
return numeral(num).format('0a');
|
||||
}
|
|
@ -7,6 +7,6 @@
|
|||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/x-pack/packages/observability/observability_utils'],
|
||||
rootDir: '../../../../..',
|
||||
roots: ['<rootDir>/x-pack/packages/observability/observability_utils/observability_utils_common'],
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/observability-utils",
|
||||
"id": "@kbn/observability-utils-common",
|
||||
"owner": "@elastic/observability-ui"
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
export interface DocumentAnalysis {
|
||||
total: number;
|
||||
sampled: number;
|
||||
fields: Array<{
|
||||
name: string;
|
||||
types: string[];
|
||||
cardinality: number | null;
|
||||
values: Array<string | number | boolean>;
|
||||
empty: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface TruncatedDocumentAnalysis {
|
||||
fields: string[];
|
||||
total: number;
|
||||
sampled: number;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function addCapturingGroupsToRegex(regex: string): string {
|
||||
// Match all parts of the regex that are not special characters
|
||||
// We treat constant parts as sequences of characters that are not part of regex syntax
|
||||
return regex.replaceAll(/((?:\.\*\?)|(?:\.\+\?)|(?:\+\?))/g, (...args) => {
|
||||
return `(${args[1]})`;
|
||||
});
|
||||
}
|
||||
|
||||
export function highlightPatternFromRegex(pattern: string, str: string): string {
|
||||
// First, add non-capturing groups to the regex around constant parts
|
||||
const updatedPattern = addCapturingGroupsToRegex(pattern);
|
||||
|
||||
const regex = new RegExp(updatedPattern, 'ds');
|
||||
|
||||
const matches = str.match(regex) as
|
||||
| (RegExpMatchArray & { indices: Array<[number, number]> })
|
||||
| null;
|
||||
|
||||
const slices: string[] = [];
|
||||
|
||||
matches?.forEach((_, index) => {
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [, prevEnd] = index > 1 ? matches?.indices[index - 1] : [undefined, undefined];
|
||||
const [start, end] = matches?.indices[index];
|
||||
|
||||
const literalSlice = prevEnd !== undefined ? str.slice(prevEnd, start) : undefined;
|
||||
|
||||
if (literalSlice) {
|
||||
slices.push(`<em>${literalSlice}</em>`);
|
||||
}
|
||||
|
||||
const slice = str.slice(start, end);
|
||||
slices.push(slice);
|
||||
|
||||
if (index === matches.length - 1) {
|
||||
slices.push(str.slice(end));
|
||||
}
|
||||
});
|
||||
|
||||
return slices.join('');
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 { castArray, sortBy, uniq } from 'lodash';
|
||||
import type { DocumentAnalysis } from './document_analysis';
|
||||
|
||||
export function mergeSampleDocumentsWithFieldCaps({
|
||||
total,
|
||||
samples,
|
||||
fieldCaps,
|
||||
}: {
|
||||
total: number;
|
||||
samples: Array<Record<string, unknown | unknown[]>>;
|
||||
fieldCaps: Array<{ name: string; esTypes?: string[] }>;
|
||||
}): DocumentAnalysis {
|
||||
const nonEmptyFields = new Set<string>();
|
||||
const fieldValues = new Map<string, Array<string | number | boolean>>();
|
||||
|
||||
for (const document of samples) {
|
||||
Object.keys(document).forEach((field) => {
|
||||
if (!nonEmptyFields.has(field)) {
|
||||
nonEmptyFields.add(field);
|
||||
}
|
||||
|
||||
const values = castArray(document[field]);
|
||||
|
||||
const currentFieldValues = fieldValues.get(field) ?? [];
|
||||
|
||||
values.forEach((value) => {
|
||||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
||||
currentFieldValues.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
fieldValues.set(field, currentFieldValues);
|
||||
});
|
||||
}
|
||||
|
||||
const fields = fieldCaps.flatMap((spec) => {
|
||||
const values = fieldValues.get(spec.name);
|
||||
|
||||
const countByValues = new Map<string | number | boolean, number>();
|
||||
|
||||
values?.forEach((value) => {
|
||||
const currentCount = countByValues.get(value) ?? 0;
|
||||
countByValues.set(value, currentCount + 1);
|
||||
});
|
||||
|
||||
const sortedValues = sortBy(
|
||||
Array.from(countByValues.entries()).map(([value, count]) => {
|
||||
return {
|
||||
value,
|
||||
count,
|
||||
};
|
||||
}),
|
||||
'count',
|
||||
'desc'
|
||||
);
|
||||
|
||||
return {
|
||||
name: spec.name,
|
||||
types: spec.esTypes ?? [],
|
||||
empty: !nonEmptyFields.has(spec.name),
|
||||
cardinality: countByValues.size || null,
|
||||
values: uniq(sortedValues.flatMap(({ value }) => value)),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
total,
|
||||
sampled: samples.length,
|
||||
fields,
|
||||
};
|
||||
}
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { partition, shuffle } from 'lodash';
|
||||
import { truncateList } from '@kbn/inference-common';
|
||||
import type { DocumentAnalysis, TruncatedDocumentAnalysis } from './document_analysis';
|
||||
|
||||
export function sortAndTruncateAnalyzedFields(
|
||||
analysis: DocumentAnalysis
|
||||
): TruncatedDocumentAnalysis {
|
||||
const { fields, ...meta } = analysis;
|
||||
const [nonEmptyFields, emptyFields] = partition(analysis.fields, (field) => !field.empty);
|
||||
|
||||
const sortedFields = [...shuffle(nonEmptyFields), ...shuffle(emptyFields)];
|
||||
|
||||
return {
|
||||
...meta,
|
||||
fields: truncateList(
|
||||
sortedFields.map((field) => {
|
||||
let label = `${field.name}:${field.types.join(',')}`;
|
||||
|
||||
if (field.empty) {
|
||||
return `${name} (empty)`;
|
||||
}
|
||||
|
||||
label += ` - ${field.cardinality} distinct values`;
|
||||
|
||||
if (field.name === '@timestamp' || field.name === 'event.ingested') {
|
||||
return `${label}`;
|
||||
}
|
||||
|
||||
const shortValues = field.values.filter((value) => {
|
||||
return String(value).length <= 1024;
|
||||
});
|
||||
|
||||
if (shortValues.length) {
|
||||
return `${label} (${truncateList(
|
||||
shortValues.map((value) => '`' + value + '`'),
|
||||
field.types.includes('text') || field.types.includes('match_only_text') ? 2 : 10
|
||||
).join(', ')})`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}),
|
||||
500
|
||||
).sort(),
|
||||
};
|
||||
}
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { ShortIdTable } from './short_id_table';
|
||||
|
||||
describe('shortIdTable', () => {
|
||||
it('generates at least 10k unique ids consistently', () => {
|
||||
const ids = new Set();
|
||||
|
||||
const table = new ShortIdTable();
|
||||
|
||||
let i = 10_000;
|
||||
while (i--) {
|
||||
const id = table.take(String(i));
|
||||
ids.add(id);
|
||||
}
|
||||
|
||||
expect(ids.size).toBe(10_000);
|
||||
});
|
||||
|
||||
it('returns the original id based on the generated id', () => {
|
||||
const table = new ShortIdTable();
|
||||
|
||||
const idsByOriginal = new Map<string, string>();
|
||||
|
||||
let i = 100;
|
||||
while (i--) {
|
||||
const id = table.take(String(i));
|
||||
idsByOriginal.set(String(i), id);
|
||||
}
|
||||
|
||||
expect(idsByOriginal.size).toBe(100);
|
||||
|
||||
expect(() => {
|
||||
Array.from(idsByOriginal.entries()).forEach(([originalId, shortId]) => {
|
||||
const returnedOriginalId = table.lookup(shortId);
|
||||
if (returnedOriginalId !== originalId) {
|
||||
throw Error(
|
||||
`Expected shortId ${shortId} to return ${originalId}, but ${returnedOriginalId} was returned instead`
|
||||
);
|
||||
}
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
function generateShortId(size: number): string {
|
||||
let id = '';
|
||||
let i = size;
|
||||
while (i--) {
|
||||
const index = Math.floor(Math.random() * ALPHABET.length);
|
||||
id += ALPHABET[index];
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
const MAX_ATTEMPTS_AT_LENGTH = 100;
|
||||
|
||||
export class ShortIdTable {
|
||||
private byShortId: Map<string, string> = new Map();
|
||||
private byOriginalId: Map<string, string> = new Map();
|
||||
|
||||
constructor() {}
|
||||
|
||||
take(originalId: string) {
|
||||
if (this.byOriginalId.has(originalId)) {
|
||||
return this.byOriginalId.get(originalId)!;
|
||||
}
|
||||
|
||||
let uniqueId: string | undefined;
|
||||
let attemptsAtLength = 0;
|
||||
let length = 4;
|
||||
while (!uniqueId) {
|
||||
const nextId = generateShortId(length);
|
||||
attemptsAtLength++;
|
||||
if (!this.byShortId.has(nextId)) {
|
||||
uniqueId = nextId;
|
||||
} else if (attemptsAtLength >= MAX_ATTEMPTS_AT_LENGTH) {
|
||||
attemptsAtLength = 0;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
this.byShortId.set(uniqueId, originalId);
|
||||
this.byOriginalId.set(originalId, uniqueId);
|
||||
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
lookup(shortId: string) {
|
||||
return this.byShortId.get(shortId);
|
||||
}
|
||||
}
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const P_VALUE_SIGNIFICANCE_HIGH = 1e-6;
|
||||
export const P_VALUE_SIGNIFICANCE_MEDIUM = 0.001;
|
||||
|
||||
export function pValueToLabel(pValue: number): 'high' | 'medium' | 'low' {
|
||||
if (pValue <= P_VALUE_SIGNIFICANCE_HIGH) {
|
||||
return 'high';
|
||||
} else if (pValue <= P_VALUE_SIGNIFICANCE_MEDIUM) {
|
||||
return 'medium';
|
||||
} else {
|
||||
return 'low';
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@ export function unflattenObject(source: Record<string, any>, target: Record<stri
|
|||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in source) {
|
||||
const val = source[key as keyof typeof source];
|
||||
|
||||
if (Array.isArray(val)) {
|
||||
const unflattenedArray = val.map((item) => {
|
||||
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/observability-utils",
|
||||
"name": "@kbn/observability-utils-common",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
|
@ -16,11 +16,8 @@
|
|||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/std",
|
||||
"@kbn/core",
|
||||
"@kbn/es-types",
|
||||
"@kbn/apm-utils",
|
||||
"@kbn/es-query",
|
||||
"@kbn/safer-lodash-set",
|
||||
"@kbn/inference-common",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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 { mapValues } from 'lodash';
|
||||
import { mergeSampleDocumentsWithFieldCaps } from '@kbn/observability-utils-common/llm/log_analysis/merge_sample_documents_with_field_caps';
|
||||
import { DocumentAnalysis } from '@kbn/observability-utils-common/llm/log_analysis/document_analysis';
|
||||
import type { ObservabilityElasticsearchClient } from '../es/client/create_observability_es_client';
|
||||
import { kqlQuery } from '../es/queries/kql_query';
|
||||
import { rangeQuery } from '../es/queries/range_query';
|
||||
|
||||
export async function analyzeDocuments({
|
||||
esClient,
|
||||
kuery,
|
||||
start,
|
||||
end,
|
||||
index,
|
||||
}: {
|
||||
esClient: ObservabilityElasticsearchClient;
|
||||
kuery: string;
|
||||
start: number;
|
||||
end: number;
|
||||
index: string | string[];
|
||||
}): Promise<DocumentAnalysis> {
|
||||
const [fieldCaps, hits] = await Promise.all([
|
||||
esClient.fieldCaps('get_field_caps_for_document_analysis', {
|
||||
index,
|
||||
fields: '*',
|
||||
index_filter: {
|
||||
bool: {
|
||||
filter: rangeQuery(start, end),
|
||||
},
|
||||
},
|
||||
}),
|
||||
esClient
|
||||
.search('get_document_samples', {
|
||||
index,
|
||||
size: 1000,
|
||||
track_total_hits: true,
|
||||
query: {
|
||||
bool: {
|
||||
must: [...kqlQuery(kuery), ...rangeQuery(start, end)],
|
||||
should: [
|
||||
{
|
||||
function_score: {
|
||||
functions: [
|
||||
{
|
||||
random_score: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
sort: {
|
||||
_score: {
|
||||
order: 'desc',
|
||||
},
|
||||
},
|
||||
_source: false,
|
||||
fields: ['*' as const],
|
||||
})
|
||||
.then((response) => ({
|
||||
hits: response.hits.hits.map((hit) =>
|
||||
mapValues(hit.fields!, (value) => (value.length === 1 ? value[0] : value))
|
||||
),
|
||||
total: response.hits.total,
|
||||
})),
|
||||
]);
|
||||
|
||||
const analysis = mergeSampleDocumentsWithFieldCaps({
|
||||
samples: hits.hits,
|
||||
total: hits.total.value,
|
||||
fieldCaps: Object.entries(fieldCaps.fields).map(([name, specs]) => {
|
||||
return { name, esTypes: Object.keys(specs) };
|
||||
}),
|
||||
});
|
||||
|
||||
return analysis;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 { compact, uniq } from 'lodash';
|
||||
import { ObservabilityElasticsearchClient } from '../es/client/create_observability_es_client';
|
||||
import { excludeFrozenQuery } from '../es/queries/exclude_frozen_query';
|
||||
import { kqlQuery } from '../es/queries/kql_query';
|
||||
|
||||
export async function getDataStreamsForEntity({
|
||||
esClient,
|
||||
kuery,
|
||||
index,
|
||||
}: {
|
||||
esClient: ObservabilityElasticsearchClient;
|
||||
kuery: string;
|
||||
index: string | string[];
|
||||
}) {
|
||||
const response = await esClient.search('get_data_streams_for_entity', {
|
||||
track_total_hits: false,
|
||||
index,
|
||||
size: 0,
|
||||
terminate_after: 1,
|
||||
timeout: '1ms',
|
||||
aggs: {
|
||||
indices: {
|
||||
terms: {
|
||||
field: '_index',
|
||||
size: 10000,
|
||||
},
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
filter: [...excludeFrozenQuery(), ...kqlQuery(kuery)],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const allIndices =
|
||||
response.aggregations?.indices.buckets.map((bucket) => bucket.key as string) ?? [];
|
||||
|
||||
if (!allIndices.length) {
|
||||
return {
|
||||
dataStreams: [],
|
||||
};
|
||||
}
|
||||
|
||||
const resolveIndexResponse = await esClient.client.indices.resolveIndex({
|
||||
name: allIndices,
|
||||
});
|
||||
|
||||
const dataStreams = uniq(
|
||||
compact(await resolveIndexResponse.indices.flatMap((idx) => idx.data_stream))
|
||||
);
|
||||
|
||||
return {
|
||||
dataStreams,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { AlertsClient } from '@kbn/rule-registry-plugin/server';
|
||||
import {
|
||||
ALERT_GROUP_FIELD,
|
||||
ALERT_GROUP_VALUE,
|
||||
ALERT_STATUS,
|
||||
ALERT_STATUS_ACTIVE,
|
||||
ALERT_TIME_RANGE,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { kqlQuery } from '../../es/queries/kql_query';
|
||||
import { rangeQuery } from '../../es/queries/range_query';
|
||||
|
||||
export async function getAlertsForEntity({
|
||||
start,
|
||||
end,
|
||||
entity,
|
||||
alertsClient,
|
||||
rulesClient,
|
||||
size,
|
||||
}: {
|
||||
start: number;
|
||||
end: number;
|
||||
entity: Record<string, unknown>;
|
||||
alertsClient: AlertsClient;
|
||||
rulesClient: RulesClient;
|
||||
size: number;
|
||||
}) {
|
||||
const alertsKuery = Object.entries(entity)
|
||||
.map(([field, value]) => {
|
||||
return `(${[
|
||||
`(${ALERT_GROUP_FIELD}:"${field}" AND ${ALERT_GROUP_VALUE}:"${value}")`,
|
||||
`(${field}:"${value}")`,
|
||||
].join(' OR ')})`;
|
||||
})
|
||||
.join(' AND ');
|
||||
|
||||
const openAlerts = await alertsClient.find({
|
||||
size,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...kqlQuery(alertsKuery),
|
||||
...rangeQuery(start, end, ALERT_TIME_RANGE),
|
||||
{ term: { [ALERT_STATUS]: ALERT_STATUS_ACTIVE } },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return openAlerts;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export function getAnomaliesForEntity({
|
||||
start,
|
||||
end,
|
||||
entity,
|
||||
}: {
|
||||
start: number;
|
||||
end: number;
|
||||
entity: Record<string, unknown>;
|
||||
}) {}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 { ObservabilityElasticsearchClient } from '../../es/client/create_observability_es_client';
|
||||
import { kqlQuery } from '../../es/queries/kql_query';
|
||||
|
||||
export async function getSlosForEntity({
|
||||
start,
|
||||
end,
|
||||
entity,
|
||||
esClient,
|
||||
sloSummaryIndices,
|
||||
size,
|
||||
spaceId,
|
||||
}: {
|
||||
start: number;
|
||||
end: number;
|
||||
entity: Record<string, unknown>;
|
||||
esClient: ObservabilityElasticsearchClient;
|
||||
sloSummaryIndices: string | string[];
|
||||
size: number;
|
||||
spaceId: string;
|
||||
}) {
|
||||
const slosKuery = Object.entries(entity)
|
||||
.map(([field, value]) => {
|
||||
return `(slo.groupings.${field}:"${value}")`;
|
||||
})
|
||||
.join(' AND ');
|
||||
|
||||
const sloSummaryResponse = await esClient.search('get_slo_summaries_for_entity', {
|
||||
index: sloSummaryIndices,
|
||||
size,
|
||||
track_total_hits: false,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...kqlQuery(slosKuery),
|
||||
{
|
||||
range: {
|
||||
'slo.createdAt': {
|
||||
lte: end,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
summaryUpdatedAt: {
|
||||
gte: start,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
spaceId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
...sloSummaryResponse,
|
||||
hits: {
|
||||
...sloSummaryResponse.hits,
|
||||
hits: sloSummaryResponse.hits.hits.map((hit) => {
|
||||
return {
|
||||
...hit,
|
||||
_source: hit._source as Record<string, any> & {
|
||||
status: 'VIOLATED' | 'DEGRADED' | 'HEALTHY' | 'NO_DATA';
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
|
@ -5,11 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
EsqlQueryRequest,
|
||||
FieldCapsRequest,
|
||||
FieldCapsResponse,
|
||||
MsearchRequest,
|
||||
SearchResponse,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
import { withSpan } from '@kbn/apm-utils';
|
||||
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
|
||||
import type { ESQLSearchResponse, ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types';
|
||||
import { withSpan } from '@kbn/apm-utils';
|
||||
import type { EsqlQueryRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { esqlResultToPlainObjects } from '../utils/esql_result_to_plain_objects';
|
||||
import { Required } from 'utility-types';
|
||||
import { esqlResultToPlainObjects } from '../esql_result_to_plain_objects';
|
||||
|
||||
type SearchRequest = ESSearchRequest & {
|
||||
index: string | string[];
|
||||
|
@ -39,7 +46,17 @@ export interface ObservabilityElasticsearchClient {
|
|||
search<TDocument = unknown, TSearchRequest extends SearchRequest = SearchRequest>(
|
||||
operationName: string,
|
||||
parameters: TSearchRequest
|
||||
): Promise<InferSearchResponseOf<TDocument, TSearchRequest>>;
|
||||
): Promise<InferSearchResponseOf<TDocument, TSearchRequest, { restTotalHitsAsInt: false }>>;
|
||||
msearch<TDocument = unknown>(
|
||||
operationName: string,
|
||||
parameters: MsearchRequest
|
||||
): Promise<{
|
||||
responses: Array<SearchResponse<TDocument>>;
|
||||
}>;
|
||||
fieldCaps(
|
||||
operationName: string,
|
||||
request: Required<FieldCapsRequest, 'index_filter' | 'fields' | 'index'>
|
||||
): Promise<FieldCapsResponse>;
|
||||
esql<TOutput = unknown, TQueryParams extends EsqlOutputParameters = EsqlOutputParameters>(
|
||||
operationName: string,
|
||||
parameters: TQueryParams
|
||||
|
@ -60,8 +77,38 @@ export function createObservabilityEsClient({
|
|||
logger: Logger;
|
||||
plugin: string;
|
||||
}): ObservabilityElasticsearchClient {
|
||||
// wraps the ES calls in a named APM span for better analysis
|
||||
// (otherwise it would just eg be a _search span)
|
||||
const callWithLogger = <T>(
|
||||
operationName: string,
|
||||
request: Record<string, any>,
|
||||
callback: () => Promise<T>
|
||||
) => {
|
||||
logger.debug(() => `Request (${operationName}):\n${JSON.stringify(request)}`);
|
||||
return withSpan(
|
||||
{
|
||||
name: operationName,
|
||||
labels: {
|
||||
plugin,
|
||||
},
|
||||
},
|
||||
callback,
|
||||
logger
|
||||
).then((response) => {
|
||||
logger.trace(() => `Response (${operationName}):\n${JSON.stringify(response, null, 2)}`);
|
||||
return response;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
client,
|
||||
fieldCaps(operationName, parameters) {
|
||||
return callWithLogger(operationName, parameters, () => {
|
||||
return client.fieldCaps({
|
||||
...parameters,
|
||||
});
|
||||
});
|
||||
},
|
||||
esql<TOutput = unknown, TSearchRequest extends EsqlParameters = EsqlParameters>(
|
||||
operationName: string,
|
||||
{ parseOutput = true, format = 'json', columnar = false, ...parameters }: TSearchRequest
|
||||
|
@ -93,24 +140,17 @@ export function createObservabilityEsClient({
|
|||
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;
|
||||
return callWithLogger(operationName, parameters, () => {
|
||||
return client.search<TDocument>(parameters) as unknown as Promise<
|
||||
InferSearchResponseOf<TDocument, TSearchRequest, { restTotalHitsAsInt: false }>
|
||||
>;
|
||||
});
|
||||
},
|
||||
msearch<TDocument = unknown>(operationName: string, parameters: MsearchRequest) {
|
||||
return callWithLogger(operationName, parameters, () => {
|
||||
return client.msearch<TDocument>(parameters) as unknown as Promise<{
|
||||
responses: Array<SearchResponse<TDocument>>;
|
||||
}>;
|
||||
});
|
||||
},
|
||||
};
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { ESQLSearchResponse } from '@kbn/es-types';
|
||||
import { unflattenObject } from '../../object/unflatten_object';
|
||||
import { unflattenObject } from '@kbn/observability-utils-common/object/unflatten_object';
|
||||
|
||||
export function esqlResultToPlainObjects<TDocument = unknown>(
|
||||
result: ESQLSearchResponse
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
export function excludeFrozenQuery(): estypes.QueryDslQueryContainer[] {
|
||||
return [
|
||||
{
|
||||
bool: {
|
||||
must_not: [
|
||||
{
|
||||
term: {
|
||||
_tier: 'data_frozen',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
|
@ -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)];
|
||||
}
|
|
@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
|
@ -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 } }];
|
||||
}
|
|
@ -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/observability_utils_server'],
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-server",
|
||||
"id": "@kbn/observability-utils-server",
|
||||
"owner": "@elastic/observability-ui"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/observability-utils-server",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/es-types",
|
||||
"@kbn/apm-utils",
|
||||
"@kbn/es-query",
|
||||
"@kbn/observability-utils-common",
|
||||
"@kbn/alerting-plugin",
|
||||
"@kbn/rule-registry-plugin",
|
||||
"@kbn/rule-data-utils",
|
||||
]
|
||||
}
|
|
@ -23,7 +23,7 @@ import { ValuesType } from 'utility-types';
|
|||
import type { APMError, Metric, Span, Transaction, Event } from '@kbn/apm-types/es_schemas_ui';
|
||||
import type { InspectResponse } from '@kbn/observability-plugin/typings/common';
|
||||
import type { DataTier } from '@kbn/observability-shared-plugin/common';
|
||||
import { excludeTiersQuery } from '@kbn/observability-utils/es/queries/exclude_tiers_query';
|
||||
import { excludeTiersQuery } from '@kbn/observability-utils-common/es/queries/exclude_tiers_query';
|
||||
import { withApmSpan } from '../../../../utils';
|
||||
import type { ApmDataSource } from '../../../../../common/data_source';
|
||||
import { cancelEsRequestOnAbort } from '../cancel_es_request_on_abort';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import type { DataTier } from '@kbn/observability-shared-plugin/common';
|
||||
import { excludeTiersQuery } from '@kbn/observability-utils/es/queries/exclude_tiers_query';
|
||||
import { excludeTiersQuery } from '@kbn/observability-utils-common/es/queries/exclude_tiers_query';
|
||||
|
||||
export function getDataTierFilterCombined({
|
||||
filter,
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import type { DedotObject } from '@kbn/utility-types';
|
||||
import * as APM_EVENT_FIELDS_MAP from '@kbn/apm-types/es_fields';
|
||||
import type { ValuesType } from 'utility-types';
|
||||
import { unflattenObject } from '@kbn/observability-utils/object/unflatten_object';
|
||||
import { mergePlainObjects } from '@kbn/observability-utils/object/merge_plain_objects';
|
||||
import { unflattenObject } from '@kbn/observability-utils-common/object/unflatten_object';
|
||||
import { mergePlainObjects } from '@kbn/observability-utils-common/object/merge_plain_objects';
|
||||
import { castArray, isArray } from 'lodash';
|
||||
import { AgentName } from '@kbn/elastic-agent-utils';
|
||||
import { EventOutcome } from '@kbn/apm-types/src/es_schemas/raw/fields';
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
"@kbn/apm-utils",
|
||||
"@kbn/core-http-server",
|
||||
"@kbn/security-plugin-types-server",
|
||||
"@kbn/observability-utils",
|
||||
"@kbn/utility-types",
|
||||
"@kbn/elastic-agent-utils"
|
||||
"@kbn/elastic-agent-utils",
|
||||
"@kbn/observability-utils-common"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import { type EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client';
|
||||
import { type InfraMetricsClient } from '../../lib/helpers/get_infra_metrics_client';
|
||||
import { getDataStreamTypes } from './get_data_stream_types';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { type EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client';
|
||||
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
|
||||
import { EntityDataStreamType } from '@kbn/observability-shared-plugin/common';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import { castArray } from 'lodash';
|
||||
import { type InfraMetricsClient } from '../../lib/helpers/get_infra_metrics_client';
|
||||
import { getHasMetricsData } from './get_has_metrics_data';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { ENTITY_LATEST, entitiesAliasPattern } from '@kbn/entities-schema';
|
||||
import { type EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client';
|
||||
import { ENTITY_TYPE, SOURCE_DATA_STREAM_TYPE } from '@kbn/observability-shared-plugin/common';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
|
||||
const ENTITIES_LATEST_ALIAS = entitiesAliasPattern({
|
||||
type: '*',
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { METRICS_APP_ID } from '@kbn/deeplinks-observability/constants';
|
||||
import { entityCentricExperience } from '@kbn/observability-plugin/common';
|
||||
import { createObservabilityEsClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import { createObservabilityEsClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import { ENTITY_TYPES } from '@kbn/observability-shared-plugin/common';
|
||||
import { getInfraMetricsClient } from '../../lib/helpers/get_infra_metrics_client';
|
||||
import { InfraBackendLibs } from '../../lib/infra_types';
|
||||
|
|
|
@ -114,9 +114,9 @@
|
|||
"@kbn/management-settings-ids",
|
||||
"@kbn/core-ui-settings-common",
|
||||
"@kbn/entityManager-plugin",
|
||||
"@kbn/observability-utils",
|
||||
"@kbn/entities-schema",
|
||||
"@kbn/zod"
|
||||
"@kbn/zod",
|
||||
"@kbn/observability-utils-server"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { EuiSpacer } from '@elastic/eui';
|
|||
import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
|
||||
import React from 'react';
|
||||
import useEffectOnce from 'react-use/lib/useEffectOnce';
|
||||
import { flattenObject } from '@kbn/observability-utils/object/flatten_object';
|
||||
import { flattenObject } from '@kbn/observability-utils-common/object/flatten_object';
|
||||
import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useAbortableAsync } from '@kbn/observability-utils/hooks/use_abortable_async';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
|
||||
import { useState } from 'react';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { useAbortableAsync } from '@kbn/observability-utils/hooks/use_abortable_async';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import {
|
||||
ENTITIES_LATEST_ALIAS,
|
||||
type EntityGroup,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { type ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import { type ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
|
||||
import type { EntityInstance } from '@kbn/entities-schema';
|
||||
import { ENTITIES_LATEST_ALIAS } from '../../../common/entities';
|
||||
|
|
|
@ -5,19 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import {
|
||||
ENTITY_LAST_SEEN,
|
||||
ENTITY_TYPE,
|
||||
ENTITY_DISPLAY_NAME,
|
||||
} from '@kbn/observability-shared-plugin/common';
|
||||
import type { QueryDslQueryContainer, ScalarValue } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { EntityInstance } from '@kbn/entities-schema';
|
||||
import {
|
||||
ENTITY_DISPLAY_NAME,
|
||||
ENTITY_LAST_SEEN,
|
||||
ENTITY_TYPE,
|
||||
} from '@kbn/observability-shared-plugin/common';
|
||||
import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import {
|
||||
ENTITIES_LATEST_ALIAS,
|
||||
InventoryEntity,
|
||||
MAX_NUMBER_OF_ENTITIES,
|
||||
type EntityColumnIds,
|
||||
InventoryEntity,
|
||||
} from '../../../common/entities';
|
||||
import { getBuiltinEntityDefinitionIdESQLWhereClause } from './query_helper';
|
||||
|
||||
|
|
|
@ -6,19 +6,19 @@
|
|||
*/
|
||||
import { INVENTORY_APP_ID } from '@kbn/deeplinks-observability/constants';
|
||||
import { jsonRt } from '@kbn/io-ts-utils';
|
||||
import { createObservabilityEsClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
|
||||
import { joinByKey } from '@kbn/observability-utils-common/array/join_by_key';
|
||||
import { createObservabilityEsClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import * as t from 'io-ts';
|
||||
import { orderBy } from 'lodash';
|
||||
import { joinByKey } from '@kbn/observability-utils/array/join_by_key';
|
||||
import { entityColumnIdsRt, InventoryEntity } from '../../../common/entities';
|
||||
import { createInventoryServerRoute } from '../create_inventory_server_route';
|
||||
import { getEntityTypes } from './get_entity_types';
|
||||
import { getLatestEntities } from './get_latest_entities';
|
||||
import { InventoryEntity, entityColumnIdsRt } from '../../../common/entities';
|
||||
import { createAlertsClient } from '../../lib/create_alerts_client.ts/create_alerts_client';
|
||||
import { getLatestEntitiesAlerts } from './get_latest_entities_alerts';
|
||||
import { getIdentityFieldsPerEntityType } from './get_identity_fields_per_entity_type';
|
||||
import { createInventoryServerRoute } from '../create_inventory_server_route';
|
||||
import { getEntityGroupsBy } from './get_entity_groups';
|
||||
import { getEntityTypes } from './get_entity_types';
|
||||
import { getIdentityFieldsPerEntityType } from './get_identity_fields_per_entity_type';
|
||||
import { getLatestEntities } from './get_latest_entities';
|
||||
import { getLatestEntitiesAlerts } from './get_latest_entities_alerts';
|
||||
|
||||
export const getEntityTypesRoute = createInventoryServerRoute({
|
||||
endpoint: 'GET /internal/inventory/entities/types',
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import { type ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import { type ObservabilityElasticsearchClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import { getBuiltinEntityDefinitionIdESQLWhereClause } from '../entities/query_helper';
|
||||
import { ENTITIES_LATEST_ALIAS } from '../../../common/entities';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { createObservabilityEsClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
|
||||
import { createObservabilityEsClient } from '@kbn/observability-utils-server/es/client/create_observability_es_client';
|
||||
import { INVENTORY_APP_ID } from '@kbn/deeplinks-observability/constants';
|
||||
import { createInventoryServerRoute } from '../create_inventory_server_route';
|
||||
import { getHasData } from './get_has_data';
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"@kbn/server-route-repository",
|
||||
"@kbn/shared-ux-link-redirect-app",
|
||||
"@kbn/typed-react-router-config",
|
||||
"@kbn/observability-utils",
|
||||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/i18n",
|
||||
"@kbn/deeplinks-observability",
|
||||
|
@ -57,6 +56,9 @@
|
|||
"@kbn/deeplinks-analytics",
|
||||
"@kbn/controls-plugin",
|
||||
"@kbn/securitysolution-io-ts-types",
|
||||
"@kbn/react-hooks"
|
||||
"@kbn/react-hooks",
|
||||
"@kbn/observability-utils-common",
|
||||
"@kbn/observability-utils-browser",
|
||||
"@kbn/observability-utils-server"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { EuiLoadingSpinner, EuiFlexItem } from '@elastic/eui';
|
|||
import { css } from '@emotion/css';
|
||||
import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
|
||||
import type { GlobalWidgetParameters } from '@kbn/investigate-plugin/public';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils/hooks/use_abortable_async';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { v4 } from 'uuid';
|
||||
import { ErrorMessage } from '../../components/error_message';
|
||||
|
|
|
@ -10,7 +10,7 @@ import type { ESQLSearchResponse } from '@kbn/es-types';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { type GlobalWidgetParameters } from '@kbn/investigate-plugin/public';
|
||||
import type { Suggestion } from '@kbn/lens-plugin/public';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils/hooks/use_abortable_async';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
|
||||
import React, { useMemo } from 'react';
|
||||
import { ErrorMessage } from '../../components/error_message';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { ESQLColumn, ESQLRow } from '@kbn/es-types';
|
|||
import { GlobalWidgetParameters } from '@kbn/investigate-plugin/public';
|
||||
import { Item } from '@kbn/investigation-shared';
|
||||
import type { Suggestion } from '@kbn/lens-plugin/public';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils/hooks/use_abortable_async';
|
||||
import { useAbortableAsync } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { ErrorMessage } from '../../../../components/error_message';
|
||||
import { SuggestVisualizationList } from '../../../../components/suggest_visualization_list';
|
||||
|
|
|
@ -10,7 +10,7 @@ import moment from 'moment';
|
|||
import { Chart, Axis, AreaSeries, Position, ScaleType, Settings } from '@elastic/charts';
|
||||
import { useActiveCursor } from '@kbn/charts-plugin/public';
|
||||
import { EuiSkeletonText } from '@elastic/eui';
|
||||
import { getBrushData } from '@kbn/observability-utils/chart/utils';
|
||||
import { getBrushData } from '@kbn/observability-utils-browser/chart/utils';
|
||||
import { Group } from '@kbn/observability-alerting-rule-utils';
|
||||
import { ALERT_GROUP } from '@kbn/rule-data-utils';
|
||||
import { SERVICE_NAME } from '@kbn/observability-shared-plugin/common';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import pLimit from 'p-limit';
|
||||
import { estypes } from '@elastic/elasticsearch';
|
||||
import { castArray, sortBy, uniq, partition, shuffle } from 'lodash';
|
||||
import { truncateList } from '@kbn/inference-plugin/common/utils/truncate_list';
|
||||
import { truncateList } from '@kbn/inference-common';
|
||||
import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types';
|
||||
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import { rangeQuery, excludeFrozenQuery } from './queries';
|
||||
|
|
|
@ -62,13 +62,13 @@
|
|||
"@kbn/licensing-plugin",
|
||||
"@kbn/rule-data-utils",
|
||||
"@kbn/entities-schema",
|
||||
"@kbn/inference-plugin",
|
||||
"@kbn/core-elasticsearch-server",
|
||||
"@kbn/calculate-auto",
|
||||
"@kbn/ml-random-sampler-utils",
|
||||
"@kbn/charts-plugin",
|
||||
"@kbn/observability-utils",
|
||||
"@kbn/observability-alerting-rule-utils",
|
||||
"@kbn/observability-utils-browser",
|
||||
"@kbn/usage-collection-plugin",
|
||||
"@kbn/inference-common",
|
||||
],
|
||||
}
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -6447,7 +6447,15 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/observability-utils@link:x-pack/packages/observability/observability_utils":
|
||||
"@kbn/observability-utils-browser@link:x-pack/packages/observability/observability_utils/observability_utils_browser":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/observability-utils-common@link:x-pack/packages/observability/observability_utils/observability_utils_common":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/observability-utils-server@link:x-pack/packages/observability/observability_utils/observability_utils_server":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue