Compare commits

..

43 Commits

Author SHA1 Message Date
Naman Verma
ef142037f9 Merge branch 'main' into nv/6703 2026-02-24 16:16:57 +05:30
Naman Verma
83964c5f12 fix: err check for type scanning 2026-02-24 16:16:33 +05:30
Vinicius Lourenço
cb1a2a8a13 perf(bundle-size): lazy load pages to reduce main bundle size (#10230)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
Release Drafter / update_release_draft (push) Waiting to run
2026-02-24 10:41:40 +00:00
Naman Verma
c5d0689673 Merge branch 'main' into nv/6703 2026-02-24 16:01:13 +05:30
Naman Verma
707387ff78 test: integration test for no param scenario 2026-02-24 15:58:24 +05:30
Nikhil Soni
1a5d37b25a fix: add missing filtering for ip address for scalar data (#10264)
* fix: add missing filtering for ip address for scalar data

In domain listing api for external api monitoring,
we have option to filter out the IP address but
it only handles timeseries and raw type data while
domain list handler returns scalar data.

* fix: switch to new derived attributes for ip filtering

---------

Co-authored-by: Nityananda Gohain <nityanandagohain@gmail.com>
2026-02-24 10:26:10 +00:00
Piyush Singariya
bc4273f2f8 chore: test clickhouse version 25.12.5 (#10402) 2026-02-24 14:55:51 +05:30
Abhi kumar
77fdd28e93 Chore/yaxis cleanup (#10397)
Some checks failed
build-staging / staging (push) Has been cancelled
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* fix: fixed unit converstion support across thresholds and yaxisunit

* fix: fixed tsc

* fix: fixed failing tests

* chore: cleaned up old yaxisselector

* chore: minor change
2026-02-24 08:57:14 +00:00
Karan Balani
8e08a42617 feat: control visibility of root user in list user api using flagger (#10381) 2026-02-24 08:29:36 +00:00
Naman Verma
a75a597e87 test: fix space aggregation in integration tests 2026-02-24 13:57:35 +05:30
Abhi kumar
2c3042304a fix: fixed unit converstion support across thresholds and yaxisunit (#10393)
* fix: fixed unit converstion support across thresholds and yaxisunit

* fix: fixed tsc

* fix: fixed failing tests

* chore: minor change
2026-02-24 13:47:28 +05:30
Naman Verma
85caa01bf2 chore: run yarn gen api 2026-02-24 13:37:36 +05:30
Naman Verma
5f725ee4d4 chore: openapi spec update 2026-02-24 13:32:45 +05:30
Naman Verma
a6f3301877 chore: remove histogram_count from err message 2026-02-24 13:31:54 +05:30
Naman Verma
79f62c7678 chore: non nil return of metric types map 2026-02-24 13:29:56 +05:30
Naman Verma
ba9c1ba64f feat: remove histogram_count and add metric type from time series data to use in statement builder 2026-02-24 13:28:29 +05:30
Naman Verma
350f53600a Merge branch 'main' into nv/6703 2026-02-24 12:42:49 +05:30
Naman Verma
59e8d310ec feat: add support for no aggregation param 2026-02-24 12:42:34 +05:30
Ishan
c9da09256e feat: 3729 Add to alert flow from Logs Explorer doesn't work (#10241)
* feat: add to alert bug

* feat: moved logic to util

* feat: updated null checks

* feat: reverting to first commit

* feat: list panel check

* feat: list panel try/catch

* feat: added testcases
2026-02-24 12:38:29 +05:30
Naman Verma
91d0e456f6 Merge branch 'main' into nv/6703 2026-02-24 11:43:38 +05:30
Naman Verma
f4e1f10635 Merge branch 'main' into nv/6703 2026-02-23 21:08:20 +05:30
Srikanth Chekuri
6b363c6a21 Merge branch 'main' into nv/6703 2026-02-23 19:48:16 +05:30
Naman Verma
0ce179e9c0 Merge branch 'main' into nv/6703 2026-02-23 16:44:18 +05:30
Naman Verma
52409f4a80 test: gauge metric tests 2026-02-23 08:36:32 +05:30
Naman Verma
7109af859c test: fix base time in week based test for multi temporality 2026-02-23 08:36:17 +05:30
Naman Verma
adec9cac5e Merge branch 'main' into nv/6703 2026-02-22 20:57:55 +05:30
Naman Verma
4bd5f804ec fix: open api test fix 2026-02-20 13:35:39 +05:30
Naman Verma
a349b6935d Merge branch 'main' into nv/6703 2026-02-20 13:33:04 +05:30
Naman Verma
5780fac789 Merge branch 'main' into nv/6703 2026-02-20 12:48:21 +05:30
Naman Verma
3450a98fdf fix: open api spec generation 2026-02-20 12:47:11 +05:30
Naman Verma
4fa5c42a36 Merge branch 'main' into nv/6703 2026-02-20 11:26:51 +05:30
Naman Verma
3555a73ac0 fix: typo fix in test comment 2026-02-20 11:25:03 +05:30
Naman Verma
ed2d8d06cb test: integration tests for count based aggregation 2026-02-20 11:23:52 +05:30
Naman Verma
203318a206 fix: fix query for greater than comparison 2026-02-20 11:22:28 +05:30
Naman Verma
67fbf5a548 chore: use comparison param directly in metric aggregation 2026-02-20 08:44:26 +05:30
Naman Verma
195dd36078 Merge branch 'main' into nv/6703 2026-02-20 08:18:55 +05:30
Naman Verma
2c12f0bd03 Merge branch 'main' into nv/6703 2026-02-19 18:26:38 +05:30
Naman Verma
fcbb1843d9 chore: check for le only based on metric type 2026-02-19 16:03:46 +05:30
Naman Verma
00dff5c930 chore: rename limit to threshold in ComparisonSpaceAggregationParam 2026-02-19 16:02:11 +05:30
Naman Verma
c7e5258329 Merge branch 'main' into nv/6703 2026-02-19 15:40:24 +05:30
Naman Verma
84a7be22f7 chore: generate new openapi spec 2026-02-19 11:24:00 +05:30
Naman Verma
c5923f942f chore: fix typo in struct name 2026-02-19 11:23:38 +05:30
Naman Verma
f91eee7e50 feat: add support for count based aggregation in histogram metrics 2026-02-19 08:45:44 +05:30
46 changed files with 2735 additions and 1080 deletions

View File

@@ -54,7 +54,7 @@ jobs:
- sqlite
clickhouse-version:
- 25.5.6
- 25.10.5
- 25.12.5
schema-migrator-version:
- v0.142.0
postgres-version:

View File

@@ -842,6 +842,14 @@ components:
- temporality
- isMonotonic
type: object
MetrictypesComparisonSpaceAggregationParam:
properties:
operator:
type: string
threshold:
format: double
type: number
type: object
MetrictypesSpaceAggregation:
enum:
- sum
@@ -1138,6 +1146,8 @@ components:
type: object
Querybuildertypesv5MetricAggregation:
properties:
comparisonSpaceAggregationParam:
$ref: '#/components/schemas/MetrictypesComparisonSpaceAggregationParam'
metricName:
type: string
reduceTo:

View File

@@ -308,3 +308,15 @@ export const PublicDashboardPage = Loadable(
/* webpackChunkName: "Public Dashboard Page" */ 'pages/PublicDashboard'
),
);
export const AlertTypeSelectionPage = Loadable(
() =>
import(
/* webpackChunkName: "Alert Type Selection Page" */ 'pages/AlertTypeSelection'
),
);
export const MeterExplorerPage = Loadable(
() =>
import(/* webpackChunkName: "Meter Explorer Page" */ 'pages/MeterExplorer'),
);

View File

@@ -1,12 +1,10 @@
import { RouteProps } from 'react-router-dom';
import ROUTES from 'constants/routes';
import AlertTypeSelectionPage from 'pages/AlertTypeSelection';
import MessagingQueues from 'pages/MessagingQueues';
import MeterExplorer from 'pages/MeterExplorer';
import {
AlertHistory,
AlertOverview,
AlertTypeSelectionPage,
AllAlertChannels,
AllErrors,
ApiMonitoring,
@@ -29,6 +27,8 @@ import {
LogsExplorer,
LogsIndexToFields,
LogsSaveViews,
MessagingQueuesMainPage,
MeterExplorerPage,
MetricsExplorer,
OldLogsExplorer,
Onboarding,
@@ -399,28 +399,28 @@ const routes: AppRoutes[] = [
{
path: ROUTES.MESSAGING_QUEUES_KAFKA,
exact: true,
component: MessagingQueues,
component: MessagingQueuesMainPage,
key: 'MESSAGING_QUEUES_KAFKA',
isPrivate: true,
},
{
path: ROUTES.MESSAGING_QUEUES_CELERY_TASK,
exact: true,
component: MessagingQueues,
component: MessagingQueuesMainPage,
key: 'MESSAGING_QUEUES_CELERY_TASK',
isPrivate: true,
},
{
path: ROUTES.MESSAGING_QUEUES_OVERVIEW,
exact: true,
component: MessagingQueues,
component: MessagingQueuesMainPage,
key: 'MESSAGING_QUEUES_OVERVIEW',
isPrivate: true,
},
{
path: ROUTES.MESSAGING_QUEUES_KAFKA_DETAIL,
exact: true,
component: MessagingQueues,
component: MessagingQueuesMainPage,
key: 'MESSAGING_QUEUES_KAFKA_DETAIL',
isPrivate: true,
},
@@ -463,21 +463,21 @@ const routes: AppRoutes[] = [
{
path: ROUTES.METER,
exact: true,
component: MeterExplorer,
component: MeterExplorerPage,
key: 'METER',
isPrivate: true,
},
{
path: ROUTES.METER_EXPLORER,
exact: true,
component: MeterExplorer,
component: MeterExplorerPage,
key: 'METER_EXPLORER',
isPrivate: true,
},
{
path: ROUTES.METER_EXPLORER_VIEWS,
exact: true,
component: MeterExplorer,
component: MeterExplorerPage,
key: 'METER_EXPLORER_VIEWS',
isPrivate: true,
},

View File

@@ -1006,6 +1006,18 @@ export interface MetricsexplorertypesUpdateMetricMetadataRequestDTO {
unit: string;
}
export interface MetrictypesComparisonSpaceAggregationParamDTO {
/**
* @type string
*/
operator?: string;
/**
* @type number
* @format double
*/
threshold?: number;
}
export enum MetrictypesSpaceAggregationDTO {
sum = 'sum',
avg = 'avg',
@@ -1367,6 +1379,7 @@ export interface Querybuildertypesv5LogAggregationDTO {
}
export interface Querybuildertypesv5MetricAggregationDTO {
comparisonSpaceAggregationParam?: MetrictypesComparisonSpaceAggregationParamDTO;
/**
* @type string
*/

View File

@@ -70,6 +70,9 @@
padding-left: 0;
}
transition: background-color 0.2s ease-in;
&:hover {
background-color: rgba(171, 189, 255, 0.04) !important;
}
}
.log-selected-fields {
@@ -180,6 +183,11 @@
.log-value {
color: var(--text-slate-400);
}
.log-line {
&:hover {
background-color: var(--text-vanilla-200) !important;
}
}
}
.dark {

View File

@@ -49,12 +49,6 @@ export const Container = styled(Card)<{
${({ $isActiveLog, $isDarkMode, $logType }): string =>
getActiveLogBackground($isActiveLog, $isDarkMode, $logType)}
}
&:hover .ant-card-body {
${({ $isDarkMode, $logType }): string =>
getActiveLogBackground(true, $isDarkMode, $logType)}
}
`;
export const LogContainer = styled.div<LogContainerProps>`

View File

@@ -8,6 +8,7 @@ import styled from 'styled-components';
import {
getActiveLogBackground,
getCustomHighlightBackground,
getDefaultLogBackground,
} from 'utils/logs';
import { RawLogContentProps } from './types';
@@ -47,9 +48,7 @@ export const RawLogViewContainer = styled(Row)<{
${({ $isReadOnly, $isActiveLog, $isDarkMode, $logType }): string =>
$isActiveLog
? getActiveLogBackground($isActiveLog, $isDarkMode, $logType)
: !$isReadOnly
? `&:hover { ${getActiveLogBackground(true, $isDarkMode, $logType)} }`
: ''}
: getDefaultLogBackground($isReadOnly, $isDarkMode)}
${({ $isHightlightedLog, $isDarkMode }): string =>
$isHightlightedLog

View File

@@ -1,4 +1,5 @@
import { UniversalYAxisUnit } from '../types';
import { YAxisCategoryNames } from '../constants';
import { UniversalYAxisUnit, YAxisCategory } from '../types';
import {
getUniversalNameFromMetricUnit,
mapMetricUnitToUniversalUnit,
@@ -41,29 +42,29 @@ describe('YAxisUnitSelector utils', () => {
describe('mergeCategories', () => {
it('merges categories correctly', () => {
const categories1 = [
const categories1: YAxisCategory[] = [
{
name: 'Data',
name: YAxisCategoryNames.Data,
units: [
{ name: 'bytes', id: UniversalYAxisUnit.BYTES },
{ name: 'kilobytes', id: UniversalYAxisUnit.KILOBYTES },
],
},
];
const categories2 = [
const categories2: YAxisCategory[] = [
{
name: 'Data',
name: YAxisCategoryNames.Data,
units: [{ name: 'bits', id: UniversalYAxisUnit.BITS }],
},
{
name: 'Time',
name: YAxisCategoryNames.Time,
units: [{ name: 'seconds', id: UniversalYAxisUnit.SECONDS }],
},
];
const mergedCategories = mergeCategories(categories1, categories2);
expect(mergedCategories).toEqual([
{
name: 'Data',
name: YAxisCategoryNames.Data,
units: [
{ name: 'bytes', id: UniversalYAxisUnit.BYTES },
{ name: 'kilobytes', id: UniversalYAxisUnit.KILOBYTES },
@@ -71,7 +72,7 @@ describe('YAxisUnitSelector utils', () => {
],
},
{
name: 'Time',
name: YAxisCategoryNames.Time,
units: [{ name: 'seconds', id: UniversalYAxisUnit.SECONDS }],
},
]);

View File

@@ -1,5 +1,36 @@
import { UnitFamilyConfig, UniversalYAxisUnit, YAxisUnit } from './types';
export enum YAxisCategoryNames {
Time = 'Time',
Data = 'Data',
DataRate = 'Data Rate',
Count = 'Count',
Operations = 'Operations',
Percentage = 'Percentage',
Boolean = 'Boolean',
None = 'None',
HashRate = 'Hash Rate',
Miscellaneous = 'Miscellaneous',
Acceleration = 'Acceleration',
Angular = 'Angular',
Area = 'Area',
Flops = 'FLOPs',
Concentration = 'Concentration',
Currency = 'Currency',
Datetime = 'Datetime',
PowerElectrical = 'Power/Electrical',
Flow = 'Flow',
Force = 'Force',
Mass = 'Mass',
Length = 'Length',
Pressure = 'Pressure',
Radiation = 'Radiation',
RotationSpeed = 'Rotation Speed',
Temperature = 'Temperature',
Velocity = 'Velocity',
Volume = 'Volume',
}
// Mapping of universal y-axis units to their AWS, UCUM, and OpenMetrics equivalents (if available)
export const UniversalYAxisUnitMappings: Partial<
Record<UniversalYAxisUnit, Set<YAxisUnit> | null>

View File

@@ -1,10 +1,11 @@
import { Y_AXIS_UNIT_NAMES } from './constants';
import { YAxisCategoryNames } from './constants';
import { UniversalYAxisUnit, YAxisCategory } from './types';
// Base categories for the universal y-axis units
export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
{
name: 'Time',
name: YAxisCategoryNames.Time,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.SECONDS],
@@ -37,7 +38,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Data',
name: YAxisCategoryNames.Data,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BYTES],
@@ -154,7 +155,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Data Rate',
name: YAxisCategoryNames.DataRate,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BYTES_SECOND],
@@ -295,7 +296,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Count',
name: YAxisCategoryNames.Count,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.COUNT],
@@ -312,7 +313,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Operations',
name: YAxisCategoryNames.Operations,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.OPS_SECOND],
@@ -353,7 +354,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Percentage',
name: YAxisCategoryNames.Percentage,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PERCENT],
@@ -366,7 +367,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Boolean',
name: YAxisCategoryNames.Boolean,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TRUE_FALSE],
@@ -382,7 +383,7 @@ export const BASE_Y_AXIS_CATEGORIES: YAxisCategory[] = [
export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
{
name: 'Time',
name: YAxisCategoryNames.Time,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.DURATION_MS],
@@ -419,7 +420,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Data Rate',
name: YAxisCategoryNames.DataRate,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.DATA_RATE_PACKETS_PER_SECOND],
@@ -428,7 +429,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Boolean',
name: YAxisCategoryNames.Boolean,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.ON_OFF],
@@ -437,7 +438,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'None',
name: YAxisCategoryNames.None,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.NONE],
@@ -446,7 +447,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Hash Rate',
name: YAxisCategoryNames.HashRate,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.HASH_RATE_HASHES_PER_SECOND],
@@ -479,7 +480,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Miscellaneous',
name: YAxisCategoryNames.Miscellaneous,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MISC_STRING],
@@ -520,7 +521,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Acceleration',
name: YAxisCategoryNames.Acceleration,
units: [
{
name:
@@ -541,7 +542,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Angular',
name: YAxisCategoryNames.Angular,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.ANGULAR_DEGREE],
@@ -566,7 +567,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Area',
name: YAxisCategoryNames.Area,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.AREA_SQUARE_METERS],
@@ -583,7 +584,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'FLOPs',
name: YAxisCategoryNames.Flops,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.FLOPS_FLOPS],
@@ -620,7 +621,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Concentration',
name: YAxisCategoryNames.Concentration,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.CONCENTRATION_PPM],
@@ -677,7 +678,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Currency',
name: YAxisCategoryNames.Currency,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.CURRENCY_USD],
@@ -774,7 +775,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Datetime',
name: YAxisCategoryNames.Datetime,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.DATETIME_ISO],
@@ -811,7 +812,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Power/Electrical',
name: YAxisCategoryNames.PowerElectrical,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.POWER_WATT],
@@ -968,7 +969,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Flow',
name: YAxisCategoryNames.Flow,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.FLOW_GALLONS_PER_MINUTE],
@@ -1005,7 +1006,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Force',
name: YAxisCategoryNames.Force,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.FORCE_NEWTON_METERS],
@@ -1026,7 +1027,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Mass',
name: YAxisCategoryNames.Mass,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MASS_MILLIGRAM],
@@ -1051,7 +1052,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Length',
name: YAxisCategoryNames.Length,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.LENGTH_MILLIMETER],
@@ -1080,7 +1081,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Pressure',
name: YAxisCategoryNames.Pressure,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PRESSURE_MILLIBAR],
@@ -1117,7 +1118,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Radiation',
name: YAxisCategoryNames.Radiation,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.RADIATION_BECQUEREL],
@@ -1174,7 +1175,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Rotation Speed',
name: YAxisCategoryNames.RotationSpeed,
units: [
{
name:
@@ -1200,7 +1201,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Temperature',
name: YAxisCategoryNames.Temperature,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TEMPERATURE_CELSIUS],
@@ -1217,7 +1218,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Velocity',
name: YAxisCategoryNames.Velocity,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.VELOCITY_METERS_PER_SECOND],
@@ -1238,7 +1239,7 @@ export const ADDITIONAL_Y_AXIS_CATEGORIES: YAxisCategory[] = [
],
},
{
name: 'Volume',
name: YAxisCategoryNames.Volume,
units: [
{
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.VOLUME_MILLILITER],

View File

@@ -1,3 +1,5 @@
import { YAxisCategoryNames } from './constants';
export interface YAxisUnitSelectorProps {
value: string | undefined;
onChange: (value: UniversalYAxisUnit) => void;
@@ -669,7 +671,7 @@ export interface UnitFamilyConfig {
}
export interface YAxisCategory {
name: string;
name: YAxisCategoryNames;
units: {
name: string;
id: UniversalYAxisUnit;

View File

@@ -172,23 +172,51 @@ function ExplorerOptions({
const { user } = useAppContext();
const handleConditionalQueryModification = useCallback(
// eslint-disable-next-line sonarjs/cognitive-complexity
(defaultQuery: Query | null): string => {
const queryToUse = defaultQuery || query;
if (!queryToUse) {
throw new Error('No query provided');
}
if (
queryToUse?.builder?.queryData?.[0]?.aggregateOperator !==
StringOperators.NOOP
StringOperators.NOOP &&
sourcepage !== DataSource.LOGS
) {
return JSON.stringify(queryToUse);
}
// Modify aggregateOperator to count, as noop is not supported in alerts
// Convert NOOP to COUNT for alerts and strip orderBy for logs
const modifiedQuery = cloneDeep(queryToUse);
if (modifiedQuery && modifiedQuery.builder?.queryData) {
modifiedQuery.builder.queryData = modifiedQuery.builder.queryData.map(
(item) => {
const updatedItem = { ...item };
modifiedQuery.builder.queryData[0].aggregateOperator = StringOperators.COUNT;
if (updatedItem.aggregateOperator === StringOperators.NOOP) {
updatedItem.aggregateOperator = StringOperators.COUNT;
}
return JSON.stringify(modifiedQuery);
// Alerts do not support order by on logs explorer queries
if (sourcepage === DataSource.LOGS && panelType === PANEL_TYPES.LIST) {
updatedItem.orderBy = [];
}
return updatedItem;
},
);
}
try {
return JSON.stringify(modifiedQuery);
} catch (err) {
throw new Error(
'Failed to stringify modified query: ' +
(err instanceof Error ? err.message : String(err)),
);
}
},
[query],
[panelType, query, sourcepage],
);
const onCreateAlertsHandler = useCallback(
@@ -757,9 +785,9 @@ function ExplorerOptions({
);
}, [
disabled,
query,
isOneChartPerQuery,
onCreateAlertsHandler,
query,
splitedQueries,
]);

View File

@@ -1,3 +1,4 @@
import { useHistory } from 'react-router-dom';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { MOCK_QUERY } from 'container/QueryTable/Drilldown/__tests__/mockTableData';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
@@ -15,6 +16,11 @@ import { getExplorerToolBarVisibility } from '../utils';
// Mock dependencies
jest.mock('hooks/dashboard/useUpdateDashboard');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: jest.fn(),
}));
jest.mock('../utils', () => ({
getExplorerToolBarVisibility: jest.fn(),
generateRGBAFromHex: jest.fn(() => 'rgba(0, 0, 0, 0.08)'),
@@ -29,6 +35,7 @@ const mockGetExplorerToolBarVisibility = jest.mocked(
);
const mockUseUpdateDashboard = jest.mocked(useUpdateDashboard);
const mockUseHistory = jest.mocked(useHistory);
// Mock data
const TEST_QUERY_ID = 'test-query-id';
@@ -103,7 +110,6 @@ describe('ExplorerOptionWrapper', () => {
beforeEach(() => {
jest.clearAllMocks();
mockGetExplorerToolBarVisibility.mockReturnValue(true);
// Mock useUpdateDashboard to return a mutation object
mockUseUpdateDashboard.mockReturnValue(({
mutate: jest.fn(),
@@ -117,6 +123,28 @@ describe('ExplorerOptionWrapper', () => {
} as unknown) as ReturnType<typeof useUpdateDashboard>);
});
it('should navigate to alert creation page when "Create an Alert" is clicked in logs-explorer', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
const mockPush = jest.fn();
mockUseHistory.mockReturnValue(({
push: mockPush,
} as unknown) as ReturnType<typeof useHistory>);
renderExplorerOptionWrapper({ sourcepage: DataSource.LOGS });
const createAlertButton = screen.getByRole('button', {
name: 'Create an Alert',
});
await user.click(createAlertButton);
expect(mockPush).toHaveBeenCalledTimes(1);
const calledWith = mockPush.mock.calls[0][0] as string;
const [path, search = ''] = calledWith.split('?');
expect(path).toBe('/alerts/new');
const params = new URLSearchParams(search);
expect(params.has('compositeQuery')).toBe(true);
});
describe('onExport functionality', () => {
it('should call onExport when New Dashboard button is clicked in export modal', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });

View File

@@ -70,8 +70,12 @@ export const TableRowStyled = styled.tr<{
&:hover {
${TableCellStyled} {
${({ $isDarkMode, $logType }): string =>
getActiveLogBackground(true, $isDarkMode, $logType)}
${({ $isActiveLog, $isDarkMode }): string =>
$isActiveLog
? getActiveLogBackground()
: `background-color: ${
!$isDarkMode ? 'var(--bg-vanilla-200)' : 'rgba(171, 189, 255, 0.04)'
}`}
}
.log-line-action-buttons {
display: flex;

View File

@@ -18,8 +18,8 @@ jest.mock('lib/query/createTableColumnsFromQuery', () => ({
jest.mock('container/NewWidget/utils', () => ({
unitOptions: jest.fn(() => [
{ value: 'none', label: 'None' },
{ value: 'percent', label: 'Percent' },
{ value: 'ms', label: 'Milliseconds' },
{ value: '%', label: 'Percent (0 - 100)' },
{ value: 'ms', label: 'Milliseconds (ms)' },
]),
}));
@@ -39,7 +39,7 @@ const defaultProps = {
],
thresholdTableOptions: 'cpu_usage',
columnUnits: { cpu_usage: 'percent', memory_usage: 'bytes' },
yAxisUnit: 'percent',
yAxisUnit: '%',
moveThreshold: jest.fn(),
};

View File

@@ -1,99 +0,0 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { AutoComplete, Input, Typography } from 'antd';
import { find } from 'lodash-es';
import { flattenedCategories } from './dataFormatCategories';
const findCategoryById = (
searchValue: string,
): Record<string, string> | undefined =>
find(flattenedCategories, (option) => option.id === searchValue);
const findCategoryByName = (
searchValue: string,
): Record<string, string> | undefined =>
find(flattenedCategories, (option) => option.name === searchValue);
type OnSelectType = Dispatch<SetStateAction<string>> | ((val: string) => void);
/**
* @deprecated Use DashboardYAxisUnitSelectorWrapper instead.
*/
function YAxisUnitSelector({
value,
onSelect,
fieldLabel,
handleClear,
}: {
value: string;
onSelect: OnSelectType;
fieldLabel: string;
handleClear?: () => void;
}): JSX.Element {
const [inputValue, setInputValue] = useState('');
// Sync input value with the actual value prop
useEffect(() => {
const category = findCategoryById(value);
setInputValue(category?.name || '');
}, [value]);
const onSelectHandler = (selectedValue: string): void => {
const category = findCategoryByName(selectedValue);
if (category) {
onSelect(category.id);
setInputValue(selectedValue);
}
};
const onChangeHandler = (inputValue: string): void => {
setInputValue(inputValue);
// Clear the yAxisUnit if input is empty or doesn't match any option
if (!inputValue) {
onSelect('');
}
};
const onClearHandler = (): void => {
setInputValue('');
onSelect('');
if (handleClear) {
handleClear();
}
};
const options = flattenedCategories.map((options) => ({
value: options.name,
}));
return (
<div className="y-axis-unit-selector">
<Typography.Text className="heading">{fieldLabel}</Typography.Text>
<AutoComplete
style={{ width: '100%' }}
rootClassName="y-axis-root-popover"
options={options}
allowClear
value={inputValue}
onChange={onChangeHandler}
onClear={onClearHandler}
onSelect={onSelectHandler}
filterOption={(inputValue, option): boolean => {
if (option) {
return (
option.value.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
);
}
return false;
}}
>
<Input placeholder="Unit" rootClassName="input" />
</AutoComplete>
</div>
);
}
export default YAxisUnitSelector;
YAxisUnitSelector.defaultProps = {
handleClear: (): void => {},
};

View File

@@ -1,240 +0,0 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { act } from 'react-dom/test-utils';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import YAxisUnitSelector from '../YAxisUnitSelector';
// Mock the dataFormatCategories to have predictable test data
jest.mock('../dataFormatCategories', () => ({
flattenedCategories: [
{ id: 'seconds', name: 'seconds (s)' },
{ id: 'milliseconds', name: 'milliseconds (ms)' },
{ id: 'hours', name: 'hours (h)' },
{ id: 'minutes', name: 'minutes (m)' },
],
}));
const MOCK_SECONDS = 'seconds';
const MOCK_MILLISECONDS = 'milliseconds';
describe('YAxisUnitSelector', () => {
const defaultProps = {
value: MOCK_SECONDS,
onSelect: jest.fn(),
fieldLabel: 'Y Axis Unit',
handleClear: jest.fn(),
};
let user: ReturnType<typeof userEvent.setup>;
beforeEach(() => {
jest.clearAllMocks();
user = userEvent.setup();
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('Rendering (Read) & (write)', () => {
it('renders with correct field label', () => {
render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
expect(screen.getByText('Y Axis Unit')).toBeInTheDocument();
const input = screen.getByRole('combobox');
expect(input).toHaveValue('seconds (s)');
});
it('renders with custom field label', () => {
render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel="Custom Unit Label"
handleClear={defaultProps.handleClear}
/>,
);
expect(screen.getByText('Custom Unit Label')).toBeInTheDocument();
});
it('displays empty input when value prop is empty', () => {
render(
<YAxisUnitSelector
value=""
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
expect(screen.getByDisplayValue('')).toBeInTheDocument();
});
it('shows placeholder text', () => {
render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
expect(screen.getByPlaceholderText('Unit')).toBeInTheDocument();
});
it('handles numeric input', async () => {
render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
const input = screen.getByRole('combobox');
await user.clear(input);
await user.type(input, '12345');
expect(input).toHaveValue('12345');
});
it('handles mixed content input', async () => {
render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
const input = screen.getByRole('combobox');
await user.clear(input);
await user.type(input, 'Test123!@#');
expect(input).toHaveValue('Test123!@#');
});
});
describe('State Management', () => {
it('syncs input value with value prop changes', async () => {
const { rerender } = render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
const input = screen.getByRole('combobox');
// Initial value
expect(input).toHaveValue('seconds (s)');
// Change value prop
rerender(
<YAxisUnitSelector
value={MOCK_MILLISECONDS}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
await waitFor(() => {
expect(input).toHaveValue('milliseconds (ms)');
});
});
it('handles empty value prop correctly', async () => {
const { rerender } = render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
const input = screen.getByRole('combobox');
// Change to empty value
rerender(
<YAxisUnitSelector
value=""
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
await waitFor(() => {
expect(input).toHaveValue('');
});
});
it('handles invalid value prop gracefully', async () => {
const { rerender } = render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
const input = screen.getByRole('combobox');
// Change to invalid value
rerender(
<YAxisUnitSelector
value="invalid_id"
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
await waitFor(() => {
expect(input).toHaveValue('');
});
});
it('maintains local state during typing', async () => {
render(
<YAxisUnitSelector
value={defaultProps.value}
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
const input = screen.getByRole('combobox');
// first clear then type
await user.clear(input);
await user.type(input, 'test');
expect(input).toHaveValue('test');
// Value prop change should not override local typing
await act(async () => {
// Simulate prop change
render(
<YAxisUnitSelector
value="bytes"
onSelect={defaultProps.onSelect}
fieldLabel={defaultProps.fieldLabel}
handleClear={defaultProps.handleClear}
/>,
);
});
// Local typing should be preserved
expect(input).toHaveValue('test');
});
});
});

View File

@@ -1,613 +1,53 @@
import { flattenDeep } from 'lodash-es';
import {
AccelerationFormats,
AngularFormats,
AreaFormats,
BooleanFormats,
CategoryNames,
ConcentrationFormats,
CurrencyFormats,
DataFormats,
DataRateFormats,
DataTypeCategories,
DatetimeFormats,
FlopsFormats,
FlowFormats,
ForceFormats,
HashRateFormats,
LengthFormats,
MassFormats,
MiscellaneousFormats,
PowerElectricalFormats,
PressureFormats,
RadiationFormats,
RotationSpeedFormats,
TemperatureFormats,
ThroughputFormats,
TimeFormats,
VelocityFormats,
VolumeFormats,
} from './types';
UniversalUnitToGrafanaUnit,
YAxisCategoryNames,
} from 'components/YAxisUnitSelector/constants';
import { YAxisSource } from 'components/YAxisUnitSelector/types';
import { getYAxisCategories } from 'components/YAxisUnitSelector/utils';
import { convertValue } from 'lib/getConvertedValue';
export const dataTypeCategories: DataTypeCategories = [
{
name: CategoryNames.Time,
formats: [
{ name: 'Hertz (1/s)', id: TimeFormats.Hertz },
{ name: 'nanoseconds (ns)', id: TimeFormats.Nanoseconds },
{ name: 'microseconds (µs)', id: TimeFormats.Microseconds },
{ name: 'milliseconds (ms)', id: TimeFormats.Milliseconds },
{ name: 'seconds (s)', id: TimeFormats.Seconds },
{ name: 'minutes (m)', id: TimeFormats.Minutes },
{ name: 'hours (h)', id: TimeFormats.Hours },
{ name: 'days (d)', id: TimeFormats.Days },
{ name: 'duration in ms (dtdurationms)', id: TimeFormats.DurationMs },
{ name: 'duration in s (dtdurations)', id: TimeFormats.DurationS },
{ name: 'duration in h:m:s (dthms)', id: TimeFormats.DurationHms },
{ name: 'duration in d:h:m:s (dtdhms)', id: TimeFormats.DurationDhms },
{ name: 'timeticks (timeticks)', id: TimeFormats.Timeticks },
{ name: 'clock in ms (clockms)', id: TimeFormats.ClockMs },
{ name: 'clock in s (clocks)', id: TimeFormats.ClockS },
],
},
{
name: CategoryNames.Throughput,
formats: [
{ name: 'counts/sec (cps)', id: ThroughputFormats.CountsPerSec },
{ name: 'ops/sec (ops)', id: ThroughputFormats.OpsPerSec },
{ name: 'requests/sec (reqps)', id: ThroughputFormats.RequestsPerSec },
{ name: 'reads/sec (rps)', id: ThroughputFormats.ReadsPerSec },
{ name: 'writes/sec (wps)', id: ThroughputFormats.WritesPerSec },
{ name: 'I/O operations/sec (iops)', id: ThroughputFormats.IOOpsPerSec },
{ name: 'counts/min (cpm)', id: ThroughputFormats.CountsPerMin },
{ name: 'ops/min (opm)', id: ThroughputFormats.OpsPerMin },
{ name: 'reads/min (rpm)', id: ThroughputFormats.ReadsPerMin },
{ name: 'writes/min (wpm)', id: ThroughputFormats.WritesPerMin },
],
},
{
name: CategoryNames.Data,
formats: [
{ name: 'bytes(IEC)', id: DataFormats.BytesIEC },
{ name: 'bytes(SI)', id: DataFormats.BytesSI },
{ name: 'bits(IEC)', id: DataFormats.BitsIEC },
{ name: 'bits(SI)', id: DataFormats.BitsSI },
{ name: 'kibibytes', id: DataFormats.KibiBytes },
{ name: 'kilobytes', id: DataFormats.KiloBytes },
{ name: 'mebibytes', id: DataFormats.MebiBytes },
{ name: 'megabytes', id: DataFormats.MegaBytes },
{ name: 'gibibytes', id: DataFormats.GibiBytes },
{ name: 'gigabytes', id: DataFormats.GigaBytes },
{ name: 'tebibytes', id: DataFormats.TebiBytes },
{ name: 'terabytes', id: DataFormats.TeraBytes },
{ name: 'pebibytes', id: DataFormats.PebiBytes },
{ name: 'petabytes', id: DataFormats.PetaBytes },
],
},
{
name: CategoryNames.DataRate,
formats: [
{ name: 'packets/sec', id: DataRateFormats.PacketsPerSec },
{ name: 'bytes/sec(IEC)', id: DataRateFormats.BytesPerSecIEC },
{ name: 'bytes/sec(SI)', id: DataRateFormats.BytesPerSecSI },
{ name: 'bits/sec(IEC)', id: DataRateFormats.BitsPerSecIEC },
{ name: 'bits/sec(SI)', id: DataRateFormats.BitsPerSecSI },
{ name: 'kibibytes/sec', id: DataRateFormats.KibiBytesPerSec },
{ name: 'kibibits/sec', id: DataRateFormats.KibiBitsPerSec },
{ name: 'kilobytes/sec', id: DataRateFormats.KiloBytesPerSec },
{ name: 'kilobits/sec', id: DataRateFormats.KiloBitsPerSec },
{ name: 'mebibytes/sec', id: DataRateFormats.MebiBytesPerSec },
{ name: 'mebibits/sec', id: DataRateFormats.MebiBitsPerSec },
{ name: 'megabytes/sec', id: DataRateFormats.MegaBytesPerSec },
{ name: 'megabits/sec', id: DataRateFormats.MegaBitsPerSec },
{ name: 'gibibytes/sec', id: DataRateFormats.GibiBytesPerSec },
{ name: 'gibibits/sec', id: DataRateFormats.GibiBitsPerSec },
{ name: 'gigabytes/sec', id: DataRateFormats.GigaBytesPerSec },
{ name: 'gigabits/sec', id: DataRateFormats.GigaBitsPerSec },
{ name: 'tebibytes/sec', id: DataRateFormats.TebiBytesPerSec },
{ name: 'tebibits/sec', id: DataRateFormats.TebiBitsPerSec },
{ name: 'terabytes/sec', id: DataRateFormats.TeraBytesPerSec },
{ name: 'terabits/sec', id: DataRateFormats.TeraBitsPerSec },
{ name: 'pebibytes/sec', id: DataRateFormats.PebiBytesPerSec },
{ name: 'pebibits/sec', id: DataRateFormats.PebiBitsPerSec },
{ name: 'petabytes/sec', id: DataRateFormats.PetaBytesPerSec },
{ name: 'petabits/sec', id: DataRateFormats.PetaBitsPerSec },
],
},
{
name: CategoryNames.HashRate,
formats: [
{ name: 'hashes/sec', id: HashRateFormats.HashesPerSec },
{ name: 'kilohashes/sec', id: HashRateFormats.KiloHashesPerSec },
{ name: 'megahashes/sec', id: HashRateFormats.MegaHashesPerSec },
{ name: 'gigahashes/sec', id: HashRateFormats.GigaHashesPerSec },
{ name: 'terahashes/sec', id: HashRateFormats.TeraHashesPerSec },
{ name: 'petahashes/sec', id: HashRateFormats.PetaHashesPerSec },
{ name: 'exahashes/sec', id: HashRateFormats.ExaHashesPerSec },
],
},
{
name: CategoryNames.Miscellaneous,
formats: [
{ name: 'none', id: MiscellaneousFormats.None },
{ name: 'String', id: MiscellaneousFormats.String },
{ name: 'short', id: MiscellaneousFormats.Short },
{ name: 'Percent (0-100)', id: MiscellaneousFormats.Percent },
{ name: 'Percent (0.0-1.0)', id: MiscellaneousFormats.PercentUnit },
{ name: 'Humidity (%H)', id: MiscellaneousFormats.Humidity },
{ name: 'Decibel', id: MiscellaneousFormats.Decibel },
{ name: 'Hexadecimal (0x)', id: MiscellaneousFormats.Hexadecimal0x },
{ name: 'Hexadecimal', id: MiscellaneousFormats.Hexadecimal },
{ name: 'Scientific notation', id: MiscellaneousFormats.ScientificNotation },
{ name: 'Locale format', id: MiscellaneousFormats.LocaleFormat },
{ name: 'Pixels', id: MiscellaneousFormats.Pixels },
],
},
{
name: CategoryNames.Acceleration,
formats: [
{ name: 'Meters/sec²', id: AccelerationFormats.MetersPerSecondSquared },
{ name: 'Feet/sec²', id: AccelerationFormats.FeetPerSecondSquared },
{ name: 'G unit', id: AccelerationFormats.GUnit },
],
},
{
name: CategoryNames.Angle,
formats: [
{ name: 'Degrees (°)', id: AngularFormats.Degree },
{ name: 'Radians', id: AngularFormats.Radian },
{ name: 'Gradian', id: AngularFormats.Gradian },
{ name: 'Arc Minutes', id: AngularFormats.ArcMinute },
{ name: 'Arc Seconds', id: AngularFormats.ArcSecond },
],
},
{
name: CategoryNames.Area,
formats: [
{ name: 'Square Meters (m²)', id: AreaFormats.SquareMeters },
{ name: 'Square Feet (ft²)', id: AreaFormats.SquareFeet },
{ name: 'Square Miles (mi²)', id: AreaFormats.SquareMiles },
],
},
{
name: CategoryNames.Computation,
formats: [
{ name: 'FLOP/s', id: FlopsFormats.FLOPs },
{ name: 'MFLOP/s', id: FlopsFormats.MFLOPs },
{ name: 'GFLOP/s', id: FlopsFormats.GFLOPs },
{ name: 'TFLOP/s', id: FlopsFormats.TFLOPs },
{ name: 'PFLOP/s', id: FlopsFormats.PFLOPs },
{ name: 'EFLOP/s', id: FlopsFormats.EFLOPs },
{ name: 'ZFLOP/s', id: FlopsFormats.ZFLOPs },
{ name: 'YFLOP/s', id: FlopsFormats.YFLOPs },
],
},
{
name: CategoryNames.Concentration,
formats: [
{ name: 'parts-per-million (ppm)', id: ConcentrationFormats.PPM },
{ name: 'parts-per-billion (ppb)', id: ConcentrationFormats.PPB },
{ name: 'nanogram per cubic meter (ng/m³)', id: ConcentrationFormats.NgM3 },
{
name: 'nanogram per normal cubic meter (ng/Nm³)',
id: ConcentrationFormats.NgNM3,
},
{ name: 'microgram per cubic meter (μg/m³)', id: ConcentrationFormats.UgM3 },
{
name: 'microgram per normal cubic meter (μg/Nm³)',
id: ConcentrationFormats.UgNM3,
},
{ name: 'milligram per cubic meter (mg/m³)', id: ConcentrationFormats.MgM3 },
{
name: 'milligram per normal cubic meter (mg/Nm³)',
id: ConcentrationFormats.MgNM3,
},
{ name: 'gram per cubic meter (g/m³)', id: ConcentrationFormats.GM3 },
{
name: 'gram per normal cubic meter (g/Nm³)',
id: ConcentrationFormats.GNM3,
},
{ name: 'milligrams per decilitre (mg/dL)', id: ConcentrationFormats.MgDL },
{ name: 'millimoles per litre (mmol/L)', id: ConcentrationFormats.MmolL },
],
},
{
name: CategoryNames.Currency,
formats: [
{ name: 'Dollars ($)', id: CurrencyFormats.USD },
{ name: 'Pounds (£)', id: CurrencyFormats.GBP },
{ name: 'Euro (€)', id: CurrencyFormats.EUR },
{ name: 'Yen (¥)', id: CurrencyFormats.JPY },
{ name: 'Rubles (₽)', id: CurrencyFormats.RUB },
{ name: 'Hryvnias (₴)', id: CurrencyFormats.UAH },
{ name: 'Real (R$)', id: CurrencyFormats.BRL },
{ name: 'Danish Krone (kr)', id: CurrencyFormats.DKK },
{ name: 'Icelandic Króna (kr)', id: CurrencyFormats.ISK },
{ name: 'Norwegian Krone (kr)', id: CurrencyFormats.NOK },
{ name: 'Swedish Krona (kr)', id: CurrencyFormats.SEK },
{ name: 'Czech koruna (czk)', id: CurrencyFormats.CZK },
{ name: 'Swiss franc (CHF)', id: CurrencyFormats.CHF },
{ name: 'Polish Złoty (PLN)', id: CurrencyFormats.PLN },
{ name: 'Bitcoin (฿)', id: CurrencyFormats.BTC },
{ name: 'Milli Bitcoin (฿)', id: CurrencyFormats.MBTC },
{ name: 'Micro Bitcoin (฿)', id: CurrencyFormats.UBTC },
{ name: 'South African Rand (R)', id: CurrencyFormats.ZAR },
{ name: 'Indian Rupee (₹)', id: CurrencyFormats.INR },
{ name: 'South Korean Won (₩)', id: CurrencyFormats.KRW },
{ name: 'Indonesian Rupiah (Rp)', id: CurrencyFormats.IDR },
{ name: 'Philippine Peso (PHP)', id: CurrencyFormats.PHP },
{ name: 'Vietnamese Dong (VND)', id: CurrencyFormats.VND },
],
},
{
name: CategoryNames.Datetime,
formats: [
{ name: 'Datetime ISO', id: DatetimeFormats.ISO },
{
name: 'Datetime ISO (No date if today)',
id: DatetimeFormats.ISONoDateIfToday,
},
{ name: 'Datetime US', id: DatetimeFormats.US },
{
name: 'Datetime US (No date if today)',
id: DatetimeFormats.USNoDateIfToday,
},
{ name: 'Datetime local', id: DatetimeFormats.Local },
{
name: 'Datetime local (No date if today)',
id: DatetimeFormats.LocalNoDateIfToday,
},
{ name: 'Datetime default', id: DatetimeFormats.System },
{ name: 'From Now', id: DatetimeFormats.FromNow },
],
},
{
name: CategoryNames.Energy,
formats: [
{ name: 'Watt (W)', id: PowerElectricalFormats.WATT },
{ name: 'Kilowatt (kW)', id: PowerElectricalFormats.KWATT },
{ name: 'Megawatt (MW)', id: PowerElectricalFormats.MEGWATT },
{ name: 'Gigawatt (GW)', id: PowerElectricalFormats.GWATT },
{ name: 'Milliwatt (mW)', id: PowerElectricalFormats.MWATT },
{ name: 'Watt per square meter (W/m²)', id: PowerElectricalFormats.WM2 },
{ name: 'Volt-Ampere (VA)', id: PowerElectricalFormats.VOLTAMP },
{ name: 'Kilovolt-Ampere (kVA)', id: PowerElectricalFormats.KVOLTAMP },
{
name: 'Volt-Ampere reactive (VAr)',
id: PowerElectricalFormats.VOLTAMPREACT,
},
{
name: 'Kilovolt-Ampere reactive (kVAr)',
id: PowerElectricalFormats.KVOLTAMPREACT,
},
{ name: 'Watt-hour (Wh)', id: PowerElectricalFormats.WATTH },
{
name: 'Watt-hour per Kilogram (Wh/kg)',
id: PowerElectricalFormats.WATTHPERKG,
},
{ name: 'Kilowatt-hour (kWh)', id: PowerElectricalFormats.KWATTH },
{ name: 'Kilowatt-min (kWm)', id: PowerElectricalFormats.KWATTM },
{ name: 'Ampere-hour (Ah)', id: PowerElectricalFormats.AMPH },
{ name: 'Kiloampere-hour (kAh)', id: PowerElectricalFormats.KAMPH },
{ name: 'Milliampere-hour (mAh)', id: PowerElectricalFormats.MAMPH },
{ name: 'Joule (J)', id: PowerElectricalFormats.JOULE },
{ name: 'Electron volt (eV)', id: PowerElectricalFormats.EV },
{ name: 'Ampere (A)', id: PowerElectricalFormats.AMP },
{ name: 'Kiloampere (kA)', id: PowerElectricalFormats.KAMP },
{ name: 'Milliampere (mA)', id: PowerElectricalFormats.MAMP },
{ name: 'Volt (V)', id: PowerElectricalFormats.VOLT },
{ name: 'Kilovolt (kV)', id: PowerElectricalFormats.KVOLT },
{ name: 'Millivolt (mV)', id: PowerElectricalFormats.MVOLT },
{ name: 'Decibel-milliwatt (dBm)', id: PowerElectricalFormats.DBM },
{ name: 'Ohm (Ω)', id: PowerElectricalFormats.OHM },
{ name: 'Kiloohm (kΩ)', id: PowerElectricalFormats.KOHM },
{ name: 'Megaohm (MΩ)', id: PowerElectricalFormats.MOHM },
{ name: 'Farad (F)', id: PowerElectricalFormats.FARAD },
{ name: 'Microfarad (µF)', id: PowerElectricalFormats.µFARAD },
{ name: 'Nanofarad (nF)', id: PowerElectricalFormats.NFARAD },
{ name: 'Picofarad (pF)', id: PowerElectricalFormats.PFARAD },
{ name: 'Femtofarad (fF)', id: PowerElectricalFormats.FFARAD },
{ name: 'Henry (H)', id: PowerElectricalFormats.HENRY },
{ name: 'Millihenry (mH)', id: PowerElectricalFormats.MHENRY },
{ name: 'Microhenry (µH)', id: PowerElectricalFormats.µHENRY },
{ name: 'Lumens (Lm)', id: PowerElectricalFormats.LUMENS },
],
},
{
name: CategoryNames.Flow,
formats: [
{ name: 'Gallons/min (gpm)', id: FlowFormats.FLOWGPM },
{ name: 'Cubic meters/sec (cms)', id: FlowFormats.FLOWCMS },
{ name: 'Cubic feet/sec (cfs)', id: FlowFormats.FLOWCFS },
{ name: 'Cubic feet/min (cfm)', id: FlowFormats.FLOWCFM },
{ name: 'Litre/hour', id: FlowFormats.LITREH },
{ name: 'Litre/min (L/min)', id: FlowFormats.FLOWLPM },
{ name: 'milliLitre/min (mL/min)', id: FlowFormats.FLOWMLPM },
{ name: 'Lux (lx)', id: FlowFormats.LUX },
],
},
{
name: CategoryNames.Force,
formats: [
{ name: 'Newton-meters (Nm)', id: ForceFormats.FORCENM },
{ name: 'Kilonewton-meters (kNm)', id: ForceFormats.FORCEKNM },
{ name: 'Newtons (N)', id: ForceFormats.FORCEN },
{ name: 'Kilonewtons (kN)', id: ForceFormats.FORCEKN },
],
},
{
name: CategoryNames.Mass,
formats: [
{ name: 'milligram (mg)', id: MassFormats.MASSMG },
{ name: 'gram (g)', id: MassFormats.MASSG },
{ name: 'pound (lb)', id: MassFormats.MASSLB },
{ name: 'kilogram (kg)', id: MassFormats.MASSKG },
{ name: 'metric ton (t)', id: MassFormats.MASST },
],
},
{
name: CategoryNames.Length,
formats: [
{ name: 'millimeter (mm)', id: LengthFormats.LENGTHMM },
{ name: 'inch (in)', id: LengthFormats.LENGTHIN },
{ name: 'feet (ft)', id: LengthFormats.LENGTHFT },
{ name: 'meter (m)', id: LengthFormats.LENGTHM },
{ name: 'kilometer (km)', id: LengthFormats.LENGTHKM },
{ name: 'mile (mi)', id: LengthFormats.LENGTHMI },
],
},
{
name: CategoryNames.Pressure,
formats: [
{ name: 'Millibars', id: PressureFormats.PRESSUREMBAR },
{ name: 'Bars', id: PressureFormats.PRESSUREBAR },
{ name: 'Kilobars', id: PressureFormats.PRESSUREKBAR },
{ name: 'Pascals', id: PressureFormats.PRESSUREPA },
{ name: 'Hectopascals', id: PressureFormats.PRESSUREHPA },
{ name: 'Kilopascals', id: PressureFormats.PRESSUREKPA },
{ name: 'Inches of mercury', id: PressureFormats.PRESSUREHG },
{ name: 'PSI', id: PressureFormats.PRESSUREPSI },
],
},
{
name: CategoryNames.Radiation,
formats: [
{ name: 'Becquerel (Bq)', id: RadiationFormats.RADBQ },
{ name: 'curie (Ci)', id: RadiationFormats.RADCI },
{ name: 'Gray (Gy)', id: RadiationFormats.RADGY },
{ name: 'rad', id: RadiationFormats.RADRAD },
{ name: 'Sievert (Sv)', id: RadiationFormats.RADSV },
{ name: 'milliSievert (mSv)', id: RadiationFormats.RADMSV },
{ name: 'microSievert (µSv)', id: RadiationFormats.RADUSV },
{ name: 'rem', id: RadiationFormats.RADREM },
{ name: 'Exposure (C/kg)', id: RadiationFormats.RADEXPCKG },
{ name: 'roentgen (R)', id: RadiationFormats.RADR },
{ name: 'Sievert/hour (Sv/h)', id: RadiationFormats.RADSVH },
{ name: 'milliSievert/hour (mSv/h)', id: RadiationFormats.RADMSVH },
{ name: 'microSievert/hour (µSv/h)', id: RadiationFormats.RADUSVH },
],
},
{
name: CategoryNames.RotationSpeed,
formats: [
{ name: 'Revolutions per minute (rpm)', id: RotationSpeedFormats.ROTRPM },
{ name: 'Hertz (Hz)', id: RotationSpeedFormats.ROTHZ },
{ name: 'Radians per second (rad/s)', id: RotationSpeedFormats.ROTRADS },
{ name: 'Degrees per second (°/s)', id: RotationSpeedFormats.ROTDEGS },
],
},
{
name: CategoryNames.Temperature,
formats: [
{ name: 'Celsius (°C)', id: TemperatureFormats.CELSIUS },
{ name: 'Fahrenheit (°F)', id: TemperatureFormats.FAHRENHEIT },
{ name: 'Kelvin (K)', id: TemperatureFormats.KELVIN },
],
},
{
name: CategoryNames.Velocity,
formats: [
{ name: 'meters/second (m/s)', id: VelocityFormats.METERS_PER_SECOND },
{ name: 'kilometers/hour (km/h)', id: VelocityFormats.KILOMETERS_PER_HOUR },
{ name: 'miles/hour (mph)', id: VelocityFormats.MILES_PER_HOUR },
{ name: 'knot (kn)', id: VelocityFormats.KNOT },
],
},
{
name: CategoryNames.Volume,
formats: [
{ name: 'millilitre (mL)', id: VolumeFormats.MILLILITRE },
{ name: 'litre (L)', id: VolumeFormats.LITRE },
{ name: 'cubic meter', id: VolumeFormats.CUBIC_METER },
{ name: 'Normal cubic meter', id: VolumeFormats.NORMAL_CUBIC_METER },
{ name: 'cubic decimeter', id: VolumeFormats.CUBIC_DECIMETER },
{ name: 'gallons', id: VolumeFormats.GALLONS },
],
},
{
name: CategoryNames.Boolean,
formats: [
{ name: 'True / False', id: BooleanFormats.TRUE_FALSE },
{ name: 'Yes / No', id: BooleanFormats.YES_NO },
{ name: 'On / Off', id: BooleanFormats.ON_OFF },
],
},
];
// Function to get the category name for a given unit ID (Grafana or universal)
export const getCategoryName = (unitId: string): YAxisCategoryNames | null => {
const categories = getYAxisCategories(YAxisSource.DASHBOARDS);
export const flattenedCategories = flattenDeep(
dataTypeCategories.map((category) => category.formats),
);
const foundCategory = categories.find((category) =>
category.units.some((unit) => {
// Units in Y-axis categories use universal unit IDs.
// Thresholds / column units often use Grafana-style IDs.
// Treat a unit as matching if either:
// - it is already the universal ID, or
// - it matches the mapped Grafana ID for that universal unit.
if (unit.id === unitId) {
return true;
}
type ConversionFactors = {
[key: string]: {
[key: string]: number | null;
};
const grafanaId = UniversalUnitToGrafanaUnit[unit.id];
return grafanaId === unitId;
}),
);
return foundCategory ? foundCategory.name : null;
};
// Object containing conversion factors for various categories and formats
const conversionFactors: ConversionFactors = {
[CategoryNames.Time]: {
[TimeFormats.Hertz]: 1,
[TimeFormats.Nanoseconds]: 1e-9,
[TimeFormats.Microseconds]: 1e-6,
[TimeFormats.Milliseconds]: 1e-3,
[TimeFormats.Seconds]: 1,
[TimeFormats.Minutes]: 60,
[TimeFormats.Hours]: 3600,
[TimeFormats.Days]: 86400,
[TimeFormats.DurationMs]: 1e-3,
[TimeFormats.DurationS]: 1,
[TimeFormats.DurationHms]: null, // Requires special handling
[TimeFormats.DurationDhms]: null, // Requires special handling
[TimeFormats.Timeticks]: null, // Requires special handling
[TimeFormats.ClockMs]: 1e-3,
[TimeFormats.ClockS]: 1,
},
[CategoryNames.Throughput]: {
[ThroughputFormats.CountsPerSec]: 1,
[ThroughputFormats.OpsPerSec]: 1,
[ThroughputFormats.RequestsPerSec]: 1,
[ThroughputFormats.ReadsPerSec]: 1,
[ThroughputFormats.WritesPerSec]: 1,
[ThroughputFormats.IOOpsPerSec]: 1,
[ThroughputFormats.CountsPerMin]: 1 / 60,
[ThroughputFormats.OpsPerMin]: 1 / 60,
[ThroughputFormats.ReadsPerMin]: 1 / 60,
[ThroughputFormats.WritesPerMin]: 1 / 60,
},
[CategoryNames.Data]: {
[DataFormats.BytesIEC]: 1,
[DataFormats.BytesSI]: 1,
[DataFormats.BitsIEC]: 0.125,
[DataFormats.BitsSI]: 0.125,
[DataFormats.KibiBytes]: 1024,
[DataFormats.KiloBytes]: 1000,
[DataFormats.MebiBytes]: 1048576,
[DataFormats.MegaBytes]: 1000000,
[DataFormats.GibiBytes]: 1073741824,
[DataFormats.GigaBytes]: 1000000000,
[DataFormats.TebiBytes]: 1099511627776,
[DataFormats.TeraBytes]: 1000000000000,
[DataFormats.PebiBytes]: 1125899906842624,
[DataFormats.PetaBytes]: 1000000000000000,
},
[CategoryNames.DataRate]: {
[DataRateFormats.PacketsPerSec]: null, // Cannot convert directly to other data rates
[DataRateFormats.BytesPerSecIEC]: 1,
[DataRateFormats.BytesPerSecSI]: 1,
[DataRateFormats.BitsPerSecIEC]: 0.125,
[DataRateFormats.BitsPerSecSI]: 0.125,
[DataRateFormats.KibiBytesPerSec]: 1024,
[DataRateFormats.KibiBitsPerSec]: 128,
[DataRateFormats.KiloBytesPerSec]: 1000,
[DataRateFormats.KiloBitsPerSec]: 125,
[DataRateFormats.MebiBytesPerSec]: 1048576,
[DataRateFormats.MebiBitsPerSec]: 131072,
[DataRateFormats.MegaBytesPerSec]: 1000000,
[DataRateFormats.MegaBitsPerSec]: 125000,
[DataRateFormats.GibiBytesPerSec]: 1073741824,
[DataRateFormats.GibiBitsPerSec]: 134217728,
[DataRateFormats.GigaBytesPerSec]: 1000000000,
[DataRateFormats.GigaBitsPerSec]: 125000000,
[DataRateFormats.TebiBytesPerSec]: 1099511627776,
[DataRateFormats.TebiBitsPerSec]: 137438953472,
[DataRateFormats.TeraBytesPerSec]: 1000000000000,
[DataRateFormats.TeraBitsPerSec]: 125000000000,
[DataRateFormats.PebiBytesPerSec]: 1125899906842624,
[DataRateFormats.PebiBitsPerSec]: 140737488355328,
[DataRateFormats.PetaBytesPerSec]: 1000000000000000,
[DataRateFormats.PetaBitsPerSec]: 125000000000000,
},
[CategoryNames.Miscellaneous]: {
[MiscellaneousFormats.None]: null,
[MiscellaneousFormats.String]: null,
[MiscellaneousFormats.Short]: null,
[MiscellaneousFormats.Percent]: 1,
[MiscellaneousFormats.PercentUnit]: 100,
[MiscellaneousFormats.Humidity]: 1,
[MiscellaneousFormats.Decibel]: null,
[MiscellaneousFormats.Hexadecimal0x]: null,
[MiscellaneousFormats.Hexadecimal]: null,
[MiscellaneousFormats.ScientificNotation]: null,
[MiscellaneousFormats.LocaleFormat]: null,
[MiscellaneousFormats.Pixels]: null,
},
[CategoryNames.Boolean]: {
[BooleanFormats.TRUE_FALSE]: null, // Not convertible
[BooleanFormats.YES_NO]: null, // Not convertible
[BooleanFormats.ON_OFF]: null, // Not convertible
},
};
// Function to get the conversion factor between two units in a specific category
function getConversionFactor(
fromUnit: string,
toUnit: string,
category: CategoryNames,
): number | null {
// Retrieves the conversion factors for the specified category
const categoryFactors = conversionFactors[category];
if (!categoryFactors) {
return null; // Returns null if the category does not exist
}
const fromFactor = categoryFactors[fromUnit];
const toFactor = categoryFactors[toUnit];
if (
fromFactor === undefined ||
toFactor === undefined ||
fromFactor === null ||
toFactor === null
) {
return null; // Returns null if either unit does not exist or is not convertible
}
return fromFactor / toFactor; // Returns the conversion factor ratio
}
// Function to convert a value from one unit to another
export function convertUnit(
value: number,
fromUnitId?: string,
toUnitId?: string,
): number | null {
let fromUnit: string | undefined;
let toUnit: string | undefined;
// Finds the category that contains the specified units and extracts fromUnit and toUnit using array methods
const category = dataTypeCategories.find((category) =>
category.formats.some((format) => {
if (format.id === fromUnitId) {
fromUnit = format.id;
}
if (format.id === toUnitId) {
toUnit = format.id;
}
return fromUnit && toUnit; // Break out early if both units are found
}),
);
if (!category || !fromUnit || !toUnit) {
if (!fromUnitId || !toUnitId) {
return null;
} // Return null if category or units are not found
}
// Gets the conversion factor for the specified units
const conversionFactor = getConversionFactor(
fromUnit,
toUnit,
category.name as any,
);
if (conversionFactor === null) {
const fromCategory = getCategoryName(fromUnitId);
const toCategory = getCategoryName(toUnitId);
// If either unit is unknown or the categories don't match, the conversion is invalid
if (!fromCategory || !toCategory || fromCategory !== toCategory) {
return null;
} // Return null if conversion is not possible
}
return value * conversionFactor;
// Delegate the actual numeric conversion (or identity) to the shared helper,
// which understands both Grafana-style and universal unit IDs.
return convertValue(value, fromUnitId, toUnitId);
}
// Function to get the category name for a given unit ID
export const getCategoryName = (unitId: string): CategoryNames | null => {
// Finds the category that contains the specified unit ID
const foundCategory = dataTypeCategories.find((category) =>
category.formats.some((format) => format.id === unitId),
);
return foundCategory ? (foundCategory.name as CategoryNames) : null;
};

View File

@@ -2,6 +2,9 @@ import { Layout } from 'react-grid-layout';
import { DefaultOptionType } from 'antd/es/select';
import { omitIdFromQuery } from 'components/ExplorerCard/utils';
import { PrecisionOptionsEnum } from 'components/Graph/types';
import { YAxisCategoryNames } from 'components/YAxisUnitSelector/constants';
import { YAxisSource } from 'components/YAxisUnitSelector/types';
import { getYAxisCategories } from 'components/YAxisUnitSelector/utils';
import {
initialQueryBuilderFormValuesMap,
PANEL_TYPES,
@@ -21,11 +24,7 @@ import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
import { DataSource } from 'types/common/queryBuilder';
import {
dataTypeCategories,
getCategoryName,
} from './RightContainer/dataFormatCategories';
import { CategoryNames } from './RightContainer/types';
import { getCategoryName } from './RightContainer/dataFormatCategories';
export const getIsQueryModified = (
currentQuery: Query,
@@ -606,14 +605,21 @@ export const PANEL_TYPE_TO_QUERY_TYPES: Record<PANEL_TYPES, EQueryType[]> = {
* the label and value for each format.
*/
export const getCategorySelectOptionByName = (
name?: CategoryNames | string,
): DefaultOptionType[] =>
dataTypeCategories
.find((category) => category.name === name)
?.formats.map((format) => ({
label: format.name,
value: format.id,
})) || [];
name?: YAxisCategoryNames,
): DefaultOptionType[] => {
const categories = getYAxisCategories(YAxisSource.DASHBOARDS);
if (!categories.length) {
return [];
}
return (
categories
.find((category) => category.name === name)
?.units.map((unit) => ({
label: unit.name,
value: unit.id,
})) || []
);
};
/**
* Generates unit options based on the provided column unit.

View File

@@ -1,10 +1,13 @@
import { CategoryNames } from 'container/NewWidget/RightContainer/types';
import { YAxisCategoryNames } from 'components/YAxisUnitSelector/constants';
export const categoryToSupport = [
CategoryNames.Data,
CategoryNames.DataRate,
CategoryNames.Time,
CategoryNames.Throughput,
CategoryNames.Miscellaneous,
CategoryNames.Boolean,
export const categoryToSupport: YAxisCategoryNames[] = [
YAxisCategoryNames.None,
YAxisCategoryNames.Data,
YAxisCategoryNames.DataRate,
YAxisCategoryNames.Time,
YAxisCategoryNames.Count,
YAxisCategoryNames.Operations,
YAxisCategoryNames.Percentage,
YAxisCategoryNames.Miscellaneous,
YAxisCategoryNames.Boolean,
];

View File

@@ -3,6 +3,7 @@ package configflagger
import (
"context"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
@@ -32,6 +33,10 @@ func New(ctx context.Context, ps factory.ProviderSettings, c flagger.Config, reg
for name, value := range c.Config.Boolean {
feature, _, err := registry.GetByString(name)
if err != nil {
if errors.Ast(err, errors.TypeNotFound) {
settings.Logger().WarnContext(ctx, "skipping unknown feature flag", "name", name, "kind", "boolean")
continue
}
return nil, err
}
@@ -46,6 +51,10 @@ func New(ctx context.Context, ps factory.ProviderSettings, c flagger.Config, reg
for name, value := range c.Config.String {
feature, _, err := registry.GetByString(name)
if err != nil {
if errors.Ast(err, errors.TypeNotFound) {
settings.Logger().WarnContext(ctx, "skipping unknown feature flag", "name", name, "kind", "string")
continue
}
return nil, err
}
@@ -60,6 +69,10 @@ func New(ctx context.Context, ps factory.ProviderSettings, c flagger.Config, reg
for name, value := range c.Config.Float {
feature, _, err := registry.GetByString(name)
if err != nil {
if errors.Ast(err, errors.TypeNotFound) {
settings.Logger().WarnContext(ctx, "skipping unknown feature flag", "name", name, "kind", "float")
continue
}
return nil, err
}
@@ -74,6 +87,10 @@ func New(ctx context.Context, ps factory.ProviderSettings, c flagger.Config, reg
for name, value := range c.Config.Integer {
feature, _, err := registry.GetByString(name)
if err != nil {
if errors.Ast(err, errors.TypeNotFound) {
settings.Logger().WarnContext(ctx, "skipping unknown feature flag", "name", name, "kind", "integer")
continue
}
return nil, err
}
@@ -88,6 +105,10 @@ func New(ctx context.Context, ps factory.ProviderSettings, c flagger.Config, reg
for name, value := range c.Config.Object {
feature, _, err := registry.GetByString(name)
if err != nil {
if errors.Ast(err, errors.TypeNotFound) {
settings.Logger().WarnContext(ctx, "skipping unknown feature flag", "name", name, "kind", "object")
continue
}
return nil, err
}

View File

@@ -5,6 +5,7 @@ import "github.com/SigNoz/signoz/pkg/types/featuretypes"
var (
FeatureUseSpanMetrics = featuretypes.MustNewName("use_span_metrics")
FeatureKafkaSpanEval = featuretypes.MustNewName("kafka_span_eval")
FeatureHideRootUser = featuretypes.MustNewName("hide_root_user")
)
func MustNewRegistry() featuretypes.Registry {
@@ -25,6 +26,14 @@ func MustNewRegistry() featuretypes.Registry {
DefaultVariant: featuretypes.MustNewName("disabled"),
Variants: featuretypes.NewBooleanVariants(),
},
&featuretypes.Feature{
Name: FeatureHideRootUser,
Kind: featuretypes.KindBoolean,
Stage: featuretypes.StageStable,
Description: "Controls whether root admin user is hidden or not",
DefaultVariant: featuretypes.MustNewName("disabled"),
Variants: featuretypes.NewBooleanVariants(),
},
)
if err != nil {
panic(err)

View File

@@ -120,6 +120,8 @@ func FilterResponse(results []*qbtypes.QueryRangeResponse) []*qbtypes.QueryRange
}
}
resultData.Rows = filteredRows
case *qbtypes.ScalarData:
resultData.Data = filterScalarDataIPs(resultData.Columns, resultData.Data)
}
filteredData = append(filteredData, result)
@@ -145,6 +147,39 @@ func shouldIncludeSeries(series *qbtypes.TimeSeries) bool {
return true
}
func filterScalarDataIPs(columns []*qbtypes.ColumnDescriptor, data [][]any) [][]any {
// Find column indices for server address fields
serverColIndices := make([]int, 0)
for i, col := range columns {
if col.Name == derivedKeyHTTPHost {
serverColIndices = append(serverColIndices, i)
}
}
if len(serverColIndices) == 0 {
return data
}
filtered := make([][]any, 0, len(data))
for _, row := range data {
includeRow := true
for _, colIdx := range serverColIndices {
if colIdx < len(row) {
if strVal, ok := row[colIdx].(string); ok {
if net.ParseIP(strVal) != nil {
includeRow = false
break
}
}
}
}
if includeRow {
filtered = append(filtered, row)
}
}
return filtered
}
func shouldIncludeRow(row *qbtypes.RawRow) bool {
if row.Data != nil {
if domainVal, ok := row.Data[derivedKeyHTTPHost]; ok {

View File

@@ -117,6 +117,59 @@ func TestFilterResponse(t *testing.T) {
},
},
},
{
name: "should filter out IP addresses from scalar data",
input: []*qbtypes.QueryRangeResponse{
{
Data: qbtypes.QueryData{
Results: []any{
&qbtypes.ScalarData{
QueryName: "endpoints",
Columns: []*qbtypes.ColumnDescriptor{
{
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: derivedKeyHTTPHost},
Type: qbtypes.ColumnTypeGroup,
},
{
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "endpoints"},
Type: qbtypes.ColumnTypeAggregation,
},
},
Data: [][]any{
{"192.168.1.1", 10},
{"example.com", 20},
{"10.0.0.1", 5},
},
},
},
},
},
},
expected: []*qbtypes.QueryRangeResponse{
{
Data: qbtypes.QueryData{
Results: []any{
&qbtypes.ScalarData{
QueryName: "endpoints",
Columns: []*qbtypes.ColumnDescriptor{
{
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: derivedKeyHTTPHost},
Type: qbtypes.ColumnTypeGroup,
},
{
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "endpoints"},
Type: qbtypes.ColumnTypeAggregation,
},
},
Data: [][]any{
{"example.com", 20},
},
},
},
},
},
},
},
}
for _, tt := range tests {

View File

@@ -2,18 +2,22 @@ package impluser
import (
"context"
"slices"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type getter struct {
store types.UserStore
store types.UserStore
flagger flagger.Flagger
}
func NewGetter(store types.UserStore) user.Getter {
return &getter{store: store}
func NewGetter(store types.UserStore, flagger flagger.Flagger) user.Getter {
return &getter{store: store, flagger: flagger}
}
func (module *getter) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*types.User, error) {
@@ -26,6 +30,14 @@ func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*ty
return nil, err
}
// filter root users if feature flag `hide_root_users` is true
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
hideRootUsers := module.flagger.BooleanOrEmpty(ctx, flagger.FeatureHideRootUser, evalCtx)
if hideRootUsers {
users = slices.DeleteFunc(users, func(user *types.User) bool { return user.IsRoot })
}
return users, nil
}

View File

@@ -80,11 +80,16 @@ func (q *builderQuery[T]) Fingerprint() string {
case qbtypes.LogAggregation:
aggParts = append(aggParts, a.Expression)
case qbtypes.MetricAggregation:
aggParts = append(aggParts, fmt.Sprintf("%s:%s:%s:%s",
var spaceAggParamStr string
if a.ComparisonSpaceAggregationParam != nil {
spaceAggParamStr = a.ComparisonSpaceAggregationParam.StringValue()
}
aggParts = append(aggParts, fmt.Sprintf("%s:%s:%s:%s:%s",
a.MetricName,
a.Temporality.StringValue(),
a.TimeAggregation.StringValue(),
a.SpaceAggregation.StringValue(),
spaceAggParamStr,
))
}
}

View File

@@ -276,15 +276,17 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
// Fetch temporality for all metrics at once
var metricTemporality map[string]metrictypes.Temporality
var metricTypes map[string]metrictypes.Type
if len(metricNames) > 0 {
var err error
metricTemporality, err = q.metadataStore.FetchTemporalityMulti(ctx, req.Start, req.End, metricNames...)
metricTemporality, metricTypes, err = q.metadataStore.FetchTemporalityAndTypeMulti(ctx, req.Start, req.End, metricNames...)
if err != nil {
q.logger.WarnContext(ctx, "failed to fetch metric temporality", "error", err, "metrics", metricNames)
// Continue without temporality - statement builder will handle unspecified
metricTemporality = make(map[string]metrictypes.Temporality)
metricTypes = make(map[string]metrictypes.Type)
}
q.logger.DebugContext(ctx, "fetched metric temporalities", "metric_temporality", metricTemporality)
q.logger.DebugContext(ctx, "fetched metric temporalities and types", "metric_temporality", metricTemporality, "metric_types", metricTypes)
}
queries := make(map[string]qbtypes.Query)
@@ -380,6 +382,12 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
if spec.Aggregations[i].Temporality == metrictypes.Unknown {
spec.Aggregations[i].Temporality = metrictypes.Unspecified
}
if spec.Aggregations[i].MetricName != "" && spec.Aggregations[i].Type == metrictypes.UnspecifiedType {
if foundMetricType, ok := metricTypes[spec.Aggregations[i].MetricName]; ok && foundMetricType != metrictypes.UnspecifiedType {
spec.Aggregations[i].Type = foundMetricType
}
}
}
spec.ShiftBy = extractShiftFromBuilderQuery(spec)
timeRange := adjustTimeRangeForShift(spec, qbtypes.TimeRange{From: req.Start, To: req.End}, req.RequestType)

View File

@@ -11,8 +11,11 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/queryparser"
"github.com/SigNoz/signoz/pkg/sharder"
@@ -41,7 +44,13 @@ func TestNewHandlers(t *testing.T) {
queryParser := queryparser.New(providerSettings)
require.NoError(t, err)
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule)
flagger, err := flagger.New(context.Background(), instrumentationtest.New().ToProviderSettings(), flagger.Config{}, flagger.MustNewRegistry())
require.NoError(t, err)
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings), flagger)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule, userGetter)
querierHandler := querier.NewHandler(providerSettings, nil, nil)
handlers := NewHandlers(modules, providerSettings, nil, querierHandler, nil, nil, nil, nil, nil, nil, nil)

View File

@@ -85,11 +85,11 @@ func NewModules(
queryParser queryparser.QueryParser,
config Config,
dashboard dashboard.Module,
userGetter user.Getter,
) Modules {
quickfilter := implquickfilter.NewModule(implquickfilter.NewStore(sqlstore))
orgSetter := implorganization.NewSetter(implorganization.NewStore(sqlstore), alertmanager, quickfilter)
user := impluser.NewModule(impluser.NewStore(sqlstore, providerSettings), tokenizer, emailing, providerSettings, orgSetter, authz, analytics, config.User)
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings))
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
return Modules{

View File

@@ -11,8 +11,11 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/queryparser"
"github.com/SigNoz/signoz/pkg/sharder"
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
@@ -40,7 +43,13 @@ func TestNewModules(t *testing.T) {
queryParser := queryparser.New(providerSettings)
require.NoError(t, err)
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule)
flagger, err := flagger.New(context.Background(), instrumentationtest.New().ToProviderSettings(), flagger.Config{}, flagger.MustNewRegistry())
require.NoError(t, err)
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings), flagger)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule, userGetter)
reflectVal := reflect.ValueOf(modules)
for i := 0; i < reflectVal.NumField(); i++ {

View File

@@ -1,11 +1,13 @@
package signoz
import (
"context"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/nfmanagertest"
"github.com/SigNoz/signoz/pkg/analytics"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
@@ -75,7 +77,12 @@ func TestNewProviderFactories(t *testing.T) {
})
assert.NotPanics(t, func() {
userGetter := impluser.NewGetter(impluser.NewStore(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual), instrumentationtest.New().ToProviderSettings()))
flagger, err := flagger.New(context.Background(), instrumentationtest.New().ToProviderSettings(), flagger.Config{}, flagger.MustNewRegistry())
if err != nil {
panic(err)
}
userGetter := impluser.NewGetter(impluser.NewStore(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual), instrumentationtest.New().ToProviderSettings()), flagger)
orgGetter := implorganization.NewGetter(implorganization.NewStore(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual)), nil)
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherEqual)
NewStatsReporterProviderFactories(telemetryStore, []statsreporter.StatsCollector{}, orgGetter, userGetter, tokenizertest.NewMockTokenizer(t), version.Build{}, analytics.Config{Enabled: true})

View File

@@ -280,7 +280,7 @@ func New(
}
// Initialize user getter
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings))
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings), flagger)
licensingProviderFactory := licenseProviderFactory(sqlstore, zeus, orgGetter, analytics)
licensing, err := licensingProviderFactory.New(
@@ -388,7 +388,7 @@ func New(
}
// Initialize all modules
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, analytics, querier, telemetrystore, telemetryMetadataStore, authNs, authz, cache, queryParser, config, dashboard)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, analytics, querier, telemetrystore, telemetryMetadataStore, authNs, authz, cache, queryParser, config, dashboard, userGetter)
userService := impluser.NewService(providerSettings, impluser.NewStore(sqlstore, providerSettings), modules.User, orgGetter, authz, config.User.Root)

View File

@@ -1616,40 +1616,53 @@ func (t *telemetryMetaStore) FetchTemporality(ctx context.Context, queryTimeRang
}
func (t *telemetryMetaStore) FetchTemporalityMulti(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string]metrictypes.Temporality, error) {
temporalities, _, err := t.FetchTemporalityAndTypeMulti(ctx, queryTimeRangeStartTs, queryTimeRangeEndTs, metricNames...)
return temporalities, err
}
func (t *telemetryMetaStore) FetchTemporalityAndTypeMulti(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error) {
if len(metricNames) == 0 {
return make(map[string]metrictypes.Temporality), nil
return make(map[string]metrictypes.Temporality), make(map[string]metrictypes.Type), nil
}
result := make(map[string]metrictypes.Temporality)
metricsTemporality, err := t.fetchMetricsTemporality(ctx, queryTimeRangeStartTs, queryTimeRangeEndTs, metricNames...)
temporalities := make(map[string]metrictypes.Temporality)
types := make(map[string]metrictypes.Type)
metricsTemporality, metricTypes, err := t.fetchMetricsTemporalityAndType(ctx, queryTimeRangeStartTs, queryTimeRangeEndTs, metricNames...)
if err != nil {
return nil, err
return nil, nil, err
}
// TODO: return error after table migration are run
meterMetricsTemporality, _ := t.fetchMeterSourceMetricsTemporality(ctx, metricNames...)
meterMetricsTemporality, meterMetricsTypes, _ := t.fetchMeterSourceMetricsTemporalityAndType(ctx, metricNames...)
// For metrics not found in the database, set to Unknown
for _, metricName := range metricNames {
if temporality, exists := metricsTemporality[metricName]; exists && len(temporality) > 0 {
if len(temporality) > 1 {
result[metricName] = metrictypes.Multiple
temporalities[metricName] = metrictypes.Multiple
} else {
result[metricName] = temporality[0]
temporalities[metricName] = temporality[0]
}
} else if temporality, exists := meterMetricsTemporality[metricName]; exists {
temporalities[metricName] = temporality
continue
} else {
temporalities[metricName] = metrictypes.Unknown
}
if temporality, exists := meterMetricsTemporality[metricName]; exists {
result[metricName] = temporality
continue
if metricType, exists := metricTypes[metricName]; exists {
types[metricName] = metricType
} else if meterMetricType, exists := meterMetricsTypes[metricName]; exists {
types[metricName] = meterMetricType
} else {
types[metricName] = metrictypes.UnspecifiedType
}
result[metricName] = metrictypes.Unknown
}
return result, nil
return temporalities, types, nil
}
func (t *telemetryMetaStore) fetchMetricsTemporality(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string][]metrictypes.Temporality, error) {
result := make(map[string][]metrictypes.Temporality)
func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string][]metrictypes.Temporality, map[string]metrictypes.Type, error) {
temporalities := make(map[string][]metrictypes.Temporality)
types := make(map[string]metrictypes.Type)
adjustedStartTs, adjustedEndTs, tsTableName, _ := telemetrymetrics.WhichTSTableToUse(queryTimeRangeStartTs, queryTimeRangeEndTs, nil)
@@ -1660,6 +1673,7 @@ func (t *telemetryMetaStore) fetchMetricsTemporality(ctx context.Context, queryT
sb := sqlbuilder.Select(
"metric_name",
"temporality",
"any(type) AS type",
).
From(t.metricsDBName + "." + tsTableName)
@@ -1678,15 +1692,15 @@ func (t *telemetryMetaStore) fetchMetricsTemporality(ctx context.Context, queryT
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to fetch metric temporality")
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to fetch metric temporality")
}
defer rows.Close()
// Process results
for rows.Next() {
var metricName, temporalityStr string
if err := rows.Scan(&metricName, &temporalityStr); err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to scan temporality result")
var metricName, temporalityStr, metricTypeStr string
if err := rows.Scan(&metricName, &temporalityStr, &metricTypeStr); err != nil {
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to scan temporality result")
}
// Convert string to Temporality type
@@ -1703,22 +1717,30 @@ func (t *telemetryMetaStore) fetchMetricsTemporality(ctx context.Context, queryT
temporality = metrictypes.Unknown
}
if temporality != metrictypes.Unknown {
result[metricName] = append(result[metricName], temporality)
temporalities[metricName] = append(temporalities[metricName], temporality)
}
var metricType metrictypes.Type
err = metricType.Scan(metricTypeStr)
if err != nil {
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to scan metric-type result")
}
types[metricName] = metricType
}
if err := rows.Err(); err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error iterating over metrics temporality rows")
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error iterating over metrics temporality rows")
}
return result, nil
return temporalities, types, nil
}
func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporality(ctx context.Context, metricNames ...string) (map[string]metrictypes.Temporality, error) {
result := make(map[string]metrictypes.Temporality)
func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporalityAndType(ctx context.Context, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error) {
temporalities := make(map[string]metrictypes.Temporality)
types := make(map[string]metrictypes.Type)
sb := sqlbuilder.Select(
"metric_name",
"argMax(temporality, unix_milli) as temporality",
"any(type) AS type",
).From(t.meterDBName + "." + t.meterFieldsTblName)
// Filter by metric names (in the temporality column due to data mix-up)
@@ -1733,15 +1755,15 @@ func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporality(ctx context.Cont
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to fetch meter metric temporality")
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to fetch meter metric temporality")
}
defer rows.Close()
// Process results
for rows.Next() {
var metricName, temporalityStr string
if err := rows.Scan(&metricName, &temporalityStr); err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to scan temporality result")
var metricName, temporalityStr, meterMetricTypeStr string
if err := rows.Scan(&metricName, &temporalityStr, &meterMetricTypeStr); err != nil {
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to scan temporality result")
}
// Convert string to Temporality type
@@ -1758,10 +1780,16 @@ func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporality(ctx context.Cont
temporality = metrictypes.Unknown
}
result[metricName] = temporality
temporalities[metricName] = temporality
var metricType metrictypes.Type
err = metricType.Scan(meterMetricTypeStr)
if err != nil {
return nil, nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to scan metric-type result")
}
types[metricName] = metricType
}
return result, nil
return temporalities, types, nil
}
// chunkSizeFirstSeenMetricMetadata limits the number of tuples per SQL query to avoid hitting the max_query_size limit.

View File

@@ -123,8 +123,7 @@ func (b *MetricQueryStatementBuilder) buildPipelineStatement(
origTimeAgg := query.Aggregations[0].TimeAggregation
origGroupBy := slices.Clone(query.GroupBy)
if query.Aggregations[0].SpaceAggregation.IsPercentile() &&
query.Aggregations[0].Type != metrictypes.ExpHistogramType {
if query.Aggregations[0].Type == metrictypes.HistogramType {
// add le in the group by if doesn't exist
leExists := false
for _, g := range query.GroupBy {
@@ -154,7 +153,11 @@ func (b *MetricQueryStatementBuilder) buildPipelineStatement(
}
// make the time aggregation rate and space aggregation sum
query.Aggregations[0].TimeAggregation = metrictypes.TimeAggregationRate
if query.Aggregations[0].SpaceAggregation.IsPercentile() {
query.Aggregations[0].TimeAggregation = metrictypes.TimeAggregationRate
} else {
query.Aggregations[0].TimeAggregation = metrictypes.TimeAggregationIncrease
}
query.Aggregations[0].SpaceAggregation = metrictypes.SpaceAggregationSum
}
@@ -551,6 +554,9 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect(
cteArgs [][]any,
query qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation],
) (*qbtypes.Statement, error) {
metricType := query.Aggregations[0].Type
spaceAgg := query.Aggregations[0].SpaceAggregation
combined := querybuilder.CombineCTEs(cteFragments)
var args []any
@@ -560,12 +566,8 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect(
sb := sqlbuilder.NewSelectBuilder()
var quantile float64
if query.Aggregations[0].SpaceAggregation.IsPercentile() {
quantile = query.Aggregations[0].SpaceAggregation.Percentile()
}
if quantile != 0 && query.Aggregations[0].Type != metrictypes.ExpHistogramType {
if metricType == metrictypes.HistogramType && spaceAgg.IsPercentile() {
quantile := query.Aggregations[0].SpaceAggregation.Percentile()
sb.Select("ts")
for _, g := range query.GroupBy {
sb.SelectMore(fmt.Sprintf("`%s`", g.TelemetryFieldKey.Name))
@@ -577,12 +579,36 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect(
sb.From("__spatial_aggregation_cte")
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
sb.GroupBy("ts")
if query.Having != nil && query.Having.Expression != "" {
rewriter := querybuilder.NewHavingExpressionRewriter()
rewrittenExpr := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations)
sb.Having(rewrittenExpr)
}
} else if metricType == metrictypes.HistogramType && spaceAgg == metrictypes.SpaceAggregationCount && query.Aggregations[0].ComparisonSpaceAggregationParam != nil {
sb.Select("ts")
for _, g := range query.GroupBy {
sb.SelectMore(fmt.Sprintf("`%s`", g.TelemetryFieldKey.Name))
}
aggQuery, err := AggregationQueryForHistogramCountWithParams(query.Aggregations[0].ComparisonSpaceAggregationParam)
if err != nil {
return nil, err
}
sb.SelectMore(aggQuery)
sb.From("__spatial_aggregation_cte")
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
sb.GroupBy("ts")
if query.Having != nil && query.Having.Expression != "" {
rewriter := querybuilder.NewHavingExpressionRewriter()
rewrittenExpr := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations)
sb.Having(rewrittenExpr)
}
} else {
// for count aggregation on histograms with no params, the exact result of spatial aggregation can be sent forward
sb.Select("*")
sb.From("__spatial_aggregation_cte")
if query.Having != nil && query.Having.Expression != "" {
@@ -593,6 +619,9 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect(
}
sb.OrderBy(querybuilder.GroupByKeys(query.GroupBy)...)
sb.OrderBy("ts")
if metricType == metrictypes.HistogramType && spaceAgg == metrictypes.SpaceAggregationCount && query.Aggregations[0].ComparisonSpaceAggregationParam == nil {
sb.OrderBy("toFloat64(le)")
}
q, a := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
return &qbtypes.Statement{Query: combined + q, Args: append(args, a...)}, nil

View File

@@ -1,6 +1,7 @@
package telemetrymetrics
import (
"fmt"
"time"
"github.com/SigNoz/signoz/pkg/errors"
@@ -308,3 +309,20 @@ func AggregationColumnForSamplesTable(
}
return aggregationColumn, nil
}
func AggregationQueryForHistogramCountWithParams(param *metrictypes.ComparisonSpaceAggregationParam) (string, error) {
if param == nil {
return "", errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "no aggregation param provided for histogram count")
}
histogramCountThreshold := param.Threshold
switch param.Operater {
case "<=":
return fmt.Sprintf("argMaxIf(value, toFloat64(le), toFloat64(le) <= %f) + (argMinIf(value, toFloat64(le), toFloat64(le) > %f) - argMaxIf(value, toFloat64(le), toFloat64(le) <= %f)) * (%f - maxIf(toFloat64(le), toFloat64(le) <= %f)) / (minIf(toFloat64(le), toFloat64(le) > %f) - maxIf(toFloat64(le), toFloat64(le) <= %f)) AS value", histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold), nil
case ">":
return fmt.Sprintf("argMax(value, toFloat64(le)) - (argMaxIf(value, toFloat64(le), toFloat64(le) <= %f) + (argMinIf(value, toFloat64(le), toFloat64(le) > %f) - argMaxIf(value, toFloat64(le), toFloat64(le) <= %f)) * (%f - maxIf(toFloat64(le), toFloat64(le) <= %f)) / (minIf(toFloat64(le), toFloat64(le) > %f) - maxIf(toFloat64(le), toFloat64(le) <= %f))) AS value", histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold, histogramCountThreshold), nil
default:
return "", errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid space aggregation operator, should be one of the following: [`<=`, `>`]")
}
}

View File

@@ -2,6 +2,7 @@ package metrictypes
import (
"database/sql/driver"
"fmt"
"strings"
"github.com/SigNoz/signoz/pkg/errors"
@@ -256,3 +257,12 @@ type MetricTableHints struct {
type MetricValueFilter struct {
Value float64
}
type ComparisonSpaceAggregationParam struct {
Operater string `json:"operator"`
Threshold float64 `json:"threshold"`
}
func (param ComparisonSpaceAggregationParam) StringValue() string {
return fmt.Sprintf("{\"operator\": \"%s\", \"limit\": \"%f\"}", param.Operater, param.Threshold)
}

View File

@@ -446,6 +446,8 @@ type MetricAggregation struct {
TimeAggregation metrictypes.TimeAggregation `json:"timeAggregation"`
// space aggregation to apply to the query
SpaceAggregation metrictypes.SpaceAggregation `json:"spaceAggregation"`
// param for space aggregation if needed
ComparisonSpaceAggregationParam *metrictypes.ComparisonSpaceAggregationParam `json:"comparisonSpaceAggregationParam"`
// table hints to use for the query
TableHints *metrictypes.MetricTableHints `json:"-"`
// value filter to apply to the query

View File

@@ -32,6 +32,8 @@ type MetadataStore interface {
// FetchTemporalityMulti fetches the temporality for multiple metrics
FetchTemporalityMulti(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string]metrictypes.Temporality, error)
FetchTemporalityAndTypeMulti(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error)
// ListLogsJSONIndexes lists the JSON indexes for the logs table.
ListLogsJSONIndexes(ctx context.Context, filters ...string) (map[string][]schemamigrator.Index, error)

View File

@@ -16,6 +16,7 @@ type MockMetadataStore struct {
RelatedValuesMap map[string][]string
AllValuesMap map[string]*telemetrytypes.TelemetryFieldValues
TemporalityMap map[string]metrictypes.Temporality
TypeMap map[string]metrictypes.Type
PromotedPathsMap map[string]struct{}
LogsJSONIndexesMap map[string][]schemamigrator.Index
LookupKeysMap map[telemetrytypes.MetricMetadataLookupKey]int64
@@ -28,6 +29,7 @@ func NewMockMetadataStore() *MockMetadataStore {
RelatedValuesMap: make(map[string][]string),
AllValuesMap: make(map[string]*telemetrytypes.TelemetryFieldValues),
TemporalityMap: make(map[string]metrictypes.Temporality),
TypeMap: make(map[string]metrictypes.Type),
PromotedPathsMap: make(map[string]struct{}),
LogsJSONIndexesMap: make(map[string][]schemamigrator.Index),
LookupKeysMap: make(map[telemetrytypes.MetricMetadataLookupKey]int64),
@@ -287,6 +289,27 @@ func (m *MockMetadataStore) FetchTemporalityMulti(ctx context.Context, queryTime
return result, nil
}
// FetchTemporalityMulti fetches the temporality for multiple metrics
func (m *MockMetadataStore) FetchTemporalityAndTypeMulti(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error) {
temporalities := make(map[string]metrictypes.Temporality)
types := make(map[string]metrictypes.Type)
for _, metricName := range metricNames {
if temporality, exists := m.TemporalityMap[metricName]; exists {
temporalities[metricName] = temporality
} else {
temporalities[metricName] = metrictypes.Unknown
}
if metricType, exists := m.TypeMap[metricName]; exists {
types[metricName] = metricType
} else {
types[metricName] = metrictypes.UnspecifiedType
}
}
return temporalities, types, nil
}
// SetTemporality sets the temporality for a metric in the mock store
func (m *MockMetadataStore) SetTemporality(metricName string, temporality metrictypes.Temporality) {
m.TemporalityMap[metricName] = temporality

View File

@@ -52,6 +52,7 @@ def build_builder_query(
time_aggregation: str,
space_aggregation: str,
*,
comparisonSpaceAggregationParam: Optional[Dict] = None,
temporality: Optional[str] = None,
step_interval: int = DEFAULT_STEP_INTERVAL,
group_by: Optional[List[str]] = None,
@@ -74,7 +75,8 @@ def build_builder_query(
}
if temporality:
spec["aggregations"][0]["temporality"] = temporality
if comparisonSpaceAggregationParam:
spec["aggregations"][0]["comparisonSpaceAggregationParam"] = comparisonSpaceAggregationParam
if group_by:
spec["groupBy"] = [
{

View File

@@ -353,7 +353,7 @@ def test_for_week_long_time_range(
metrics = Metrics.load_from_file(
MULTI_TEMPORALITY_FILE_10h,
base_time=now - timedelta(minutes=600),
base_time=now - timedelta(minutes=1441),
metric_name_override=metric_name,
)
insert_metrics(metrics)

View File

@@ -0,0 +1,322 @@
"""
Look at the multi_temporality_counters_1h.jsonl file for the relevant data
"""
import random
from datetime import datetime, timedelta, timezone
from http import HTTPStatus
from typing import Callable, List
import pytest
from fixtures import types
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
from fixtures.metrics import Metrics
from fixtures.querier import (
build_builder_query,
get_all_series,
get_series_values,
make_query_request,
)
from fixtures.utils import get_testdata_file_path
FILE = get_testdata_file_path("histogram_data_1h.jsonl")
@pytest.mark.parametrize(
"threshold, operator, first_value, last_value",
[
(1000, "<=", 11, 69),
(100, "<=", 1.1, 6.9),
(7500, "<=", 16.75, 74.75),
(8000, "<=", 17, 75),
(80000, "<=", 17, 75), ## cuz we don't know the max value in infinity, all numbers beyond the biggest finite bucket will report the same answer
(1000, ">", 7, 7),
(100, ">", 16.9, 69.1),
(7500, ">", 1.25, 1.25),
(8000, ">", 1, 1),
(80000, ">", 1, 1), ## cuz we don't know the max value in infinity, all numbers beyond the biggest finite bucket will report the same answer
],
)
def test_histogram_count_for_one_endpoint(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
threshold: float,
operator: str,
first_value: float,
last_value: float,
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_bucket"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
"increase",
"count",
comparisonSpaceAggregationParam={
"threshold": threshold,
"operator": operator
},
filter_expression='endpoint = "/health"',
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
result_values = sorted(get_series_values(data, "A"), key=lambda x: x["timestamp"])
assert len(result_values) == 59
assert result_values[0]["value"] == first_value
assert result_values[-1]["value"] == last_value
@pytest.mark.parametrize(
"threshold, operator, first_value, last_value",
[
(1000, "<=", 22, 138),
(100, "<=", 2.2, 13.8),
(7500, "<=", 33.5, 149.5),
(8000, "<=", 34, 150),
(80000, "<=", 34, 150), ## cuz we don't know the max value in infinity, all numbers beyond the biggest finite bucket will report the same answer
(1000, ">", 14, 14),
(100, ">", 33.8, 138.2),
(7500, ">", 2.5, 2.5),
(8000, ">", 2, 2),
(80000, ">", 2, 2), ## cuz we don't know the max value in infinity, all numbers beyond the biggest finite bucket will report the same answer
],
)
def test_histogram_count_for_one_service(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
threshold: float,
operator: str,
first_value: float,
last_value: float,
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_bucket"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
"increase",
"count",
comparisonSpaceAggregationParam={
"threshold": threshold,
"operator": operator
},
filter_expression='service = "api"',
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
result_values = sorted(get_series_values(data, "A"), key=lambda x: x["timestamp"])
assert len(result_values) == 59
assert result_values[0]["value"] == first_value
assert result_values[-1]["value"] == last_value
@pytest.mark.parametrize(
"threshold, operator, zeroth_value, first_value, last_value",
[
(1000, "<=", 12345, 11, 69),
(100, "<=", 1234.5, 1.1, 6.9),
(7500, "<=", 12345, 16.75, 74.75),
(8000, "<=", 12345, 17, 75),
(80000, "<=", 12345, 17, 75), ## cuz we don't know the max value in infinity, all numbers beyond the biggest finite bucket will report the same answer
(1000, ">", 0, 7, 7),
(100, ">", 11110.5, 16.9, 69.1),
(7500, ">", 0, 1.25, 1.25),
(8000, ">", 0, 1, 1),
(80000, ">", 0, 1, 1), ## cuz we don't know the max value in infinity, all numbers beyond the biggest finite bucket will report the same answer
],
)
def test_histogram_count_for_delta_service(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
threshold: float,
operator: str,
zeroth_value: float,
first_value: float,
last_value: float,
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_bucket"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
"increase",
"count",
comparisonSpaceAggregationParam={
"threshold": threshold,
"operator": operator
},
filter_expression='service = "web"',
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
result_values = sorted(get_series_values(data, "A"), key=lambda x: x["timestamp"])
assert len(result_values) == 60 ## in delta, the value at 10:01 will also be reported
assert result_values[0]["value"] == zeroth_value
assert result_values[1]["value"] == first_value ## to keep parallel to the cumulative test cases, first_value refers to the value at 10:02
assert result_values[-1]["value"] == last_value
@pytest.mark.parametrize(
"threshold, operator, zeroth_value, first_value, last_value",
[
(1000, "<=", 12345, 33, 207),
(100, "<=", 1234.5, 3.3, 20.7),
(7500, "<=", 12345, 50.25, 224.25),
(8000, "<=", 12345, 51, 225),
(80000, "<=", 12345, 51, 225),
(1000, ">", 0, 21, 21),
(100, ">", 11110.5, 50.7, 207.3),
(7500, ">", 0, 3.75, 3.75),
(8000, ">", 0, 3, 3),
(80000, ">", 0, 3, 3),
],
)
def test_histogram_count_for_all_services(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
threshold: float,
operator: str,
zeroth_value: float,
first_value: float,
last_value: float,
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_bucket"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
"increase",
"count",
comparisonSpaceAggregationParam={
"threshold": threshold,
"operator": operator
},
## no services filter, this tests for multitemporality handling as well
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
result_values = sorted(get_series_values(data, "A"), key=lambda x: x["timestamp"])
assert len(result_values) == 60 ## in delta, the value at 10:01 will also be reported
assert result_values[0]["value"] == zeroth_value
assert result_values[1]["value"] == first_value ## to keep parallel to the cumulative test cases, first_value refers to the value at 10:02
assert result_values[-1]["value"] == last_value
def test_histogram_count_no_param(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_bucket"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
"increase",
"count",
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
all_series = get_all_series(data, "A")
assert (
len(all_series) == 8
), f"Expected 8 series for 8 le buckets, got {len(all_series)}"
le_buckets = {}
for series in all_series:
le = series.get("labels", [{}])[0].get("value", "unknown")
values = sorted(series.get("values", []), key=lambda x: x["timestamp"])
le_buckets[le] = values
expected_buckets = {"1000", "1500", "2000", "4000", "5000", "6000", "8000", "+Inf"}
assert (
set(le_buckets.keys()) == expected_buckets
), f"Expected endpoints {expected_buckets}, got {set(le_buckets.keys())}"
first_values = {"1000": 33, "1500": 36, "2000": 39, "4000": 42, "5000": 45, "6000": 48, "8000": 51, "+Inf": 54}
last_values = {"1000": 207, "1500": 210, "2000": 213, "4000": 216, "5000": 219, "6000": 222, "8000": 225, "+Inf": 228}
for le, values in le_buckets.items():
assert len(values) == 60
for v in values:
assert (
v["value"] >= 0
), f"Count for {le} should not be negative: {v['value']}"
assert values[0]["value"] == 12345
assert values[1]["value"] == first_values[le] ## to keep parallel to the cumulative test cases, first_value refers to the value at 10:02
assert values[-1]["value"] == last_values[le]

View File

@@ -0,0 +1,141 @@
import random
from datetime import datetime, timedelta, timezone
from http import HTTPStatus
from typing import Callable, List
import pytest
from fixtures import types
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
from fixtures.metrics import Metrics
from fixtures.querier import (
build_builder_query,
get_all_series,
get_series_values,
make_query_request,
)
from fixtures.utils import get_testdata_file_path
FILE = get_testdata_file_path("gauge_data_1h.jsonl")
@pytest.mark.parametrize(
"time_agg, space_agg, service, num_elements, start_val, first_val, twentieth_min_val, after_twentieth_min_val",
[
("avg", "avg", "api", 60, 400, 800, 800, 400),
("avg", "avg", "web", 50, 800, 800, 800, 600),
("avg", "avg", "lab", 60, 500, 700, 700, 500),
("sum", "sum", "api", 60, 400, 800, 800, 400),
("sum", "sum", "web", 50, 800, 800, 800, 600),
("sum", "sum", "lab", 60, 1000, 1400, 1400, 1000),
("max", "max", "api", 60, 400, 800, 800, 400),
("max", "max", "web", 50, 800, 800, 800, 600),
("max", "max", "lab", 60, 600, 800, 800, 600),
("avg", "sum", "api", 60, 400, 800, 800, 400),
("avg", "sum", "web", 50, 800, 800, 800, 600),
("avg", "sum", "lab", 60, 500, 700, 700, 500),
("max", "sum", "api", 60, 400, 800, 800, 400),
("max", "sum", "web", 50, 800, 800, 800, 600),
("max", "sum", "lab", 60, 600, 800, 800, 600),
],
)
def test_for_one_service(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
time_agg: str,
space_agg: str,
service: str,
num_elements: float,
start_val: float,
first_val: float,
twentieth_min_val: float,
after_twentieth_min_val: float ## web service has a gap of 10 mins after the 20th minute
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_memory_usage"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
time_agg,
space_agg,
filter_expression=f'service = "{service}"',
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
result_values = sorted(get_series_values(data, "A"), key=lambda x: x["timestamp"])
assert len(result_values) == num_elements
assert result_values[0]["value"] == start_val
assert result_values[1]["value"] == first_val
assert result_values[19]["value"] == twentieth_min_val
assert result_values[20]["value"] == after_twentieth_min_val
@pytest.mark.parametrize(
"time_agg, space_agg, start_val, first_val, twentieth_min_val, twenty_first_min_val, thirty_first_min_val",
[
("avg", "avg", 566.667, 766.667, 766.667, 450, 500),
("avg", "sum", 1700, 2300, 2300, 900, 1500),
("avg", "max", 800, 800, 800, 500, 600),
("max", "avg", 600, 800, 800, 500, 533.333),
("max", "sum", 1800, 2400, 2400, 1000, 1600),
("max", "max", 800, 800, 800, 600, 600),
],
)
def test_for_multiple_aggregations(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
insert_metrics: Callable[[List[Metrics]], None],
time_agg: str,
space_agg: str,
start_val: float,
first_val: float,
twentieth_min_val: float,
twenty_first_min_val: float, ## web service has a gap of 10 mins after the 20th minute
thirty_first_min_val: float
) -> None:
now = datetime.now(tz=timezone.utc).replace(second=0, microsecond=0)
start_ms = int((now - timedelta(minutes=65)).timestamp() * 1000)
end_ms = int(now.timestamp() * 1000)
metric_name = f"test_memory_usage"
metrics = Metrics.load_from_file(
FILE,
base_time=now - timedelta(minutes=60),
metric_name_override=metric_name,
)
insert_metrics(metrics)
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
query = build_builder_query(
"A",
metric_name,
time_agg,
space_agg,
)
response = make_query_request(signoz, token, start_ms, end_ms, [query])
assert response.status_code == HTTPStatus.OK
data = response.json()
result_values = sorted(get_series_values(data, "A"), key=lambda x: x["timestamp"])
assert len(result_values) == 60
assert result_values[0]["value"] == start_val
assert result_values[1]["value"] == first_val
assert result_values[19]["value"] == twentieth_min_val
assert result_values[20]["value"] == twenty_first_min_val
assert result_values[30]["value"] == thirty_first_min_val

View File

@@ -0,0 +1,229 @@
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:01:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:02:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:03:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:05:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:06:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:07:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:08:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:09:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:10:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:11:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:12:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:13:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:14:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:15:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:16:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:17:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:18:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:19:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:20:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:21:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:22:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:23:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:24:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:25:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:26:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:27:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:28:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:29:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:30:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:31:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:32:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:33:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:34:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:35:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:36:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:37:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:38:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:39:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:40:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:41:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:42:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:43:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:44:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:45:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:46:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:47:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:48:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:49:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:50:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:51:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:52:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:53:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:54:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:55:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:56:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:57:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:58:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T10:59:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"api"},"timestamp":"2025-01-10T11:00:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:01:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:02:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:03:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:05:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:06:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:07:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:08:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:09:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:10:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:11:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:12:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:13:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:14:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:15:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:16:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:17:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:18:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:19:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:20:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:31:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:32:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:33:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:34:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:35:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:36:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:37:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:38:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:39:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:40:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:41:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:42:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:43:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:44:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:45:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:46:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:47:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:48:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:49:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:50:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:51:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:52:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:53:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:54:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:55:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:56:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:57:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:58:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T10:59:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"web"},"timestamp":"2025-01-10T11:00:00+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:01:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:01:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:02:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:02:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:03:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:03:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:04:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:05:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:05:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:06:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:06:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:07:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:07:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:08:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:08:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:09:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:09:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:10:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:10:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:11:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:11:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:12:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:12:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:13:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:13:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:14:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:14:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:15:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:15:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:16:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:16:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:17:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:17:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:18:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:18:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:19:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:19:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:20:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:20:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:21:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:21:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:22:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:22:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:23:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:23:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:24:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:24:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:25:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:25:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:26:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:26:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:27:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:27:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:28:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:28:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:29:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:29:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:30:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:30:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:31:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:31:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:32:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:32:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:33:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:33:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:34:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:34:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:35:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:35:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:36:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:36:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:37:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:37:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:38:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:38:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:39:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:39:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:40:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:40:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:41:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:41:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:42:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:42:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:43:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:43:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:44:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:44:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:45:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:45:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:46:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:46:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:47:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:47:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:48:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:48:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:49:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:49:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:50:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:50:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:51:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:51:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:52:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:52:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:53:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:53:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:54:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:54:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:55:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:55:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:56:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:56:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:57:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:57:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:58:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:58:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:59:00+00:00","value":400,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T10:59:30+00:00","value":600,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}
{"metric_name":"system.memory.usage","labels":{"__temporality__":"Unspecified","service":"lab"},"timestamp":"2025-01-10T11:00:00+00:00","value":800,"temporality":"Unspecified","type_":"Gauge","is_monotonic":true,"flags":0,"description":"","unit":"","env":"default","resource_attrs":{},"scope_attrs":{}}

File diff suppressed because it is too large Load Diff