Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a8636de
fix(nav): hide nav groups if all items are feature flagged
andrewazores Aug 29, 2025
8e0d8e7
diagnostics > thread dump set to beta feature level
andrewazores Aug 29, 2025
bc4ec94
remove tabbing from Thread Dumps view, correct page title
andrewazores Aug 29, 2025
c146b36
rename files and components
andrewazores Aug 29, 2025
dfd3fe5
Revert "remove tabbing from Thread Dumps view, correct page title"
andrewazores Aug 29, 2025
1049430
remove empty component wrapper
andrewazores Aug 29, 2025
bd15814
feature flag dashboard card button for thread dumps
andrewazores Aug 29, 2025
bc6ccdf
move target selector into Thread Dumps card to prepare for All Target…
andrewazores Aug 29, 2025
c46ebfa
lint
andrewazores Aug 29, 2025
7b2a343
fixup! remove empty component wrapper
andrewazores Aug 29, 2025
6a2876c
rename
andrewazores Aug 29, 2025
fd049d1
fixup! rename
andrewazores Aug 29, 2025
86c324c
add Diagnotics > Capture > Diagnostics view
andrewazores Aug 29, 2025
6ac3efc
un-nest
andrewazores Aug 29, 2025
8dc0b85
relabel
andrewazores Aug 29, 2025
7387023
correct link
andrewazores Aug 29, 2025
864498e
clean up table actions
andrewazores Aug 29, 2025
39652e9
remove 'upload' button
andrewazores Aug 29, 2025
cd2bfb1
fixup! remove 'upload' button
andrewazores Aug 29, 2025
09f1787
remove nested card styling
andrewazores Aug 29, 2025
15ed4d9
fixup! remove nested card styling
andrewazores Aug 29, 2025
193e7d9
Revert "clean up table actions"
andrewazores Aug 29, 2025
08c3c76
shorten download action label
andrewazores Aug 29, 2025
02f83c1
include file size in thread dump table
andrewazores Aug 29, 2025
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
3 changes: 2 additions & 1 deletion locales/en/public.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
"DATETIME": "Date and Time"
},
"Diagnostics": {
"THREAD_DUMPS_TAB_TITLE": "Thread Dumps"
"TARGET_THREAD_DUMPS_TAB_TITLE": "Targets"
},
"DiagnosticsCard": {
"DIAGNOSTICS_ACTION_FAILURE": "Diagnostics Failure: {{kind}}",
Expand All @@ -374,6 +374,7 @@
"DIAGNOSTICS_CARD_TITLE": "Diagnostics",
"DIAGNOSTICS_GC_BUTTON": "Invoke Garbage Collection",
"DIAGNOSTICS_THREAD_DUMP_BUTTON": "Invoke Thread Dump",
"DIAGNOSTICS_THREAD_DUMP_TABLE_TOOLTIP": "View captured Thread Dumps",
"DIAGONSTICS_THREAD_REDIRECT_BUTTON": "View collected Thread Dumps",
"KINDS": {
"GC": "Garbage Collection",
Expand Down
14 changes: 9 additions & 5 deletions src/app/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ export const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
.map((route, idx) => renderable(route, idx));
if (!k) {
items = renderables;
} else if (!renderables.length) {
items = [];
} else {
const anyActive = rs.some((r) => isActiveRoute(r));
items = [
Expand All @@ -612,11 +614,13 @@ export const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
aria-label={t('AppLayout.TOOLBAR.ARIA_LABELS.GLOBAL_NAVIGATION')}
>
<NavList>
{Array.from(groups.entries()).map(([groupTitle, items]) => (
<NavGroup title={t(groupTitle)} key={groupTitle}>
{items}
</NavGroup>
))}
{Array.from(groups.entries())
.filter(([_, items]) => items.length)
.map(([groupTitle, items]) => (
<NavGroup title={t(groupTitle)} key={groupTitle}>
{items}
</NavGroup>
))}
</NavList>
</Nav>
);
Expand Down
1 change: 0 additions & 1 deletion src/app/Archives/Archives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ export const Archives: React.FC<ArchivesProps> = ({ ...props }) => {
<Card isCompact>
<CardBody>{cardBody}</CardBody>
</Card>
<></>
</BreadcrumbPage>
);
};
Expand Down
71 changes: 41 additions & 30 deletions src/app/Dashboard/Diagnostics/DiagnosticsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
DashboardCardDescriptor,
} from '@app/Dashboard/types';
import { CryostatLink } from '@app/Shared/Components/CryostatLink';
import { FeatureFlag } from '@app/Shared/Components/FeatureFlag';
import { NotificationCategory } from '@app/Shared/Services/api.types';
import { NotificationsContext } from '@app/Shared/Services/Notifications.service';
import { FeatureLevel } from '@app/Shared/Services/service.types';
Expand All @@ -41,6 +42,8 @@ import {
EmptyStateFooter,
ActionList,
Tooltip,
Stack,
StackItem,
} from '@patternfly/react-core';
import { ListIcon, WrenchIcon } from '@patternfly/react-icons';
import * as React from 'react';
Expand Down Expand Up @@ -141,36 +144,44 @@ export const DiagnosticsCard: DashboardCardFC<DiagnosticsCardProps> = (props) =>
/>
<EmptyStateBody>{t('DiagnosticsCard.DIAGNOSTICS_CARD_DESCRIPTION')}</EmptyStateBody>
<EmptyStateFooter>
<ActionList>
<Button
variant="primary"
onClick={handleGC}
spinnerAriaValueText="Invoke GC"
spinnerAriaLabel="invoke-gc"
isLoading={running}
>
{t('DiagnosticsCard.DIAGNOSTICS_GC_BUTTON')}
</Button>
</ActionList>
<ActionList>
<Button
variant="primary"
onClick={handleThreadDump}
spinnerAriaValueText="Invoke Thread Dump"
spinnerAriaLabel="invoke-thread-dump"
isLoading={running}
>
{t('DiagnosticsCard.DIAGNOSTICS_THREAD_DUMP_BUTTON')}
</Button>
<Tooltip content={t('DiagnosticsCard.DIAGNOSTICS_THREAD_DUMP_TABLE_TOOLTIP')}>
<Button
variant="primary"
isAriaDisabled={!threadDumpReady}
component={(props) => <CryostatLink {...props} to="/diagnostics" />}
icon={<ListIcon />}
/>
</Tooltip>
</ActionList>
<Stack hasGutter>
<StackItem>
<ActionList>
<Button
variant="primary"
onClick={handleGC}
spinnerAriaValueText="Invoke GC"
spinnerAriaLabel="invoke-gc"
isLoading={running}
>
{t('DiagnosticsCard.DIAGNOSTICS_GC_BUTTON')}
</Button>
</ActionList>
</StackItem>
<StackItem>
<FeatureFlag level={FeatureLevel.BETA}>
<ActionList>
<Button
variant="primary"
onClick={handleThreadDump}
spinnerAriaValueText="Invoke Thread Dump"
spinnerAriaLabel="invoke-thread-dump"
isLoading={running}
>
{t('DiagnosticsCard.DIAGNOSTICS_THREAD_DUMP_BUTTON')}
</Button>
<Tooltip content={t('DiagnosticsCard.DIAGNOSTICS_THREAD_DUMP_TABLE_TOOLTIP')}>
<Button
variant="primary"
isAriaDisabled={!threadDumpReady}
component={(props) => <CryostatLink {...props} to="/thread-dumps" />}
icon={<ListIcon />}
/>
</Tooltip>
</ActionList>
</FeatureFlag>
</StackItem>
</Stack>
</EmptyStateFooter>
</EmptyState>
</Bullseye>
Expand Down
94 changes: 94 additions & 0 deletions src/app/Diagnostics/AnalyzeThreadDumps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright The Cryostat Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BreadcrumbPage } from '@app/BreadcrumbPage/BreadcrumbPage';
import { NullableTarget } from '@app/Shared/Services/api.types';
import { ServiceContext } from '@app/Shared/Services/Services';
import { NoTargetSelected } from '@app/TargetView/NoTargetSelected';
import { TargetContextSelector } from '@app/TargetView/TargetContextSelector';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { getActiveTab, switchTab } from '@app/utils/utils';
import { Card, CardBody, Stack, StackItem, Tab, Tabs, TabTitleText } from '@patternfly/react-core';
import { t } from 'i18next';
import * as React from 'react';
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';
import { ThreadDumpsTable } from './ThreadDumpsTable';

export interface AnalyzeThreadDumpsProps {}

enum AnalyzeThreadDumpsTab {
THREAD_DUMPS = 'target-thread-dumps',
}

export const AnalyzeThreadDumps: React.FC<AnalyzeThreadDumpsProps> = ({ ...props }) => {
const { search, pathname } = useLocation();
const navigate = useNavigate();
const context = React.useContext(ServiceContext);
const addSubscription = useSubscriptions();

const [target, setTarget] = React.useState(undefined as NullableTarget);

React.useEffect(() => {
addSubscription(context.target.target().subscribe((t) => setTarget(t)));
}, [addSubscription, context.target, setTarget]);

const activeTab = React.useMemo(() => {
return getActiveTab(search, 'tab', Object.values(AnalyzeThreadDumpsTab), AnalyzeThreadDumpsTab.THREAD_DUMPS);
}, [search]);

const onTabSelect = React.useCallback(
(_: React.MouseEvent, key: string | number) =>
switchTab(navigate, pathname, search, { tabKey: 'tab', tabValue: `${key}` }),
[navigate, pathname, search],
);

const cardBody = React.useMemo(
() => (
<Tabs id="threadDumps" activeKey={activeTab} onSelect={onTabSelect} unmountOnExit>
<Tab
id="threadDumps"
eventKey={AnalyzeThreadDumpsTab.THREAD_DUMPS}
title={<TabTitleText>{t('Diagnostics.TARGET_THREAD_DUMPS_TAB_TITLE')}</TabTitleText>}
data-quickstart-id="thread-dumps-tab"
>
<Stack hasGutter>
<StackItem>
<TargetContextSelector />
</StackItem>
<StackItem>
{target ? (
<ThreadDumpsTable />
) : (
// FIXME this should be an "AllTargetsThreadDumpsTable" like the AllTargetsArchivedRecordingsTable
<NoTargetSelected />
)}
</StackItem>
</Stack>
</Tab>
</Tabs>
),
[activeTab, onTabSelect, target],
);

return (
<BreadcrumbPage {...props} pageTitle="Thread Dumps">
<Card isFullHeight>
<CardBody isFilled>{cardBody}</CardBody>
</Card>
</BreadcrumbPage>
);
};

export default AnalyzeThreadDumps;
Loading
Loading