mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 09:59:04 +08:00
feat: added metric page in messaging queues (#6399)
* feat: added metric page in messaging queues * feat: added misc fixes * feat: removed a class name from mqcards * feat: added lightMode styles for kafka 2.0 (#6400) * feat: resolved comments and used strings
This commit is contained in:
parent
831540eaf0
commit
63872983c6
24
frontend/public/locales/en-GB/messagingQueues.json
Normal file
24
frontend/public/locales/en-GB/messagingQueues.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"metricGraphCategory": {
|
||||
"brokerMetrics": {
|
||||
"title": "Broker Metrics",
|
||||
"description": "The Kafka Broker metrics here inform you of data loss/delay through unclean leader elections and network throughputs, as well as request fails through request purgatories and timeouts metrics"
|
||||
},
|
||||
"consumerMetrics": {
|
||||
"title": "Consumer Metrics",
|
||||
"description": "Kafka Consumer metrics provide insights into lag between message production and consumption, success rates and latency of message delivery, and the volume of data consumed."
|
||||
},
|
||||
"producerMetrics": {
|
||||
"title": "Producer Metrics",
|
||||
"description": "Kafka Producers send messages to brokers for storage and distribution by topic. These metrics inform you of the volume and rate of data sent, and the success rate of message delivery."
|
||||
},
|
||||
"brokerJVMMetrics": {
|
||||
"title": "Broker JVM Metrics",
|
||||
"description": "Kafka brokers are Java applications that expose JVM metrics to inform on the broker's system health. Garbage collection metrics like those below provide key insights into free memory, broker performance, and heap size. You need to enable new_gc_metrics for this section to populate."
|
||||
},
|
||||
"partitionMetrics": {
|
||||
"title": "Partition Metrics",
|
||||
"description": "Kafka partitions are the unit of parallelism in Kafka. These metrics inform you of the number of partitions per topic, the current offset of each partition, the oldest offset, and the number of in-sync replicas."
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +1,54 @@
|
||||
{
|
||||
"breadcrumb": "Messaging Queues",
|
||||
"header": "Kafka / Overview",
|
||||
"overview": {
|
||||
"title": "Start sending data in as little as 20 minutes",
|
||||
"subtitle": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"configureConsumer": {
|
||||
"title": "Configure Consumer",
|
||||
"description": "Add consumer data sources to gain insights and enhance monitoring.",
|
||||
"button": "Get Started"
|
||||
},
|
||||
"configureProducer": {
|
||||
"title": "Configure Producer",
|
||||
"description": "Add producer data sources to gain insights and enhance monitoring.",
|
||||
"button": "Get Started"
|
||||
},
|
||||
"monitorKafka": {
|
||||
"title": "Monitor kafka",
|
||||
"description": "Add your Kafka source to gain insights and enhance activity tracking.",
|
||||
"button": "Get Started"
|
||||
},
|
||||
"summarySection": {
|
||||
"viewDetailsButton": "View Details"
|
||||
},
|
||||
"confirmModal": {
|
||||
"content": "Before navigating to the details page, please make sure you have configured all the required setup to ensure correct data monitoring.",
|
||||
"okText": "Proceed"
|
||||
}
|
||||
}
|
||||
"breadcrumb": "Messaging Queues",
|
||||
"header": "Kafka / Overview",
|
||||
"overview": {
|
||||
"title": "Start sending data in as little as 20 minutes",
|
||||
"subtitle": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"configureConsumer": {
|
||||
"title": "Configure Consumer",
|
||||
"description": "Add consumer data sources to gain insights and enhance monitoring.",
|
||||
"button": "Get Started"
|
||||
},
|
||||
"configureProducer": {
|
||||
"title": "Configure Producer",
|
||||
"description": "Add producer data sources to gain insights and enhance monitoring.",
|
||||
"button": "Get Started"
|
||||
},
|
||||
"monitorKafka": {
|
||||
"title": "Monitor kafka",
|
||||
"description": "Add your Kafka source to gain insights and enhance activity tracking.",
|
||||
"button": "Get Started"
|
||||
},
|
||||
"summarySection": {
|
||||
"viewDetailsButton": "View Details",
|
||||
"consumer": {
|
||||
"title": "Consumer lag view",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"producer": {
|
||||
"title": "Producer latency view",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"partition": {
|
||||
"title": "Partition Latency view",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"dropRate": {
|
||||
"title": "Drop Rate view",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"metricPage": {
|
||||
"title": "Metric View",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
}
|
||||
},
|
||||
"confirmModal": {
|
||||
"content": "Before navigating to the details page, please make sure you have configured all the required setup to ensure correct data monitoring.",
|
||||
"okText": "Proceed"
|
||||
},
|
||||
"overviewSummarySection": {
|
||||
"title": "Monitor Your Data Streams",
|
||||
"subtitle": "Monitor key Kafka metrics like consumer lag and latency to ensure efficient data flow and troubleshoot in real time."
|
||||
}
|
||||
}
|
||||
|
24
frontend/public/locales/en/messagingQueues.json
Normal file
24
frontend/public/locales/en/messagingQueues.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"metricGraphCategory": {
|
||||
"brokerMetrics": {
|
||||
"title": "Broker Metrics",
|
||||
"description": "The Kafka Broker metrics here inform you of data loss/delay through unclean leader elections and network throughputs, as well as request fails through request purgatories and timeouts metrics"
|
||||
},
|
||||
"consumerMetrics": {
|
||||
"title": "Consumer Metrics",
|
||||
"description": "Kafka Consumer metrics provide insights into lag between message production and consumption, success rates and latency of message delivery, and the volume of data consumed."
|
||||
},
|
||||
"producerMetrics": {
|
||||
"title": "Producer Metrics",
|
||||
"description": "Kafka Producers send messages to brokers for storage and distribution by topic. These metrics inform you of the volume and rate of data sent, and the success rate of message delivery."
|
||||
},
|
||||
"brokerJVMMetrics": {
|
||||
"title": "Broker JVM Metrics",
|
||||
"description": "Kafka brokers are Java applications that expose JVM metrics to inform on the broker's system health. Garbage collection metrics like those below provide key insights into free memory, broker performance, and heap size. You need to enable new_gc_metrics for this section to populate."
|
||||
},
|
||||
"partitionMetrics": {
|
||||
"title": "Partition Metrics",
|
||||
"description": "Kafka partitions are the unit of parallelism in Kafka. These metrics inform you of the number of partitions per topic, the current offset of each partition, the oldest offset, and the number of in-sync replicas."
|
||||
}
|
||||
}
|
||||
}
|
@ -37,6 +37,10 @@
|
||||
"dropRate": {
|
||||
"title": "Drop Rate view",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
},
|
||||
"metricPage": {
|
||||
"title": "Metric View",
|
||||
"description": "Connect and Monitor Your Data Streams"
|
||||
}
|
||||
},
|
||||
"confirmModal": {
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
} from '../MessagingQueuesUtils';
|
||||
import DropRateView from '../MQDetails/DropRateView/DropRateView';
|
||||
import MessagingQueueOverview from '../MQDetails/MessagingQueueOverview';
|
||||
import MetricPage from '../MQDetails/MetricPage/MetricPage';
|
||||
import MessagingQueuesDetails from '../MQDetails/MQDetails';
|
||||
import MessagingQueuesConfigOptions from '../MQGraph/MQConfigOptions';
|
||||
import MessagingQueuesGraph from '../MQGraph/MQGraph';
|
||||
@ -60,6 +61,10 @@ function MQDetailPage(): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
const showMessagingQueueDetails =
|
||||
selectedView !== MessagingQueuesViewType.dropRate.value &&
|
||||
selectedView !== MessagingQueuesViewType.metricPage.value;
|
||||
|
||||
return (
|
||||
<div className="messaging-queue-container">
|
||||
<div className="messaging-breadcrumb">
|
||||
@ -82,7 +87,7 @@ function MQDetailPage(): JSX.Element {
|
||||
setSelectedView(value);
|
||||
updateUrlQuery({ [QueryParams.mqServiceView]: value });
|
||||
}}
|
||||
value={mqServiceView}
|
||||
value={selectedView}
|
||||
options={[
|
||||
{
|
||||
label: MessagingQueuesViewType.consumerLag.label,
|
||||
@ -100,6 +105,10 @@ function MQDetailPage(): JSX.Element {
|
||||
label: MessagingQueuesViewType.dropRate.label,
|
||||
value: MessagingQueuesViewType.dropRate.value,
|
||||
},
|
||||
{
|
||||
label: MessagingQueuesViewType.metricPage.label,
|
||||
value: MessagingQueuesViewType.metricPage.value,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@ -112,6 +121,8 @@ function MQDetailPage(): JSX.Element {
|
||||
</div>
|
||||
) : selectedView === MessagingQueuesViewType.dropRate.value ? (
|
||||
<DropRateView />
|
||||
) : selectedView === MessagingQueuesViewType.metricPage.value ? (
|
||||
<MetricPage />
|
||||
) : (
|
||||
<MessagingQueueOverview
|
||||
selectedView={selectedView}
|
||||
@ -119,7 +130,7 @@ function MQDetailPage(): JSX.Element {
|
||||
setOption={setproducerLatencyOption}
|
||||
/>
|
||||
)}
|
||||
{selectedView !== MessagingQueuesViewType.dropRate.value && (
|
||||
{showMessagingQueueDetails && (
|
||||
<div className="messaging-queue-details">
|
||||
<MessagingQueuesDetails
|
||||
selectedView={selectedView}
|
||||
|
@ -28,3 +28,16 @@
|
||||
width: 200px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.evaluation-time-selector {
|
||||
.eval-title {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.ant-selector {
|
||||
background-color: var(--bg-vanilla-200);
|
||||
border: 1px solid var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,9 +230,7 @@ function DropRateView(): JSX.Element {
|
||||
return (
|
||||
<div className={cx('mq-overview-container', 'droprate-view')}>
|
||||
<div className="mq-overview-title">
|
||||
<div className="drop-rat-title">
|
||||
{MessagingQueuesViewType.dropRate.label}
|
||||
</div>
|
||||
{MessagingQueuesViewType.dropRate.label}
|
||||
<EvaluationTimeSelector setInterval={setInterval} />
|
||||
</div>
|
||||
<Table
|
||||
|
@ -22,15 +22,13 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.drop-rat-title {
|
||||
color: var(--bg-vanilla-200);
|
||||
color: var(--bg-vanilla-200);
|
||||
|
||||
font-family: Inter;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
}
|
||||
font-family: Inter;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.mq-details-options {
|
||||
@ -116,3 +114,67 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.mq-overview-container {
|
||||
background: var(--bg-vanilla-200);
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.mq-overview-title {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.mq-details-options {
|
||||
.ant-radio-button-wrapper {
|
||||
border-color: var(--bg-vanilla-300);
|
||||
color: var(--bg-slate-200);
|
||||
}
|
||||
.ant-radio-button-wrapper-checked {
|
||||
color: var(--bg-slate-200);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
.ant-radio-button-wrapper-disabled {
|
||||
background: var(--bg-vanilla-100);
|
||||
color: var(--bg-vanilla-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.droprate-view {
|
||||
.mq-table {
|
||||
.ant-table-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.ant-table-tbody {
|
||||
.ant-table-cell {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-thead {
|
||||
.ant-table-cell {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-data-style {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.trace-id-list {
|
||||
.traceid-style {
|
||||
.traceid-text {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.remaing-count {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
ProducerLatencyOptions,
|
||||
SelectedTimelineQuery,
|
||||
} from '../MessagingQueuesUtils';
|
||||
import { ComingSoon } from '../MQCommon/MQCommon';
|
||||
import MessagingQueuesTable from './MQTables/MQTables';
|
||||
|
||||
const MQServiceDetailTypePerView = (
|
||||
@ -28,7 +27,6 @@ const MQServiceDetailTypePerView = (
|
||||
MessagingQueueServiceDetailType.ConsumerDetails,
|
||||
MessagingQueueServiceDetailType.ProducerDetails,
|
||||
MessagingQueueServiceDetailType.NetworkLatency,
|
||||
MessagingQueueServiceDetailType.PartitionHostMetrics,
|
||||
],
|
||||
[MessagingQueuesViewType.partitionLatency.value]: [
|
||||
MessagingQueueServiceDetailType.ConsumerDetails,
|
||||
@ -62,22 +60,8 @@ function MessagingQueuesOptions({
|
||||
const detailTypes =
|
||||
MQServiceDetailTypePerView(producerLatencyOption)[selectedView] || [];
|
||||
return detailTypes.map((detailType) => (
|
||||
<Radio.Button
|
||||
key={detailType}
|
||||
value={detailType}
|
||||
disabled={
|
||||
detailType === MessagingQueueServiceDetailType.PartitionHostMetrics
|
||||
}
|
||||
className={
|
||||
detailType === MessagingQueueServiceDetailType.PartitionHostMetrics
|
||||
? 'disabled-option'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<Radio.Button key={detailType} value={detailType}>
|
||||
{ConsumerLagDetailTitle[detailType]}
|
||||
{detailType === MessagingQueueServiceDetailType.PartitionHostMetrics && (
|
||||
<ComingSoon />
|
||||
)}
|
||||
</Radio.Button>
|
||||
));
|
||||
};
|
||||
|
@ -0,0 +1,115 @@
|
||||
import { Typography } from 'antd';
|
||||
import { CardContainer } from 'container/GridCardLayout/styles';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import MetricPageGridGraph from './MetricPageGraph';
|
||||
import {
|
||||
averageRequestLatencyWidgetData,
|
||||
brokerCountWidgetData,
|
||||
brokerNetworkThroughputWidgetData,
|
||||
bytesConsumedWidgetData,
|
||||
consumerFetchRateWidgetData,
|
||||
consumerGroupMemberWidgetData,
|
||||
consumerLagByGroupWidgetData,
|
||||
consumerOffsetWidgetData,
|
||||
ioWaitTimeWidgetData,
|
||||
kafkaProducerByteRateWidgetData,
|
||||
messagesConsumedWidgetData,
|
||||
producerFetchRequestPurgatoryWidgetData,
|
||||
requestResponseWidgetData,
|
||||
requestTimesWidgetData,
|
||||
} from './MetricPageUtil';
|
||||
|
||||
interface MetricSectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
graphCount: Widgets[];
|
||||
}
|
||||
|
||||
function MetricSection({
|
||||
title,
|
||||
description,
|
||||
graphCount,
|
||||
}: MetricSectionProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
return (
|
||||
<div className="metric-column-graph">
|
||||
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||
<div className="row-panel">
|
||||
<Typography.Text className="section-title">{title}</Typography.Text>
|
||||
</div>
|
||||
</CardContainer>
|
||||
<Typography.Text className="graph-description">
|
||||
{description}
|
||||
</Typography.Text>
|
||||
<div className="metric-page-grid">
|
||||
{graphCount.map((widgetData) => (
|
||||
<MetricPageGridGraph
|
||||
key={`graph-${widgetData.id}`}
|
||||
widgetData={widgetData}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MetricColumnGraphs(): JSX.Element {
|
||||
const { t } = useTranslation('messagingQueues');
|
||||
|
||||
const metricsData = [
|
||||
{
|
||||
title: t('metricGraphCategory.brokerMetrics.title'),
|
||||
description: t('metricGraphCategory.brokerMetrics.description'),
|
||||
graphCount: [
|
||||
brokerCountWidgetData,
|
||||
requestTimesWidgetData,
|
||||
producerFetchRequestPurgatoryWidgetData,
|
||||
brokerNetworkThroughputWidgetData,
|
||||
],
|
||||
id: 'broker-metrics',
|
||||
},
|
||||
{
|
||||
title: t('metricGraphCategory.producerMetrics.title'),
|
||||
description: t('metricGraphCategory.producerMetrics.description'),
|
||||
graphCount: [
|
||||
ioWaitTimeWidgetData,
|
||||
requestResponseWidgetData,
|
||||
averageRequestLatencyWidgetData,
|
||||
kafkaProducerByteRateWidgetData,
|
||||
bytesConsumedWidgetData,
|
||||
],
|
||||
id: 'producer-metrics',
|
||||
},
|
||||
{
|
||||
title: t('metricGraphCategory.consumerMetrics.title'),
|
||||
description: t('metricGraphCategory.consumerMetrics.description'),
|
||||
graphCount: [
|
||||
consumerOffsetWidgetData,
|
||||
consumerGroupMemberWidgetData,
|
||||
consumerLagByGroupWidgetData,
|
||||
consumerFetchRateWidgetData,
|
||||
messagesConsumedWidgetData,
|
||||
],
|
||||
id: 'consumer-metrics',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="metric-column-graph-container">
|
||||
{metricsData.map((metric) => (
|
||||
<MetricSection
|
||||
key={metric.id}
|
||||
title={metric.title}
|
||||
description={metric.description}
|
||||
graphCount={metric?.graphCount || []}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MetricColumnGraphs;
|
@ -0,0 +1,128 @@
|
||||
.metric-page {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
|
||||
.metric-page-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.row-panel {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.metric-page-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
|
||||
.metric-graph {
|
||||
height: 320px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metric-page-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.graph-description {
|
||||
padding: 16px 10px 16px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.row-panel {
|
||||
border-radius: 4px;
|
||||
background: rgba(18, 19, 23, 0.4);
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
height: 48px !important;
|
||||
|
||||
.ant-typography {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.row-panel-section {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
.row-icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metric-column-graph-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 10px;
|
||||
|
||||
.metric-column-graph {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.row-panel {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.metric-page-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
gap: 10px;
|
||||
|
||||
.metric-graph {
|
||||
height: 320px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metric-column-graph-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.metric-page {
|
||||
.row-panel {
|
||||
.row-panel-section {
|
||||
.row-icon {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
import './MetricPage.styles.scss';
|
||||
|
||||
import { Typography } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { CardContainer } from 'container/GridCardLayout/styles';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import MetricColumnGraphs from './MetricColumnGraphs';
|
||||
import MetricPageGridGraph from './MetricPageGraph';
|
||||
import {
|
||||
cpuRecentUtilizationWidgetData,
|
||||
currentOffsetPartitionWidgetData,
|
||||
insyncReplicasWidgetData,
|
||||
jvmGcCollectionsElapsedWidgetData,
|
||||
jvmGCCountWidgetData,
|
||||
jvmMemoryHeapWidgetData,
|
||||
oldestOffsetWidgetData,
|
||||
partitionCountPerTopicWidgetData,
|
||||
} from './MetricPageUtil';
|
||||
|
||||
interface CollapsibleMetricSectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
graphCount: Widgets[];
|
||||
isCollapsed: boolean;
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
function CollapsibleMetricSection({
|
||||
title,
|
||||
description,
|
||||
graphCount,
|
||||
isCollapsed,
|
||||
onToggle,
|
||||
}: CollapsibleMetricSectionProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
return (
|
||||
<div className="metric-page-container">
|
||||
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||
<div className={cx('row-panel')}>
|
||||
<div className="row-panel-section">
|
||||
<Typography.Text className="section-title">{title}</Typography.Text>
|
||||
{isCollapsed ? (
|
||||
<ChevronDown size={14} onClick={onToggle} className="row-icon" />
|
||||
) : (
|
||||
<ChevronUp size={14} onClick={onToggle} className="row-icon" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContainer>
|
||||
{!isCollapsed && (
|
||||
<>
|
||||
<Typography.Text className="graph-description">
|
||||
{description}
|
||||
</Typography.Text>
|
||||
<div className="metric-page-grid">
|
||||
{graphCount.map((widgetData) => (
|
||||
<MetricPageGridGraph
|
||||
key={`graph-${widgetData.id}`}
|
||||
widgetData={widgetData}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MetricPage(): JSX.Element {
|
||||
const [collapsedSections, setCollapsedSections] = useState<{
|
||||
[key: string]: boolean;
|
||||
}>({
|
||||
producerMetrics: false,
|
||||
consumerMetrics: false,
|
||||
});
|
||||
|
||||
const toggleCollapse = (key: string): void => {
|
||||
setCollapsedSections((prev) => ({
|
||||
...prev,
|
||||
[key]: !prev[key],
|
||||
}));
|
||||
};
|
||||
|
||||
const { t } = useTranslation('messagingQueues');
|
||||
|
||||
const metricSections = [
|
||||
{
|
||||
key: 'bokerJVMMetrics',
|
||||
title: t('metricGraphCategory.brokerJVMMetrics.title'),
|
||||
description: t('metricGraphCategory.brokerJVMMetrics.description'),
|
||||
graphCount: [
|
||||
jvmGCCountWidgetData,
|
||||
jvmGcCollectionsElapsedWidgetData,
|
||||
cpuRecentUtilizationWidgetData,
|
||||
jvmMemoryHeapWidgetData,
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'partitionMetrics',
|
||||
title: t('metricGraphCategory.partitionMetrics.title'),
|
||||
description: t('metricGraphCategory.partitionMetrics.description'),
|
||||
graphCount: [
|
||||
partitionCountPerTopicWidgetData,
|
||||
currentOffsetPartitionWidgetData,
|
||||
oldestOffsetWidgetData,
|
||||
insyncReplicasWidgetData,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="metric-page">
|
||||
<MetricColumnGraphs />
|
||||
{metricSections.map(({ key, title, description, graphCount }) => (
|
||||
<CollapsibleMetricSection
|
||||
key={key}
|
||||
title={title}
|
||||
description={description}
|
||||
graphCount={graphCount}
|
||||
isCollapsed={collapsedSections[key]}
|
||||
onToggle={(): void => toggleCollapse(key)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MetricPage;
|
@ -0,0 +1,59 @@
|
||||
import './MetricPage.styles.scss';
|
||||
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||
import GridCard from 'container/GridCardLayout/GridCard';
|
||||
import { Card } from 'container/GridCardLayout/styles';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
function MetricPageGridGraph({
|
||||
widgetData,
|
||||
}: {
|
||||
widgetData: Widgets;
|
||||
}): JSX.Element {
|
||||
const history = useHistory();
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const urlQuery = useUrlQuery();
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const onDragSelect = useCallback(
|
||||
(start: number, end: number) => {
|
||||
const startTimestamp = Math.trunc(start);
|
||||
const endTimestamp = Math.trunc(end);
|
||||
|
||||
urlQuery.set(QueryParams.startTime, startTimestamp.toString());
|
||||
urlQuery.set(QueryParams.endTime, endTimestamp.toString());
|
||||
const generatedUrl = `${pathname}?${urlQuery.toString()}`;
|
||||
history.push(generatedUrl);
|
||||
|
||||
if (startTimestamp !== endTimestamp) {
|
||||
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
||||
}
|
||||
},
|
||||
[dispatch, history, pathname, urlQuery],
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
isDarkMode={isDarkMode}
|
||||
$panelType={PANEL_TYPES.TIME_SERIES}
|
||||
className="metric-graph"
|
||||
>
|
||||
<GridCard
|
||||
widget={widgetData}
|
||||
headerMenuList={[...ViewMenuAction]}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default MetricPageGridGraph;
|
File diff suppressed because it is too large
Load Diff
@ -166,3 +166,77 @@
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.mq-health-check-modal {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-400);
|
||||
background: var(--bg-vanilla-200);
|
||||
|
||||
.ant-modal-header {
|
||||
border-bottom: 1px solid var(--bg-vanilla-400);
|
||||
background: var(--bg-vanilla-200);
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.attribute-select {
|
||||
.ant-select-selector {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
|
||||
.tree-text {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.ant-tree {
|
||||
.ant-tree-title {
|
||||
.attribute-error-title {
|
||||
color: var(--bg-amber-500);
|
||||
|
||||
.tree-text {
|
||||
color: var(--bg-amber-500);
|
||||
}
|
||||
}
|
||||
|
||||
.attribute-success-title {
|
||||
.success-attribute-icon {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loader-container {
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.config-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
&.missing-config-btn {
|
||||
background: var(--bg-amber-100);
|
||||
color: var(--bg-amber-500);
|
||||
|
||||
&:hover {
|
||||
color: var(--bg-amber-600) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.missing-config-btn {
|
||||
.config-btn-content {
|
||||
border-right: 1px solid var(--bg-amber-600);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -222,6 +222,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
:nth-child(2),
|
||||
:nth-child(4) {
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
&.summary-section {
|
||||
.overview-info-card {
|
||||
min-height: 144px;
|
||||
@ -331,6 +337,10 @@
|
||||
.messaging-breadcrumb {
|
||||
color: var(--bg-ink-400);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.message-queue-text {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
.messaging-header {
|
||||
color: var(--bg-ink-400);
|
||||
|
@ -156,7 +156,7 @@ function MessagingQueues(): JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overview-info-card middle-card">
|
||||
<div className="overview-info-card">
|
||||
<div>
|
||||
<p className="card-title">{t('summarySection.producer.title')}</p>
|
||||
<p className="card-info-text">
|
||||
@ -174,7 +174,7 @@ function MessagingQueues(): JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overview-info-card middle-card">
|
||||
<div className="overview-info-card">
|
||||
<div>
|
||||
<p className="card-title">{t('summarySection.partition.title')}</p>
|
||||
<p className="card-info-text">
|
||||
@ -210,6 +210,24 @@ function MessagingQueues(): JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overview-info-card">
|
||||
<div>
|
||||
<p className="card-title">{t('summarySection.metricPage.title')}</p>
|
||||
<p className="card-info-text">
|
||||
{t('summarySection.metricPage.description')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="button-grp">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={(): void =>
|
||||
redirectToDetailsPage(MessagingQueuesViewType.metricPage.value)
|
||||
}
|
||||
>
|
||||
{t('summarySection.viewDetailsButton')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -222,7 +222,8 @@ export enum MessagingQueuesViewTypeOptions {
|
||||
ConsumerLag = 'consumerLag',
|
||||
PartitionLatency = 'partitionLatency',
|
||||
ProducerLatency = 'producerLatency',
|
||||
ConsumerLatency = 'consumerLatency',
|
||||
DropRate = 'dropRate',
|
||||
MetricPage = 'metricPage',
|
||||
}
|
||||
|
||||
export const MessagingQueuesViewType = {
|
||||
@ -240,7 +241,11 @@ export const MessagingQueuesViewType = {
|
||||
},
|
||||
dropRate: {
|
||||
label: 'Drop Rate view',
|
||||
value: 'dropRate',
|
||||
value: MessagingQueuesViewTypeOptions.DropRate,
|
||||
},
|
||||
metricPage: {
|
||||
label: 'Metric view',
|
||||
value: MessagingQueuesViewTypeOptions.MetricPage,
|
||||
},
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user