diff --git a/.changeset/clickable-log-components.md b/.changeset/clickable-log-components.md new file mode 100644 index 000000000..85acd64a9 --- /dev/null +++ b/.changeset/clickable-log-components.md @@ -0,0 +1,5 @@ +--- +'@openchoreo/backstage-plugin-openchoreo-observability': minor +--- + +Made component names in the project-level logging view clickable, allowing users to navigate directly to the component-scoped logging view while preserving the current time range and any relevant filters. diff --git a/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.test.tsx b/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.test.tsx index c71371266..5efce186d 100644 --- a/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.test.tsx +++ b/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.test.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { LogEntry } from './LogEntry'; @@ -48,7 +49,11 @@ function renderLogEntry( expanded, onToggleExpand: () => setExpanded(prev => !prev), }; - return ; + return ( + + + + ); }; return render(); } @@ -97,6 +102,21 @@ describe('LogEntry', () => { expect(screen.getByText('api-service')).toBeInTheDocument(); }); + it('renders component name link with correct path', () => { + renderLogEntry({ + selectedFields: [...allFields, LogEntryField.ComponentName], + entityNamespace: 'my-namespace', + entityKind: 'Component', + }); + + const link = screen.getByRole('link', { name: 'api-service' }); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute( + 'href', + '/catalog/my-namespace/component/api-service/runtime-logs', + ); + }); + it('expands to show metadata on row click', async () => { const user = userEvent.setup(); renderLogEntry(); diff --git a/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.tsx b/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.tsx index aa0edcf2c..907a62fe3 100644 --- a/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.tsx +++ b/plugins/openchoreo-observability/src/components/RuntimeLogs/LogEntry.tsx @@ -9,8 +9,12 @@ import { } from '@material-ui/core'; import FileCopyOutlined from '@material-ui/icons/FileCopyOutlined'; import { LogEntry as LogEntryType, LogEntryField } from './types'; +import { Link } from '@backstage/core-components'; +import { useLocation } from 'react-router-dom'; import { useLogEntryStyles } from './styles'; import { getColumnStyle } from './columns'; +import { Entity } from '@backstage/catalog-model'; +import { buildRuntimeLogsBasePath } from '@openchoreo/backstage-plugin-react'; /** * Render-prop slot for a per-row action button (assistant integration, @@ -29,6 +33,8 @@ interface LogEntryProps { environmentName?: string; projectName?: string; componentName?: string; + entityNamespace?: string; + entityKind?: string; /** * Whether this row is currently expanded. Controlled by the parent so that * expansion survives the virtualizer unmounting the row off-screen. @@ -53,12 +59,15 @@ export const LogEntry: FC = ({ environmentName, projectName, componentName, + entityNamespace, + entityKind, expanded, onToggleExpand, getLogsSnapshot, renderRowAction, }) => { const classes = useLogEntryStyles(); + const location = useLocation(); const [copySuccess, setCopySuccess] = useState(false); const handleCopyLog = async (event: MouseEvent) => { @@ -140,13 +149,32 @@ export const LogEntry: FC = ({ } if (field === LogEntryField.ComponentName) { + const compName = log.metadata?.componentName ?? componentName ?? ''; + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: entityKind || 'Component', + metadata: { + name: compName, + namespace: entityNamespace, + }, + }; + const basePath = buildRuntimeLogsBasePath(entity); return ( - {log.metadata?.componentName ?? componentName ?? ''} + {compName ? ( + e.stopPropagation()} + > + {compName} + + ) : ( + '' + )} ); } diff --git a/plugins/openchoreo-observability/src/components/RuntimeLogs/LogsTable.tsx b/plugins/openchoreo-observability/src/components/RuntimeLogs/LogsTable.tsx index f332a8fbf..506f4b298 100644 --- a/plugins/openchoreo-observability/src/components/RuntimeLogs/LogsTable.tsx +++ b/plugins/openchoreo-observability/src/components/RuntimeLogs/LogsTable.tsx @@ -21,6 +21,8 @@ interface LogsTableProps { environmentName?: string; projectName?: string; componentName?: string; + entityNamespace?: string; + entityKind?: string; renderRowAction?: RenderLogRowAction; } @@ -37,6 +39,8 @@ export const LogsTable: FC = ({ environmentName, projectName, componentName, + entityNamespace, + entityKind, renderRowAction, }) => { const classes = useLogsTableStyles(); @@ -164,6 +168,8 @@ export const LogsTable: FC = ({ environmentName={environmentName} projectName={projectName} componentName={componentName} + entityNamespace={entityNamespace} + entityKind={entityKind} expanded={expanded.has(key)} onToggleExpand={() => toggle(key)} getLogsSnapshot={getLogsSnapshot} diff --git a/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx b/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx index a02a108a9..c59856328 100644 --- a/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx +++ b/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx @@ -241,6 +241,8 @@ const ObservabilityProjectRuntimeLogsContent = ({ selectedEnvironment?.displayName || selectedEnvironment?.name } projectName={projectName} + entityNamespace={entity.metadata.namespace} + entityKind="component" renderRowAction={renderRowAction} /> diff --git a/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx b/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx index 44df01637..903a64eb6 100644 --- a/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx +++ b/plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx @@ -257,6 +257,8 @@ const ObservabilityRuntimeLogsContent = ({ } projectName={project} componentName={componentName} + entityNamespace={entity.metadata.namespace} + entityKind={entity.kind} renderRowAction={renderRowAction} />