mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
feat(slo): Add slo usage telemetry (#150393)
This commit is contained in:
parent
1a3c9f0015
commit
fe4239abe0
7 changed files with 325 additions and 1 deletions
|
@ -67,7 +67,12 @@ export function SlosPage() {
|
|||
defaultMessage: 'SLOs',
|
||||
}),
|
||||
rightSideItems: [
|
||||
<EuiButton color="primary" fill onClick={handleClickCreateSlo}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
onClick={handleClickCreateSlo}
|
||||
data-test-subj="slosPage-createNewSloButton"
|
||||
>
|
||||
{i18n.translate('xpack.observability.slos.sloList.pageHeader.createNewButtonLabel', {
|
||||
defaultMessage: 'Create new SLO',
|
||||
})}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { savedObjectsRepositoryMock, ElasticsearchClientMock } from '@kbn/core/server/mocks';
|
||||
import { CollectorFetchContext } from '@kbn/usage-collection-plugin/server';
|
||||
import { fetcher } from './fetcher';
|
||||
|
||||
let savedObjectClient: ReturnType<typeof savedObjectsRepositoryMock.create>;
|
||||
|
||||
let closeMock: jest.Mock;
|
||||
let esClient: ElasticsearchClientMock;
|
||||
|
||||
describe('SLO usage collector fetcher', () => {
|
||||
beforeEach(() => {
|
||||
savedObjectClient = savedObjectsRepositoryMock.create();
|
||||
closeMock = jest.fn();
|
||||
});
|
||||
|
||||
it('without any existing slo', async () => {
|
||||
savedObjectClient.createPointInTimeFinder.mockReturnValue({
|
||||
find: async function* find() {
|
||||
return {
|
||||
[Symbol.asyncIterator]: async () => {},
|
||||
next: () => {},
|
||||
};
|
||||
},
|
||||
close: closeMock,
|
||||
});
|
||||
|
||||
const results = await fetcher({
|
||||
soClient: savedObjectClient,
|
||||
esClient,
|
||||
} as CollectorFetchContext);
|
||||
|
||||
expect(closeMock).toHaveBeenCalled();
|
||||
expect(results.slo.total).toEqual(0);
|
||||
});
|
||||
});
|
|
@ -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 { CollectorFetchContext } from '@kbn/usage-collection-plugin/server';
|
||||
import { StoredSLO } from '../../domain/models';
|
||||
import { SO_SLO_TYPE } from '../../saved_objects';
|
||||
import { Usage } from './type';
|
||||
|
||||
export const fetcher = async (context: CollectorFetchContext) => {
|
||||
const finder = context.soClient.createPointInTimeFinder<StoredSLO>({
|
||||
type: SO_SLO_TYPE,
|
||||
perPage: 100,
|
||||
});
|
||||
|
||||
let usage: Usage['slo'] = {
|
||||
total: 0,
|
||||
by_status: {
|
||||
enabled: 0,
|
||||
disabled: 0,
|
||||
},
|
||||
by_sli_type: {},
|
||||
by_rolling_duration: {},
|
||||
by_calendar_aligned_duration: {},
|
||||
by_budgeting_method: {
|
||||
occurrences: 0,
|
||||
timeslices: 0,
|
||||
},
|
||||
};
|
||||
|
||||
for await (const response of finder.find()) {
|
||||
usage = response.saved_objects.reduce((acc, so) => {
|
||||
return {
|
||||
...acc,
|
||||
total: acc.total + 1,
|
||||
by_status: {
|
||||
...acc.by_status,
|
||||
...(so.attributes.enabled && { enabled: acc.by_status.enabled + 1 }),
|
||||
...(!so.attributes.enabled && { disabled: acc.by_status.disabled + 1 }),
|
||||
},
|
||||
by_sli_type: {
|
||||
...acc.by_sli_type,
|
||||
[so.attributes.indicator.type]: (acc.by_sli_type[so.attributes.indicator.type] ?? 0) + 1,
|
||||
},
|
||||
by_rolling_duration: {
|
||||
...acc.by_rolling_duration,
|
||||
...('isRolling' in so.attributes.timeWindow && {
|
||||
[so.attributes.timeWindow.duration]:
|
||||
(acc.by_rolling_duration[so.attributes.timeWindow.duration] ?? 0) + 1,
|
||||
}),
|
||||
},
|
||||
by_calendar_aligned_duration: {
|
||||
...acc.by_calendar_aligned_duration,
|
||||
...('calendar' in so.attributes.timeWindow && {
|
||||
[so.attributes.timeWindow.duration]:
|
||||
(acc.by_calendar_aligned_duration[so.attributes.timeWindow.duration] ?? 0) + 1,
|
||||
}),
|
||||
},
|
||||
by_budgeting_method: {
|
||||
...acc.by_budgeting_method,
|
||||
...(so.attributes.budgetingMethod === 'occurrences' && {
|
||||
occurrences: acc.by_budgeting_method.occurrences + 1,
|
||||
}),
|
||||
...(so.attributes.budgetingMethod === 'timeslices' && {
|
||||
timeslices: acc.by_budgeting_method.timeslices + 1,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}, usage);
|
||||
}
|
||||
|
||||
await finder.close();
|
||||
|
||||
return {
|
||||
slo: usage,
|
||||
};
|
||||
};
|
|
@ -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 { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { fetcher } from './fetcher';
|
||||
import type { Usage } from './type';
|
||||
|
||||
export function registerSloUsageCollector(usageCollection?: UsageCollectionSetup): void {
|
||||
if (!usageCollection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sloUsageCollector = usageCollection.makeUsageCollector<Usage>({
|
||||
type: 'slo',
|
||||
schema: {
|
||||
slo: {
|
||||
total: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The total number of slos in the cluster',
|
||||
},
|
||||
},
|
||||
by_status: {
|
||||
enabled: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of enabled slos in the cluster',
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of disabled slos in the cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
by_sli_type: {
|
||||
DYNAMIC_KEY: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of slos by sli type in the cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
by_rolling_duration: {
|
||||
DYNAMIC_KEY: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of slos by rolling duration in the cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
by_calendar_aligned_duration: {
|
||||
DYNAMIC_KEY: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of slos by calendar aligned duration in the cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
by_budgeting_method: {
|
||||
occurrences: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of slos by timeslices budgeting method in the cluster',
|
||||
},
|
||||
},
|
||||
timeslices: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'The number of slos by occurrences budgeting method in the cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isReady: () => true,
|
||||
fetch: fetcher,
|
||||
});
|
||||
|
||||
// register usage collector
|
||||
usageCollection.registerCollector(sloUsageCollector);
|
||||
}
|
29
x-pack/plugins/observability/server/lib/collectors/type.ts
Normal file
29
x-pack/plugins/observability/server/lib/collectors/type.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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export interface Usage {
|
||||
slo: {
|
||||
total: number;
|
||||
by_status: {
|
||||
enabled: number;
|
||||
disabled: number;
|
||||
};
|
||||
by_sli_type: {
|
||||
[sli_type: string]: number;
|
||||
};
|
||||
by_rolling_duration: {
|
||||
[duration: string]: number;
|
||||
};
|
||||
by_calendar_aligned_duration: {
|
||||
[duration: string]: number;
|
||||
};
|
||||
by_budgeting_method: {
|
||||
occurrences: number;
|
||||
timeslices: number;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -23,6 +23,7 @@ import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_fr
|
|||
import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/rule-registry-plugin/common/assets';
|
||||
import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server';
|
||||
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import {
|
||||
kubernetesGuideId,
|
||||
kubernetesGuideConfig,
|
||||
|
@ -40,6 +41,7 @@ import { casesFeatureId, observabilityFeatureId } from '../common';
|
|||
import { slo } from './saved_objects';
|
||||
import { OBSERVABILITY_FEATURE_ID, RULE_REGISTRATION_CONTEXT } from './common/constants';
|
||||
import { registerRuleTypes } from './lib/rules/register_rule_types';
|
||||
import { registerSloUsageCollector } from './lib/collectors/register';
|
||||
|
||||
export type ObservabilityPluginSetup = ReturnType<ObservabilityPlugin['setup']>;
|
||||
|
||||
|
@ -49,6 +51,7 @@ interface PluginSetup {
|
|||
spaces: SpacesPluginStart;
|
||||
alerting: PluginSetupContract;
|
||||
guidedOnboarding: GuidedOnboardingPluginSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
}
|
||||
|
||||
export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
||||
|
@ -174,6 +177,8 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
});
|
||||
|
||||
registerRuleTypes(plugins.alerting, this.logger, ruleDataClient);
|
||||
|
||||
registerSloUsageCollector(plugins.usageCollection);
|
||||
}
|
||||
|
||||
const start = () => core.getStartServices().then(([coreStart]) => coreStart);
|
||||
|
|
|
@ -13204,6 +13204,82 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"slo": {
|
||||
"properties": {
|
||||
"slo": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The total number of slos in the cluster"
|
||||
}
|
||||
},
|
||||
"by_status": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of enabled slos in the cluster"
|
||||
}
|
||||
},
|
||||
"disabled": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of disabled slos in the cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"by_sli_type": {
|
||||
"properties": {
|
||||
"DYNAMIC_KEY": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of slos by sli type in the cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"by_rolling_duration": {
|
||||
"properties": {
|
||||
"DYNAMIC_KEY": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of slos by rolling duration in the cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"by_calendar_aligned_duration": {
|
||||
"properties": {
|
||||
"DYNAMIC_KEY": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of slos by calendar aligned duration in the cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"by_budgeting_method": {
|
||||
"properties": {
|
||||
"occurrences": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of slos by timeslices budgeting method in the cluster"
|
||||
}
|
||||
},
|
||||
"timeslices": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of slos by occurrences budgeting method in the cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"spaces": {
|
||||
"properties": {
|
||||
"usesFeatureControls": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue