mirror of
https://github.com/SigNoz/signoz.git
synced 2026-07-14 18:40:26 +01:00
Compare commits
5 Commits
issue_5674
...
feat/expor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9c4e96656 | ||
|
|
13760e8e2b | ||
|
|
a257184a19 | ||
|
|
466edf1f1c | ||
|
|
849353b5c0 |
@@ -1495,7 +1495,6 @@ components:
|
||||
- cassandradb
|
||||
- redis
|
||||
- cloudsql_postgres
|
||||
- memorystore_redis
|
||||
type: string
|
||||
CloudintegrationtypesServiceMetadata:
|
||||
properties:
|
||||
@@ -4260,6 +4259,34 @@ components:
|
||||
type: number
|
||||
clusterName:
|
||||
type: string
|
||||
counts:
|
||||
properties:
|
||||
daemonSets:
|
||||
format: int64
|
||||
type: integer
|
||||
deployments:
|
||||
format: int64
|
||||
type: integer
|
||||
jobs:
|
||||
format: int64
|
||||
type: integer
|
||||
namespaces:
|
||||
format: int64
|
||||
type: integer
|
||||
nodes:
|
||||
format: int64
|
||||
type: integer
|
||||
statefulSets:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- nodes
|
||||
- namespaces
|
||||
- deployments
|
||||
- daemonSets
|
||||
- jobs
|
||||
- statefulSets
|
||||
type: object
|
||||
meta:
|
||||
additionalProperties:
|
||||
type: string
|
||||
@@ -4280,6 +4307,7 @@ components:
|
||||
- nodeCountsByReadiness
|
||||
- podCountsByPhase
|
||||
- podCountsByStatus
|
||||
- counts
|
||||
- meta
|
||||
type: object
|
||||
InframonitoringtypesClusters:
|
||||
@@ -4801,6 +4829,26 @@ components:
|
||||
type: object
|
||||
InframonitoringtypesNamespaceRecord:
|
||||
properties:
|
||||
counts:
|
||||
properties:
|
||||
daemonSets:
|
||||
format: int64
|
||||
type: integer
|
||||
deployments:
|
||||
format: int64
|
||||
type: integer
|
||||
jobs:
|
||||
format: int64
|
||||
type: integer
|
||||
statefulSets:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- deployments
|
||||
- daemonSets
|
||||
- jobs
|
||||
- statefulSets
|
||||
type: object
|
||||
meta:
|
||||
additionalProperties:
|
||||
type: string
|
||||
@@ -4824,6 +4872,7 @@ components:
|
||||
- namespaceMemory
|
||||
- podCountsByPhase
|
||||
- podCountsByStatus
|
||||
- counts
|
||||
- meta
|
||||
type: object
|
||||
InframonitoringtypesNamespaces:
|
||||
|
||||
@@ -2814,7 +2814,6 @@ export enum CloudintegrationtypesServiceIDDTO {
|
||||
cassandradb = 'cassandradb',
|
||||
redis = 'redis',
|
||||
cloudsql_postgres = 'cloudsql_postgres',
|
||||
memorystore_redis = 'memorystore_redis',
|
||||
}
|
||||
export type CloudintegrationtypesCloudIntegrationServiceDTOAnyOf = {
|
||||
/**
|
||||
@@ -5670,6 +5669,39 @@ export interface InframonitoringtypesChecksDTO {
|
||||
type: InframonitoringtypesCheckTypeDTO;
|
||||
}
|
||||
|
||||
export type InframonitoringtypesClusterRecordDTOCounts = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
daemonSets: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
deployments: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
jobs: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
namespaces: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
nodes: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
statefulSets: number;
|
||||
};
|
||||
|
||||
export type InframonitoringtypesClusterRecordDTOMetaAnyOf = {
|
||||
[key: string]: string;
|
||||
};
|
||||
@@ -5814,6 +5846,10 @@ export interface InframonitoringtypesClusterRecordDTO {
|
||||
* @type string
|
||||
*/
|
||||
clusterName: string;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
counts: InframonitoringtypesClusterRecordDTOCounts;
|
||||
/**
|
||||
* @type object,null
|
||||
*/
|
||||
@@ -6369,6 +6405,29 @@ export interface InframonitoringtypesJobsDTO {
|
||||
warning?: Querybuildertypesv5QueryWarnDataDTO;
|
||||
}
|
||||
|
||||
export type InframonitoringtypesNamespaceRecordDTOCounts = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
daemonSets: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
deployments: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
jobs: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
statefulSets: number;
|
||||
};
|
||||
|
||||
export type InframonitoringtypesNamespaceRecordDTOMetaAnyOf = {
|
||||
[key: string]: string;
|
||||
};
|
||||
@@ -6380,6 +6439,10 @@ export type InframonitoringtypesNamespaceRecordDTOMeta =
|
||||
InframonitoringtypesNamespaceRecordDTOMetaAnyOf | null;
|
||||
|
||||
export interface InframonitoringtypesNamespaceRecordDTO {
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
counts: InframonitoringtypesNamespaceRecordDTOCounts;
|
||||
/**
|
||||
* @type object,null
|
||||
*/
|
||||
|
||||
@@ -191,14 +191,6 @@
|
||||
min-height: 0;
|
||||
overflow-y: visible;
|
||||
|
||||
.time-series-view-container-header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.time-series-view {
|
||||
flex-shrink: 0;
|
||||
height: 65vh;
|
||||
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
getListQuery,
|
||||
getQueryByPanelType,
|
||||
} from 'container/LogsExplorerViews/explorerUtils';
|
||||
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters/BuilderUnitsFilter';
|
||||
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
|
||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
||||
@@ -461,18 +460,17 @@ function LogsExplorerViewsContainer({
|
||||
)}
|
||||
{selectedPanelType === PANEL_TYPES.TIME_SERIES && !showLiveLogs && (
|
||||
<div className="time-series-view-container">
|
||||
<div className="time-series-view-container-header">
|
||||
<BuilderUnitsFilter onChange={onUnitChange} yAxisUnit={yAxisUnit} />
|
||||
</div>
|
||||
<TimeSeriesView
|
||||
isLoading={isLoading || isFetching}
|
||||
data={data}
|
||||
isError={isError}
|
||||
error={error as APIError}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onYAxisUnitChange={onUnitChange}
|
||||
isFilterApplied={!isEmpty(listQuery?.filters?.items)}
|
||||
dataSource={DataSource.LOGS}
|
||||
setWarning={setWarning}
|
||||
allowExport
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -311,6 +311,7 @@ function TimeSeries({
|
||||
dataSource={DataSource.METRICS}
|
||||
error={queries[index].error as APIError}
|
||||
setWarning={setWarning}
|
||||
allowExport
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
min-height: 350px;
|
||||
padding: 0px 12px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
height: 50vh;
|
||||
min-height: 350px;
|
||||
|
||||
@@ -16,6 +16,7 @@ import Uplot from 'components/Uplot';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch';
|
||||
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters/BuilderUnitsFilter';
|
||||
import { getLocalStorageGraphVisibilityState } from 'container/GridCardLayout/GridCard/utils';
|
||||
import { LogsLoading } from 'container/LogsLoading/LogsLoading';
|
||||
import EmptyMetricsSearch from 'container/MetricsExplorer/Explorer/EmptyMetricsSearch';
|
||||
@@ -41,11 +42,14 @@ import { SuccessResponse, Warning } from 'types/api';
|
||||
import { LegendPosition } from 'types/api/dashboard/getAll';
|
||||
import APIError from 'types/api/error';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryRangeResponseV5 } from 'types/api/v5/queryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import uPlot from 'uplot';
|
||||
import { getTimeRange } from 'utils/getTimeRange';
|
||||
|
||||
import TimeseriesExportMenu from './TimeseriesExportMenu';
|
||||
|
||||
import './TimeSeriesView.styles.scss';
|
||||
|
||||
function TimeSeriesView({
|
||||
@@ -59,6 +63,8 @@ function TimeSeriesView({
|
||||
setWarning,
|
||||
panelType = PANEL_TYPES.TIME_SERIES,
|
||||
stackBarChart = false,
|
||||
allowExport = false,
|
||||
onYAxisUnitChange,
|
||||
}: TimeSeriesViewProps): JSX.Element {
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -244,10 +250,33 @@ function TimeSeriesView({
|
||||
[baseChartOptions, stackedBands],
|
||||
);
|
||||
|
||||
const showExport = allowExport && !!data?.rawV5Response;
|
||||
const showHeader = showExport || !!onYAxisUnitChange;
|
||||
|
||||
return (
|
||||
<div className="time-series-view">
|
||||
{isError && error && <ErrorInPlace error={error as APIError} />}
|
||||
|
||||
{showHeader && (
|
||||
<div className="time-series-view__header">
|
||||
<div>
|
||||
{onYAxisUnitChange && (
|
||||
<BuilderUnitsFilter onChange={onYAxisUnitChange} yAxisUnit={yAxisUnit} />
|
||||
)}
|
||||
</div>
|
||||
{showExport && data?.rawV5Response && (
|
||||
<TimeseriesExportMenu
|
||||
dataSource={dataSource}
|
||||
yAxisUnit={yAxisUnit}
|
||||
queryResponse={data.rawV5Response}
|
||||
query={currentQuery}
|
||||
legendMap={data.legendMap}
|
||||
fileName={`${dataSource}-timeseries`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="graph-container"
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
@@ -295,7 +324,11 @@ function TimeSeriesView({
|
||||
}
|
||||
|
||||
interface TimeSeriesViewProps {
|
||||
data?: SuccessResponse<MetricRangePayloadProps> & { warning?: Warning };
|
||||
data?: SuccessResponse<MetricRangePayloadProps> & {
|
||||
warning?: Warning;
|
||||
rawV5Response?: QueryRangeResponseV5;
|
||||
legendMap?: Record<string, string>;
|
||||
};
|
||||
yAxisUnit?: string;
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
@@ -305,6 +338,11 @@ interface TimeSeriesViewProps {
|
||||
setWarning?: Dispatch<SetStateAction<Warning | undefined>>;
|
||||
panelType?: PANEL_TYPES;
|
||||
stackBarChart?: boolean;
|
||||
// Opt-in: render the client-side export menu (Logs explorer for now).
|
||||
allowExport?: boolean;
|
||||
// Opt-in: render the y-axis unit selector in the header (views without their
|
||||
// own selector, e.g. Logs). Metrics keeps its separate YAxisUnitSelector.
|
||||
onYAxisUnitChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
TimeSeriesView.defaultProps = {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
.timeseries-export-popover {
|
||||
width: 240px;
|
||||
padding: 0 12px 12px 12px;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
color: var(--l1-foreground);
|
||||
font-family: Inter;
|
||||
font-size: var(--periscope-font-size-small);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.88px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.export-format {
|
||||
padding: 12px 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// radio option labels — radix popover content inherits the root font
|
||||
// size; pin to the app's 13px base the antd popover used to impose
|
||||
label {
|
||||
font-size: var(--periscope-font-size-base);
|
||||
}
|
||||
}
|
||||
|
||||
.export-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Download } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
|
||||
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { useClientExport } from 'hooks/useExportData/useClientExport';
|
||||
import { ExportFormat } from 'lib/exportData/types';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryRangeResponseV5 } from 'types/api/v5/queryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import './TimeseriesExportMenu.styles.scss';
|
||||
|
||||
interface TimeseriesExportMenuProps {
|
||||
dataSource: DataSource;
|
||||
queryResponse: QueryRangeResponseV5;
|
||||
query?: Query;
|
||||
yAxisUnit?: string;
|
||||
legendMap?: Record<string, string>;
|
||||
fileName?: string;
|
||||
}
|
||||
|
||||
// Download menu for in-memory timeseries data (client-side serialization).
|
||||
// The raw/list backend export keeps its own menu in DownloadOptionsMenu.
|
||||
export default function TimeseriesExportMenu({
|
||||
dataSource,
|
||||
queryResponse,
|
||||
query,
|
||||
yAxisUnit,
|
||||
legendMap,
|
||||
fileName,
|
||||
}: TimeseriesExportMenuProps): JSX.Element {
|
||||
const [exportFormat, setExportFormat] = useState<string>(ExportFormat.Csv);
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
|
||||
|
||||
const { isExporting, handleExport: handleClientExport } = useClientExport({
|
||||
response: queryResponse,
|
||||
query,
|
||||
yAxisUnit,
|
||||
legendMap,
|
||||
fileName,
|
||||
});
|
||||
|
||||
const handleExport = useCallback((): void => {
|
||||
setIsPopoverOpen(false);
|
||||
handleClientExport({ format: exportFormat as ExportFormat });
|
||||
}, [exportFormat, handleClientExport]);
|
||||
|
||||
return (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||
<TooltipSimple title="Download">
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
aria-label="Download"
|
||||
data-testid={`timeseries-export-${dataSource}`}
|
||||
disabled={isExporting}
|
||||
loading={isExporting}
|
||||
>
|
||||
<Download size={14} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</TooltipSimple>
|
||||
<PopoverContent align="end" className="timeseries-export-popover">
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<RadioGroup value={exportFormat} onChange={setExportFormat}>
|
||||
<RadioGroupItem value={ExportFormat.Csv}>csv</RadioGroupItem>
|
||||
<RadioGroupItem value={ExportFormat.Jsonl}>jsonl</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
className="export-button"
|
||||
onClick={handleExport}
|
||||
disabled={isExporting}
|
||||
loading={isExporting}
|
||||
prefix={<Download size={16} />}
|
||||
>
|
||||
Export
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import store from 'store';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import TimeSeriesView from '../TimeSeriesView';
|
||||
|
||||
jest.mock('components/Uplot', () => ({
|
||||
__esModule: true,
|
||||
default: (): JSX.Element => <div data-testid="uplot-chart" />,
|
||||
}));
|
||||
|
||||
jest.mock('../TimeseriesExportMenu', () => ({
|
||||
__esModule: true,
|
||||
default: (): JSX.Element => <div data-testid="timeseries-export-menu" />,
|
||||
}));
|
||||
|
||||
jest.mock('container/QueryBuilder/filters/BuilderUnitsFilter', () => ({
|
||||
BuilderUnitsFilter: (): JSX.Element => (
|
||||
<div data-testid="builder-units-filter" />
|
||||
),
|
||||
}));
|
||||
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({
|
||||
useQueryBuilder: (): unknown => ({ currentQuery: null }),
|
||||
}));
|
||||
|
||||
jest.mock('lib/uPlotLib/getUplotChartOptions', () => ({
|
||||
getUPlotChartOptions: (): unknown => ({}),
|
||||
}));
|
||||
|
||||
jest.mock('lib/uPlotLib/utils/getUplotChartData', () => ({
|
||||
getUPlotChartData: (): number[][] => [
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
],
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'container/DashboardContainer/visualization/charts/utils/stackSeriesUtils',
|
||||
() => ({ stackSeries: (): unknown => ({ data: [], bands: [] }) }),
|
||||
);
|
||||
|
||||
jest.mock('container/GridCardLayout/GridCard/utils', () => ({
|
||||
getLocalStorageGraphVisibilityState: (): unknown => ({
|
||||
graphVisibilityStates: [],
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('providers/Timezone', () => ({
|
||||
useTimezone: (): unknown => ({ timezone: { value: 'UTC' } }),
|
||||
}));
|
||||
|
||||
jest.mock('hooks/useDimensions', () => ({
|
||||
useResizeObserver: (): unknown => ({ width: 800, height: 400 }),
|
||||
}));
|
||||
|
||||
jest.mock('api/common/logEvent', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockStore = configureStore([])({ ...store.getState() });
|
||||
|
||||
const rawV5Response = {
|
||||
type: 'time_series',
|
||||
data: { results: [] },
|
||||
meta: {},
|
||||
};
|
||||
|
||||
function makeData(withRawV5: boolean): any {
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: { data: { result: [], resultType: '' } },
|
||||
...(withRawV5 ? { rawV5Response, legendMap: {} } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function renderView(props: {
|
||||
allowExport?: boolean;
|
||||
withRawV5?: boolean;
|
||||
onYAxisUnitChange?: (value: string) => void;
|
||||
}): ReturnType<typeof render> {
|
||||
const { allowExport, withRawV5 = true, onYAxisUnitChange } = props;
|
||||
return render(
|
||||
<Provider store={mockStore}>
|
||||
<MemoryRouter>
|
||||
<TimeSeriesView
|
||||
isLoading={false}
|
||||
isError={false}
|
||||
isFilterApplied
|
||||
dataSource={DataSource.LOGS}
|
||||
data={makeData(withRawV5)}
|
||||
allowExport={allowExport}
|
||||
onYAxisUnitChange={onYAxisUnitChange}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</Provider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe('TimeSeriesView header gating', () => {
|
||||
it('renders the export menu when allowExport is set and raw V5 data is present', () => {
|
||||
const { queryByTestId } = renderView({ allowExport: true });
|
||||
expect(queryByTestId('timeseries-export-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders no export menu without allowExport', () => {
|
||||
const { queryByTestId } = renderView({});
|
||||
expect(queryByTestId('timeseries-export-menu')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders no export menu when the raw V5 response is missing', () => {
|
||||
const { queryByTestId } = renderView({ allowExport: true, withRawV5: false });
|
||||
expect(queryByTestId('timeseries-export-menu')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the unit selector only when onYAxisUnitChange is passed', () => {
|
||||
const withUnit = renderView({ onYAxisUnitChange: jest.fn() });
|
||||
expect(withUnit.queryByTestId('builder-units-filter')).toBeInTheDocument();
|
||||
withUnit.unmount();
|
||||
|
||||
const withoutUnit = renderView({ allowExport: true });
|
||||
expect(
|
||||
withoutUnit.queryByTestId('builder-units-filter'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders no header row when neither export nor unit selector is enabled', () => {
|
||||
const { container } = renderView({ withRawV5: false });
|
||||
expect(container.querySelector('.time-series-view__header')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
import { fireEvent, render, screen } from 'tests/test-utils';
|
||||
import { QueryRangeResponseV5 } from 'types/api/v5/queryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import TimeseriesExportMenu from '../TimeseriesExportMenu';
|
||||
|
||||
const mockHandleExport = jest.fn();
|
||||
let mockIsExporting = false;
|
||||
|
||||
jest.mock('hooks/useExportData/useClientExport', () => ({
|
||||
useClientExport: (): unknown => ({
|
||||
isExporting: mockIsExporting,
|
||||
handleExport: mockHandleExport,
|
||||
}),
|
||||
}));
|
||||
|
||||
const response = {
|
||||
type: 'time_series',
|
||||
data: { results: [] },
|
||||
meta: {},
|
||||
} as unknown as QueryRangeResponseV5;
|
||||
|
||||
const TEST_ID = `timeseries-export-${DataSource.LOGS}`;
|
||||
|
||||
function renderMenu(): void {
|
||||
render(
|
||||
<TimeseriesExportMenu
|
||||
dataSource={DataSource.LOGS}
|
||||
queryResponse={response}
|
||||
fileName="logs-timeseries"
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
describe('TimeseriesExportMenu', () => {
|
||||
beforeEach(() => {
|
||||
mockHandleExport.mockReset();
|
||||
mockIsExporting = false;
|
||||
});
|
||||
|
||||
it('renders the download trigger button', () => {
|
||||
renderMenu();
|
||||
expect(screen.getByTestId(TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows only format options — no shape, row-count, or column controls', () => {
|
||||
renderMenu();
|
||||
fireEvent.click(screen.getByTestId(TEST_ID));
|
||||
|
||||
expect(screen.getByText('FORMAT')).toBeInTheDocument();
|
||||
expect(screen.getByRole('radio', { name: 'csv' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('radio', { name: 'jsonl' })).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText('Number of Rows')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Columns')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('radio', { name: 'long' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('radio', { name: 'wide' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('exports as csv by default', () => {
|
||||
renderMenu();
|
||||
fireEvent.click(screen.getByTestId(TEST_ID));
|
||||
fireEvent.click(screen.getByText('Export'));
|
||||
|
||||
expect(mockHandleExport).toHaveBeenCalledTimes(1);
|
||||
expect(mockHandleExport).toHaveBeenCalledWith({ format: 'csv' });
|
||||
});
|
||||
|
||||
it('exports as jsonl when selected', () => {
|
||||
renderMenu();
|
||||
fireEvent.click(screen.getByTestId(TEST_ID));
|
||||
fireEvent.click(screen.getByRole('radio', { name: 'jsonl' }));
|
||||
fireEvent.click(screen.getByText('Export'));
|
||||
|
||||
expect(mockHandleExport).toHaveBeenCalledWith({ format: 'jsonl' });
|
||||
});
|
||||
|
||||
it('disables the trigger while an export is in progress', () => {
|
||||
mockIsExporting = true;
|
||||
renderMenu();
|
||||
|
||||
expect(screen.getByTestId(TEST_ID)).toBeDisabled();
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,41 @@
|
||||
import { SuccessResponse } from 'types/api/index';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryRangeResponseV5, TimeSeriesData } from 'types/api/v5/queryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
|
||||
type ConvertibleData = SuccessResponse<MetricRangePayloadProps> & {
|
||||
rawV5Response?: QueryRangeResponseV5;
|
||||
};
|
||||
|
||||
// Applies the same ns→ms conversion to the raw V5 tree, so client-side export
|
||||
// serializes the values the chart displays (not the original nanoseconds).
|
||||
function convertRawV5ValuesToMs(
|
||||
response: QueryRangeResponseV5,
|
||||
): QueryRangeResponseV5 {
|
||||
if (response.type !== 'time_series') {
|
||||
return response;
|
||||
}
|
||||
|
||||
const results = (response.data.results as TimeSeriesData[]).map((result) => ({
|
||||
...result,
|
||||
aggregations: (result.aggregations ?? []).map((bucket) => ({
|
||||
...bucket,
|
||||
series: (bucket.series ?? []).map((series) => ({
|
||||
...series,
|
||||
values: (series.values ?? []).map((value) => ({
|
||||
...value,
|
||||
value: value.value / 1000000,
|
||||
})),
|
||||
})),
|
||||
})),
|
||||
}));
|
||||
|
||||
return { ...response, data: { ...response.data, results } };
|
||||
}
|
||||
|
||||
export const convertDataValueToMs = (
|
||||
data?: SuccessResponse<MetricRangePayloadProps>,
|
||||
): SuccessResponse<MetricRangePayloadProps> | undefined => {
|
||||
data?: ConvertibleData,
|
||||
): ConvertibleData | undefined => {
|
||||
const convertedData = data;
|
||||
|
||||
const convertedResult: QueryData[] = data?.payload?.data?.result
|
||||
@@ -22,5 +53,11 @@ export const convertDataValueToMs = (
|
||||
convertedData.payload.data.result = convertedResult;
|
||||
}
|
||||
|
||||
if (convertedData?.rawV5Response) {
|
||||
convertedData.rawV5Response = convertRawV5ValuesToMs(
|
||||
convertedData.rawV5Response,
|
||||
);
|
||||
}
|
||||
|
||||
return convertedData;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,11 @@ import { SuccessResponseV2, Warning } from 'types/api';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { MetricQueryRangeSuccessResponse } from 'types/api/metrics/getQueryRange';
|
||||
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { ExecStats, MetricRangePayloadV5 } from 'types/api/v5/queryRange';
|
||||
import {
|
||||
ExecStats,
|
||||
MetricRangePayloadV5,
|
||||
QueryRangeResponseV5,
|
||||
} from 'types/api/v5/queryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
@@ -192,6 +196,8 @@ export async function GetMetricQueryRange(
|
||||
| SuccessResponseV2<MetricRangePayloadV5>;
|
||||
let warning: Warning | undefined;
|
||||
let meta: ExecStats | undefined;
|
||||
// Raw V5 response, kept before it's converted to legacy — powers client-side export.
|
||||
let rawV5Response: QueryRangeResponseV5 | undefined;
|
||||
|
||||
const panelType = props.originalGraphType || props.graphType;
|
||||
|
||||
@@ -268,6 +274,8 @@ export async function GetMetricQueryRange(
|
||||
endTime: props.end * 1000,
|
||||
});
|
||||
|
||||
rawV5Response = publicResponse.data.data;
|
||||
|
||||
// Convert V5 response to legacy format for components
|
||||
response = convertV5ResponseToLegacy(
|
||||
{
|
||||
@@ -288,6 +296,8 @@ export async function GetMetricQueryRange(
|
||||
headers,
|
||||
);
|
||||
|
||||
rawV5Response = v5Response.data.data;
|
||||
|
||||
// Convert V5 response to legacy format for components
|
||||
response = convertV5ResponseToLegacy(
|
||||
{
|
||||
@@ -366,6 +376,8 @@ export async function GetMetricQueryRange(
|
||||
...response,
|
||||
warning,
|
||||
meta,
|
||||
rawV5Response,
|
||||
legendMap,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { exportScalarData } from '../exportScalarData';
|
||||
|
||||
const query = {
|
||||
queryType: 'builder',
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
queryName: 'A',
|
||||
dataSource: 'logs',
|
||||
aggregations: [{ expression: 'count()' }],
|
||||
groupBy: [
|
||||
{ key: 'service.name', dataType: 'string', type: 'tag', id: 'svc' },
|
||||
],
|
||||
legend: '',
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
},
|
||||
} as unknown as Query;
|
||||
|
||||
function makeResponse(
|
||||
tables: {
|
||||
queryName: string;
|
||||
columns: { name: string; id?: string; isValueColumn: boolean }[];
|
||||
rows: Record<string, string | number>[];
|
||||
}[],
|
||||
): SuccessResponse<MetricRangePayloadProps> {
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: {
|
||||
data: {
|
||||
resultType: 'scalar',
|
||||
result: tables.map((table) => ({
|
||||
queryName: table.queryName,
|
||||
legend: '',
|
||||
series: null,
|
||||
list: null,
|
||||
table: {
|
||||
columns: table.columns.map((col) => ({
|
||||
...col,
|
||||
queryName: table.queryName,
|
||||
})),
|
||||
rows: table.rows.map((row) => ({ data: row })),
|
||||
},
|
||||
})),
|
||||
},
|
||||
},
|
||||
} as unknown as SuccessResponse<MetricRangePayloadProps>;
|
||||
}
|
||||
|
||||
describe('exportScalarData', () => {
|
||||
it('serializes the table exactly as QueryTable prepares it', () => {
|
||||
const data = makeResponse([
|
||||
{
|
||||
queryName: 'A',
|
||||
columns: [
|
||||
{ name: 'service.name', id: 'service.name', isValueColumn: false },
|
||||
{ name: 'count()', id: 'A', isValueColumn: true },
|
||||
],
|
||||
rows: [
|
||||
{ 'service.name': 'frontend', A: 120 },
|
||||
{ 'service.name': 'cart', A: 80 },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const table = exportScalarData({ data, query });
|
||||
|
||||
// group + aggregation columns, raw values, on-screen order — inherited
|
||||
// 1:1 from createTableColumnsFromQuery (the renderer's own preparer)
|
||||
expect(table).toStrictEqual({
|
||||
headers: ['service.name', 'count()'],
|
||||
rows: [
|
||||
['frontend', 120],
|
||||
['cart', 80],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an empty table for an empty response', () => {
|
||||
const table = exportScalarData({
|
||||
data: makeResponse([]),
|
||||
query,
|
||||
});
|
||||
|
||||
expect(table.rows).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
import { exportTableData } from '../exportTableData';
|
||||
|
||||
const columns = [
|
||||
{ name: 'service.name', key: 'service.name' },
|
||||
{ name: 'count()', key: 'A', isValueColumn: true },
|
||||
{ name: 'avg(duration)', key: 'B', isValueColumn: true },
|
||||
];
|
||||
|
||||
describe('exportTableData', () => {
|
||||
it('serializes raw values in display column order', () => {
|
||||
const table = exportTableData({
|
||||
columns,
|
||||
dataSource: [
|
||||
{ 'service.name': 'frontend', A: 120, B: 45.5 },
|
||||
{ 'service.name': 'cart', A: 80, B: 12 },
|
||||
],
|
||||
});
|
||||
|
||||
expect(table).toStrictEqual({
|
||||
headers: ['service.name', 'count()', 'avg(duration)'],
|
||||
rows: [
|
||||
['frontend', 120, 45.5],
|
||||
['cart', 80, 12],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('appends column units to value columns only, skipping display-only ids', () => {
|
||||
const table = exportTableData({
|
||||
columns,
|
||||
dataSource: [{ 'service.name': 'frontend', A: 120, B: 45.5 }],
|
||||
columnUnits: { A: 'short', B: 'ms', 'service.name': 'ms' },
|
||||
});
|
||||
|
||||
// group column never gets a unit; 'short' is display-only and skipped
|
||||
expect(table.headers).toStrictEqual([
|
||||
'service.name',
|
||||
'count()',
|
||||
'avg(duration) (ms)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('marks missing cells as blank gaps', () => {
|
||||
const table = exportTableData({
|
||||
columns,
|
||||
dataSource: [{ 'service.name': 'frontend', A: 120 }],
|
||||
});
|
||||
|
||||
expect(table.rows).toStrictEqual([['frontend', 120, '']]);
|
||||
});
|
||||
|
||||
it('returns a headers-only table for empty data', () => {
|
||||
expect(exportTableData({ columns, dataSource: [] })).toStrictEqual({
|
||||
headers: ['service.name', 'count()', 'avg(duration)'],
|
||||
rows: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -81,6 +81,18 @@ describe('exportTimeseriesData', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('omits display-only format ids (short/none) from headers', () => {
|
||||
const data = [
|
||||
makeQuery('A', [{ series: [makeSeries({ service: 'a' }, [[1000, 1]])] }]),
|
||||
];
|
||||
|
||||
const short = exportTimeseriesData({ data, yAxisUnit: 'short' });
|
||||
expect(short.headers[short.headers.length - 1]).toBe('value');
|
||||
|
||||
const none = exportTimeseriesData({ data, yAxisUnit: 'none' });
|
||||
expect(none.headers[none.headers.length - 1]).toBe('value');
|
||||
});
|
||||
|
||||
it('multi-query: query is its own column; label keys are unioned', () => {
|
||||
const data = [
|
||||
makeQuery('A', [{ series: [makeSeries({ service: 'x' }, [[1000, 1]])] }]),
|
||||
|
||||
48
frontend/src/lib/exportData/exportScalarData.ts
Normal file
48
frontend/src/lib/exportData/exportScalarData.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
import { exportTableData } from './exportTableData';
|
||||
import { SerializedTable } from './types';
|
||||
|
||||
interface ExportScalarDataArgs {
|
||||
// The queryRange response object the table mount already holds (the
|
||||
// formatForWeb payload carrying webTables).
|
||||
data?: SuccessResponse<MetricRangePayloadProps>;
|
||||
query: Query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a scalar/table queryRange response into a table — via
|
||||
* createTableColumnsFromQuery, the exact preparer QueryTable renders from, so
|
||||
* the export inherits the on-screen merge, naming and column order 1:1.
|
||||
*/
|
||||
export function exportScalarData({
|
||||
data,
|
||||
query,
|
||||
}: ExportScalarDataArgs): SerializedTable {
|
||||
const queryTableData = (data?.payload?.data?.newResult?.data?.result ||
|
||||
data?.payload?.data?.result ||
|
||||
[]) as QueryDataV3[];
|
||||
|
||||
const { columns, dataSource } = createTableColumnsFromQuery({
|
||||
query,
|
||||
queryTableData,
|
||||
});
|
||||
|
||||
return exportTableData({
|
||||
// antd widens title/dataIndex; createTableColumnsFromQuery always sets strings
|
||||
columns: columns.map((column) => {
|
||||
const rawIndex = 'dataIndex' in column ? column.dataIndex : undefined;
|
||||
const key =
|
||||
typeof rawIndex === 'string' || typeof rawIndex === 'number'
|
||||
? String(rawIndex)
|
||||
: '';
|
||||
const name = typeof column.title === 'string' ? column.title : key;
|
||||
return { name, key: key || name };
|
||||
}),
|
||||
dataSource: dataSource as unknown as Record<string, unknown>[],
|
||||
});
|
||||
}
|
||||
46
frontend/src/lib/exportData/exportTableData.ts
Normal file
46
frontend/src/lib/exportData/exportTableData.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { SerializedTable } from './types';
|
||||
import { withUnit } from './withUnit';
|
||||
|
||||
/** Generic table-model column — any prepared table (QueryTable, dashboard
|
||||
* tables, plain antd tables) adapts to this in a line or two. */
|
||||
export interface ExportTableColumn {
|
||||
/** Display name, used as the export header (column order = array order). */
|
||||
name: string;
|
||||
/** Key into each dataSource record. */
|
||||
key: string;
|
||||
isValueColumn?: boolean;
|
||||
}
|
||||
|
||||
interface ExportTableDataArgs {
|
||||
columns: ExportTableColumn[];
|
||||
dataSource: Record<string, unknown>[];
|
||||
/** Per-column display unit, keyed by column key (dashboards; absent in explorer). */
|
||||
columnUnits?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a prepared table model into a format-agnostic table — raw values
|
||||
* in display column order (lossless; no cell formatting applied).
|
||||
*/
|
||||
export function exportTableData({
|
||||
columns,
|
||||
dataSource,
|
||||
columnUnits,
|
||||
}: ExportTableDataArgs): SerializedTable {
|
||||
const headers = columns.map((column) =>
|
||||
column.isValueColumn
|
||||
? withUnit(column.name, columnUnits?.[column.key])
|
||||
: column.name,
|
||||
);
|
||||
|
||||
const rows = dataSource.map((record) =>
|
||||
columns.map((column) => {
|
||||
const value = record[column.key];
|
||||
return value === undefined || value === null
|
||||
? ''
|
||||
: (value as string | number);
|
||||
}),
|
||||
);
|
||||
|
||||
return { headers, rows };
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { TimeSeries, TimeSeriesData } from 'types/api/v5/queryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
|
||||
import { SerializedTable } from './types';
|
||||
import { withUnit } from './withUnit';
|
||||
|
||||
interface ExportTimeseriesDataArgs {
|
||||
data: TimeSeriesData[];
|
||||
@@ -98,11 +99,6 @@ function flatten(
|
||||
return flat;
|
||||
}
|
||||
|
||||
// Appends the y-axis unit to the value header: `value` → `value (ms)`.
|
||||
function withUnit(header: string, yAxisUnit?: string): string {
|
||||
return yAxisUnit ? `${header} (${yAxisUnit})` : header;
|
||||
}
|
||||
|
||||
function toIso(timestamp: number): string {
|
||||
return new Date(timestamp).toISOString();
|
||||
}
|
||||
|
||||
11
frontend/src/lib/exportData/withUnit.ts
Normal file
11
frontend/src/lib/exportData/withUnit.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// Display-format ids, not physical units — meaningful on a chart axis
|
||||
// (compact-number formatting) but misleading in an export header.
|
||||
const DISPLAY_ONLY_UNITS = new Set(['short', 'none']);
|
||||
|
||||
/** Appends a unit to a header: `value` → `value (ms)`. Skips display-only ids. */
|
||||
export function withUnit(header: string, unit?: string): string {
|
||||
if (!unit || DISPLAY_ONLY_UNITS.has(unit)) {
|
||||
return header;
|
||||
}
|
||||
return `${header} (${unit})`;
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import { useSelector } from 'react-redux';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters';
|
||||
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
|
||||
import { convertDataValueToMs } from 'container/TimeSeriesView/utils';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
@@ -116,9 +115,6 @@ function TimeSeriesViewContainer({
|
||||
|
||||
return (
|
||||
<div className="trace-explorer-time-series-view-container">
|
||||
<div className="trace-explorer-time-series-view-container-header">
|
||||
<BuilderUnitsFilter onChange={onUnitChange} yAxisUnit={yAxisUnit} />
|
||||
</div>
|
||||
<TimeSeriesView
|
||||
isFilterApplied={isFilterApplied}
|
||||
isError={isError}
|
||||
@@ -126,8 +122,10 @@ function TimeSeriesViewContainer({
|
||||
isLoading={isLoading || isFetching}
|
||||
data={responseData}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onYAxisUnitChange={onUnitChange}
|
||||
dataSource={dataSource}
|
||||
setWarning={setWarning}
|
||||
allowExport
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,11 @@ import {
|
||||
IClickHouseQuery,
|
||||
IPromQLQuery,
|
||||
} from '../queryBuilder/queryBuilderData';
|
||||
import { ExecStats, QueryRangeRequestV5 } from '../v5/queryRange';
|
||||
import {
|
||||
ExecStats,
|
||||
QueryRangeRequestV5,
|
||||
QueryRangeResponseV5,
|
||||
} from '../v5/queryRange';
|
||||
import { QueryData, QueryDataV3 } from '../widgets/getQuery';
|
||||
|
||||
export type QueryRangePayload = {
|
||||
@@ -48,6 +52,9 @@ export interface MetricQueryRangeSuccessResponse extends SuccessResponse<
|
||||
> {
|
||||
warning?: Warning;
|
||||
meta?: ExecStats;
|
||||
// Raw V5 response (pre-legacy-conversion) + per-query legend map, for client-side export.
|
||||
rawV5Response?: QueryRangeResponseV5;
|
||||
legendMap?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface MetricRangePayloadV3 {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><defs><style>.cls-1,.cls-4{fill:#669df6;}.cls-2{fill:#4285f4;}.cls-2,.cls-3,.cls-4{fill-rule:evenodd;}.cls-3{fill:#aecbfa;}</style></defs><title>Icon_24px_MemoryStore_Color</title><g data-name="Product Icons"><g ><rect class="cls-1" x="2" y="3.94" width="3.33" height="2.58"/><rect class="cls-1" x="2" y="8.45" width="3.33" height="2.58"/><rect class="cls-1" x="2" y="12.97" width="3.33" height="2.58"/><rect class="cls-1" x="2" y="17.48" width="3.33" height="2.58"/><rect class="cls-1" x="18.67" y="3.94" width="3.33" height="2.58"/><rect class="cls-1" x="18.67" y="8.45" width="3.33" height="2.58"/><rect class="cls-1" x="18.67" y="12.97" width="3.33" height="2.58"/><rect class="cls-1" x="18.67" y="17.48" width="3.33" height="2.58"/><polygon class="cls-2" points="21.33 6.52 18.67 6.52 18.67 3.94 21.33 6.52"/><polygon class="cls-2" points="21.33 11.03 18.67 11.03 18.67 8.45 21.33 11.03"/><polygon class="cls-2" points="21.33 15.55 18.67 15.55 18.67 12.97 21.33 15.55"/><polygon class="cls-2" points="21.33 20.07 18.67 20.07 18.67 17.48 21.33 20.07"/><path class="cls-3" d="M5.33,22H18.67V2H5.33Zm6-9H8l4.67-7.74V11H16l-4.67,7.74Z"/><polygon class="cls-4" points="11.33 22 11.33 18.77 16 11.03 12.67 11.03 12.67 2 18.67 2 18.67 22 11.33 22"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,94 +0,0 @@
|
||||
{
|
||||
"id": "memorystore_redis",
|
||||
"title": "GCP Memorystore Redis",
|
||||
"icon": "file://icon.svg",
|
||||
"overview": "file://overview.md",
|
||||
"supportedSignals": {
|
||||
"metrics": true,
|
||||
"logs": true
|
||||
},
|
||||
"dataCollected": {
|
||||
"metrics": [
|
||||
{
|
||||
"name": "redis.googleapis.com/server/uptime",
|
||||
"unit": "Seconds",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/stats/memory/usage_ratio",
|
||||
"unit": "None",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/stats/memory/system_memory_usage_ratio",
|
||||
"unit": "None",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/stats/cpu_utilization_main_thread",
|
||||
"unit": "Seconds",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/clients/connected",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/stats/cache_hit_ratio",
|
||||
"unit": "None",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/keyspace/keys",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/commands/usec_per_call",
|
||||
"unit": "Microseconds",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/commands/calls",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/clients/blocked",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "redis.googleapis.com/stats/reject_connections_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
}
|
||||
],
|
||||
"logs": []
|
||||
},
|
||||
"telemetryCollectionStrategy": {
|
||||
"gcp": {}
|
||||
},
|
||||
"assets": {
|
||||
"dashboards": [
|
||||
{
|
||||
"id": "overview",
|
||||
"title": "GCP Memorystore Redis Overview",
|
||||
"description": "Overview of GCP Memorystore Redis metrics",
|
||||
"definition": "file://assets/dashboards/overview.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
### Monitor GCP Memorystore Redis with SigNoz
|
||||
|
||||
Collect key GCP Memorystore Redis metrics and view them with an out of the box dashboard.
|
||||
@@ -21,6 +21,7 @@ func buildClusterRecords(
|
||||
nodeConditionCountsMap map[string]nodeConditionCounts,
|
||||
podPhaseCountsMap map[string]podPhaseCounts,
|
||||
podStatusCounts map[string]podStatusCounts,
|
||||
resourceCounts map[string]map[string]int64,
|
||||
) []inframonitoringtypes.ClusterRecord {
|
||||
metricsMap := parseFullQueryResponse(resp, groupBy)
|
||||
|
||||
@@ -74,6 +75,15 @@ func buildClusterRecords(
|
||||
record.PodCountsByStatus = podStatusCountsToResponse(podStatusCountsForGroup)
|
||||
}
|
||||
|
||||
if counts, ok := resourceCounts[compositeKey]; ok {
|
||||
record.Counts.Nodes = counts[inframonitoringtypes.NodeNameAttrKey]
|
||||
record.Counts.Namespaces = counts[inframonitoringtypes.NamespaceNameAttrKey]
|
||||
record.Counts.Deployments = counts[inframonitoringtypes.DeploymentNameAttrKey]
|
||||
record.Counts.DaemonSets = counts[inframonitoringtypes.DaemonSetNameAttrKey]
|
||||
record.Counts.Jobs = counts[inframonitoringtypes.JobNameAttrKey]
|
||||
record.Counts.StatefulSets = counts[inframonitoringtypes.StatefulSetNameAttrKey]
|
||||
}
|
||||
|
||||
if attrs, ok := metadataMap[compositeKey]; ok {
|
||||
for k, v := range attrs {
|
||||
record.Meta[k] = v
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package implinframonitoring
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/inframonitoringtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
@@ -35,6 +37,26 @@ var clusterAttrKeysForMetadata = []string{
|
||||
"k8s.cluster.name",
|
||||
}
|
||||
|
||||
// clusterCountAttrKeys are the resource attributes whose distinct values are
|
||||
// counted per cluster. Node name is read from the node metric universe, while
|
||||
// namespace + workload names come from the pod metric universe — both unioned
|
||||
// into clusterCountMetricNamesList.
|
||||
var clusterCountAttrKeys = []string{
|
||||
inframonitoringtypes.NodeNameAttrKey,
|
||||
inframonitoringtypes.NamespaceNameAttrKey,
|
||||
inframonitoringtypes.DeploymentNameAttrKey,
|
||||
inframonitoringtypes.DaemonSetNameAttrKey,
|
||||
inframonitoringtypes.JobNameAttrKey,
|
||||
inframonitoringtypes.StatefulSetNameAttrKey,
|
||||
}
|
||||
|
||||
// clusterMetricNamesListForCounts is the metric universe for per-cluster distinct
|
||||
// counts. It unions the pod universe (carries namespace + workload owner labels)
|
||||
// with the cluster/node universe (carries k8s.node.name), so a single query can
|
||||
// count nodes, namespaces, and workloads per cluster. Overlapping pod
|
||||
// phase/status metrics are left in — harmless in a metric_name IN (...) list.
|
||||
var clusterMetricNamesListForCounts = slices.Concat(podsTableMetricNamesList, clustersTableMetricNamesList)
|
||||
|
||||
var orderByToClustersQueryNames = map[string][]string{
|
||||
inframonitoringtypes.ClustersOrderByCPU: {"A"},
|
||||
inframonitoringtypes.ClustersOrderByCPUAllocatable: {"B"},
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/inframonitoringtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
@@ -762,3 +763,197 @@ func (m *module) getMetadata(
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// countAttrIdentityTuples maps a counted attr to the full label tuple that
|
||||
// uniquely identifies its entity, so uniqExact counts real entities rather than
|
||||
// bare names (namespace-scoped workloads with the same name across namespaces,
|
||||
// or cluster-scoped nodes/namespaces across clusters, would otherwise collapse).
|
||||
// Attrs absent from this map are counted on their bare name.
|
||||
var countAttrIdentityTuples = map[string][]string{
|
||||
inframonitoringtypes.NamespaceNameAttrKey: {inframonitoringtypes.ClusterNameAttrKey, inframonitoringtypes.NamespaceNameAttrKey},
|
||||
inframonitoringtypes.NodeNameAttrKey: {inframonitoringtypes.ClusterNameAttrKey, inframonitoringtypes.NodeNameAttrKey},
|
||||
inframonitoringtypes.DeploymentNameAttrKey: {inframonitoringtypes.ClusterNameAttrKey, inframonitoringtypes.NamespaceNameAttrKey, inframonitoringtypes.DeploymentNameAttrKey},
|
||||
inframonitoringtypes.DaemonSetNameAttrKey: {inframonitoringtypes.ClusterNameAttrKey, inframonitoringtypes.NamespaceNameAttrKey, inframonitoringtypes.DaemonSetNameAttrKey},
|
||||
inframonitoringtypes.JobNameAttrKey: {inframonitoringtypes.ClusterNameAttrKey, inframonitoringtypes.NamespaceNameAttrKey, inframonitoringtypes.JobNameAttrKey},
|
||||
inframonitoringtypes.StatefulSetNameAttrKey: {inframonitoringtypes.ClusterNameAttrKey, inframonitoringtypes.NamespaceNameAttrKey, inframonitoringtypes.StatefulSetNameAttrKey},
|
||||
}
|
||||
|
||||
// getPerGroupDistinctCounts returns, per groupBy combination, the exact distinct
|
||||
// count of each attr in attrNames within the time range and metric universe.
|
||||
// It mirrors getMetadata: fingerprints come from the samples table, labels are
|
||||
// read from the timeseries table (raw only, or raw+reduced union when reduction
|
||||
// is enabled), and the user filter is merged with the page-groups IN clauses.
|
||||
// The returned map keys group column values by "\x00", mapping to attr -> count.
|
||||
func (m *module) getPerGroupDistinctCounts(
|
||||
ctx context.Context,
|
||||
orgID valuer.UUID,
|
||||
start, end int64,
|
||||
filter *qbtypes.Filter,
|
||||
groupBy []qbtypes.GroupByKey,
|
||||
pageGroups []map[string]string,
|
||||
attrNames []string,
|
||||
metricNames []string,
|
||||
) (map[string]map[string]int64, error) {
|
||||
if len(pageGroups) == 0 || len(groupBy) == 0 {
|
||||
return map[string]map[string]int64{}, nil
|
||||
}
|
||||
if len(attrNames) == 0 {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "attrNames must not be empty")
|
||||
}
|
||||
if len(metricNames) == 0 {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "metricNames must not be empty")
|
||||
}
|
||||
|
||||
// Merge user filter with page-groups IN clauses.
|
||||
userFilterExpr := ""
|
||||
if filter != nil {
|
||||
userFilterExpr = filter.Expression
|
||||
}
|
||||
pageGroupsFilterExpr := buildPageGroupsFilterExpr(pageGroups)
|
||||
mergedFilterExpr := mergeFilterExpressions(userFilterExpr, pageGroupsFilterExpr)
|
||||
|
||||
reductionEnabled := m.fl.BooleanOrEmpty(ctx, flagger.FeatureEnableMetricsReduction, featuretypes.NewFlaggerEvaluationContext(orgID))
|
||||
|
||||
// Step-floor the window and pick the right tables — same bounds the QB v5
|
||||
// metric querier uses (see alignedMetricWindow / getMetadata).
|
||||
samplesStartMs, flooredEndMs, tsAdjustedStartMs, distributedTimeSeriesTbl, _, _, localSamplesTbl := alignedMetricWindow(start, end)
|
||||
|
||||
fpSB := m.buildSamplesTblFingerprintSubQuery(metricNames, localSamplesTbl, samplesStartMs, flooredEndMs)
|
||||
|
||||
groupByCols := make([]string, len(groupBy))
|
||||
for i, key := range groupBy {
|
||||
groupByCols[i] = key.Name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
|
||||
// SELECT: one JSONExtractString per groupBy col + one uniqExactIf per attr.
|
||||
selectCols := make([]string, 0, len(groupByCols)+len(attrNames))
|
||||
for _, col := range groupByCols {
|
||||
selectCols = append(selectCols,
|
||||
fmt.Sprintf("JSONExtractString(labels, %s) AS %s", sb.Var(col), quoteIdentifier(col)),
|
||||
)
|
||||
}
|
||||
for _, attr := range attrNames {
|
||||
// Guard on != '' so a series missing the attr isn't counted as one empty value.
|
||||
extract := fmt.Sprintf("JSONExtractString(labels, %s)", sb.Var(attr))
|
||||
|
||||
// Count on the entity's full identity tuple where one is defined, so
|
||||
// same-named entities in different scopes (e.g. workloads sharing a name
|
||||
// across namespaces) aren't collapsed. Falls back to the bare name.
|
||||
valueExpr := extract
|
||||
if tuple, ok := countAttrIdentityTuples[attr]; ok {
|
||||
parts := make([]string, len(tuple))
|
||||
for i, col := range tuple {
|
||||
parts[i] = fmt.Sprintf("JSONExtractString(labels, %s)", sb.Var(col))
|
||||
}
|
||||
valueExpr = fmt.Sprintf("(%s)", strings.Join(parts, ", "))
|
||||
}
|
||||
|
||||
selectCols = append(selectCols,
|
||||
fmt.Sprintf("uniqExactIf(%s, %s != '') AS %s", valueExpr, extract, quoteIdentifier(attr)),
|
||||
)
|
||||
}
|
||||
sb.Select(selectCols...)
|
||||
|
||||
if reductionEnabled {
|
||||
var filterClause *sqlbuilder.WhereClause
|
||||
if mergedFilterExpr != "" {
|
||||
var err error
|
||||
filterClause, err = m.buildFilterClause(ctx, &qbtypes.Filter{Expression: mergedFilterExpr}, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
reducedFpSB := m.buildReducedSamplesTblFingerprintSubQuery(metricNames, samplesStartMs, flooredEndMs)
|
||||
|
||||
rawSrc := sqlbuilder.NewSelectBuilder()
|
||||
rawSrc.Select("labels")
|
||||
rawSrc.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, distributedTimeSeriesTbl))
|
||||
rawSrc.Where(
|
||||
rawSrc.In("metric_name", sqlbuilder.List(metricNames)),
|
||||
rawSrc.GE("unix_milli", tsAdjustedStartMs),
|
||||
rawSrc.LE("unix_milli", flooredEndMs),
|
||||
fmt.Sprintf("fingerprint IN (%s)", rawSrc.Var(fpSB)),
|
||||
)
|
||||
if filterClause != nil {
|
||||
rawSrc.AddWhereClause(sqlbuilder.CopyWhereClause(filterClause))
|
||||
}
|
||||
|
||||
reducedSrc := sqlbuilder.NewSelectBuilder()
|
||||
reducedSrc.Select("labels")
|
||||
reducedSrc.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.TimeseriesV4ReducedTableName))
|
||||
reducedSrc.Where(
|
||||
reducedSrc.In("metric_name", sqlbuilder.List(metricNames)),
|
||||
reducedSrc.GE("unix_milli", tsAdjustedStartMs),
|
||||
reducedSrc.LE("unix_milli", flooredEndMs),
|
||||
fmt.Sprintf("fingerprint IN (%s)", reducedSrc.Var(reducedFpSB)),
|
||||
)
|
||||
if filterClause != nil {
|
||||
reducedSrc.AddWhereClause(sqlbuilder.CopyWhereClause(filterClause))
|
||||
}
|
||||
|
||||
sb.From(sb.BuilderAs(sqlbuilder.UnionAll(rawSrc, reducedSrc), "series"))
|
||||
} else {
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, distributedTimeSeriesTbl))
|
||||
sb.Where(
|
||||
sb.In("metric_name", sqlbuilder.List(metricNames)),
|
||||
sb.GE("unix_milli", tsAdjustedStartMs),
|
||||
sb.LE("unix_milli", flooredEndMs),
|
||||
fmt.Sprintf("fingerprint IN (%s)", sb.Var(fpSB)),
|
||||
)
|
||||
if mergedFilterExpr != "" {
|
||||
filterClause, err := m.buildFilterClause(ctx, &qbtypes.Filter{Expression: mergedFilterExpr}, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if filterClause != nil {
|
||||
sb.AddWhereClause(filterClause)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groupByAliases := make([]string, 0, len(groupByCols))
|
||||
for _, col := range groupByCols {
|
||||
groupByAliases = append(groupByAliases, quoteIdentifier(col))
|
||||
}
|
||||
sb.GroupBy(groupByAliases...)
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
rows, err := m.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
result := make(map[string]map[string]int64)
|
||||
for rows.Next() {
|
||||
groupVals := make([]string, len(groupByCols))
|
||||
counts := make([]uint64, len(attrNames))
|
||||
|
||||
scanPtrs := make([]any, 0, len(groupByCols)+len(attrNames))
|
||||
for i := range groupVals {
|
||||
scanPtrs = append(scanPtrs, &groupVals[i])
|
||||
}
|
||||
for i := range counts {
|
||||
scanPtrs = append(scanPtrs, &counts[i])
|
||||
}
|
||||
|
||||
if err := rows.Scan(scanPtrs...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attrCounts := make(map[string]int64, len(attrNames))
|
||||
for i, attr := range attrNames {
|
||||
attrCounts[attr] = int64(counts[i])
|
||||
}
|
||||
result[compositeKeyFromList(groupVals)] = attrCounts
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -632,6 +632,7 @@ func (m *module) ListNamespaces(ctx context.Context, orgID valuer.UUID, req *inf
|
||||
phaseCounts map[string]podPhaseCounts
|
||||
podStatusCounts map[string]podStatusCounts
|
||||
podStatusWarning *qbtypes.QueryWarnData
|
||||
resourceCounts map[string]map[string]int64
|
||||
)
|
||||
|
||||
g, gCtx := errgroup.WithContext(ctx)
|
||||
@@ -651,12 +652,17 @@ func (m *module) ListNamespaces(ctx context.Context, orgID valuer.UUID, req *inf
|
||||
podStatusCounts, podStatusWarning, err = m.getPerGroupPodStatusCountsWithReqMetricChecks(gCtx, req.Start, req.End, req.Filter, req.GroupBy, pageGroups)
|
||||
return err
|
||||
})
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
resourceCounts, err = m.getPerGroupDistinctCounts(gCtx, orgID, req.Start, req.End, req.Filter, req.GroupBy, pageGroups, namespaceCountAttrKeys, namespacesTableMetricNamesList)
|
||||
return err
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Records = buildNamespaceRecords(queryResp, pageGroups, req.GroupBy, metadataMap, phaseCounts, podStatusCounts)
|
||||
resp.Records = buildNamespaceRecords(queryResp, pageGroups, req.GroupBy, metadataMap, phaseCounts, podStatusCounts, resourceCounts)
|
||||
resp.Warning = mergeQueryWarnings(queryResp.Warning, podStatusWarning)
|
||||
|
||||
return resp, nil
|
||||
@@ -732,6 +738,7 @@ func (m *module) ListClusters(ctx context.Context, orgID valuer.UUID, req *infra
|
||||
podPhaseCountsMap map[string]podPhaseCounts
|
||||
podStatusCounts map[string]podStatusCounts
|
||||
podStatusWarning *qbtypes.QueryWarnData
|
||||
resourceCounts map[string]map[string]int64
|
||||
)
|
||||
|
||||
g, gCtx := errgroup.WithContext(ctx)
|
||||
@@ -756,12 +763,17 @@ func (m *module) ListClusters(ctx context.Context, orgID valuer.UUID, req *infra
|
||||
podStatusCounts, podStatusWarning, err = m.getPerGroupPodStatusCountsWithReqMetricChecks(gCtx, req.Start, req.End, req.Filter, req.GroupBy, pageGroups)
|
||||
return err
|
||||
})
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
resourceCounts, err = m.getPerGroupDistinctCounts(gCtx, orgID, req.Start, req.End, req.Filter, req.GroupBy, pageGroups, clusterCountAttrKeys, clusterMetricNamesListForCounts)
|
||||
return err
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Records = buildClusterRecords(queryResp, pageGroups, req.GroupBy, metadataMap, nodeConditionCountsMap, podPhaseCountsMap, podStatusCounts)
|
||||
resp.Records = buildClusterRecords(queryResp, pageGroups, req.GroupBy, metadataMap, nodeConditionCountsMap, podPhaseCountsMap, podStatusCounts, resourceCounts)
|
||||
resp.Warning = mergeQueryWarnings(queryResp.Warning, podStatusWarning)
|
||||
|
||||
return resp, nil
|
||||
|
||||
@@ -19,6 +19,7 @@ func buildNamespaceRecords(
|
||||
metadataMap map[string]map[string]string,
|
||||
phaseCounts map[string]podPhaseCounts,
|
||||
podStatusCounts map[string]podStatusCounts,
|
||||
resourceCounts map[string]map[string]int64,
|
||||
) []inframonitoringtypes.NamespaceRecord {
|
||||
metricsMap := parseFullQueryResponse(resp, groupBy)
|
||||
|
||||
@@ -57,6 +58,13 @@ func buildNamespaceRecords(
|
||||
record.PodCountsByStatus = podStatusCountsToResponse(podStatusCountsForGroup)
|
||||
}
|
||||
|
||||
if counts, ok := resourceCounts[compositeKey]; ok {
|
||||
record.Counts.Deployments = counts[inframonitoringtypes.DeploymentNameAttrKey]
|
||||
record.Counts.DaemonSets = counts[inframonitoringtypes.DaemonSetNameAttrKey]
|
||||
record.Counts.Jobs = counts[inframonitoringtypes.JobNameAttrKey]
|
||||
record.Counts.StatefulSets = counts[inframonitoringtypes.StatefulSetNameAttrKey]
|
||||
}
|
||||
|
||||
if attrs, ok := metadataMap[compositeKey]; ok {
|
||||
for k, v := range attrs {
|
||||
record.Meta[k] = v
|
||||
|
||||
@@ -32,6 +32,16 @@ var namespaceAttrKeysForMetadata = []string{
|
||||
"k8s.cluster.name",
|
||||
}
|
||||
|
||||
// namespaceCountAttrKeys are the workload resource attributes whose distinct
|
||||
// values are counted per namespace. They are read from the pod metric universe,
|
||||
// which carries the owner workload names for each pod series.
|
||||
var namespaceCountAttrKeys = []string{
|
||||
inframonitoringtypes.DeploymentNameAttrKey,
|
||||
inframonitoringtypes.DaemonSetNameAttrKey,
|
||||
inframonitoringtypes.JobNameAttrKey,
|
||||
inframonitoringtypes.StatefulSetNameAttrKey,
|
||||
}
|
||||
|
||||
var orderByToNamespacesQueryNames = map[string][]string{
|
||||
inframonitoringtypes.NamespacesOrderByCPU: {"A"},
|
||||
inframonitoringtypes.NamespacesOrderByMemory: {"D"},
|
||||
|
||||
@@ -42,7 +42,6 @@ var (
|
||||
|
||||
// GCP services.
|
||||
GCPServiceCloudSQLPostgres = ServiceID{valuer.NewString("cloudsql_postgres")}
|
||||
GCPServiceMemorystoreRedis = ServiceID{valuer.NewString("memorystore_redis")}
|
||||
)
|
||||
|
||||
func (ServiceID) Enum() []any {
|
||||
@@ -75,7 +74,6 @@ func (ServiceID) Enum() []any {
|
||||
AzureServiceCassandraDB,
|
||||
AzureServiceRedis,
|
||||
GCPServiceCloudSQLPostgres,
|
||||
GCPServiceMemorystoreRedis,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +112,6 @@ var SupportedServices = map[CloudProviderType][]ServiceID{
|
||||
},
|
||||
CloudProviderTypeGCP: {
|
||||
GCPServiceCloudSQLPostgres,
|
||||
GCPServiceMemorystoreRedis,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,15 @@ type ClusterRecord struct {
|
||||
NodeCountsByReadiness NodeCountsByReadiness `json:"nodeCountsByReadiness" required:"true"`
|
||||
PodCountsByPhase PodCountsByPhase `json:"podCountsByPhase" required:"true"`
|
||||
PodCountsByStatus PodCountsByStatus `json:"podCountsByStatus" required:"true"`
|
||||
Meta map[string]string `json:"meta" required:"true"`
|
||||
Counts struct {
|
||||
Nodes int64 `json:"nodes" required:"true"`
|
||||
Namespaces int64 `json:"namespaces" required:"true"`
|
||||
Deployments int64 `json:"deployments" required:"true"`
|
||||
DaemonSets int64 `json:"daemonSets" required:"true"`
|
||||
Jobs int64 `json:"jobs" required:"true"`
|
||||
StatefulSets int64 `json:"statefulSets" required:"true"`
|
||||
} `json:"counts" required:"true"`
|
||||
Meta map[string]string `json:"meta" required:"true"`
|
||||
}
|
||||
|
||||
// PostableClusters is the request body for the v2 clusters list API.
|
||||
|
||||
@@ -22,7 +22,13 @@ type NamespaceRecord struct {
|
||||
NamespaceMemory float64 `json:"namespaceMemory" required:"true"`
|
||||
PodCountsByPhase PodCountsByPhase `json:"podCountsByPhase" required:"true"`
|
||||
PodCountsByStatus PodCountsByStatus `json:"podCountsByStatus" required:"true"`
|
||||
Meta map[string]string `json:"meta" required:"true"`
|
||||
Counts struct {
|
||||
Deployments int64 `json:"deployments" required:"true"`
|
||||
DaemonSets int64 `json:"daemonSets" required:"true"`
|
||||
Jobs int64 `json:"jobs" required:"true"`
|
||||
StatefulSets int64 `json:"statefulSets" required:"true"`
|
||||
} `json:"counts" required:"true"`
|
||||
Meta map[string]string `json:"meta" required:"true"`
|
||||
}
|
||||
|
||||
// PostableNamespaces is the request body for the v2 namespaces list API.
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c1-n1","k8s.node.uid":"acc-c1-n1-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c1-n1","k8s.node.uid":"acc-c1-n1-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c1-n1","k8s.node.uid":"acc-c1-n1-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n1-p-uid","k8s.pod.name":"acc-c1-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n1","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n1-p-uid","k8s.pod.name":"acc-c1-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n1","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n1-p-uid","k8s.pod.name":"acc-c1-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n1","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n1-p-uid","k8s.pod.name":"acc-c1-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n1","k8s.cluster.name":"acc-cluster-1","k8s.deployment.name":"dep-a"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n1-p-uid","k8s.pod.name":"acc-c1-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n1","k8s.cluster.name":"acc-cluster-1","k8s.deployment.name":"dep-a"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n1-p-uid","k8s.pod.name":"acc-c1-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n1","k8s.cluster.name":"acc-cluster-1","k8s.deployment.name":"dep-a"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c1-n2","k8s.node.uid":"acc-c1-n2-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c1-n2","k8s.node.uid":"acc-c1-n2-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c1-n2","k8s.node.uid":"acc-c1-n2-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
@@ -31,9 +31,12 @@
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c1-n2","k8s.node.uid":"acc-c1-n2-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c1-n2","k8s.node.uid":"acc-c1-n2-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c1-n2","k8s.node.uid":"acc-c1-n2-uid","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p-uid","k8s.pod.name":"acc-c1-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p-uid","k8s.pod.name":"acc-c1-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p-uid","k8s.pod.name":"acc-c1-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p-uid","k8s.pod.name":"acc-c1-n2-p","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1","k8s.statefulset.name":"sts-a"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p-uid","k8s.pod.name":"acc-c1-n2-p","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1","k8s.statefulset.name":"sts-a"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p-uid","k8s.pod.name":"acc-c1-n2-p","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1","k8s.statefulset.name":"sts-a"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p2-uid","k8s.pod.name":"acc-c1-n2-p2","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1","k8s.deployment.name":"dep-a"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p2-uid","k8s.pod.name":"acc-c1-n2-p2","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1","k8s.deployment.name":"dep-a"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c1-n2-p2-uid","k8s.pod.name":"acc-c1-n2-p2","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c1-n2","k8s.cluster.name":"acc-cluster-1","k8s.deployment.name":"dep-a"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n1","k8s.node.uid":"acc-c2-n1-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n1","k8s.node.uid":"acc-c2-n1-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n1","k8s.node.uid":"acc-c2-n1-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
@@ -49,9 +52,9 @@
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n1","k8s.node.uid":"acc-c2-n1-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n1","k8s.node.uid":"acc-c2-n1-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n1","k8s.node.uid":"acc-c2-n1-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n1-p-uid","k8s.pod.name":"acc-c2-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n1","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n1-p-uid","k8s.pod.name":"acc-c2-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n1","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n1-p-uid","k8s.pod.name":"acc-c2-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n1","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n1-p-uid","k8s.pod.name":"acc-c2-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n1","k8s.cluster.name":"acc-cluster-2","k8s.deployment.name":"dep-b"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n1-p-uid","k8s.pod.name":"acc-c2-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n1","k8s.cluster.name":"acc-cluster-2","k8s.deployment.name":"dep-b"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n1-p-uid","k8s.pod.name":"acc-c2-n1-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n1","k8s.cluster.name":"acc-cluster-2","k8s.deployment.name":"dep-b"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n2","k8s.node.uid":"acc-c2-n2-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n2","k8s.node.uid":"acc-c2-n2-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n2","k8s.node.uid":"acc-c2-n2-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
@@ -67,9 +70,9 @@
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n2","k8s.node.uid":"acc-c2-n2-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n2","k8s.node.uid":"acc-c2-n2-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n2","k8s.node.uid":"acc-c2-n2-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n2-p-uid","k8s.pod.name":"acc-c2-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n2","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n2-p-uid","k8s.pod.name":"acc-c2-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n2","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n2-p-uid","k8s.pod.name":"acc-c2-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n2","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n2-p-uid","k8s.pod.name":"acc-c2-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n2","k8s.cluster.name":"acc-cluster-2","k8s.daemonset.name":"ds-b"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n2-p-uid","k8s.pod.name":"acc-c2-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n2","k8s.cluster.name":"acc-cluster-2","k8s.daemonset.name":"ds-b"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n2-p-uid","k8s.pod.name":"acc-c2-n2-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n2","k8s.cluster.name":"acc-cluster-2","k8s.daemonset.name":"ds-b"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n3","k8s.node.uid":"acc-c2-n3-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n3","k8s.node.uid":"acc-c2-n3-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"acc-c2-n3","k8s.node.uid":"acc-c2-n3-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
@@ -85,6 +88,6 @@
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n3","k8s.node.uid":"acc-c2-n3-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n3","k8s.node.uid":"acc-c2-n3-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"acc-c2-n3","k8s.node.uid":"acc-c2-n3-uid","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n3-p-uid","k8s.pod.name":"acc-c2-n3-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n3","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n3-p-uid","k8s.pod.name":"acc-c2-n3-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n3","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n3-p-uid","k8s.pod.name":"acc-c2-n3-p","k8s.namespace.name":"ns-x","k8s.node.name":"acc-c2-n3","k8s.cluster.name":"acc-cluster-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n3-p-uid","k8s.pod.name":"acc-c2-n3-p","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c2-n3","k8s.cluster.name":"acc-cluster-2","k8s.job.name":"job-b"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n3-p-uid","k8s.pod.name":"acc-c2-n3-p","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c2-n3","k8s.cluster.name":"acc-cluster-2","k8s.job.name":"job-b"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-c2-n3-p-uid","k8s.pod.name":"acc-c2-n3-p","k8s.namespace.name":"ns-y","k8s.node.name":"acc-c2-n3","k8s.cluster.name":"acc-cluster-2","k8s.job.name":"job-b"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
|
||||
@@ -12,10 +12,18 @@
|
||||
},
|
||||
"podCountsByPhase": {
|
||||
"pending": 0,
|
||||
"running": 2,
|
||||
"running": 3,
|
||||
"succeeded": 0,
|
||||
"failed": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"counts": {
|
||||
"nodes": 2,
|
||||
"namespaces": 2,
|
||||
"deployments": 2,
|
||||
"daemonSets": 0,
|
||||
"jobs": 0,
|
||||
"statefulSets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -34,6 +42,14 @@
|
||||
"succeeded": 0,
|
||||
"failed": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"counts": {
|
||||
"nodes": 3,
|
||||
"namespaces": 2,
|
||||
"deployments": 1,
|
||||
"daemonSets": 1,
|
||||
"jobs": 1,
|
||||
"statefulSets": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:00:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:02:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:04:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:00:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:02:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:04:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1a-uid","k8s.pod.name":"acc-p1a","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-1"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":100000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p1b-uid","k8s.pod.name":"acc-p1b","k8s.namespace.name":"acc-ns-1","k8s.cluster.name":"cluster-x","k8s.deployment.name":"web-2"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:00:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:02:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:04:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2a-uid","k8s.pod.name":"acc-p2a","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.deployment.name":"api"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:00:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:02:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:04:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2b-uid","k8s.pod.name":"acc-p2b","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.daemonset.name":"agent"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.cpu.usage","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.75,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:00:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:02:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.memory.working_set","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:04:00+00:00","value":200000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"acc-p2c-uid","k8s.pod.name":"acc-p2c","k8s.namespace.name":"acc-ns-2","k8s.cluster.name":"cluster-y","k8s.job.name":"batch"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
"namespaceName": "acc-ns-1",
|
||||
"namespaceCPU": 1.0,
|
||||
"namespaceMemory": 200000000.0,
|
||||
"podCountsByPhase": {"pending": 0, "running": 2, "succeeded": 0, "failed": 0, "unknown": 0}
|
||||
"podCountsByPhase": {"pending": 0, "running": 2, "succeeded": 0, "failed": 0, "unknown": 0},
|
||||
"counts": {"deployments": 2, "daemonSets": 0, "jobs": 0, "statefulSets": 0}
|
||||
},
|
||||
{
|
||||
"namespaceName": "acc-ns-2",
|
||||
"namespaceCPU": 2.25,
|
||||
"namespaceMemory": 600000000.0,
|
||||
"podCountsByPhase": {"pending": 0, "running": 3, "succeeded": 0, "failed": 0, "unknown": 0}
|
||||
"podCountsByPhase": {"pending": 0, "running": 3, "succeeded": 0, "failed": 0, "unknown": 0},
|
||||
"counts": {"deployments": 1, "daemonSets": 1, "jobs": 1, "statefulSets": 0}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ def test_namespaces_accuracy(
|
||||
"namespaceCPU",
|
||||
"namespaceMemory",
|
||||
"podCountsByPhase",
|
||||
"counts",
|
||||
"meta",
|
||||
):
|
||||
assert field in record, f"missing {field} in {record!r}"
|
||||
@@ -81,6 +82,9 @@ def test_namespaces_accuracy(
|
||||
for bucket in ("pending", "running", "succeeded", "failed", "unknown"):
|
||||
assert bucket in record["podCountsByPhase"]
|
||||
assert isinstance(record["podCountsByPhase"][bucket], int)
|
||||
for bucket in ("deployments", "daemonSets", "jobs", "statefulSets"):
|
||||
assert bucket in record["counts"]
|
||||
assert isinstance(record["counts"][bucket], int)
|
||||
|
||||
assert record["meta"].get("k8s.namespace.name") == record["namespaceName"]
|
||||
assert "k8s.cluster.name" in record["meta"]
|
||||
@@ -90,6 +94,7 @@ def test_namespaces_accuracy(
|
||||
for field in ("namespaceCPU", "namespaceMemory"):
|
||||
assert compare_values(record[field], exp[field], 1e-6), f"{record['namespaceName']}.{field}: got {record[field]}, expected {exp[field]}"
|
||||
assert record["podCountsByPhase"] == exp["podCountsByPhase"]
|
||||
assert record["counts"] == exp["counts"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -78,6 +78,7 @@ def test_clusters_accuracy(
|
||||
"clusterMemoryAllocatable",
|
||||
"nodeCountsByReadiness",
|
||||
"podCountsByPhase",
|
||||
"counts",
|
||||
"meta",
|
||||
):
|
||||
assert field in record, f"missing {field} in {record!r}"
|
||||
@@ -88,6 +89,9 @@ def test_clusters_accuracy(
|
||||
for bucket in ("pending", "running", "succeeded", "failed", "unknown"):
|
||||
assert bucket in record["podCountsByPhase"]
|
||||
assert isinstance(record["podCountsByPhase"][bucket], int)
|
||||
for bucket in ("nodes", "namespaces", "deployments", "daemonSets", "jobs", "statefulSets"):
|
||||
assert bucket in record["counts"]
|
||||
assert isinstance(record["counts"][bucket], int)
|
||||
|
||||
assert record["meta"].get("k8s.cluster.name") == record["clusterName"]
|
||||
|
||||
@@ -102,6 +106,7 @@ def test_clusters_accuracy(
|
||||
assert compare_values(record[field], exp[field], 1e-6), f"{record['clusterName']}.{field}: got {record[field]}, expected {exp[field]}"
|
||||
assert record["nodeCountsByReadiness"] == exp["nodeCountsByReadiness"]
|
||||
assert record["podCountsByPhase"] == exp["podCountsByPhase"]
|
||||
assert record["counts"] == exp["counts"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user