Skip to content

Commit 21bef2d

Browse files
committed
Fix logs summary to invoke observability API
1 parent c3747da commit 21bef2d

File tree

1 file changed

+93
-18
lines changed

1 file changed

+93
-18
lines changed

plugins/openchoreo/src/components/RuntimeLogs/OverviewCard/useLogsSummary.ts

Lines changed: 93 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { useCallback, useEffect, useState } from 'react';
22
import { useEntity } from '@backstage/plugin-catalog-react';
3-
import { useApi } from '@backstage/core-plugin-api';
3+
import {
4+
useApi,
5+
discoveryApiRef,
6+
fetchApiRef,
7+
} from '@backstage/core-plugin-api';
48
import { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';
59
import { openChoreoClientApiRef } from '../../../api/OpenChoreoClientApi';
610
import { calculateTimeRange } from '../../../api/runtimeLogs';
7-
import type { LogEntry, Environment } from '../types';
11+
import type { LogEntry, Environment, LogsResponse } from '../types';
812

913
interface LogsSummaryState {
1014
errorCount: number;
@@ -23,6 +27,8 @@ interface LogsSummaryState {
2327
export function useLogsSummary() {
2428
const { entity } = useEntity();
2529
const client = useApi(openChoreoClientApiRef);
30+
const discoveryApi = useApi(discoveryApiRef);
31+
const fetchApi = useApi(fetchApiRef);
2632

2733
const [state, setState] = useState<LogsSummaryState>({
2834
errorCount: 0,
@@ -44,6 +50,35 @@ export function useLogsSummary() {
4450
throw new Error('Component ID not found');
4551
}
4652

53+
// Get project ID
54+
const project = entity.metadata.annotations?.[CHOREO_ANNOTATIONS.PROJECT];
55+
const organization =
56+
entity.metadata.annotations?.[CHOREO_ANNOTATIONS.ORGANIZATION];
57+
58+
if (!project || !organization) {
59+
throw new Error('Project or organization not found in annotations');
60+
}
61+
62+
// Fetch project details to get projectId
63+
const projectUrl = new URL(
64+
`${await discoveryApi.getBaseUrl('openchoreo')}/project`,
65+
);
66+
projectUrl.search = new URLSearchParams({
67+
projectName: project,
68+
organizationName: organization,
69+
}).toString();
70+
71+
const projectResponse = await fetchApi.fetch(projectUrl.toString());
72+
if (!projectResponse.ok) {
73+
throw new Error('Failed to fetch project details');
74+
}
75+
const projectData = await projectResponse.json();
76+
const projectId = projectData.uid;
77+
78+
if (!projectId) {
79+
throw new Error('Project ID not found');
80+
}
81+
4782
// Get environments
4883
const environments: Environment[] = await client.getEnvironments(entity);
4984

@@ -60,28 +95,68 @@ export function useLogsSummary() {
6095
const selectedEnv = environments[0];
6196
const componentName =
6297
entity.metadata.annotations?.[CHOREO_ANNOTATIONS.COMPONENT];
63-
64-
if (!componentName) {
65-
throw new Error('Component name not found in annotations');
98+
const projectName =
99+
entity.metadata.annotations?.[CHOREO_ANNOTATIONS.PROJECT];
100+
const orgName =
101+
entity.metadata.annotations?.[CHOREO_ANNOTATIONS.ORGANIZATION];
102+
103+
if (!componentName || !projectName || !orgName) {
104+
throw new Error(
105+
'Component name, project, or organization not found in annotations',
106+
);
66107
}
67108

68109
const { startTime, endTime } = calculateTimeRange('1h');
69110

70-
// Fetch logs to get counts
71-
const response = await client.getRuntimeLogs(entity, {
72-
componentId,
73-
componentName,
74-
environmentId: selectedEnv.id,
75-
environmentName: selectedEnv.resourceName,
76-
logLevels: [], // Get all levels
77-
startTime,
78-
endTime,
79-
limit: 100, // Limit for performance, we just need counts
80-
offset: 0,
111+
// Call observability backend directly
112+
const baseUrl = await discoveryApi.getBaseUrl(
113+
'openchoreo-observability-backend',
114+
);
115+
const url = new URL(
116+
`${baseUrl}/logs/component/${componentName}?orgName=${encodeURIComponent(
117+
orgName,
118+
)}&projectName=${encodeURIComponent(projectName)}`,
119+
);
120+
121+
const response = await fetchApi.fetch(url.toString(), {
122+
method: 'POST',
123+
headers: {
124+
'Content-Type': 'application/json',
125+
},
126+
body: JSON.stringify({
127+
componentId,
128+
projectId,
129+
environmentId: selectedEnv.id,
130+
environmentName: selectedEnv.resourceName,
131+
componentName,
132+
orgName,
133+
projectName,
134+
options: {
135+
limit: 100, // Limit for performance, we just need counts
136+
startTime,
137+
endTime,
138+
logLevels: [], // Get all levels
139+
},
140+
}),
81141
});
82142

143+
if (!response.ok) {
144+
const errorData = await response.json().catch(() => ({}));
145+
if (
146+
errorData.message?.includes('Observability is not enabled') ||
147+
response.status === 404
148+
) {
149+
throw new Error('Observability is not enabled for this component');
150+
}
151+
throw new Error(
152+
`Failed to fetch runtime logs: ${response.status} ${response.statusText}`,
153+
);
154+
}
155+
156+
const data: LogsResponse = await response.json();
157+
83158
// Count errors and warnings
84-
const logs: LogEntry[] = response.logs || [];
159+
const logs: LogEntry[] = data.logs || [];
85160
const errorCount = logs.filter(log => log.logLevel === 'ERROR').length;
86161
const warningCount = logs.filter(log => log.logLevel === 'WARN').length;
87162

@@ -117,7 +192,7 @@ export function useLogsSummary() {
117192
}));
118193
}
119194
}
120-
}, [entity, client]);
195+
}, [entity, client, discoveryApi, fetchApi]);
121196

122197
const refresh = useCallback(async () => {
123198
setState(prev => ({ ...prev, refreshing: true }));

0 commit comments

Comments
 (0)