Skip to content

Commit a945a25

Browse files
committed
feat: add database hyperlink to logging service
1 parent 7aacdfe commit a945a25

File tree

8 files changed

+148
-8
lines changed

8 files changed

+148
-8
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {FileText} from '@gravity-ui/icons';
2+
import type {ButtonSize} from '@gravity-ui/uikit';
3+
import {Button, Icon} from '@gravity-ui/uikit';
4+
5+
interface LogsButtonProps {
6+
className?: string;
7+
href: string;
8+
size?: ButtonSize;
9+
}
10+
11+
export function LogsButton({href, className, size = 'xs'}: LogsButtonProps) {
12+
return (
13+
<Button href={href} target="_blank" className={className} size={size} title="Database logs">
14+
<Icon data={FileText} />
15+
</Button>
16+
);
17+
}

src/components/TenantNameWrapper/TenantNameWrapper.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,25 @@ export function TenantNameWrapper({tenant, additionalTenantsProps}: TenantNameWr
3939
const isExternalLink = Boolean(backend);
4040

4141
const monitoringLink = additionalTenantsProps?.getMonitoringLink?.(tenant.Name, tenant.Type);
42+
const logsLink = additionalTenantsProps?.getLogsLink?.(tenant.Name);
4243

4344
return (
4445
<CellWithPopover
45-
disabled={!isUserAllowedToMakeChanges || !monitoringLink}
46+
disabled={!isUserAllowedToMakeChanges || (!monitoringLink && !logsLink)}
4647
delayClosing={200}
4748
content={
48-
monitoringLink ? (
49+
monitoringLink || logsLink ? (
4950
<DefinitionList responsive>
5051
<DefinitionList.Item name={i18n('field_links')}>
51-
<LinkWithIcon
52-
title={i18n('field_monitoring-link')}
53-
url={monitoringLink}
54-
/>
52+
{monitoringLink && (
53+
<LinkWithIcon
54+
title={i18n('field_monitoring-link')}
55+
url={monitoringLink}
56+
/>
57+
)}
58+
{logsLink && (
59+
<LinkWithIcon title={i18n('field_logs-link')} url={logsLink} />
60+
)}
5561
</DefinitionList.Item>
5662
</DefinitionList>
5763
) : null
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"field_links": "Links",
33
"field_monitoring-link": "Monitoring",
4+
"field_logs-link": "Logs",
45
"context_unknown": "unknown database"
56
}

src/containers/AppWithClusters/ExtendedCluster/ExtendedCluster.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {cn} from '../../../utils/cn';
1111
import {USE_CLUSTER_BALANCER_AS_BACKEND_KEY} from '../../../utils/constants';
1212
import {useSetting} from '../../../utils/hooks';
1313
import {useAdditionalNodesProps} from '../../../utils/hooks/useAdditionalNodesProps';
14-
import type {GetMonitoringClusterLink, GetMonitoringLink} from '../../../utils/monitoring';
14+
import type {
15+
GetLogsLink,
16+
GetMonitoringClusterLink,
17+
GetMonitoringLink,
18+
} from '../../../utils/monitoring';
1519
import {getCleanBalancerValue, removeViewerPathname} from '../../../utils/parseBalancer';
1620
import {getBackendFromNodeHost, getBackendFromRawNodeData} from '../../../utils/prepareBackend';
1721
import type {Cluster} from '../../Cluster/Cluster';
@@ -63,6 +67,7 @@ const getAdditionalTenantsProps = (
6367
balancer: string | undefined,
6468
useClusterBalancerAsBackend: boolean | undefined,
6569
getMonitoringLink?: GetMonitoringLink,
70+
getLogsLink?: GetLogsLink,
6671
) => {
6772
const additionalTenantsProps: AdditionalTenantsProps = {};
6873

@@ -99,18 +104,34 @@ const getAdditionalTenantsProps = (
99104
};
100105
}
101106

107+
if (monitoring && getLogsLink) {
108+
additionalTenantsProps.getLogsLink = (dbName?: string) => {
109+
if (dbName) {
110+
return getLogsLink({
111+
dbName,
112+
clusterName,
113+
monitoring,
114+
});
115+
}
116+
117+
return null;
118+
};
119+
}
120+
102121
return additionalTenantsProps;
103122
};
104123

105124
interface ExtendedClusterProps {
106125
component: typeof Cluster;
107126
getMonitoringLink?: GetMonitoringLink;
108127
getMonitoringClusterLink?: GetMonitoringClusterLink;
128+
getLogsLink?: GetLogsLink;
109129
}
110130
export function ExtendedCluster({
111131
component: ClusterComponent,
112132
getMonitoringLink,
113133
getMonitoringClusterLink,
134+
getLogsLink,
114135
}: ExtendedClusterProps) {
115136
const additionalNodesProps = useAdditionalNodesProps();
116137
const {name, balancer, monitoring} = useClusterBaseInfo();
@@ -132,6 +153,7 @@ export function ExtendedCluster({
132153
balancer,
133154
useClusterBalancerAsBackend,
134155
getMonitoringLink,
156+
getLogsLink,
135157
)}
136158
additionalNodesProps={additionalNodesProps}
137159
/>

src/containers/AppWithClusters/ExtendedTenant/ExtendedTenant.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import {useClusterBaseInfo} from '../../../store/reducers/cluster/cluster';
22
import type {ETenantType} from '../../../types/api/tenant';
33
import {useAdditionalNodesProps} from '../../../utils/hooks/useAdditionalNodesProps';
4-
import type {GetMonitoringLink} from '../../../utils/monitoring';
4+
import type {GetLogsLink, GetMonitoringLink} from '../../../utils/monitoring';
55
import type {Tenant} from '../../Tenant/Tenant';
66

77
export interface ExtendedTenantProps {
88
component: typeof Tenant;
99
getMonitoringLink?: GetMonitoringLink;
10+
getLogsLink?: GetLogsLink;
1011
}
1112

1213
export function ExtendedTenant({
1314
component: TenantComponent,
1415
getMonitoringLink,
16+
getLogsLink,
1517
}: ExtendedTenantProps) {
1618
const {monitoring} = useClusterBaseInfo();
1719
const additionalNodesProps = useAdditionalNodesProps();
@@ -26,6 +28,16 @@ export function ExtendedTenant({
2628
});
2729
}
2830

31+
return null;
32+
},
33+
getLogsLink: (dbName?: string) => {
34+
if (monitoring && dbName && getLogsLink) {
35+
return getLogsLink({
36+
dbName,
37+
monitoring,
38+
});
39+
}
40+
2941
return null;
3042
},
3143
};

src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Loader} from '@gravity-ui/uikit';
22

33
import {EntityStatus} from '../../../../components/EntityStatus/EntityStatus';
4+
import {LogsButton} from '../../../../components/LogsButton/LogsButton';
45
import {MonitoringButton} from '../../../../components/MonitoringButton/MonitoringButton';
56
import {overviewApi} from '../../../../store/reducers/overview/overview';
67
import {TENANT_METRICS_TABS_IDS} from '../../../../store/reducers/tenant/constants';
@@ -150,6 +151,7 @@ export function TenantOverview({
150151
}
151152

152153
const monitoringLink = additionalTenantProps?.getMonitoringLink?.(Name, Type);
154+
const logsLink = additionalTenantProps?.getLogsLink?.(Name);
153155

154156
return (
155157
<div className={b()}>
@@ -158,6 +160,7 @@ export function TenantOverview({
158160
<div className={b('top')}>
159161
{renderName()}
160162
{monitoringLink && <MonitoringButton href={monitoringLink} />}
163+
{logsLink && <LogsButton href={logsLink} />}
161164
</div>
162165
<MetricsCards
163166
poolsCpuStats={poolsStats}

src/types/additionalProps.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface AdditionalClusterProps {
1515
export interface AdditionalTenantsProps {
1616
prepareTenantBackend?: (backend: string | NodeAddress | undefined) => string | undefined;
1717
getMonitoringLink?: (name?: string, type?: ETenantType) => string | null;
18+
getLogsLink?: (name?: string) => string | null;
1819
}
1920

2021
export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints' | 'NodeId'>;

src/utils/monitoring.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,81 @@ export function parseMonitoringData(monitoring: string): ParsedMonitoringData |
9999

100100
return undefined;
101101
}
102+
103+
interface GetLogsLinkProps {
104+
dbName: string;
105+
clusterName?: string;
106+
monitoring: string;
107+
project?: string;
108+
service?: string;
109+
level?: string;
110+
from?: string;
111+
to?: string;
112+
columns?: string[];
113+
groupByField?: string;
114+
chartType?: string;
115+
linesMode?: string;
116+
}
117+
118+
export type GetLogsLink = typeof getLogsLink;
119+
120+
export function getLogsLink({
121+
dbName,
122+
clusterName,
123+
monitoring,
124+
project = 'kikimr',
125+
service = 'ydb',
126+
level = 'ERROR',
127+
from = 'now-1h',
128+
to = 'now',
129+
columns = ['level', 'time', 'message', 'host'],
130+
groupByField = 'level',
131+
chartType = 'line',
132+
linesMode = 'single',
133+
}: GetLogsLinkProps): string {
134+
try {
135+
const data = parseMonitoringData(monitoring);
136+
137+
if (data) {
138+
const finalClusterName = data.cluster_name || clusterName || '';
139+
140+
const url = new URL(data.monitoring_url);
141+
142+
url.pathname += '/_______/';
143+
144+
const queryFilter = {
145+
project,
146+
service,
147+
cluster: finalClusterName,
148+
database: dbName,
149+
level,
150+
};
151+
152+
url.searchParams.set('from', from);
153+
url.searchParams.set('to', to);
154+
155+
const queryString = Object.entries(queryFilter)
156+
.map(([key, value]) => `${key} = "${value}"`)
157+
.join(', ');
158+
url.searchParams.set('query', `{${queryString}}`);
159+
160+
url.searchParams.set('columns', columns.join(','));
161+
162+
if (groupByField) {
163+
url.searchParams.set('groupByField', groupByField);
164+
}
165+
166+
if (chartType) {
167+
url.searchParams.set('chartType', chartType);
168+
}
169+
170+
if (linesMode) {
171+
url.searchParams.set('linesMode', linesMode);
172+
}
173+
174+
return url.toString();
175+
}
176+
} catch {}
177+
178+
return '';
179+
}

0 commit comments

Comments
 (0)