Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,39 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'

/api/metrics/component/http:
post:
tags:
- Metrics
summary: Get component HTTP metrics
description: Retrieve HTTP request metrics (request counts, latency percentiles) for a component as time series data
operationId: getComponentHTTPMetrics
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MetricsRequest'
responses:
'200':
description: Successfully retrieved HTTP metrics
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPMetricsTimeSeries'
'400':
description: Bad request - invalid parameters
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

/api/metrics/component/usage:
post:
tags:
Expand Down Expand Up @@ -476,6 +509,7 @@ components:
MetricsRequest:
type: object
required:
- componentId
- environmentId
- projectId
properties:
Expand Down Expand Up @@ -623,6 +657,81 @@ components:
- time: '2025-01-10T12:05:00Z'
value: 2147483648

HTTPMetricsTimeSeries:
type: object
properties:
requestCount:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: Total HTTP request count time series (requests per second)
successfulRequestCount:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: Successful HTTP request count time series (status 200, requests per second)
unsuccessfulRequestCount:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: Unsuccessful HTTP request count time series (status != 200, requests per second)
meanLatency:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: Mean HTTP request latency time series (in seconds)
latencyPercentile50th:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: 50th percentile (median) HTTP request latency time series (in seconds)
latencyPercentile90th:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: 90th percentile HTTP request latency time series (in seconds)
latencyPercentile99th:
type: array
items:
$ref: '#/components/schemas/TimeValuePoint'
description: 99th percentile HTTP request latency time series (in seconds)
example:
requestCount:
- time: '2025-01-10T12:00:00Z'
value: 125.5
- time: '2025-01-10T12:05:00Z'
value: 143.2
successfulRequestCount:
- time: '2025-01-10T12:00:00Z'
value: 120.0
- time: '2025-01-10T12:05:00Z'
value: 138.5
unsuccessfulRequestCount:
- time: '2025-01-10T12:00:00Z'
value: 5.5
- time: '2025-01-10T12:05:00Z'
value: 4.7
meanLatency:
- time: '2025-01-10T12:00:00Z'
value: 0.125
- time: '2025-01-10T12:05:00Z'
value: 0.132
latencyPercentile50th:
- time: '2025-01-10T12:00:00Z'
value: 0.095
- time: '2025-01-10T12:05:00Z'
value: 0.102
latencyPercentile90th:
- time: '2025-01-10T12:00:00Z'
value: 0.250
- time: '2025-01-10T12:05:00Z'
value: 0.265
latencyPercentile99th:
- time: '2025-01-10T12:00:00Z'
value: 0.500
- time: '2025-01-10T12:05:00Z'
value: 0.520

ErrorResponse:
type: object
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ export interface paths {
patch?: never;
trace?: never;
};
'/api/metrics/component/http': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Get component HTTP metrics
* @description Retrieve HTTP request metrics (request counts, latency percentiles) for a component as time series data
*/
post: operations['getComponentHTTPMetrics'];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
'/api/metrics/component/usage': {
parameters: {
query?: never;
Expand Down Expand Up @@ -350,7 +370,7 @@ export interface components {
* @description Component identifier
* @example comp-123
*/
componentId?: string;
componentId: string;
/**
* @description Environment identifier
* @example env-dev
Expand Down Expand Up @@ -507,6 +527,96 @@ export interface components {
/** @description Memory limits time series (in bytes) */
memoryLimits?: components['schemas']['TimeValuePoint'][];
};
/**
* @example {
* "requestCount": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 125.5
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 143.2
* }
* ],
* "successfulRequestCount": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 120
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 138.5
* }
* ],
* "unsuccessfulRequestCount": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 5.5
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 4.7
* }
* ],
* "meanLatency": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 0.125
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 0.132
* }
* ],
* "latencyPercentile50th": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 0.095
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 0.102
* }
* ],
* "latencyPercentile90th": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 0.25
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 0.265
* }
* ],
* "latencyPercentile99th": [
* {
* "time": "2025-01-10T12:00:00Z",
* "value": 0.5
* },
* {
* "time": "2025-01-10T12:05:00Z",
* "value": 0.52
* }
* ]
* }
*/
HTTPMetricsTimeSeries: {
/** @description Total HTTP request count time series (requests per second) */
requestCount?: components['schemas']['TimeValuePoint'][];
/** @description Successful HTTP request count time series (status 200, requests per second) */
successfulRequestCount?: components['schemas']['TimeValuePoint'][];
/** @description Unsuccessful HTTP request count time series (status != 200, requests per second) */
unsuccessfulRequestCount?: components['schemas']['TimeValuePoint'][];
/** @description Mean HTTP request latency time series (in seconds) */
meanLatency?: components['schemas']['TimeValuePoint'][];
/** @description 50th percentile (median) HTTP request latency time series (in seconds) */
latencyPercentile50th?: components['schemas']['TimeValuePoint'][];
/** @description 90th percentile HTTP request latency time series (in seconds) */
latencyPercentile90th?: components['schemas']['TimeValuePoint'][];
/** @description 99th percentile HTTP request latency time series (in seconds) */
latencyPercentile99th?: components['schemas']['TimeValuePoint'][];
};
ErrorResponse: {
/**
* @description Error type
Expand Down Expand Up @@ -790,6 +900,48 @@ export interface operations {
};
};
};
getComponentHTTPMetrics: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
'application/json': components['schemas']['MetricsRequest'];
};
};
responses: {
/** @description Successfully retrieved HTTP metrics */
200: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['HTTPMetricsTimeSeries'];
};
};
/** @description Bad request - invalid parameters */
400: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['ErrorResponse'];
};
};
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['ErrorResponse'];
};
};
};
};
getComponentResourceMetrics: {
parameters: {
query?: never;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
createOpenChoreoApiClient,
createObservabilityClientWithUrl,
} from '@openchoreo/openchoreo-client-node';
import { Environment, ResourceMetricsTimeSeries } from '../types';
import { ComponentMetricsTimeSeries, Environment } from '../types';

/**
* Error thrown when observability is not configured for a component
Expand Down Expand Up @@ -132,7 +132,7 @@ export class ObservabilityService {
startTime?: string;
endTime?: string;
},
): Promise<ResourceMetricsTimeSeries> {
): Promise<ComponentMetricsTimeSeries> {
const startTime = Date.now();
try {
this.logger.debug(
Expand Down Expand Up @@ -203,6 +203,22 @@ export class ObservabilityService {
},
);

const {
data: httpData,
error: httpError,
response: httpResponse,
} = await obsClient.POST('/api/metrics/component/http', {
body: {
componentId,
environmentId,
projectId,
limit: options?.limit || 100,
offset: options?.offset || 0,
startTime: options?.startTime,
endTime: options?.endTime,
},
});

if (error || !response.ok) {
const errorText = await response.text();
this.logger.error(
Expand All @@ -214,6 +230,17 @@ export class ObservabilityService {
);
}

if (httpError || !httpResponse.ok) {
const errorText = await httpResponse.text();
this.logger.error(
`Failed to fetch HTTP metrics for component ${componentId}: ${httpResponse.status} ${httpResponse.statusText}`,
{ error: errorText },
);
throw new Error(
`Failed to fetch HTTP metrics: ${httpResponse.status} ${httpResponse.statusText}`,
);
}

this.logger.debug(
`Successfully fetched metrics for component ${componentId}: ${JSON.stringify(
data,
Expand All @@ -234,6 +261,13 @@ export class ObservabilityService {
memory: data.memory ?? [],
memoryRequests: data.memoryRequests ?? [],
memoryLimits: data.memoryLimits ?? [],
requestCount: httpData.requestCount ?? [],
successfulRequestCount: httpData.successfulRequestCount ?? [],
unsuccessfulRequestCount: httpData.unsuccessfulRequestCount ?? [],
meanLatency: httpData.meanLatency ?? [],
latencyPercentile50th: httpData.latencyPercentile50th ?? [],
latencyPercentile90th: httpData.latencyPercentile90th ?? [],
latencyPercentile99th: httpData.latencyPercentile99th ?? [],
};
} catch (error: unknown) {
if (error instanceof ObservabilityNotConfiguredError) {
Expand Down
4 changes: 4 additions & 0 deletions plugins/openchoreo-observability-backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export type Environment =
OpenChoreoComponents['schemas']['EnvironmentResponse'];
export type ResourceMetricsTimeSeries =
ObservabilityComponents['schemas']['ResourceMetricsTimeSeries'];

export type ComponentMetricsTimeSeries =
ObservabilityComponents['schemas']['ResourceMetricsTimeSeries'] &
ObservabilityComponents['schemas']['HTTPMetricsTimeSeries'];
Loading