11import { useCallback , useEffect , useState } from 'react' ;
22import { 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' ;
48import { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common' ;
59import { openChoreoClientApiRef } from '../../../api/OpenChoreoClientApi' ;
610import { calculateTimeRange } from '../../../api/runtimeLogs' ;
7- import type { LogEntry , Environment } from '../types' ;
11+ import type { LogEntry , Environment , LogsResponse } from '../types' ;
812
913interface LogsSummaryState {
1014 errorCount : number ;
@@ -23,6 +27,8 @@ interface LogsSummaryState {
2327export 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