1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-24 01:18:01 +02:00
unleash.unleash/src/lib/routes/admin-api/metrics.ts
Gastón Fournier 4b519ead4f
perf: Simplify queries to prometheus (#2706)
## About the changes
This PR improves our queries to Prometheus (instead of making multiple queries do only one) and improves the UI and the code. 

The reports aggregate all HTTP methods (GET, POST, PUT, DELETE, OPTIONS, HEAD and PATCH) without distinction under the same "endpoint" (a relative path inside unleash up to a certain depth)

Co-authored-by: Nuno Góis <nuno@getunleash.ai>
2022-12-19 17:06:59 +01:00

199 lines
6.4 KiB
TypeScript

import { Request, Response } from 'express';
import Controller from '../controller';
import { NONE, UPDATE_APPLICATION } from '../../types/permissions';
import { IUnleashConfig } from '../../types/option';
import { IUnleashServices } from '../../types/services';
import { Logger } from '../../logger';
import ClientInstanceService from '../../services/client-metrics/instance-service';
import { createRequestSchema } from '../../openapi/util/create-request-schema';
import { createResponseSchema } from '../../openapi/util/create-response-schema';
import { ApplicationSchema } from '../../openapi/spec/application-schema';
import { ApplicationsSchema } from '../../openapi/spec/applications-schema';
import { emptyResponse } from '../../openapi/util/standard-responses';
import { RequestsPerSecondSegmentedSchema } from 'lib/openapi/spec/requests-per-second-segmented-schema';
import { IFlagResolver } from 'lib/types';
type RpsError = string;
class MetricsController extends Controller {
private logger: Logger;
private flagResolver: IFlagResolver;
private clientInstanceService: ClientInstanceService;
constructor(
config: IUnleashConfig,
{
clientInstanceService,
openApiService,
}: Pick<IUnleashServices, 'clientInstanceService' | 'openApiService'>,
) {
super(config);
this.logger = config.getLogger('/admin-api/metrics.ts');
this.flagResolver = config.flagResolver;
this.clientInstanceService = clientInstanceService;
// deprecated routes
this.get('/seen-toggles', this.deprecated);
this.get('/seen-apps', this.deprecated);
this.get('/feature-toggles', this.deprecated);
this.get('/feature-toggles/:name', this.deprecated);
this.route({
method: 'post',
path: '/applications/:appName',
handler: this.createApplication,
permission: UPDATE_APPLICATION,
middleware: [
openApiService.validPath({
tags: ['Metrics'],
operationId: 'createApplication',
responses: {
202: emptyResponse,
},
requestBody: createRequestSchema('applicationSchema'),
}),
],
});
this.route({
method: 'delete',
path: '/applications/:appName',
handler: this.deleteApplication,
permission: UPDATE_APPLICATION,
acceptAnyContentType: true,
middleware: [
openApiService.validPath({
tags: ['Metrics'],
operationId: 'deleteApplication',
responses: {
200: emptyResponse,
},
}),
],
});
this.route({
method: 'get',
path: '/applications',
handler: this.getApplications,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Metrics'],
operationId: 'getApplications',
responses: {
200: createResponseSchema('applicationsSchema'),
},
}),
],
});
this.route({
method: 'get',
path: '/applications/:appName',
handler: this.getApplication,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Metrics'],
operationId: 'getApplication',
responses: {
200: createResponseSchema('applicationSchema'),
},
}),
],
});
this.route({
method: 'get',
path: '/rps',
handler: this.getRps,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Metrics'],
operationId: 'getRequestsPerSecond',
responses: {
200: createResponseSchema(
'requestsPerSecondSegmentedSchema',
),
},
}),
],
});
}
async deprecated(req: Request, res: Response): Promise<void> {
res.status(410).json({
lastHour: {},
lastMinute: {},
maturity: 'deprecated',
});
}
async deleteApplication(
req: Request<{ appName: string }>,
res: Response,
): Promise<void> {
const { appName } = req.params;
await this.clientInstanceService.deleteApplication(appName);
res.status(200).end();
}
async createApplication(
req: Request<{ appName: string }, unknown, ApplicationSchema>,
res: Response,
): Promise<void> {
const input = {
...req.body,
appName: req.params.appName,
};
await this.clientInstanceService.createApplication(input);
res.status(202).end();
}
async getApplications(
req: Request,
res: Response<ApplicationsSchema>,
): Promise<void> {
const query = req.query.strategyName
? { strategyName: req.query.strategyName as string }
: {};
const applications = await this.clientInstanceService.getApplications(
query,
);
res.json({ applications });
}
async getApplication(
req: Request,
res: Response<ApplicationSchema>,
): Promise<void> {
const { appName } = req.params;
const appDetails = await this.clientInstanceService.getApplication(
appName,
);
res.json(appDetails);
}
async getRps(
req: Request,
res: Response<RequestsPerSecondSegmentedSchema | RpsError>,
): Promise<void> {
if (!this.flagResolver.isEnabled('networkView')) {
res.status(404).send('Not enabled');
return;
}
try {
const hoursToQuery = 6;
const rps = await this.clientInstanceService.getRPS(hoursToQuery);
res.json(rps || {});
} catch (err) {
this.logger.error('Failed to fetch RPS metrics', err);
res.status(500).send('Error fetching RPS metrics');
}
}
}
export default MetricsController;