Compare commits

...

14 Commits

Author SHA1 Message Date
Karan Balani
3a92f33ca5 chore: update integration test flakiness 2026-06-10 18:28:15 +05:30
Karan Balani
2af4f81349 chore: fix openapi specs, lint issues and fix tests 2026-06-10 17:57:31 +05:30
Karan Balani
bfc89d79ff chore: minor changes to naming 2026-06-10 17:57:31 +05:30
Karan Balani
f9e8ce6f91 chore: remove unused parameter sqlstore in migration 2026-06-10 17:57:31 +05:30
Karan Balani
e0d87e216b refactor: sso config to support type and sso enabled in spec 2026-06-10 17:57:31 +05:30
Nikhil Mantri
27603e09d0 feat(infra-monitoring): v2 statefulsets integration tests (#11440)
* chore: updated logic and use centralized function in the module

* chore: filter metric groups

* chore: filter metric groups

* chore: formula correction

* chore: added step flooring note

* chore: comment correction

* chore: comment correction

* chore: removed function

* chore: renamed variables

* chore: added happy test

* chore: added test 2 for accuracy and test 3 for missing metrics check

* chore: added filter test 4

* chore: added 5th test for filterByStatus

* chore: added group by tests

* chore: pagination test added

* chore: added validation tests

* chore: added auth test

* chore: added all tests

* chore: fix for surfacing meta for pods custom group by

* chore: added nodes integration test suite

* chore: namespaces integration tests

* ci: register inframonitoring suite + ruff format 01_hosts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: added integration tests for clusters

* chore: formatting changed

* chore: formatting changed

* chore: formatting changed

* chore: added volumes integration tests

* chore: added deployments

* chore: added tests

* chore: added order by host.name test

* refactor(infra-monitoring): address review comments on hosts integration tests

- inline _post helper at call sites
- combine filter operator tests into parametrized test_hosts_filter
- combine bad attr/grammar tests into parametrized test_hosts_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_hosts_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align pods integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_pods_filter
- combine bad attr/grammar tests into parametrized test_pods_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_pods_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_nodes_filter
- combine bad attr/grammar tests into parametrized test_nodes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_nodes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_namespaces_filter
- combine bad attr/grammar tests into parametrized test_namespaces_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_namespaces_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_clusters_filter
- combine bad attr/grammar tests into parametrized test_clusters_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_clusters_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align volumes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_volumes_filter
- combine bad attr/grammar tests into parametrized test_volumes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_volumes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align deployments integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_deployments_filter
- combine bad attr/grammar tests into parametrized test_deployments_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_deployments_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align statefulsets integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_statefulsets_filter
- combine bad attr/grammar tests into parametrized test_statefulsets_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_statefulsets_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts groupby tests into parametrized test

- merge test_hosts_groupby_hostname + test_hosts_groupby_os_type into
  test_hosts_groupby parametrized on (dataset, group key, expected counts/values)
- preserves all assertions incl hostName populated-vs-empty branch coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts orderby tests into parametrized test

- merge total_invariant_across_orderby + orderby_correctness + orderby_by_host_name
  into test_hosts_orderby parametrized on (column, record_field) x direction
- each case asserts both the total/len invariant and sortedness; sortedness now
  covered for all metric columns, not just cpu
- single dataset (hosts_orderby.jsonl) + CONTAINS 'order-' guard on all cases

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts pagination tests

- fold offset-beyond-total case into the test_hosts_pagination page walk
  (offset K+5 expects 0 records via max(0, min(limit, K-offset)); total
  invariant covers the beyond-total page's total == K)
- single seed instead of two

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): merge hosts happy_path into test_hosts_accuracy

- happy_path and value_accuracy datasets were structurally identical
  (same 4 metrics, same sample counts, 2 hosts); one test now asserts
  shape/contract + exact metric values in a single seed/request
- drop unused hosts_happy_path.jsonl

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine pods integration tests logically

- merge happy_path into test_pods_accuracy (datasets structurally identical);
  drop unused pods_happy_path.jsonl
- merge groupby namespace/deployment into parametrized test_pods_groupby
- merge orderby invariant + correctness + by-pod-name into test_pods_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_pods_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine nodes integration tests logically

- merge happy_path into test_nodes_accuracy; drop unused nodes_happy_path.jsonl
- merge orderby invariant + correctness + by-node-name into test_nodes_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_nodes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine namespaces integration tests logically

- merge happy_path into test_namespaces_accuracy; drop unused namespaces_happy_path.jsonl
- merge orderby invariant + correctness + by-namespace-name into test_namespaces_orderby
- fold offset-beyond-total into test_namespaces_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine clusters integration tests logically

- merge happy_path into test_clusters_accuracy; drop unused clusters_happy_path.jsonl
- merge orderby invariant + correctness + by-cluster-name into test_clusters_orderby
- fold offset-beyond-total into test_clusters_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine volumes integration tests logically

- merge happy_path into test_volumes_accuracy; drop unused volumes_happy_path.jsonl
- merge orderby invariant + correctness + by-pvc-name into test_volumes_orderby
- fold offset-beyond-total into test_volumes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine deployments integration tests logically

- merge happy_path into test_deployments_accuracy; drop unused deployments_happy_path.jsonl
- merge orderby invariant + correctness + by-deployment-name into test_deployments_orderby
- fold offset-beyond-total into test_deployments_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine statefulsets integration tests logically

- merge happy_path into test_statefulsets_accuracy; drop unused statefulsets_happy_path.jsonl
- merge orderby invariant + correctness + by-statefulset-name into test_statefulsets_orderby
- fold offset-beyond-total into test_statefulsets_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in hosts filter tests

- regenerate hosts_filter_dataset.jsonl so every host mirrors the acc-h1
  sample pattern from hosts_value_accuracy.jsonl (CI-proven expected values)
- test_hosts_filter now asserts cpu/memory/wait/load15/diskUsage per filtered
  record against FILTER_DATASET_EXPECTED (1e-9), proving filters don't
  distort aggregation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): scope filter expected values inside test_hosts_filter

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in pods filter tests

- regenerate pods_filter_dataset.jsonl so every pod mirrors the acc-p1
  sample pattern from pods_value_accuracy.jsonl (CI-proven expected values)
- test_pods_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in nodes filter tests

- regenerate nodes_filter_dataset.jsonl so every node mirrors the acc-n1
  sample pattern from nodes_value_accuracy.jsonl (CI-proven expected values)
- test_nodes_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in namespaces filter tests

- regenerate namespaces_filter_dataset.jsonl so every namespace mirrors the
  acc-ns-1 sample pattern (2 pods) from namespaces_value_accuracy.jsonl
- test_namespaces_filter now asserts namespaceCPU/namespaceMemory per record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in clusters filter tests

- regenerate clusters_filter_dataset.jsonl so every cluster mirrors the
  acc-cluster-1 sample pattern (2 nodes) from clusters_value_accuracy.jsonl
- test_clusters_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in volumes filter tests

- regenerate volumes_filter_dataset.jsonl so every PVC mirrors the acc-pvc-1
  sample pattern from volumes_value_accuracy.jsonl (CI-proven expected values)
- test_volumes_filter now asserts all 6 volume fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in deployments filter tests

- regenerate deployments_filter_dataset.jsonl so every deployment mirrors the
  acc-dep-1 sample pattern (2 pods) from deployments_value_accuracy.jsonl
- test_deployments_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in statefulsets filter tests

- regenerate statefulsets_filter_dataset.jsonl so every statefulset mirrors
  the acc-ss-1 sample pattern (2 pods) from statefulsets_value_accuracy.jsonl
- test_statefulsets_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes groupby + pod-phase tests with structure

- merge pod_phase_counts_list_mode + _no_pods_on_node into parametrized
  test_nodes_pod_phase_counts ((dataset, node, filter, expected) cases)
- rename groupby_cluster -> test_nodes_groupby, parametrize over
  k8s.node.name + k8s.cluster.name; adds the node-name-in-groupBy branch
  (nodeName populated, condition derived) that hosts/pods both cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces groupby with structure

- rename groupby_cluster -> test_namespaces_groupby, parametrize over
  k8s.namespace.name + k8s.cluster.name; adds the namespace-name-in-groupBy
  branch (namespaceName populated) that hosts/pods/nodes all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters groupby with structure

- rename groupby_cloud_provider -> test_clusters_groupby, parametrize over
  k8s.cluster.name + cloud.provider; adds the cluster-name-in-groupBy branch
  (clusterName populated) that hosts/pods/nodes/namespaces all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align volumes groupby with structure

- rename groupby_namespace -> test_volumes_groupby, parametrize over
  k8s.persistentvolumeclaim.name + k8s.namespace.name; adds the
  pvc-name-in-groupBy branch (persistentVolumeClaimName populated) that the
  other endpoints all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align deployments groupby with structure

- rename groupby_namespace -> test_deployments_groupby, parametrize over
  k8s.deployment.name + k8s.namespace.name; adds the deployment-name-in-groupBy
  branch (deploymentName populated) that the other endpoints all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align statefulsets groupby with structure

- rename groupby_namespace -> test_statefulsets_groupby, parametrize over
  k8s.statefulset.name + k8s.namespace.name; adds the statefulset-name-in-groupBy
  branch (statefulSetName populated) that the other endpoints all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 10:58:22 +00:00
Gaurav Tewari
7b2882abde feat: show recent queries to user (#11523)
* feat: initial commit

* refactor: recent query

* refactor: move all components into one

* refactor: queries

* refactor: self review comments

* refactor: css

* refactor: comments

* refactor: more comments

* feat: add time feature

* refactor : more changes

* chore: remove comments

* refactor: more code

* chore: remove extra commentes

* refactor: store in local storage

* fix: update types

* refactor: more changes

* refactor: import issue

* fix: lint

* chore: update styling

* fix: update query

* feat: use source as well in query key

* refactor: review changes

* refactor: self review changes

* refactor: more changes

* fix: more refactor

* refactor: make code much better

* fix: minor refactor

* refactor: review changes

* refactor: getRecentHook and make logic simple

* chore: drop useSaveRecentQuery from gridcard

* refactor: run save logic on handleRunQuery

* chore: remove space

---------

Co-authored-by: Gaurav Tewari <tewarig@users.noreply.github.com>
2026-06-10 10:58:19 +00:00
Nikhil Mantri
30f1c2d92d feat(infra-monitoring): v2 deployments integration tests (#11437)
* chore: updated logic and use centralized function in the module

* chore: filter metric groups

* chore: filter metric groups

* chore: formula correction

* chore: added step flooring note

* chore: comment correction

* chore: comment correction

* chore: removed function

* chore: renamed variables

* chore: added happy test

* chore: added test 2 for accuracy and test 3 for missing metrics check

* chore: added filter test 4

* chore: added 5th test for filterByStatus

* chore: added group by tests

* chore: pagination test added

* chore: added validation tests

* chore: added auth test

* chore: added all tests

* chore: fix for surfacing meta for pods custom group by

* chore: added nodes integration test suite

* chore: namespaces integration tests

* ci: register inframonitoring suite + ruff format 01_hosts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: added integration tests for clusters

* chore: formatting changed

* chore: formatting changed

* chore: formatting changed

* chore: added volumes integration tests

* chore: added deployments

* chore: added order by host.name test

* refactor(infra-monitoring): address review comments on hosts integration tests

- inline _post helper at call sites
- combine filter operator tests into parametrized test_hosts_filter
- combine bad attr/grammar tests into parametrized test_hosts_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_hosts_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align pods integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_pods_filter
- combine bad attr/grammar tests into parametrized test_pods_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_pods_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_nodes_filter
- combine bad attr/grammar tests into parametrized test_nodes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_nodes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_namespaces_filter
- combine bad attr/grammar tests into parametrized test_namespaces_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_namespaces_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_clusters_filter
- combine bad attr/grammar tests into parametrized test_clusters_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_clusters_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align volumes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_volumes_filter
- combine bad attr/grammar tests into parametrized test_volumes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_volumes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align deployments integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_deployments_filter
- combine bad attr/grammar tests into parametrized test_deployments_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_deployments_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts groupby tests into parametrized test

- merge test_hosts_groupby_hostname + test_hosts_groupby_os_type into
  test_hosts_groupby parametrized on (dataset, group key, expected counts/values)
- preserves all assertions incl hostName populated-vs-empty branch coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts orderby tests into parametrized test

- merge total_invariant_across_orderby + orderby_correctness + orderby_by_host_name
  into test_hosts_orderby parametrized on (column, record_field) x direction
- each case asserts both the total/len invariant and sortedness; sortedness now
  covered for all metric columns, not just cpu
- single dataset (hosts_orderby.jsonl) + CONTAINS 'order-' guard on all cases

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts pagination tests

- fold offset-beyond-total case into the test_hosts_pagination page walk
  (offset K+5 expects 0 records via max(0, min(limit, K-offset)); total
  invariant covers the beyond-total page's total == K)
- single seed instead of two

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): merge hosts happy_path into test_hosts_accuracy

- happy_path and value_accuracy datasets were structurally identical
  (same 4 metrics, same sample counts, 2 hosts); one test now asserts
  shape/contract + exact metric values in a single seed/request
- drop unused hosts_happy_path.jsonl

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine pods integration tests logically

- merge happy_path into test_pods_accuracy (datasets structurally identical);
  drop unused pods_happy_path.jsonl
- merge groupby namespace/deployment into parametrized test_pods_groupby
- merge orderby invariant + correctness + by-pod-name into test_pods_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_pods_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine nodes integration tests logically

- merge happy_path into test_nodes_accuracy; drop unused nodes_happy_path.jsonl
- merge orderby invariant + correctness + by-node-name into test_nodes_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_nodes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine namespaces integration tests logically

- merge happy_path into test_namespaces_accuracy; drop unused namespaces_happy_path.jsonl
- merge orderby invariant + correctness + by-namespace-name into test_namespaces_orderby
- fold offset-beyond-total into test_namespaces_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine clusters integration tests logically

- merge happy_path into test_clusters_accuracy; drop unused clusters_happy_path.jsonl
- merge orderby invariant + correctness + by-cluster-name into test_clusters_orderby
- fold offset-beyond-total into test_clusters_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine volumes integration tests logically

- merge happy_path into test_volumes_accuracy; drop unused volumes_happy_path.jsonl
- merge orderby invariant + correctness + by-pvc-name into test_volumes_orderby
- fold offset-beyond-total into test_volumes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine deployments integration tests logically

- merge happy_path into test_deployments_accuracy; drop unused deployments_happy_path.jsonl
- merge orderby invariant + correctness + by-deployment-name into test_deployments_orderby
- fold offset-beyond-total into test_deployments_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in hosts filter tests

- regenerate hosts_filter_dataset.jsonl so every host mirrors the acc-h1
  sample pattern from hosts_value_accuracy.jsonl (CI-proven expected values)
- test_hosts_filter now asserts cpu/memory/wait/load15/diskUsage per filtered
  record against FILTER_DATASET_EXPECTED (1e-9), proving filters don't
  distort aggregation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): scope filter expected values inside test_hosts_filter

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in pods filter tests

- regenerate pods_filter_dataset.jsonl so every pod mirrors the acc-p1
  sample pattern from pods_value_accuracy.jsonl (CI-proven expected values)
- test_pods_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in nodes filter tests

- regenerate nodes_filter_dataset.jsonl so every node mirrors the acc-n1
  sample pattern from nodes_value_accuracy.jsonl (CI-proven expected values)
- test_nodes_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in namespaces filter tests

- regenerate namespaces_filter_dataset.jsonl so every namespace mirrors the
  acc-ns-1 sample pattern (2 pods) from namespaces_value_accuracy.jsonl
- test_namespaces_filter now asserts namespaceCPU/namespaceMemory per record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in clusters filter tests

- regenerate clusters_filter_dataset.jsonl so every cluster mirrors the
  acc-cluster-1 sample pattern (2 nodes) from clusters_value_accuracy.jsonl
- test_clusters_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in volumes filter tests

- regenerate volumes_filter_dataset.jsonl so every PVC mirrors the acc-pvc-1
  sample pattern from volumes_value_accuracy.jsonl (CI-proven expected values)
- test_volumes_filter now asserts all 6 volume fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in deployments filter tests

- regenerate deployments_filter_dataset.jsonl so every deployment mirrors the
  acc-dep-1 sample pattern (2 pods) from deployments_value_accuracy.jsonl
- test_deployments_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes groupby + pod-phase tests with structure

- merge pod_phase_counts_list_mode + _no_pods_on_node into parametrized
  test_nodes_pod_phase_counts ((dataset, node, filter, expected) cases)
- rename groupby_cluster -> test_nodes_groupby, parametrize over
  k8s.node.name + k8s.cluster.name; adds the node-name-in-groupBy branch
  (nodeName populated, condition derived) that hosts/pods both cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces groupby with structure

- rename groupby_cluster -> test_namespaces_groupby, parametrize over
  k8s.namespace.name + k8s.cluster.name; adds the namespace-name-in-groupBy
  branch (namespaceName populated) that hosts/pods/nodes all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters groupby with structure

- rename groupby_cloud_provider -> test_clusters_groupby, parametrize over
  k8s.cluster.name + cloud.provider; adds the cluster-name-in-groupBy branch
  (clusterName populated) that hosts/pods/nodes/namespaces all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align volumes groupby with structure

- rename groupby_namespace -> test_volumes_groupby, parametrize over
  k8s.persistentvolumeclaim.name + k8s.namespace.name; adds the
  pvc-name-in-groupBy branch (persistentVolumeClaimName populated) that the
  other endpoints all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align deployments groupby with structure

- rename groupby_namespace -> test_deployments_groupby, parametrize over
  k8s.deployment.name + k8s.namespace.name; adds the deployment-name-in-groupBy
  branch (deploymentName populated) that the other endpoints all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 10:35:23 +00:00
Nikhil Mantri
446dd4589f feat(infra-monitoring): v2 volumes integration tests (#11431)
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
* chore: updated logic and use centralized function in the module

* chore: filter metric groups

* chore: filter metric groups

* chore: formula correction

* chore: added step flooring note

* chore: comment correction

* chore: comment correction

* chore: removed function

* chore: renamed variables

* chore: added happy test

* chore: added test 2 for accuracy and test 3 for missing metrics check

* chore: added filter test 4

* chore: added 5th test for filterByStatus

* chore: added group by tests

* chore: pagination test added

* chore: added validation tests

* chore: added auth test

* chore: added all tests

* chore: fix for surfacing meta for pods custom group by

* chore: added nodes integration test suite

* chore: namespaces integration tests

* ci: register inframonitoring suite + ruff format 01_hosts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: added integration tests for clusters

* chore: formatting changed

* chore: formatting changed

* chore: formatting changed

* chore: added volumes integration tests

* chore: added order by host.name test

* refactor(infra-monitoring): address review comments on hosts integration tests

- inline _post helper at call sites
- combine filter operator tests into parametrized test_hosts_filter
- combine bad attr/grammar tests into parametrized test_hosts_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_hosts_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align pods integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_pods_filter
- combine bad attr/grammar tests into parametrized test_pods_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_pods_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_nodes_filter
- combine bad attr/grammar tests into parametrized test_nodes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_nodes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_namespaces_filter
- combine bad attr/grammar tests into parametrized test_namespaces_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_namespaces_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_clusters_filter
- combine bad attr/grammar tests into parametrized test_clusters_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_clusters_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align volumes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_volumes_filter
- combine bad attr/grammar tests into parametrized test_volumes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_volumes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts groupby tests into parametrized test

- merge test_hosts_groupby_hostname + test_hosts_groupby_os_type into
  test_hosts_groupby parametrized on (dataset, group key, expected counts/values)
- preserves all assertions incl hostName populated-vs-empty branch coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts orderby tests into parametrized test

- merge total_invariant_across_orderby + orderby_correctness + orderby_by_host_name
  into test_hosts_orderby parametrized on (column, record_field) x direction
- each case asserts both the total/len invariant and sortedness; sortedness now
  covered for all metric columns, not just cpu
- single dataset (hosts_orderby.jsonl) + CONTAINS 'order-' guard on all cases

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts pagination tests

- fold offset-beyond-total case into the test_hosts_pagination page walk
  (offset K+5 expects 0 records via max(0, min(limit, K-offset)); total
  invariant covers the beyond-total page's total == K)
- single seed instead of two

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): merge hosts happy_path into test_hosts_accuracy

- happy_path and value_accuracy datasets were structurally identical
  (same 4 metrics, same sample counts, 2 hosts); one test now asserts
  shape/contract + exact metric values in a single seed/request
- drop unused hosts_happy_path.jsonl

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine pods integration tests logically

- merge happy_path into test_pods_accuracy (datasets structurally identical);
  drop unused pods_happy_path.jsonl
- merge groupby namespace/deployment into parametrized test_pods_groupby
- merge orderby invariant + correctness + by-pod-name into test_pods_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_pods_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine nodes integration tests logically

- merge happy_path into test_nodes_accuracy; drop unused nodes_happy_path.jsonl
- merge orderby invariant + correctness + by-node-name into test_nodes_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_nodes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine namespaces integration tests logically

- merge happy_path into test_namespaces_accuracy; drop unused namespaces_happy_path.jsonl
- merge orderby invariant + correctness + by-namespace-name into test_namespaces_orderby
- fold offset-beyond-total into test_namespaces_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine clusters integration tests logically

- merge happy_path into test_clusters_accuracy; drop unused clusters_happy_path.jsonl
- merge orderby invariant + correctness + by-cluster-name into test_clusters_orderby
- fold offset-beyond-total into test_clusters_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine volumes integration tests logically

- merge happy_path into test_volumes_accuracy; drop unused volumes_happy_path.jsonl
- merge orderby invariant + correctness + by-pvc-name into test_volumes_orderby
- fold offset-beyond-total into test_volumes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in hosts filter tests

- regenerate hosts_filter_dataset.jsonl so every host mirrors the acc-h1
  sample pattern from hosts_value_accuracy.jsonl (CI-proven expected values)
- test_hosts_filter now asserts cpu/memory/wait/load15/diskUsage per filtered
  record against FILTER_DATASET_EXPECTED (1e-9), proving filters don't
  distort aggregation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): scope filter expected values inside test_hosts_filter

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in pods filter tests

- regenerate pods_filter_dataset.jsonl so every pod mirrors the acc-p1
  sample pattern from pods_value_accuracy.jsonl (CI-proven expected values)
- test_pods_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in nodes filter tests

- regenerate nodes_filter_dataset.jsonl so every node mirrors the acc-n1
  sample pattern from nodes_value_accuracy.jsonl (CI-proven expected values)
- test_nodes_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in namespaces filter tests

- regenerate namespaces_filter_dataset.jsonl so every namespace mirrors the
  acc-ns-1 sample pattern (2 pods) from namespaces_value_accuracy.jsonl
- test_namespaces_filter now asserts namespaceCPU/namespaceMemory per record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in clusters filter tests

- regenerate clusters_filter_dataset.jsonl so every cluster mirrors the
  acc-cluster-1 sample pattern (2 nodes) from clusters_value_accuracy.jsonl
- test_clusters_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in volumes filter tests

- regenerate volumes_filter_dataset.jsonl so every PVC mirrors the acc-pvc-1
  sample pattern from volumes_value_accuracy.jsonl (CI-proven expected values)
- test_volumes_filter now asserts all 6 volume fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes groupby + pod-phase tests with structure

- merge pod_phase_counts_list_mode + _no_pods_on_node into parametrized
  test_nodes_pod_phase_counts ((dataset, node, filter, expected) cases)
- rename groupby_cluster -> test_nodes_groupby, parametrize over
  k8s.node.name + k8s.cluster.name; adds the node-name-in-groupBy branch
  (nodeName populated, condition derived) that hosts/pods both cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces groupby with structure

- rename groupby_cluster -> test_namespaces_groupby, parametrize over
  k8s.namespace.name + k8s.cluster.name; adds the namespace-name-in-groupBy
  branch (namespaceName populated) that hosts/pods/nodes all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters groupby with structure

- rename groupby_cloud_provider -> test_clusters_groupby, parametrize over
  k8s.cluster.name + cloud.provider; adds the cluster-name-in-groupBy branch
  (clusterName populated) that hosts/pods/nodes/namespaces all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align volumes groupby with structure

- rename groupby_namespace -> test_volumes_groupby, parametrize over
  k8s.persistentvolumeclaim.name + k8s.namespace.name; adds the
  pvc-name-in-groupBy branch (persistentVolumeClaimName populated) that the
  other endpoints all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 09:30:31 +00:00
primus-bot[bot]
c0c9039428 chore(release): bump to v0.128.0 (#11633)
Co-authored-by: primus-bot[bot] <171087277+primus-bot[bot]@users.noreply.github.com>
2026-06-10 06:59:07 +00:00
Nikhil Soni
a014e9c0cb chore(trace-detail): waterfall v3 and v2 cleanup (#11609)
* chore: cleanup waterfall v3 api

We've moved to v4 as part of memory allocation optimisation

* chore: remove select all limit from waterfall request

It was added for testing only and fine tuning the limit value

* chore: update openapi specs

* chore: remove waterfall v2 since we've moved to v4 now
2026-06-10 05:15:36 +00:00
Vinicius Lourenço
b898269ddc chore(api): deprecate any hand-written api call (#11632) 2026-06-10 01:44:03 +00:00
swapnil-signoz
8fdad21a2e chore: bumping integration agent version to v0.0.11 (#11621)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
2026-06-09 22:40:30 +00:00
Nikhil Mantri
2e0d25479a feat(infra-monitoring): v2 clusters integration tests (#11430)
* chore: updated logic and use centralized function in the module

* chore: filter metric groups

* chore: filter metric groups

* chore: formula correction

* chore: added step flooring note

* chore: comment correction

* chore: comment correction

* chore: removed function

* chore: renamed variables

* chore: added happy test

* chore: added test 2 for accuracy and test 3 for missing metrics check

* chore: added filter test 4

* chore: added 5th test for filterByStatus

* chore: added group by tests

* chore: pagination test added

* chore: added validation tests

* chore: added auth test

* chore: added all tests

* chore: fix for surfacing meta for pods custom group by

* chore: added nodes integration test suite

* chore: namespaces integration tests

* ci: register inframonitoring suite + ruff format 01_hosts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: added integration tests for clusters

* chore: formatting changed

* chore: formatting changed

* chore: formatting changed

* chore: added order by host.name test

* refactor(infra-monitoring): address review comments on hosts integration tests

- inline _post helper at call sites
- combine filter operator tests into parametrized test_hosts_filter
- combine bad attr/grammar tests into parametrized test_hosts_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_hosts_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align pods integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_pods_filter
- combine bad attr/grammar tests into parametrized test_pods_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_pods_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_nodes_filter
- combine bad attr/grammar tests into parametrized test_nodes_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_nodes_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_namespaces_filter
- combine bad attr/grammar tests into parametrized test_namespaces_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_namespaces_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters integration tests with review feedback

- inline _post helper at call sites
- combine filter operator tests into parametrized test_clusters_filter
- combine bad attr/grammar tests into parametrized test_clusters_filter_invalid
- convert orderby total-invariant nested loop to stacked parametrize
- drop redundant test_clusters_auth (auth covered globally)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts groupby tests into parametrized test

- merge test_hosts_groupby_hostname + test_hosts_groupby_os_type into
  test_hosts_groupby parametrized on (dataset, group key, expected counts/values)
- preserves all assertions incl hostName populated-vs-empty branch coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts orderby tests into parametrized test

- merge total_invariant_across_orderby + orderby_correctness + orderby_by_host_name
  into test_hosts_orderby parametrized on (column, record_field) x direction
- each case asserts both the total/len invariant and sortedness; sortedness now
  covered for all metric columns, not just cpu
- single dataset (hosts_orderby.jsonl) + CONTAINS 'order-' guard on all cases

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine hosts pagination tests

- fold offset-beyond-total case into the test_hosts_pagination page walk
  (offset K+5 expects 0 records via max(0, min(limit, K-offset)); total
  invariant covers the beyond-total page's total == K)
- single seed instead of two

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): merge hosts happy_path into test_hosts_accuracy

- happy_path and value_accuracy datasets were structurally identical
  (same 4 metrics, same sample counts, 2 hosts); one test now asserts
  shape/contract + exact metric values in a single seed/request
- drop unused hosts_happy_path.jsonl

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine pods integration tests logically

- merge happy_path into test_pods_accuracy (datasets structurally identical);
  drop unused pods_happy_path.jsonl
- merge groupby namespace/deployment into parametrized test_pods_groupby
- merge orderby invariant + correctness + by-pod-name into test_pods_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_pods_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine nodes integration tests logically

- merge happy_path into test_nodes_accuracy; drop unused nodes_happy_path.jsonl
- merge orderby invariant + correctness + by-node-name into test_nodes_orderby
  ((column, record_field) x direction; sortedness now covered for all columns)
- fold offset-beyond-total into test_nodes_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine namespaces integration tests logically

- merge happy_path into test_namespaces_accuracy; drop unused namespaces_happy_path.jsonl
- merge orderby invariant + correctness + by-namespace-name into test_namespaces_orderby
- fold offset-beyond-total into test_namespaces_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): combine clusters integration tests logically

- merge happy_path into test_clusters_accuracy; drop unused clusters_happy_path.jsonl
- merge orderby invariant + correctness + by-cluster-name into test_clusters_orderby
- fold offset-beyond-total into test_clusters_pagination page walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in hosts filter tests

- regenerate hosts_filter_dataset.jsonl so every host mirrors the acc-h1
  sample pattern from hosts_value_accuracy.jsonl (CI-proven expected values)
- test_hosts_filter now asserts cpu/memory/wait/load15/diskUsage per filtered
  record against FILTER_DATASET_EXPECTED (1e-9), proving filters don't
  distort aggregation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): scope filter expected values inside test_hosts_filter

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in pods filter tests

- regenerate pods_filter_dataset.jsonl so every pod mirrors the acc-p1
  sample pattern from pods_value_accuracy.jsonl (CI-proven expected values)
- test_pods_filter now asserts the 6 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in nodes filter tests

- regenerate nodes_filter_dataset.jsonl so every node mirrors the acc-n1
  sample pattern from nodes_value_accuracy.jsonl (CI-proven expected values)
- test_nodes_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in namespaces filter tests

- regenerate namespaces_filter_dataset.jsonl so every namespace mirrors the
  acc-ns-1 sample pattern (2 pods) from namespaces_value_accuracy.jsonl
- test_namespaces_filter now asserts namespaceCPU/namespaceMemory per record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(infra-monitoring): assert metric value accuracy in clusters filter tests

- regenerate clusters_filter_dataset.jsonl so every cluster mirrors the
  acc-cluster-1 sample pattern (2 nodes) from clusters_value_accuracy.jsonl
- test_clusters_filter now asserts the 4 CPU/memory fields per filtered record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align nodes groupby + pod-phase tests with structure

- merge pod_phase_counts_list_mode + _no_pods_on_node into parametrized
  test_nodes_pod_phase_counts ((dataset, node, filter, expected) cases)
- rename groupby_cluster -> test_nodes_groupby, parametrize over
  k8s.node.name + k8s.cluster.name; adds the node-name-in-groupBy branch
  (nodeName populated, condition derived) that hosts/pods both cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align namespaces groupby with structure

- rename groupby_cluster -> test_namespaces_groupby, parametrize over
  k8s.namespace.name + k8s.cluster.name; adds the namespace-name-in-groupBy
  branch (namespaceName populated) that hosts/pods/nodes all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(infra-monitoring): align clusters groupby with structure

- rename groupby_cloud_provider -> test_clusters_groupby, parametrize over
  k8s.cluster.name + cloud.provider; adds the cluster-name-in-groupBy branch
  (clusterName populated) that hosts/pods/nodes/namespaces all cover

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 18:51:23 +00:00
144 changed files with 8401 additions and 1996 deletions

View File

@@ -190,7 +190,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.127.1
image: signoz/signoz:v0.128.0
ports:
- "8080:8080" # signoz port
# - "6060:6060" # pprof port

View File

@@ -117,7 +117,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.127.1
image: signoz/signoz:v0.128.0
ports:
- "8080:8080" # signoz port
volumes:

View File

@@ -181,7 +181,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.127.1}
image: signoz/signoz:${VERSION:-v0.128.0}
container_name: signoz
ports:
- "8080:8080" # signoz port

View File

@@ -109,7 +109,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.127.1}
image: signoz/signoz:${VERSION:-v0.128.0}
container_name: signoz
ports:
- "8080:8080" # signoz port

View File

@@ -470,25 +470,6 @@ components:
role:
type: string
type: object
AuthtypesAuthDomainConfig:
oneOf:
- $ref: '#/components/schemas/AuthtypesSamlConfig'
- $ref: '#/components/schemas/AuthtypesGoogleConfig'
- $ref: '#/components/schemas/AuthtypesOIDCConfig'
properties:
googleAuthConfig:
$ref: '#/components/schemas/AuthtypesGoogleConfig'
oidcConfig:
$ref: '#/components/schemas/AuthtypesOIDCConfig'
roleMapping:
$ref: '#/components/schemas/AuthtypesRoleMapping'
samlConfig:
$ref: '#/components/schemas/AuthtypesSamlConfig'
ssoEnabled:
type: boolean
ssoType:
$ref: '#/components/schemas/AuthtypesAuthNProvider'
type: object
AuthtypesAuthNProvider:
enum:
- google_auth
@@ -515,6 +496,48 @@ components:
nullable: true
type: array
type: object
AuthtypesAuthProviderEnvelope:
discriminator:
mapping:
google_auth: '#/components/schemas/AuthtypesAuthProviderGoogle'
oidc: '#/components/schemas/AuthtypesAuthProviderOIDC'
saml: '#/components/schemas/AuthtypesAuthProviderSAML'
propertyName: type
oneOf:
- $ref: '#/components/schemas/AuthtypesAuthProviderSAML'
- $ref: '#/components/schemas/AuthtypesAuthProviderOIDC'
- $ref: '#/components/schemas/AuthtypesAuthProviderGoogle'
type: object
AuthtypesAuthProviderGoogle:
properties:
config:
$ref: '#/components/schemas/AuthtypesGoogleConfig'
type:
$ref: '#/components/schemas/AuthtypesAuthNProvider'
required:
- type
- config
type: object
AuthtypesAuthProviderOIDC:
properties:
config:
$ref: '#/components/schemas/AuthtypesOIDCConfig'
type:
$ref: '#/components/schemas/AuthtypesAuthNProvider'
required:
- type
- config
type: object
AuthtypesAuthProviderSAML:
properties:
config:
$ref: '#/components/schemas/AuthtypesSAMLConfig'
type:
$ref: '#/components/schemas/AuthtypesAuthNProvider'
required:
- type
- config
type: object
AuthtypesCallbackAuthNSupport:
properties:
provider:
@@ -526,8 +549,6 @@ components:
properties:
authNProviderInfo:
$ref: '#/components/schemas/AuthtypesAuthNProviderInfo'
config:
$ref: '#/components/schemas/AuthtypesAuthDomainConfig'
createdAt:
format: date-time
type: string
@@ -537,6 +558,12 @@ components:
type: string
orgId:
type: string
provider:
$ref: '#/components/schemas/AuthtypesAuthProviderEnvelope'
roleMapping:
$ref: '#/components/schemas/AuthtypesRoleMapping'
ssoEnabled:
type: boolean
updatedAt:
format: date-time
type: string
@@ -634,10 +661,14 @@ components:
type: object
AuthtypesPostableAuthDomain:
properties:
config:
$ref: '#/components/schemas/AuthtypesAuthDomainConfig'
name:
type: string
provider:
$ref: '#/components/schemas/AuthtypesAuthProviderEnvelope'
roleMapping:
$ref: '#/components/schemas/AuthtypesRoleMapping'
ssoEnabled:
type: boolean
type: object
AuthtypesPostableEmailPasswordSession:
properties:
@@ -710,7 +741,7 @@ components:
useRoleAttribute:
type: boolean
type: object
AuthtypesSamlConfig:
AuthtypesSAMLConfig:
properties:
attributeMapping:
$ref: '#/components/schemas/AuthtypesAttributeMapping'
@@ -745,8 +776,12 @@ components:
type: object
AuthtypesUpdatableAuthDomain:
properties:
config:
$ref: '#/components/schemas/AuthtypesAuthDomainConfig'
provider:
$ref: '#/components/schemas/AuthtypesAuthProviderEnvelope'
roleMapping:
$ref: '#/components/schemas/AuthtypesRoleMapping'
ssoEnabled:
type: boolean
type: object
AuthtypesUserRole:
properties:
@@ -6726,11 +6761,6 @@ components:
type: object
SpantypesGettableWaterfallTrace:
properties:
aggregations:
items:
$ref: '#/components/schemas/SpantypesSpanAggregationResult'
nullable: true
type: array
endTimestampMillis:
minimum: 0
type: integer
@@ -6818,14 +6848,6 @@ components:
type: object
SpantypesPostableWaterfall:
properties:
aggregations:
items:
$ref: '#/components/schemas/SpantypesSpanAggregation'
nullable: true
type: array
limit:
minimum: 0
type: integer
selectedSpanId:
type: string
uncollapsedSpans:
@@ -20681,76 +20703,6 @@ paths:
summary: Get flamegraph view for a trace
tags:
- tracedetail
/api/v3/traces/{traceID}/waterfall:
post:
deprecated: false
description: Returns the waterfall view of spans for a given trace ID with tree
structure, metadata, and windowed pagination
operationId: GetWaterfall
parameters:
- in: path
name: traceID
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SpantypesPostableWaterfall'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/SpantypesGettableWaterfallTrace'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: Get waterfall view for a trace
tags:
- tracedetail
/api/v4/traces/{traceID}/waterfall:
post:
deprecated: false

View File

@@ -53,7 +53,7 @@ func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSett
}
func (a *AuthN) LoginURL(ctx context.Context, siteURL *url.URL, authDomain *authtypes.AuthDomain) (string, error) {
if authDomain.AuthDomainConfig().AuthNProvider != authtypes.AuthNProviderOIDC {
if authDomain.AuthDomainConfig().Provider.Type != authtypes.AuthNProviderOIDC {
return "", errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthDomainMismatch, "domain type is not oidc")
}
@@ -106,14 +106,14 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
return nil, err
}
if claims == nil && authDomain.AuthDomainConfig().OIDC.GetUserInfo {
if claims == nil && authDomain.AuthDomainConfig().Oidc().GetUserInfo {
claims, err = a.claimsFromUserInfo(ctx, oidcProvider, token)
if err != nil {
return nil, err
}
}
emailClaim, ok := claims[authDomain.AuthDomainConfig().OIDC.ClaimMapping.Email].(string)
emailClaim, ok := claims[authDomain.AuthDomainConfig().Oidc().ClaimMapping.Email].(string)
if !ok {
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "oidc: missing email in claims")
}
@@ -123,7 +123,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "oidc: failed to parse email").WithAdditional(err.Error())
}
if !authDomain.AuthDomainConfig().OIDC.InsecureSkipEmailVerified {
if !authDomain.AuthDomainConfig().Oidc().InsecureSkipEmailVerified {
emailVerifiedClaim, ok := claims["email_verified"].(bool)
if !ok {
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "oidc: missing email_verified in claims")
@@ -135,14 +135,14 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
}
name := ""
if nameClaim := authDomain.AuthDomainConfig().OIDC.ClaimMapping.Name; nameClaim != "" {
if nameClaim := authDomain.AuthDomainConfig().Oidc().ClaimMapping.Name; nameClaim != "" {
if n, ok := claims[nameClaim].(string); ok {
name = n
}
}
var groups []string
if groupsClaim := authDomain.AuthDomainConfig().OIDC.ClaimMapping.Groups; groupsClaim != "" {
if groupsClaim := authDomain.AuthDomainConfig().Oidc().ClaimMapping.Groups; groupsClaim != "" {
if claimValue, exists := claims[groupsClaim]; exists {
switch g := claimValue.(type) {
case []any:
@@ -161,7 +161,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
}
role := ""
if roleClaim := authDomain.AuthDomainConfig().OIDC.ClaimMapping.Role; roleClaim != "" {
if roleClaim := authDomain.AuthDomainConfig().Oidc().ClaimMapping.Role; roleClaim != "" {
if r, ok := claims[roleClaim].(string); ok {
role = r
}
@@ -177,11 +177,11 @@ func (a *AuthN) ProviderInfo(ctx context.Context, authDomain *authtypes.AuthDoma
}
func (a *AuthN) oidcProviderAndoauth2Config(ctx context.Context, siteURL *url.URL, authDomain *authtypes.AuthDomain) (*oidc.Provider, *oauth2.Config, error) {
if authDomain.AuthDomainConfig().OIDC.IssuerAlias != "" {
ctx = oidc.InsecureIssuerURLContext(ctx, authDomain.AuthDomainConfig().OIDC.IssuerAlias)
if authDomain.AuthDomainConfig().Oidc().IssuerAlias != "" {
ctx = oidc.InsecureIssuerURLContext(ctx, authDomain.AuthDomainConfig().Oidc().IssuerAlias)
}
oidcProvider, err := oidc.NewProvider(ctx, authDomain.AuthDomainConfig().OIDC.Issuer)
oidcProvider, err := oidc.NewProvider(ctx, authDomain.AuthDomainConfig().Oidc().Issuer)
if err != nil {
return nil, nil, err
}
@@ -194,8 +194,8 @@ func (a *AuthN) oidcProviderAndoauth2Config(ctx context.Context, siteURL *url.UR
}
return oidcProvider, &oauth2.Config{
ClientID: authDomain.AuthDomainConfig().OIDC.ClientID,
ClientSecret: authDomain.AuthDomainConfig().OIDC.ClientSecret,
ClientID: authDomain.AuthDomainConfig().Oidc().ClientID,
ClientSecret: authDomain.AuthDomainConfig().Oidc().ClientSecret,
Endpoint: oidcProvider.Endpoint(),
Scopes: scopes,
RedirectURL: (&url.URL{
@@ -212,7 +212,7 @@ func (a *AuthN) claimsFromIDToken(ctx context.Context, authDomain *authtypes.Aut
return nil, errors.New(errors.TypeNotFound, errors.CodeNotFound, "oidc: no id_token in token response")
}
verifier := provider.Verifier(&oidc.Config{ClientID: authDomain.AuthDomainConfig().OIDC.ClientID})
verifier := provider.Verifier(&oidc.Config{ClientID: authDomain.AuthDomainConfig().Oidc().ClientID})
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
return nil, errors.Newf(errors.TypeForbidden, errors.CodeForbidden, "oidc: failed to verify token").WithAdditional(err.Error())

View File

@@ -40,7 +40,7 @@ func New(ctx context.Context, store authtypes.AuthNStore, licensing licensing.Li
}
func (a *AuthN) LoginURL(ctx context.Context, siteURL *url.URL, authDomain *authtypes.AuthDomain) (string, error) {
if authDomain.AuthDomainConfig().AuthNProvider != authtypes.AuthNProviderSAML {
if authDomain.AuthDomainConfig().Provider.Type != authtypes.AuthNProviderSAML {
return "", errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthDomainMismatch, "saml: domain type is not saml")
}
@@ -101,19 +101,19 @@ func (a *AuthN) HandleCallback(ctx context.Context, formValues url.Values) (*aut
}
name := ""
if nameAttribute := authDomain.AuthDomainConfig().SAML.AttributeMapping.Name; nameAttribute != "" {
if nameAttribute := authDomain.AuthDomainConfig().Saml().AttributeMapping.Name; nameAttribute != "" {
if val := assertionInfo.Values.Get(nameAttribute); val != "" {
name = val
}
}
var groups []string
if groupAttribute := authDomain.AuthDomainConfig().SAML.AttributeMapping.Groups; groupAttribute != "" {
if groupAttribute := authDomain.AuthDomainConfig().Saml().AttributeMapping.Groups; groupAttribute != "" {
groups = assertionInfo.Values.GetAll(groupAttribute)
}
role := ""
if roleAttribute := authDomain.AuthDomainConfig().SAML.AttributeMapping.Role; roleAttribute != "" {
if roleAttribute := authDomain.AuthDomainConfig().Saml().AttributeMapping.Role; roleAttribute != "" {
if val := assertionInfo.Values.Get(roleAttribute); val != "" {
role = val
}
@@ -142,11 +142,11 @@ func (a *AuthN) serviceProvider(siteURL *url.URL, authDomain *authtypes.AuthDoma
// The ServiceProviderIssuer is the client id in case of keycloak. Since we set it to the host here, we need to set the client id == host in keycloak.
// For AWSSSO, this is the value of Application SAML audience.
return &saml2.SAMLServiceProvider{
IdentityProviderSSOURL: authDomain.AuthDomainConfig().SAML.SamlIdp,
IdentityProviderIssuer: authDomain.AuthDomainConfig().SAML.SamlEntity,
IdentityProviderSSOURL: authDomain.AuthDomainConfig().Saml().SamlIdp,
IdentityProviderIssuer: authDomain.AuthDomainConfig().Saml().SamlEntity,
ServiceProviderIssuer: siteURL.Host,
AssertionConsumerServiceURL: acsURL.String(),
SignAuthnRequests: !authDomain.AuthDomainConfig().SAML.InsecureSkipAuthNRequestsSigned,
SignAuthnRequests: !authDomain.AuthDomainConfig().Saml().InsecureSkipAuthNRequestsSigned,
AllowMissingAttributes: true,
IDPCertificateStore: certStore,
SPKeyStore: dsig.RandomKeyStoreForTest(),
@@ -159,15 +159,15 @@ func (a *AuthN) getCertificateStore(authDomain *authtypes.AuthDomain) (dsig.X509
}
var certBytes []byte
if strings.Contains(authDomain.AuthDomainConfig().SAML.SamlCert, "-----BEGIN CERTIFICATE-----") {
block, _ := pem.Decode([]byte(authDomain.AuthDomainConfig().SAML.SamlCert))
if strings.Contains(authDomain.AuthDomainConfig().Saml().SamlCert, "-----BEGIN CERTIFICATE-----") {
block, _ := pem.Decode([]byte(authDomain.AuthDomainConfig().Saml().SamlCert))
if block == nil {
return certStore, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "no valid pem cert found")
}
certBytes = block.Bytes
} else {
certData, err := base64.StdEncoding.DecodeString(authDomain.AuthDomainConfig().SAML.SamlCert)
certData, err := base64.StdEncoding.DecodeString(authDomain.AuthDomainConfig().Saml().SamlCert)
if err != nil {
return certStore, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to read certificate: %s", err.Error())
}

View File

@@ -5,6 +5,13 @@ import convertObjectIntoParams from 'lib/query/convertObjectIntoParams';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/alerts/getTriggered';
/**
* @deprecated Use the generated `useGetAlerts` hook (or `getAlerts` fetcher) from
* `api/generated/services/alerts` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const getTriggered = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createEmail';
/**
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const create = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createMsTeams';
/**
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const create = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createOpsgenie';
/**
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const create = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createPager';
/**
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const create = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createSlack';
/**
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const create = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createWebhook';
/**
* @deprecated Use the generated `useCreateChannel` hook (or `createChannel` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const create = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/delete';
/**
* @deprecated Use the generated `useDeleteChannelByID` hook (or `deleteChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const deleteChannel = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editEmail';
/**
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const editEmail = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editMsTeams';
/**
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const editMsTeams = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorResponse, ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editOpsgenie';
/**
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const editOpsgenie = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps> | ErrorResponse> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editPager';
/**
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const editPager = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editSlack';
/**
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const editSlack = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editWebhook';
/**
* @deprecated Use the generated `useUpdateChannelByID` hook (or `updateChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const editWebhook = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -5,6 +5,13 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/get';
import { Channels } from 'types/api/channels/getAll';
/**
* @deprecated Use the generated `useGetChannelByID` hook (or `getChannelByID` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const get = async (props: Props): Promise<SuccessResponseV2<Channels>> => {
try {
const response = await axios.get<PayloadProps>(`/channels/${props.id}`);

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { Channels, PayloadProps } from 'types/api/channels/getAll';
/**
* @deprecated Use the generated `useListChannels` hook (or `listChannels` fetcher) from
* `api/generated/services/channels` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const getAll = async (): Promise<SuccessResponseV2<Channels[]>> => {
try {
const response = await axios.get<PayloadProps>('/channels');

View File

@@ -5,6 +5,13 @@ import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constan
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create';
/**
* @deprecated Use the generated `useCreatePublicDashboard` hook (or `createPublicDashboard` fetcher) from
* `api/generated/services/dashboard` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const createPublicDashboard = async (
props: CreatePublicDashboardProps,
): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { GetPublicDashboardDataProps, PayloadProps,PublicDashboardDataProps } from 'types/api/dashboard/public/get';
/**
* @deprecated Use the generated `useGetPublicDashboardData` hook (or `getPublicDashboardData` fetcher) from
* `api/generated/services/dashboard` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const getPublicDashboardData = async (props: GetPublicDashboardDataProps): Promise<SuccessResponseV2<PublicDashboardDataProps>> => {
try {
const response = await axios.get<PayloadProps>(`/public/dashboards/${props.id}`);

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { GetPublicDashboardMetaProps, PayloadProps,PublicDashboardMetaProps } from 'types/api/dashboard/public/getMeta';
/**
* @deprecated Use the generated `useGetPublicDashboard` hook (or `getPublicDashboard` fetcher) from
* `api/generated/services/dashboard` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const getPublicDashboardMeta = async (props: GetPublicDashboardMetaProps): Promise<SuccessResponseV2<PublicDashboardMetaProps>> => {
try {
const response = await axios.get<PayloadProps>(`/dashboards/${props.id}/public`);

View File

@@ -6,6 +6,13 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { GetPublicDashboardWidgetDataProps } from 'types/api/dashboard/public/getWidgetData';
/**
* @deprecated Use the generated `useGetPublicDashboardWidgetQueryRange` hook (or `getPublicDashboardWidgetQueryRange` fetcher) from
* `api/generated/services/dashboard` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const getPublicDashboardWidgetData = async (props: GetPublicDashboardWidgetDataProps): Promise<SuccessResponseV2<MetricRangePayloadV5>> => {
try {
const response = await axios.get(`/public/dashboards/${props.id}/widgets/${props.index}/query_range`, {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps,RevokePublicDashboardAccessProps } from 'types/api/dashboard/public/delete';
/**
* @deprecated Use the generated `useDeletePublicDashboard` hook (or `deletePublicDashboard` fetcher) from
* `api/generated/services/dashboard` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const revokePublicDashboardAccess = async (
props: RevokePublicDashboardAccessProps,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -5,6 +5,13 @@ import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constan
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update';
/**
* @deprecated Use the generated `useUpdatePublicDashboard` hook (or `updatePublicDashboard` fetcher) from
* `api/generated/services/dashboard` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const updatePublicDashboard = async (
props: UpdatePublicDashboardProps,
): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => {

View File

@@ -9,6 +9,13 @@ interface ISubstituteVars {
compositeQuery: ICompositeMetricQuery;
}
/**
* @deprecated Use the generated `useReplaceVariables` hook (or `replaceVariables` fetcher) from
* `api/generated/services/querier` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getSubstituteVars = async (
props?: Partial<QueryRangePayloadV5>,
signal?: AbortSignal,

View File

@@ -8,6 +8,12 @@ import { FieldKeyResponse } from 'types/api/dynamicVariables/getFieldKeys';
* Get field keys for a given signal type
* @param signal Type of signal (traces, logs, metrics)
* @param name Optional search text
*
* @deprecated Use the generated `useGetFieldsKeys` hook (or `getFieldsKeys` fetcher) from
* `api/generated/services/fields` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getFieldKeys = async (
signal?: 'traces' | 'logs' | 'metrics',

View File

@@ -11,6 +11,12 @@ import { FieldValueResponse } from 'types/api/dynamicVariables/getFieldValues';
* @param name Name of the attribute for which values are being fetched
* @param value Optional search text
* @param existingQuery Optional existing query - across all present dynamic variables
*
* @deprecated Use the generated `useGetFieldsValues` hook (or `getFieldsValues` fetcher) from
* `api/generated/services/fields` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getFieldValues = async (
signal?: 'traces' | 'logs' | 'metrics',

View File

@@ -8095,10 +8095,6 @@ export interface SpantypesWaterfallSpanDTO {
}
export interface SpantypesGettableWaterfallTraceDTO {
/**
* @type array,null
*/
aggregations?: SpantypesSpanAggregationResultDTO[] | null;
/**
* @type integer
* @minimum 0
@@ -8218,15 +8214,6 @@ export interface SpantypesPostableTraceAggregationsDTO {
}
export interface SpantypesPostableWaterfallDTO {
/**
* @type array,null
*/
aggregations?: SpantypesSpanAggregationDTO[] | null;
/**
* @type integer
* @minimum 0
*/
limit?: number;
/**
* @type string
*/
@@ -10521,17 +10508,6 @@ export type GetFlamegraph200 = {
status: string;
};
export type GetWaterfallPathParameters = {
traceID: string;
};
export type GetWaterfall200 = {
data: SpantypesGettableWaterfallTraceDTO;
/**
* @type string
*/
status: string;
};
export type GetWaterfallV4PathParameters = {
traceID: string;
};

View File

@@ -16,8 +16,6 @@ import type {
GetFlamegraphPathParameters,
GetTraceAggregations200,
GetTraceAggregationsPathParameters,
GetWaterfall200,
GetWaterfallPathParameters,
GetWaterfallV4200,
GetWaterfallV4PathParameters,
RenderErrorResponseDTO,
@@ -228,105 +226,6 @@ export const useGetFlamegraph = <
> => {
return useMutation(getGetFlamegraphMutationOptions(options));
};
/**
* Returns the waterfall view of spans for a given trace ID with tree structure, metadata, and windowed pagination
* @summary Get waterfall view for a trace
*/
export const getWaterfall = (
{ traceID }: GetWaterfallPathParameters,
spantypesPostableWaterfallDTO?: BodyType<SpantypesPostableWaterfallDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetWaterfall200>({
url: `/api/v3/traces/${traceID}/waterfall`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: spantypesPostableWaterfallDTO,
signal,
});
};
export const getGetWaterfallMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof getWaterfall>>,
TError,
{
pathParams: GetWaterfallPathParameters;
data?: BodyType<SpantypesPostableWaterfallDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof getWaterfall>>,
TError,
{
pathParams: GetWaterfallPathParameters;
data?: BodyType<SpantypesPostableWaterfallDTO>;
},
TContext
> => {
const mutationKey = ['getWaterfall'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof getWaterfall>>,
{
pathParams: GetWaterfallPathParameters;
data?: BodyType<SpantypesPostableWaterfallDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return getWaterfall(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type GetWaterfallMutationResult = NonNullable<
Awaited<ReturnType<typeof getWaterfall>>
>;
export type GetWaterfallMutationBody =
| BodyType<SpantypesPostableWaterfallDTO>
| undefined;
export type GetWaterfallMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get waterfall view for a trace
*/
export const useGetWaterfall = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof getWaterfall>>,
TError,
{
pathParams: GetWaterfallPathParameters;
data?: BodyType<SpantypesPostableWaterfallDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof getWaterfall>>,
TError,
{
pathParams: GetWaterfallPathParameters;
data?: BodyType<SpantypesPostableWaterfallDTO>;
},
TContext
> => {
return useMutation(getGetWaterfallMutationOptions(options));
};
/**
* Returns the waterfall view of spans including all spans if total spans are under a limit, a max count otherwise. Aggregations are dropped compared to v3
* @summary Get waterfall view for a trace

View File

@@ -5,6 +5,13 @@ import {
QueryKeySuggestionsResponseProps,
} from 'types/api/querySuggestions/types';
/**
* @deprecated Use the generated `useGetFieldsKeys` hook (or `getFieldsKeys` fetcher) from
* `api/generated/services/fields` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getKeySuggestions = (
props: QueryKeyRequestProps,
): Promise<AxiosResponse<QueryKeySuggestionsResponseProps>> => {

View File

@@ -5,6 +5,13 @@ import {
QueryKeyValueSuggestionsResponseProps,
} from 'types/api/querySuggestions/types';
/**
* @deprecated Use the generated `useGetFieldsValues` hook (or `getFieldsValues` fetcher) from
* `api/generated/services/fields` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getValueSuggestions = (
props: QueryKeyValueRequestProps,
): Promise<AxiosResponse<QueryKeyValueSuggestionsResponseProps>> => {

View File

@@ -15,6 +15,13 @@ export interface CreateRoutingPolicyResponse {
message: string;
}
/**
* @deprecated Use the generated `useCreateRoutePolicy` hook (or `createRoutePolicy` fetcher) from
* `api/generated/services/routepolicies` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const createRoutingPolicy = async (
props: CreateRoutingPolicyBody,
): Promise<

View File

@@ -8,6 +8,13 @@ export interface DeleteRoutingPolicyResponse {
message: string;
}
/**
* @deprecated Use the generated `useDeleteRoutePolicyByID` hook (or `deleteRoutePolicyByID` fetcher) from
* `api/generated/services/routepolicies` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const deleteRoutingPolicy = async (
routingPolicyId: string,
): Promise<

View File

@@ -20,6 +20,13 @@ export interface GetRoutingPoliciesResponse {
data?: ApiRoutingPolicy[];
}
/**
* @deprecated Use the generated `useGetAllRoutePolicies` hook (or `getAllRoutePolicies` fetcher) from
* `api/generated/services/routepolicies` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getRoutingPolicies = async (
signal?: AbortSignal,
headers?: Record<string, string>,

View File

@@ -15,6 +15,13 @@ export interface UpdateRoutingPolicyResponse {
message: string;
}
/**
* @deprecated Use the generated `useUpdateRoutePolicy` hook (or `updateRoutePolicy` fetcher) from
* `api/generated/services/routepolicies` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const updateRoutingPolicy = async (
id: string,
props: UpdateRoutingPolicyBody,

View File

@@ -27,7 +27,6 @@ const getTraceV4 = async (
{
selectedSpanId: props.selectedSpanId,
uncollapsedSpans,
limit: 10000,
},
);

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/user/resetPassword';
/**
* @deprecated Use the generated `useResetPassword` hook (or `resetPassword` fetcher) from
* `api/generated/services/users` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const resetPassword = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { UsersProps } from 'types/api/user/inviteUsers';
/**
* @deprecated Use the generated `useCreateBulkInvite` hook (or `createBulkInvite` fetcher) from
* `api/generated/services/users` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const inviteUsers = async (
users: UsersProps,
): Promise<SuccessResponseV2<null>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/user/setInvite';
/**
* @deprecated Use the generated `useCreateInvite` hook (or `createInvite` fetcher) from
* `api/generated/services/users` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const sendInvite = async (
props: Props,
): Promise<SuccessResponseV2<PayloadProps>> => {

View File

@@ -5,6 +5,13 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps } from 'types/api/preferences/list';
import { OrgPreference } from 'types/api/preferences/preference';
/**
* @deprecated Use the generated `useListOrgPreferences` hook (or `listOrgPreferences` fetcher) from
* `api/generated/services/preferences` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const listPreference = async (): Promise<
SuccessResponseV2<OrgPreference[]>
> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { Props } from 'types/api/preferences/update';
/**
* @deprecated Use the generated `useUpdateOrgPreference` hook (or `updateOrgPreference` fetcher) from
* `api/generated/services/preferences` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const update = async (props: Props): Promise<SuccessResponseV2<null>> => {
try {
const response = await axios.put(`/org/preferences/${props.name}`, {

View File

@@ -5,6 +5,13 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps } from 'types/api/preferences/list';
import { UserPreference } from 'types/api/preferences/preference';
/**
* @deprecated Use the generated `useListUserPreferences` hook (or `listUserPreferences` fetcher) from
* `api/generated/services/preferences` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const list = async (): Promise<SuccessResponseV2<UserPreference[]>> => {
try {
const response = await axios.get<PayloadProps>(`/user/preferences`);

View File

@@ -5,6 +5,13 @@ import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { PayloadProps, Props } from 'types/api/preferences/get';
import { UserPreference } from 'types/api/preferences/preference';
/**
* @deprecated Use the generated `useGetUserPreference` hook (or `getUserPreference` fetcher) from
* `api/generated/services/preferences` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const get = async (
props: Props,
): Promise<SuccessResponseV2<UserPreference>> => {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
import { Props } from 'types/api/preferences/update';
/**
* @deprecated Use the generated `useUpdateUserPreference` hook (or `updateUserPreference` fetcher) from
* `api/generated/services/preferences` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const update = async (props: Props): Promise<SuccessResponseV2<null>> => {
try {
const response = await axios.put(`/user/preferences/${props.name}`, {

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
import { Props, SessionsContext } from 'types/api/v2/sessions/context/get';
/**
* @deprecated Use the generated `useGetSessionContext` hook (or `getSessionContext` fetcher) from
* `api/generated/services/sessions` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const get = async (
props: Props,
): Promise<SuccessResponseV2<SessionsContext>> => {

View File

@@ -3,6 +3,13 @@ import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios';
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
/**
* @deprecated Use the generated `useDeleteSession` hook (or `deleteSession` fetcher) from
* `api/generated/services/sessions` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const deleteSession = async (): Promise<SuccessResponseV2<null>> => {
try {
const response = await axios.delete<RawSuccessResponse<null>>('/sessions');

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
import { Props, Token } from 'types/api/v2/sessions/email_password/post';
/**
* @deprecated Use the generated `useCreateSessionByEmailPassword` hook (or `createSessionByEmailPassword` fetcher) from
* `api/generated/services/sessions` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const post = async (props: Props): Promise<SuccessResponseV2<Token>> => {
try {
const response = await axios.post<RawSuccessResponse<Token>>(

View File

@@ -4,6 +4,13 @@ import { AxiosError } from 'axios';
import { ErrorV2Resp, RawSuccessResponse, SuccessResponseV2 } from 'types/api';
import { Props, Token } from 'types/api/v2/sessions/rotate/post';
/**
* @deprecated Use the generated `useRotateSession` hook (or `rotateSession` fetcher) from
* `api/generated/services/sessions` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
const post = async (props: Props): Promise<SuccessResponseV2<Token>> => {
try {
const response = await axios.post<RawSuccessResponse<Token>>(

View File

@@ -8,6 +8,13 @@ import {
QueryRangePayloadV5,
} from 'types/api/v5/queryRange';
/**
* @deprecated Use the generated `useQueryRangeV5` hook (or `queryRangeV5` fetcher) from
* `api/generated/services/querier` instead. This hand-written client targets the
* same endpoint and will be removed once call sites migrate.
*
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
*/
export const getQueryRangeV5 = async (
props: QueryRangePayloadV5,
version: string,

View File

@@ -1,5 +1,11 @@
// TODO: Improve the styling of the query aggregation container and its components. - @YounixM , @H4ad
$dropdown-base-height: 250px;
$recents-header-height: 30px;
$recent-row-height: 36px;
// how many recents are rendered, this caps how tall the dropdown can grow to fit them.
$max-recents-shown: 5;
.code-mirror-where-clause {
width: 100%;
display: flex;
@@ -117,7 +123,23 @@
width: 100% !important;
max-width: 100% !important;
font-family: 'Space Mono', monospace !important;
min-height: 200px !important;
max-height: $dropdown-base-height !important;
overflow-y: auto !important;
// Recents render at the top of the dropdown ahead of regular suggestions.
// At the base max-height, having recents in view would crowd out the
// suggestion list below. This loop grows the dropdown by one row's worth
// of height for every recent present (up to $max-recents-shown), plus the
// section header. `:has(> li:nth-of-type(N) .cm-completionIcon-recent)`
// matches when the Nth child of <ul> is a recent — i.e. there are at
// least N recents visible.
@for $i from 1 through $max-recents-shown {
&:has(> li:nth-of-type(#{$i}) .cm-completionIcon-recent) {
max-height: $dropdown-base-height +
$recents-header-height +
($i * $recent-row-height) !important;
}
}
&::-webkit-scrollbar {
width: 0.3rem;
@@ -133,6 +155,19 @@
background: transparent;
}
completion-section {
display: block;
padding: 10px 12px 6px;
font-size: 10px !important;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--l3-foreground, var(--l2-foreground));
opacity: 0.7;
border-bottom: 0;
background-color: transparent;
}
li {
width: 100% !important;
max-width: 100% !important;
@@ -159,11 +194,78 @@
display: none !important;
}
.cm-completionDetail {
margin-left: auto;
font-style: normal;
font-size: var(--periscope-font-size-small, 11px);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 0.55;
}
&[aria-selected='true'] {
background: var(--l3-background) !important;
font-weight: 600 !important;
}
}
li:has(.cm-completionIcon-recent) {
&:hover .cm-recent-delete,
&[aria-selected='true'] .cm-recent-delete {
opacity: 1;
}
}
li .cm-completionLabel {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cm-recent-delete {
margin-left: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding: 0;
border: 0;
border-radius: 4px;
background: transparent;
color: var(--l2-foreground);
font-size: 18px;
line-height: 1;
cursor: pointer;
opacity: 0.5;
transition:
opacity 0.12s ease,
background-color 0.12s ease;
&:hover {
opacity: 1;
background: color-mix(in srgb, var(--l2-foreground) 18%, transparent);
}
}
}
&::after {
content: '↓↑ to navigate · ↵ to apply · esc to dismiss';
display: block;
padding: 8px 12px;
border-top: 1px solid var(--l1-border);
font-family: 'Space Mono', monospace;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.02em;
color: var(--l2-foreground);
opacity: 0.6;
background: color-mix(in srgb, var(--l1-background) 50%, transparent);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
}

View File

@@ -46,8 +46,15 @@ import {
import { validateQuery } from 'utils/queryValidationUtils';
import { unquote } from 'utils/stringUtils';
import { queryExamples } from './constants';
import { combineInitialAndUserExpression } from './utils';
import { getRecentQueries } from 'lib/recentQueries/getRecentQueries';
import type { SignalType } from 'types/api/v5/queryRange';
import { queryExamples, SUGGESTIONS_SECTION } from './constants';
import {
combineInitialAndUserExpression,
getRecentOptions,
renderRecentDeleteButton,
} from './utils';
import './QuerySearch.styles.scss';
@@ -1250,6 +1257,41 @@ function QuerySearch({
};
}
const signal = dataSource as SignalType;
function combinedSuggestions(
context: CompletionContext,
): CompletionResult | null {
const fullDoc = context.state.doc.toString();
const recentOptions = getRecentOptions(
getRecentQueries(signal, signalSource ?? ''),
fullDoc,
);
const result = autoSuggestions(context);
const suggestionOptions = (result?.options || []).map((opt) => ({
...opt,
section: SUGGESTIONS_SECTION,
}));
if (recentOptions.length === 0 && suggestionOptions.length === 0) {
return result;
}
if (!result) {
return {
from: 0,
to: fullDoc.length,
options: recentOptions,
};
}
return {
...result,
options: [...recentOptions, ...suggestionOptions],
};
}
// Effect to handle focus state and trigger suggestions
useEffect(() => {
const clearTimeout = toggleSuggestions(10);
@@ -1398,11 +1440,12 @@ function QuerySearch({
})}
extensions={[
autocompletion({
override: [autoSuggestions],
override: [combinedSuggestions],
defaultKeymap: true,
closeOnBlur: true,
activateOnTyping: true,
maxRenderedOptions: 50,
addToOptions: [{ render: renderRecentDeleteButton, position: 100 }],
}),
javascript({ jsx: false, typescript: false }),
EditorView.lineWrapping,

View File

@@ -1,3 +1,14 @@
export const RECENTS_SECTION = { name: 'Recent searches', rank: 1 } as const;
export const SUGGESTIONS_SECTION = { name: 'Suggestions', rank: 2 } as const;
// Custom CodeMirror Completion.type for recent-query entries. Used to discriminate
// recents from regular autocomplete completions in renderers and event handlers.
export const RECENT_COMPLETION_TYPE = 'recent';
// Max number of recents rendered in the autocomplete dropdown.
// Do change $max-recents-shown: in QuerySearch.styles.scss if you change this.
export const RECENTS_DISPLAY_CAP = 5;
export const queryExamples = [
{
label: 'Basic Query',

View File

@@ -1,3 +1,19 @@
import { closeCompletion, startCompletion } from '@codemirror/autocomplete';
import type { Completion } from '@codemirror/autocomplete';
import type { EditorView } from '@uiw/react-codemirror';
import dayjs from 'dayjs';
import { normalizeFilterExpression } from 'lib/recentQueries/normalize';
import * as recentQueriesStore from 'lib/recentQueries/recentQueriesStore';
import type { RecentQueryEntry } from 'lib/recentQueries/types';
import type { SignalType } from 'types/api/v5/queryRange';
import 'utils/timeUtils';
import {
RECENT_COMPLETION_TYPE,
RECENTS_DISPLAY_CAP,
RECENTS_SECTION,
} from './constants';
export function combineInitialAndUserExpression(
initial: string,
user: string,
@@ -38,3 +54,106 @@ export function getUserExpressionFromCombined(
}
return c;
}
// Filters and projects a list of recent-query entries into CodeMirror completions.
// Entries are supplied by the caller (typically via the useRecents hook) so this
// function stays pure and React doesn't have to re-subscribe inside CodeMirror's
// autocomplete callback.
export function getRecentOptions(
entries: RecentQueryEntry[],
fullDoc: string,
): Completion[] {
const normalizedDoc = normalizeFilterExpression(fullDoc);
const matches = entries
.filter((e) => {
const normalizedRecent = normalizeFilterExpression(e.filter.expression);
if (normalizedRecent === normalizedDoc) {
return false;
}
if (normalizedDoc === '') {
return true;
}
return normalizedRecent.includes(normalizedDoc);
})
.slice(0, RECENTS_DISPLAY_CAP);
return matches.map((entry, index) => ({
label: entry.filter.expression,
type: RECENT_COMPLETION_TYPE,
// CodeMirror sorts within a section by boost desc, then label asc. The store
// returns entries newest-first, so we mirror that by giving the newest entry
// the highest boost — otherwise CM falls back to alphabetical order and the
// "most recently used" expectation breaks. Stays within the recents section
// because section.rank keeps recents above suggestions regardless of boost.
boost: matches.length - index,
section: RECENTS_SECTION,
detail: dayjs(entry.lastUsedAt).fromNow(),
recentId: entry.id,
recentSignal: entry.signal,
recentSource: entry.source,
apply: (view: EditorView): void => {
view.dispatch({
changes: {
from: 0,
to: view.state.doc.length,
insert: entry.filter.expression,
},
selection: { anchor: entry.filter.expression.length },
});
closeCompletion(view);
},
}));
}
export function renderRecentDeleteButton(
completion: Completion,
_state: unknown,
view: EditorView | null,
): Node | null {
if (completion.type !== RECENT_COMPLETION_TYPE) {
return null;
}
const c = completion as Completion & {
recentId?: string;
recentSignal?: SignalType;
recentSource?: string;
};
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'cm-recent-delete';
btn.setAttribute('aria-label', 'Remove from recent searches');
btn.title = 'Remove from recent searches';
btn.textContent = '×';
queueMicrotask(() => {
if (btn.parentElement) {
btn.parentElement.title = completion.label;
}
});
const stop = (e: Event): void => {
e.preventDefault();
e.stopPropagation();
};
// CodeMirror's autocomplete closes the popup on pointerdown / mousedown outside
// the editor. The delete button lives inside the popup, so we must stop those
// events early — otherwise clicking × would dismiss the dropdown before the
// click handler fires and the entry wouldn't actually get removed.
btn.addEventListener('pointerdown', stop);
btn.addEventListener('mousedown', stop);
btn.addEventListener('click', (e) => {
stop(e);
if (!c.recentId || !c.recentSignal) {
return;
}
recentQueriesStore.remove(c.recentId, c.recentSignal, c.recentSource ?? '');
if (view) {
view.focus();
startCompletion(view);
}
});
return btn;
}

View File

@@ -0,0 +1,5 @@
export const STORAGE_KEY_PREFIX = 'qb_recent_v1';
export const STORAGE_VERSION = 1;
// Maximum entries kept per (signal, source) bucket. Larger than the UI's
// RECENTS_DISPLAY_CAP so deleting a visible entry surfaces an older one.
export const MAX_ENTRIES = 10;

View File

@@ -0,0 +1,15 @@
import type { SignalType } from 'types/api/v5/queryRange';
import * as store from './recentQueriesStore';
import type { RecentQueryEntry } from './types';
// Synchronous, non-subscribing read of the recent-queries bucket for a given
// (signal, source). Read-on-demand by design — subscribing here would
// reconfigure CodeMirror on every store change and close any open completion
// popup. Pair with saveQuery() for the write path.
export function getRecentQueries(
signal: SignalType,
source = '',
): RecentQueryEntry[] {
return store.list(signal, source);
}

View File

@@ -0,0 +1,100 @@
import { normalizeFilterExpression } from './normalize';
describe('normalizeFilterExpression', () => {
it('returns empty string for empty input', () => {
expect(normalizeFilterExpression('')).toBe('');
});
it('returns empty string for whitespace-only input', () => {
expect(normalizeFilterExpression(' \t ')).toBe('');
});
it('strips whitespace around operators', () => {
expect(normalizeFilterExpression(' a = 1 ')).toBe('a=1');
expect(normalizeFilterExpression('a = 1')).toBe('a=1');
expect(normalizeFilterExpression('a=1')).toBe('a=1');
});
it('lowercases AND / OR / NOT outside quotes', () => {
expect(normalizeFilterExpression('A AND B OR NOT C')).toBe('AandBornotC');
});
it('lowercases IN / LIKE / ILIKE / CONTAINS', () => {
expect(normalizeFilterExpression('host IN [1, 2] AND name LIKE "foo"')).toBe(
'hostin[1,2]andnamelike"foo"',
);
});
it('lowercases REGEXP', () => {
expect(normalizeFilterExpression('path REGEXP "foo"')).toBe(
'pathregexp"foo"',
);
expect(normalizeFilterExpression('path REGEXP "foo"')).toBe(
normalizeFilterExpression('path regexp "foo"'),
);
});
it('lowercases HAS / HASANY / HASALL / HASTOKEN function names', () => {
expect(normalizeFilterExpression('HAS(tags, "x")')).toBe(
normalizeFilterExpression('has(tags, "x")'),
);
expect(normalizeFilterExpression('HASANY(tags, ["a","b"])')).toBe(
normalizeFilterExpression('hasAny(tags, ["a","b"])'),
);
expect(normalizeFilterExpression('HASALL(tags, ["a","b"])')).toBe(
normalizeFilterExpression('hasAll(tags, ["a","b"])'),
);
expect(normalizeFilterExpression('HASTOKEN(msg, "err")')).toBe(
normalizeFilterExpression('hasToken(msg, "err")'),
);
});
it('lowercases TRUE / FALSE boolean literals', () => {
expect(normalizeFilterExpression('active = TRUE')).toBe(
normalizeFilterExpression('active = true'),
);
expect(normalizeFilterExpression('active = FALSE')).toBe(
normalizeFilterExpression('active = false'),
);
});
it('preserves whitespace and casing inside single-quoted strings', () => {
expect(normalizeFilterExpression("a = 'X Y'")).toBe("a='X Y'");
});
it('preserves whitespace and casing inside double-quoted strings', () => {
expect(normalizeFilterExpression('a = "X Y"')).toBe('a="X Y"');
});
it('does not lowercase keyword-looking substrings inside quotes', () => {
expect(normalizeFilterExpression("msg = 'AND ERROR'")).toBe(
"msg='AND ERROR'",
);
});
it('handles escaped quotes inside strings', () => {
expect(normalizeFilterExpression("msg = 'a\\'b' AND x = 1")).toBe(
"msg='a\\'b'andx=1",
);
});
it('treats two formattings of the same expression as identical', () => {
const a = normalizeFilterExpression(
'service.name = "frontend" AND severity = error',
);
const b = normalizeFilterExpression(
'service.name="frontend" and severity=error',
);
expect(a).toBe(b);
});
it('preserves unquoted value casing (treats them as identifiers)', () => {
expect(normalizeFilterExpression('status = OK')).toBe('status=OK');
});
it('handles mixed quotes in one expression', () => {
expect(normalizeFilterExpression(`a = 'X' AND b = "Y"`)).toBe(
`a='X'andb="Y"`,
);
});
});

View File

@@ -0,0 +1,56 @@
import {
OPERATORS,
QUERY_BUILDER_FUNCTIONS,
TRACE_OPERATOR_OPERATORS,
} from 'constants/antlrQueryConstants';
// Reserved keywords sourced from the ANTLR grammar constants so this list stays
// in sync with the parser. `\b` prevents partial matches inside identifiers
// (e.g. `OR` inside `originator`). `TRUE`/`FALSE` are BOOL literals, included
// so case variants of boolean values also dedup.
const WORD_KEYWORDS = [
...Object.keys(OPERATORS).filter((k) => /^[A-Z]+$/.test(k)),
...Object.keys(TRACE_OPERATOR_OPERATORS).filter((k) => /^[A-Z]+$/.test(k)),
...Object.values(QUERY_BUILDER_FUNCTIONS),
'TRUE',
'FALSE',
];
const KEYWORDS_RE = new RegExp(`\\b(${WORD_KEYWORDS.join('|')})\\b`, 'gi');
// Matches single- or double-quoted string literals, supporting escaped quotes
// (e.g. `'it\'s'` or `"a \" b"`). We preserve quoted spans verbatim during
// normalisation so user-meaningful whitespace and casing inside string values
// stays intact: `name = "Foo Bar"` must not collapse to `name="foobar"`.
const QUOTED_RE = /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/g;
// Lowercases reserved keywords and strips ALL whitespace from the unquoted regions
// of the input. Keywords are normalised so casing variants dedup; whitespace is
// dropped so formatting variants (`a=1` vs `a = 1`) dedup too.
function processOutsideQuotes(s: string): string {
return s.replace(KEYWORDS_RE, (m) => m.toLowerCase()).replace(/\s+/g, '');
}
// Produces a canonical form of a filter expression suitable for dedup-key derivation.
// Walks the input alternating between unquoted regions (where we normalise keywords
// and whitespace) and quoted regions (which we copy verbatim).
export function normalizeFilterExpression(input: string): string {
if (!input) {
return '';
}
let result = '';
let lastIndex = 0;
QUOTED_RE.lastIndex = 0;
let match = QUOTED_RE.exec(input);
while (match !== null) {
result += processOutsideQuotes(input.slice(lastIndex, match.index));
result += match[0];
lastIndex = QUOTED_RE.lastIndex;
match = QUOTED_RE.exec(input);
}
result += processOutsideQuotes(input.slice(lastIndex));
return result.trim();
}

View File

@@ -0,0 +1,247 @@
import { MAX_ENTRIES } from './constants';
import * as store from './recentQueriesStore';
import type { RecentQueryInput } from './recentQueriesStore';
import type { RecentQueryEntry } from './types';
const baseInput = (
overrides: Partial<RecentQueryInput> = {},
): RecentQueryInput => ({
signal: 'logs',
filter: { expression: "service.name = 'frontend'" },
...overrides,
});
function saveOrThrow(input: RecentQueryInput): RecentQueryEntry {
const saved = store.save(input);
if (!saved) {
throw new Error('expected save to return an entry');
}
return saved;
}
describe('recentQueries store', () => {
beforeEach(() => {
store.useRecentQueriesStore.setState({ buckets: {} });
localStorage.clear();
});
describe('save + list', () => {
it('saves an entry and lists it', () => {
store.save(baseInput());
const entries = store.list('logs');
expect(entries).toHaveLength(1);
expect(entries[0].filter.expression).toBe("service.name = 'frontend'");
expect(entries[0].id).toBeTruthy();
expect(entries[0].lastUsedAt).toBeGreaterThan(0);
});
it('does not save when the filter expression is empty', () => {
const result = store.save(baseInput({ filter: { expression: '' } }));
expect(result).toBeNull();
expect(store.list('logs')).toHaveLength(0);
});
it('does not save when the filter expression is whitespace only', () => {
const result = store.save(baseInput({ filter: { expression: ' ' } }));
expect(result).toBeNull();
expect(store.list('logs')).toHaveLength(0);
});
});
describe('LRU ordering', () => {
it('places the most recently saved entry at the front', () => {
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
store.save(baseInput({ filter: { expression: 'http.status_code >= 500' } }));
store.save(baseInput({ filter: { expression: 'attempt = 1' } }));
const entries = store.list('logs');
expect(entries.map((e) => e.filter.expression)).toStrictEqual([
'attempt = 1',
'http.status_code >= 500',
"severity_text = 'ERROR'",
]);
});
it('re-saving an existing filter bumps it to the front', () => {
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
store.save(baseInput({ filter: { expression: 'http.status_code >= 500' } }));
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
const entries = store.list('logs');
expect(entries).toHaveLength(2);
expect(entries.map((e) => e.filter.expression)).toStrictEqual([
"severity_text = 'ERROR'",
'http.status_code >= 500',
]);
});
});
describe('dedup', () => {
it('treats formatting variations of the same filter as one entry', () => {
store.save(
baseInput({
filter: { expression: "severity_text = 'ERROR' AND attempt = 1" },
}),
);
store.save(
baseInput({
filter: { expression: "severity_text='ERROR' and attempt=1" },
}),
);
expect(store.list('logs')).toHaveLength(1);
});
});
describe('signal partitioning', () => {
it('saves to the right bucket per signal', () => {
store.save(
baseInput({
signal: 'logs',
filter: { expression: "severity_text = 'ERROR'" },
}),
);
store.save(
baseInput({
signal: 'traces',
filter: { expression: "service.name = 'orders-api'" },
}),
);
store.save(
baseInput({
signal: 'metrics',
filter: { expression: 'cpu_usage > 80' },
}),
);
expect(store.list('logs')).toHaveLength(1);
expect(store.list('traces')).toHaveLength(1);
expect(store.list('metrics')).toHaveLength(1);
expect(store.list('logs')[0].filter.expression).toBe(
"severity_text = 'ERROR'",
);
expect(store.list('traces')[0].filter.expression).toBe(
"service.name = 'orders-api'",
);
expect(store.list('metrics')[0].filter.expression).toBe('cpu_usage > 80');
});
it('does not leak between signals on dedup', () => {
store.save(
baseInput({
signal: 'logs',
filter: { expression: "service.name = 'frontend'" },
}),
);
store.save(
baseInput({
signal: 'traces',
filter: { expression: "service.name = 'frontend'" },
}),
);
expect(store.list('logs')).toHaveLength(1);
expect(store.list('traces')).toHaveLength(1);
});
});
describe('LRU cap', () => {
it('caps the bucket at MAX_ENTRIES and evicts the oldest', () => {
const total = MAX_ENTRIES + 1;
for (let i = 0; i < total; i += 1) {
store.save(baseInput({ filter: { expression: `attempt = ${i}` } }));
}
const entries = store.list('logs');
expect(entries).toHaveLength(MAX_ENTRIES);
expect(entries[0].filter.expression).toBe(`attempt = ${total - 1}`);
expect(entries.some((e) => e.filter.expression === 'attempt = 0')).toBe(
false,
);
});
});
describe('remove', () => {
it('removes an entry by id', () => {
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
const saved = saveOrThrow(
baseInput({ filter: { expression: 'http.status_code >= 500' } }),
);
store.remove(saved.id, 'logs');
const entries = store.list('logs');
expect(entries).toHaveLength(1);
expect(entries[0].filter.expression).toBe("severity_text = 'ERROR'");
});
it('is a no-op when the id does not exist', () => {
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
store.remove('does-not-exist', 'logs');
expect(store.list('logs')).toHaveLength(1);
});
it('does not touch other signals', () => {
const logsEntry = saveOrThrow(
baseInput({
signal: 'logs',
filter: { expression: "service.name = 'frontend'" },
}),
);
store.save(
baseInput({
signal: 'traces',
filter: { expression: "service.name = 'frontend'" },
}),
);
store.remove(logsEntry.id, 'logs');
expect(store.list('logs')).toHaveLength(0);
expect(store.list('traces')).toHaveLength(1);
});
});
describe('persistence', () => {
it('reads back the same entries after the in-memory state is reset', () => {
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
store.save(baseInput({ filter: { expression: 'http.status_code >= 500' } }));
store.useRecentQueriesStore.setState({ buckets: {} });
const entries = store.list('logs');
expect(entries.map((e) => e.filter.expression)).toStrictEqual([
'http.status_code >= 500',
"severity_text = 'ERROR'",
]);
});
});
describe('reactive subscription via zustand', () => {
it('notifies zustand subscribers on save', () => {
const cb = jest.fn();
const unsubscribe = store.useRecentQueriesStore.subscribe(cb);
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
expect(cb).toHaveBeenCalledTimes(1);
unsubscribe();
});
it('notifies zustand subscribers on remove', () => {
const saved = saveOrThrow(
baseInput({ filter: { expression: "severity_text = 'ERROR'" } }),
);
const cb = jest.fn();
const unsubscribe = store.useRecentQueriesStore.subscribe(cb);
store.remove(saved.id, 'logs');
expect(cb).toHaveBeenCalledTimes(1);
unsubscribe();
});
it('stops notifying after unsubscribe', () => {
const cb = jest.fn();
const unsubscribe = store.useRecentQueriesStore.subscribe(cb);
unsubscribe();
store.save(baseInput({ filter: { expression: "severity_text = 'ERROR'" } }));
expect(cb).not.toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,144 @@
import get from 'api/browser/localstorage/get';
import set from 'api/browser/localstorage/set';
import type { SignalType } from 'types/api/v5/queryRange';
import { create } from 'zustand';
import { MAX_ENTRIES, STORAGE_VERSION } from './constants';
import { normalizeFilterExpression } from './normalize';
import type { RecentQueriesStoreShape, RecentQueryEntry } from './types';
import { bucketKey, makeId, normalizeSource, storageKeyFor } from './utils';
// Mirrors parsed localStorage so equal raw strings return the same array ref —
// preserves Object.is for zustand selector bail-out.
const persistedBucketCache = new Map<
string,
{ raw: string; parsed: RecentQueryEntry[] }
>();
function loadBucketFromStorage(
signal: SignalType,
source: string,
): RecentQueryEntry[] | null {
const key = bucketKey(signal, source);
try {
const raw = get(storageKeyFor(signal, source));
if (!raw) {
persistedBucketCache.delete(key);
return null;
}
const cached = persistedBucketCache.get(key);
if (cached && cached.raw === raw) {
return cached.parsed;
}
const parsedShape = JSON.parse(raw) as RecentQueriesStoreShape;
if (
parsedShape?.version !== STORAGE_VERSION ||
!Array.isArray(parsedShape.entries)
) {
persistedBucketCache.delete(key);
return null;
}
persistedBucketCache.set(key, { raw, parsed: parsedShape.entries });
return parsedShape.entries;
} catch {
persistedBucketCache.delete(key);
return null;
}
}
function saveBucketToStorage(
signal: SignalType,
source: string,
entries: RecentQueryEntry[],
): void {
try {
const raw = JSON.stringify({ version: STORAGE_VERSION, entries });
if (set(storageKeyFor(signal, source), raw)) {
persistedBucketCache.set(bucketKey(signal, source), {
raw,
parsed: entries,
});
}
} catch {
// Ignore storage errors (e.g. quota exceeded, JSON.stringify failure).
}
}
export type RecentQueryInput = Omit<
RecentQueryEntry,
'id' | 'lastUsedAt' | 'source'
> & {
source?: string;
};
type RecentQueriesState = {
buckets: Record<string, RecentQueryEntry[]>;
save: (entry: RecentQueryInput) => RecentQueryEntry | null;
remove: (id: string, signal: SignalType, source?: string) => void;
};
export const useRecentQueriesStore = create<RecentQueriesState>()(
(set, get) => ({
buckets: {},
save: (entry): RecentQueryEntry | null => {
const normalized = normalizeFilterExpression(entry.filter.expression);
if (!normalized) {
return null;
}
const source = normalizeSource(entry.source);
const key = bucketKey(entry.signal, source);
const current =
get().buckets[key] ?? loadBucketFromStorage(entry.signal, source) ?? [];
const filtered = current.filter(
(e) => normalizeFilterExpression(e.filter.expression) !== normalized,
);
const newEntry: RecentQueryEntry = {
...entry,
source,
id: makeId(entry.signal, source, normalized),
lastUsedAt: Date.now(),
};
const next = [newEntry, ...filtered].slice(0, MAX_ENTRIES);
set({ buckets: { ...get().buckets, [key]: next } });
saveBucketToStorage(entry.signal, source, next);
return newEntry;
},
remove: (id, signal, source = ''): void => {
const key = bucketKey(signal, source);
const current = get().buckets[key] ?? loadBucketFromStorage(signal, source);
if (!current) {
return;
}
const next = current.filter((e) => e.id !== id);
if (next.length === current.length) {
return;
}
set({ buckets: { ...get().buckets, [key]: next } });
saveBucketToStorage(signal, source, next);
},
}),
);
// Plain-function wrappers for non-React callers — same pattern as useColumnStore.ts.
export function save(entry: RecentQueryInput): RecentQueryEntry | null {
return useRecentQueriesStore.getState().save(entry);
}
export function remove(id: string, signal: SignalType, source = ''): void {
useRecentQueriesStore.getState().remove(id, signal, source);
}
// Synchronous bucket read with localStorage fallback for non-React callers.
export function list(signal: SignalType, source = ''): RecentQueryEntry[] {
const key = bucketKey(signal, source);
const state = useRecentQueriesStore.getState();
if (state.buckets[key]) {
return state.buckets[key];
}
return loadBucketFromStorage(signal, source) ?? [];
}

View File

@@ -0,0 +1,113 @@
import { DataSource } from 'types/common/queryBuilder';
import type { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { validateQuery } from 'utils/queryValidationUtils';
import * as store from './recentQueriesStore';
import { saveRecentQuery } from './saveRecentQuery';
jest.mock('utils/queryValidationUtils', () => ({
validateQuery: jest.fn(),
}));
const mockedValidateQuery = validateQuery as jest.MockedFunction<
typeof validateQuery
>;
const buildComposite = (
overrides: Partial<IBuilderQuery>[] = [{}],
): { builder: { queryData: IBuilderQuery[] } } => ({
builder: {
queryData: overrides.map((o, i) => ({
queryName: `Q${i}`,
dataSource: DataSource.LOGS,
aggregateOperator: 'count',
aggregateAttribute: undefined as never,
functions: [],
filter: { expression: 'service.name = "frontend"' },
groupBy: [],
expression: `Q${i}`,
disabled: false,
having: [],
limit: null,
stepInterval: null,
orderBy: [],
legend: '',
...o,
})) as IBuilderQuery[],
},
});
describe('saveRecentQuery', () => {
beforeEach(() => {
store.useRecentQueriesStore.setState({ buckets: {} });
localStorage.clear();
mockedValidateQuery.mockReturnValue({
isValid: true,
message: '',
errors: [],
});
});
it('saves the composite query when validation passes', () => {
saveRecentQuery(buildComposite());
const entries = store.list('logs');
expect(entries).toHaveLength(1);
expect(entries[0].filter.expression).toBe('service.name = "frontend"');
});
it('does not save when validateQuery rejects the expression', () => {
mockedValidateQuery.mockReturnValue({
isValid: false,
message: 'bad',
errors: [],
});
saveRecentQuery(buildComposite());
expect(store.list('logs')).toHaveLength(0);
});
it('does not save a builder query with an empty filter expression', () => {
saveRecentQuery(buildComposite([{ filter: { expression: '' } }]));
expect(store.list('logs')).toHaveLength(0);
});
it('saves each builder query in the composite separately', () => {
saveRecentQuery(
buildComposite([
{
dataSource: DataSource.LOGS,
filter: { expression: "service.name = 'frontend'" },
},
{
dataSource: DataSource.TRACES,
filter: { expression: "service.name = 'orders-api'" },
},
]),
);
expect(store.list('logs')).toHaveLength(1);
expect(store.list('traces')).toHaveLength(1);
});
it('skips builder queries whose dataSource is not a supported signal', () => {
saveRecentQuery(
buildComposite([{ dataSource: 'unknown' as IBuilderQuery['dataSource'] }]),
);
expect(store.list('logs')).toHaveLength(0);
expect(store.list('traces')).toHaveLength(0);
expect(store.list('metrics')).toHaveLength(0);
});
it('is a no-op when the composite is null, undefined, or empty', () => {
saveRecentQuery(null);
saveRecentQuery(undefined);
saveRecentQuery({ builder: { queryData: [] } });
saveRecentQuery({});
expect(store.list('logs')).toHaveLength(0);
});
});

View File

@@ -0,0 +1,52 @@
import type { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import type { SignalType } from 'types/api/v5/queryRange';
import { validateQuery } from 'utils/queryValidationUtils';
import * as store from './recentQueriesStore';
function toSignal(dataSource: IBuilderQuery['dataSource']): SignalType | null {
if (
dataSource === 'logs' ||
dataSource === 'traces' ||
dataSource === 'metrics'
) {
return dataSource;
}
return null;
}
type CompositeWithBuilder = {
builder?: { queryData?: IBuilderQuery[] };
};
// Persists each builder query in the composite as a recent entry. Call this
// only from explicit user-driven Run triggers — reacting to stagedQuery or any
// other derived state pollutes recents with navigation/refresh/go-to traffic.
export function saveRecentQuery(
query: CompositeWithBuilder | null | undefined,
): void {
const queryData = query?.builder?.queryData;
if (!Array.isArray(queryData) || queryData.length === 0) {
return;
}
queryData.forEach((q) => {
const expression = q.filter?.expression?.trim();
if (!expression) {
return;
}
const validation = validateQuery(expression);
if (!validation.isValid) {
return;
}
const signal = toSignal(q.dataSource);
if (!signal) {
return;
}
store.save({
signal,
source: q.source ?? '',
filter: q.filter ?? { expression: '' },
});
});
}

View File

@@ -0,0 +1,14 @@
import type { Filter, SignalType } from 'types/api/v5/queryRange';
export interface RecentQueryEntry {
id: string;
signal: SignalType;
source: string;
filter: Filter;
lastUsedAt: number;
}
export interface RecentQueriesStoreShape {
version: 1;
entries: RecentQueryEntry[];
}

View File

@@ -0,0 +1,24 @@
import type { SignalType } from 'types/api/v5/queryRange';
import { STORAGE_KEY_PREFIX } from './constants';
export function normalizeSource(source: string | undefined): string {
return source ?? '';
}
export function bucketKey(signal: SignalType, source: string): string {
return `${signal}:${source}`;
}
export function storageKeyFor(signal: SignalType, source: string): string {
return `${STORAGE_KEY_PREFIX}:${bucketKey(signal, source)}`;
}
// Same (signal, source, normalized filter) ⇒ same id ⇒ upsert.
export function makeId(
signal: SignalType,
source: string,
normalizedFilter: string,
): string {
return `${signal}|${source}|${normalizedFilter}`;
}

View File

@@ -41,6 +41,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
import { saveRecentQuery } from 'lib/recentQueries/saveRecentQuery';
import { replaceIncorrectObjectFields } from 'lib/replaceIncorrectObjectFields';
import { cloneDeep, get, isEqual, set } from 'lodash-es';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
@@ -1031,6 +1032,8 @@ export function QueryBuilderProvider({
if (isExplorer) {
setCalledFromHandleRunQuery(true);
}
saveRecentQuery(currentQuery);
const currentQueryData = {
...currentQuery,
builder: {

View File

@@ -2,6 +2,7 @@ import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
@@ -9,6 +10,7 @@ dayjs.extend(utc);
dayjs.extend(customParseFormat);
dayjs.extend(duration);
dayjs.extend(timezone);
dayjs.extend(relativeTime);
export function toUTCEpoch(time: number): number {
const x = new Date();

View File

@@ -10,25 +10,6 @@ import (
)
func (provider *provider) addTraceDetailRoutes(router *mux.Router) error {
if err := router.Handle("/api/v3/traces/{traceID}/waterfall", handler.New(
provider.authzMiddleware.ViewAccess(provider.traceDetailHandler.GetWaterfall),
handler.OpenAPIDef{
ID: "GetWaterfall",
Tags: []string{"tracedetail"},
Summary: "Get waterfall view for a trace",
Description: "Returns the waterfall view of spans for a given trace ID with tree structure, metadata, and windowed pagination",
Request: new(spantypes.PostableWaterfall),
RequestContentType: "application/json",
Response: new(spantypes.GettableWaterfallTrace),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
},
)).Methods(http.MethodPost).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v4/traces/{traceID}/waterfall", handler.New(
provider.authzMiddleware.ViewAccess(provider.traceDetailHandler.GetWaterfallV4),
handler.OpenAPIDef{

View File

@@ -59,7 +59,7 @@ func (a *AuthN) LoginURL(ctx context.Context, siteURL *url.URL, authDomain *auth
return "", err
}
if authDomain.AuthDomainConfig().AuthNProvider != authtypes.AuthNProviderGoogleAuth {
if authDomain.AuthDomainConfig().Provider.Type != authtypes.AuthNProviderGoogleAuth {
return "", errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthDomainMismatch, "domain type is not google")
}
@@ -111,7 +111,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "google: no id_token in token response")
}
verifier := oidcProvider.Verifier(&oidc.Config{ClientID: authDomain.AuthDomainConfig().Google.ClientID})
verifier := oidcProvider.Verifier(&oidc.Config{ClientID: authDomain.AuthDomainConfig().Google().ClientID})
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
a.settings.Logger().ErrorContext(ctx, "google: failed to verify token", errors.Attr(err))
@@ -135,7 +135,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
return nil, errors.Newf(errors.TypeForbidden, errors.CodeForbidden, "google: unexpected hd claim")
}
if !authDomain.AuthDomainConfig().Google.InsecureSkipEmailVerified {
if !authDomain.AuthDomainConfig().Google().InsecureSkipEmailVerified {
if !claims.EmailVerified {
a.settings.Logger().ErrorContext(ctx, "google: email is not verified", slog.String("email", claims.Email))
return nil, errors.Newf(errors.TypeForbidden, errors.CodeForbidden, "google: email is not verified")
@@ -148,14 +148,14 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
}
var groups []string
if authDomain.AuthDomainConfig().Google.FetchGroups {
groups, err = a.fetchGoogleWorkspaceGroups(ctx, claims.Email, authDomain.AuthDomainConfig().Google)
if authDomain.AuthDomainConfig().Google().FetchGroups {
groups, err = a.fetchGoogleWorkspaceGroups(ctx, claims.Email, authDomain.AuthDomainConfig().Google())
if err != nil {
a.settings.Logger().ErrorContext(ctx, "google: could not fetch groups", errors.Attr(err))
return nil, errors.Newf(errors.TypeInternal, errors.CodeInternal, "google: could not fetch groups").WithAdditional(err.Error())
}
allowedGroups := authDomain.AuthDomainConfig().Google.AllowedGroups
allowedGroups := authDomain.AuthDomainConfig().Google().AllowedGroups
if len(allowedGroups) > 0 {
groups = filterGroups(groups, allowedGroups)
if len(groups) == 0 {
@@ -175,8 +175,8 @@ func (a *AuthN) ProviderInfo(ctx context.Context, authDomain *authtypes.AuthDoma
func (a *AuthN) oauth2Config(siteURL *url.URL, authDomain *authtypes.AuthDomain, provider *oidc.Provider) *oauth2.Config {
return &oauth2.Config{
ClientID: authDomain.AuthDomainConfig().Google.ClientID,
ClientSecret: authDomain.AuthDomainConfig().Google.ClientSecret,
ClientID: authDomain.AuthDomainConfig().Google().ClientID,
ClientSecret: authDomain.AuthDomainConfig().Google().ClientSecret,
Endpoint: provider.Endpoint(),
Scopes: scopes,
RedirectURL: (&url.URL{

View File

@@ -38,7 +38,7 @@ func (handler *handler) Create(rw http.ResponseWriter, req *http.Request) {
return
}
authDomain, err := authtypes.NewAuthDomainFromConfig(body.Name, &body.Config, valuer.MustNewUUID(claims.OrgID))
authDomain, err := authtypes.NewAuthDomainFromConfig(body.Name, &body.AuthDomainConfig, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, err)
return
@@ -154,7 +154,7 @@ func (handler *handler) Update(rw http.ResponseWriter, r *http.Request) {
return
}
err = authDomain.Update(&body.Config)
err = authDomain.Update(&body.AuthDomainConfig)
if err != nil {
render.Error(rw, err)
return

View File

@@ -27,7 +27,7 @@ func (module *module) Get(ctx context.Context, id valuer.UUID) (*authtypes.AuthD
}
func (module *module) GetAuthNProviderInfo(ctx context.Context, domain *authtypes.AuthDomain) *authtypes.AuthNProviderInfo {
if callbackAuthN, ok := module.authNs[domain.AuthDomainConfig().AuthNProvider].(authn.CallbackAuthN); ok {
if callbackAuthN, ok := module.authNs[domain.AuthDomainConfig().Provider.Type].(authn.CallbackAuthN); ok {
return callbackAuthN.ProviderInfo(ctx, domain)
}
return &authtypes.AuthNProviderInfo{}
@@ -62,7 +62,7 @@ func (module *module) Collect(ctx context.Context, orgID valuer.UUID) (map[strin
stats := make(map[string]any)
for _, domain := range domains {
key := "authdomain." + domain.AuthDomainConfig().AuthNProvider.StringValue() + ".count"
key := "authdomain." + domain.AuthDomainConfig().Provider.Type.StringValue() + ".count"
if value, ok := stats[key]; ok {
stats[key] = value.(int64) + 1
} else {

View File

@@ -22,7 +22,7 @@ func newConfig() factory.Config {
Agent: AgentConfig{
// we will maintain the latest version of cloud integration agent from here,
// till we automate it externally or figure out a way to validate it.
Version: "v0.0.10",
Version: "v0.0.11",
},
}
}

View File

@@ -201,7 +201,7 @@ func (module *module) getOrgSessionContext(ctx context.Context, org *types.Organ
return authtypes.NewOrgSessionContext(org.ID, org.Name).AddPasswordAuthNSupport(authtypes.AuthNProviderEmailPassword), nil
}
provider, err := getProvider[authn.CallbackAuthN](authDomain.AuthDomainConfig().AuthNProvider, module.authNs)
provider, err := getProvider[authn.CallbackAuthN](authDomain.AuthDomainConfig().Provider.Type, module.authNs)
if err != nil {
return nil, err
}
@@ -211,7 +211,7 @@ func (module *module) getOrgSessionContext(ctx context.Context, org *types.Organ
return nil, err
}
return authtypes.NewOrgSessionContext(org.ID, org.Name).AddCallbackAuthNSupport(authDomain.AuthDomainConfig().AuthNProvider, loginURL), nil
return authtypes.NewOrgSessionContext(org.ID, org.Name).AddCallbackAuthNSupport(authDomain.AuthDomainConfig().Provider.Type, loginURL), nil
}
func getProvider[T authn.AuthN](authNProvider authtypes.AuthNProvider, authNs map[authtypes.AuthNProvider]authn.AuthN) (T, error) {

View File

@@ -18,27 +18,6 @@ func NewHandler(module tracedetail.Module) tracedetail.Handler {
return &handler{module: module}
}
func (h *handler) GetWaterfall(rw http.ResponseWriter, r *http.Request) {
req := new(spantypes.PostableWaterfall)
if err := binding.JSON.BindBody(r.Body, req); err != nil {
render.Error(rw, err)
return
}
if err := req.Validate(); err != nil {
render.Error(rw, err)
return
}
result, err := h.module.GetWaterfall(r.Context(), mux.Vars(r)["traceID"], req)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, result)
}
func (h *handler) GetWaterfallV4(rw http.ResponseWriter, r *http.Request) {
req := new(spantypes.PostableWaterfall)
if err := binding.JSON.BindBody(r.Body, req); err != nil {
@@ -51,7 +30,7 @@ func (h *handler) GetWaterfallV4(rw http.ResponseWriter, r *http.Request) {
return
}
result, err := h.module.GetWaterfallV4(r.Context(), mux.Vars(r)["traceID"], req.SelectedSpanID, req.UncollapsedSpans, req.Limit)
result, err := h.module.GetWaterfallV4(r.Context(), mux.Vars(r)["traceID"], req.SelectedSpanID, req.UncollapsedSpans)
if err != nil {
render.Error(rw, err)
return

View File

@@ -39,62 +39,16 @@ func NewModule(traceStore spantypes.TraceStore, providerSettings factory.Provide
return m
}
func (m *module) GetWaterfall(ctx context.Context, traceID string, req *spantypes.PostableWaterfall) (*spantypes.GettableWaterfallTrace, error) {
waterfallTrace, err := m.getTraceData(ctx, traceID)
if err != nil {
return nil, err
}
selectedSpans, uncollapsedSpans, selectedAllSpans := waterfallTrace.GetWaterfallSpans(
req.UncollapsedSpans,
req.SelectedSpanID,
min(req.Limit, m.config.Waterfall.MaxLimitToSelectAllSpans),
m.config.Waterfall.SpanPageSize,
m.config.Waterfall.MaxDepthToAutoExpand,
)
aggregationResults := make([]spantypes.SpanAggregationResult, 0, len(req.Aggregations))
for _, a := range req.Aggregations {
aggregationResults = append(aggregationResults, waterfallTrace.GetSpanAggregation(a.Aggregation, a.Field))
}
return spantypes.NewGettableWaterfallTrace(waterfallTrace, selectedSpans, uncollapsedSpans, selectedAllSpans, aggregationResults), nil
}
// getTraceData fetches all spans for a trace and builds the WaterfallTrace.
func (m *module) getTraceData(ctx context.Context, traceID string) (*spantypes.WaterfallTrace, error) {
summary, err := m.store.GetTraceSummary(ctx, traceID)
if err != nil {
return nil, err
}
spanItems, err := m.store.GetTraceSpans(ctx, traceID, summary)
if err != nil {
return nil, err
}
if len(spanItems) == 0 {
return nil, spantypes.ErrTraceNotFound
}
nodes := make([]*spantypes.WaterfallSpan, len(spanItems))
for i := range spanItems {
nodes[i] = spanItems[i].ToWaterfallSpan(traceID)
}
return spantypes.NewWaterfallTraceFromSpans(nodes), nil
}
// GetWaterfallV4 is the OOM-safe V4 waterfall.
// For large traces (NumSpans > effectiveLimit) it uses a two-step fetch:
// minimal fields for all spans to build the tree, then full fields for the
// visible window only. Aggregations are not returned.
func (m *module) GetWaterfallV4(ctx context.Context, traceID string, selectedSpanID string, uncollapsedSpans []string, selectAllLimit uint) (*spantypes.GettableWaterfallTrace, error) {
func (m *module) GetWaterfallV4(ctx context.Context, traceID string, selectedSpanID string, uncollapsedSpans []string) (*spantypes.GettableWaterfallTrace, error) {
summary, err := m.store.GetTraceSummary(ctx, traceID)
if err != nil {
return nil, err
}
effectiveLimit := min(selectAllLimit, m.config.Waterfall.MaxLimitToSelectAllSpans)
if summary.NumSpans > uint64(effectiveLimit) {
if summary.NumSpans > uint64(m.config.Waterfall.MaxLimitToSelectAllSpans) {
attrs := metric.WithAttributes(attrResponseType.String(attrResponseTypeWindowed))
m.metrics.waterfallRequestCount.Add(ctx, 1, attrs)
m.metrics.waterfallSpanCount.Add(ctx, int64(summary.NumSpans), attrs)
@@ -120,7 +74,7 @@ func (m *module) getFullWaterfall(ctx context.Context, traceID string, summary *
waterfallTrace := spantypes.NewWaterfallTraceFromSpans(nodes)
selectedSpans := waterfallTrace.GetAllSpans()
return spantypes.NewGettableWaterfallTrace(waterfallTrace, selectedSpans, nil, true, nil), nil
return spantypes.NewGettableWaterfallTrace(waterfallTrace, selectedSpans, nil, true), nil
}
func (m *module) GetTraceAggregations(ctx context.Context, traceID string, req *spantypes.PostableTraceAggregations) (*spantypes.GettableTraceAggregations, error) {
@@ -215,7 +169,7 @@ func (m *module) getWindowedWaterfall(ctx context.Context, traceID, selectedSpan
spantypes.EnrichSelectedSpans(selectedSpans, fullSpans)
return spantypes.NewGettableWaterfallTrace(
waterfallTrace, selectedSpans, uncollapsedSpans, false, nil,
waterfallTrace, selectedSpans, uncollapsedSpans, false,
), nil
}

View File

@@ -260,7 +260,7 @@ func TestGetSelectedSpans_MultipleRoots(t *testing.T) {
trace := getWaterfallTrace([]*spantypes.WaterfallSpan{root1, root2}, spanMap)
spans, _ := trace.GetSelectedSpans([]string{"root1", "root2"}, "root1", 500, 5)
traceRespnose := spantypes.NewGettableWaterfallTrace(trace, spans, nil, false, nil)
traceRespnose := spantypes.NewGettableWaterfallTrace(trace, spans, nil, false)
assert.Equal(t, []string{"root1", "child1", "root2", "child2"}, spanIDs(spans), "root1 subtree must precede root2 subtree")
assert.Equal(t, "svc-a", traceRespnose.RootServiceName, "metadata comes from first root")
@@ -567,7 +567,7 @@ func TestGetAllSpans(t *testing.T) {
)
trace := getWaterfallTrace([]*spantypes.WaterfallSpan{root}, nil)
spans := trace.GetAllSpans()
traceResponse := spantypes.NewGettableWaterfallTrace(trace, spans, nil, true, nil)
traceResponse := spantypes.NewGettableWaterfallTrace(trace, spans, nil, true)
assert.ElementsMatch(t, spanIDs(spans), []string{"root", "childA", "grandchildA", "leafA", "childB", "grandchildB", "leafB"})
assert.Equal(t, "svc", traceResponse.RootServiceName)
assert.Equal(t, "root-op", traceResponse.RootServiceEntryPoint)

View File

@@ -10,7 +10,6 @@ import (
// Handler exposes HTTP handlers for trace detail APIs.
type Handler interface {
GetWaterfall(http.ResponseWriter, *http.Request)
GetWaterfallV4(http.ResponseWriter, *http.Request)
GetTraceAggregations(http.ResponseWriter, *http.Request)
GetFlamegraph(http.ResponseWriter, *http.Request)
@@ -18,8 +17,7 @@ type Handler interface {
// Module defines the business logic for trace detail operations.
type Module interface {
GetWaterfall(ctx context.Context, traceID string, req *spantypes.PostableWaterfall) (*spantypes.GettableWaterfallTrace, error)
GetWaterfallV4(ctx context.Context, traceID string, selectedSpanID string, uncollapsedSpans []string, selectAllLimit uint) (*spantypes.GettableWaterfallTrace, error)
GetWaterfallV4(ctx context.Context, traceID string, selectedSpanID string, uncollapsedSpans []string) (*spantypes.GettableWaterfallTrace, error)
GetTraceAggregations(ctx context.Context, traceID string, req *spantypes.PostableTraceAggregations) (*spantypes.GettableTraceAggregations, error)
GetFlamegraph(ctx context.Context, traceID string, selectedSpanID string, selectFields []telemetrytypes.TelemetryFieldKey) (*spantypes.GettableFlamegraphTrace, error)
}

View File

@@ -898,223 +898,6 @@ func (r *ClickHouseReader) GetSpansForTrace(ctx context.Context, traceID string,
return searchScanResponses, nil
}
func (r *ClickHouseReader) GetWaterfallSpansForTraceWithMetadataCache(ctx context.Context, orgID valuer.UUID, traceID string) (*model.GetWaterfallSpansForTraceWithMetadataCache, error) {
cachedTraceData := new(model.GetWaterfallSpansForTraceWithMetadataCache)
err := r.cacheForTraceDetail.Get(ctx, orgID, strings.Join([]string{"getWaterfallSpansForTraceWithMetadata", traceID}, "-"), cachedTraceData)
if err != nil {
r.logger.Debug("error in retrieving getWaterfallSpansForTraceWithMetadata cache", errorsV2.Attr(err), "traceID", traceID)
return nil, err
}
if time.Since(time.UnixMilli(int64(cachedTraceData.EndTime))) < r.fluxIntervalForTraceDetail {
r.logger.Info("the trace end time falls under the flux interval, skipping getWaterfallSpansForTraceWithMetadata cache", "traceID", traceID)
return nil, errors.Errorf("the trace end time falls under the flux interval, skipping getWaterfallSpansForTraceWithMetadata cache, traceID: %s", traceID)
}
r.logger.Info("cache is successfully hit, applying cache for getWaterfallSpansForTraceWithMetadata", "traceID", traceID)
return cachedTraceData, nil
}
func (r *ClickHouseReader) GetWaterfallSpansForTraceWithMetadata(ctx context.Context, orgID valuer.UUID, traceID string, req *model.GetWaterfallSpansForTraceWithMetadataParams) (*model.GetWaterfallSpansForTraceWithMetadataResponse, error) {
response := new(model.GetWaterfallSpansForTraceWithMetadataResponse)
var startTime, endTime, durationNano, totalErrorSpans, totalSpans uint64
var spanIdToSpanNodeMap = map[string]*model.Span{}
var traceRoots []*model.Span
var serviceNameToTotalDurationMap = map[string]uint64{}
var serviceNameIntervalMap = map[string][]tracedetail.Interval{}
var hasMissingSpans bool
cachedTraceData, err := r.GetWaterfallSpansForTraceWithMetadataCache(ctx, orgID, traceID)
if err == nil {
startTime = cachedTraceData.StartTime
endTime = cachedTraceData.EndTime
durationNano = cachedTraceData.DurationNano
spanIdToSpanNodeMap = cachedTraceData.SpanIdToSpanNodeMap
serviceNameToTotalDurationMap = cachedTraceData.ServiceNameToTotalDurationMap
traceRoots = cachedTraceData.TraceRoots
totalSpans = cachedTraceData.TotalSpans
totalErrorSpans = cachedTraceData.TotalErrorSpans
hasMissingSpans = cachedTraceData.HasMissingSpans
}
if err != nil {
r.logger.Info("cache miss for getWaterfallSpansForTraceWithMetadata", "traceID", traceID)
searchScanResponses, err := r.GetSpansForTrace(ctx, traceID, fmt.Sprintf("SELECT DISTINCT ON (span_id) timestamp, duration_nano, span_id, trace_id, has_error, kind, resource_string_service$$name, name, links as references, attributes_string, attributes_number, attributes_bool, resources_string, events, status_message, status_code_string, kind_string FROM %s.%s WHERE trace_id=$1 and ts_bucket_start>=$2 and ts_bucket_start<=$3 ORDER BY timestamp ASC, name ASC", r.TraceDB, r.traceTableName))
if err != nil {
return nil, err
}
if len(searchScanResponses) == 0 {
return response, nil
}
totalSpans = uint64(len(searchScanResponses))
for _, item := range searchScanResponses {
ref := []model.OtelSpanRef{}
err := json.Unmarshal([]byte(item.References), &ref)
if err != nil {
r.logger.Error("getWaterfallSpansForTraceWithMetadata: error unmarshalling references", errorsV2.Attr(err), "traceID", traceID)
return nil, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, "getWaterfallSpansForTraceWithMetadata: error unmarshalling references %s", err.Error())
}
// merge attributes_number and attributes_bool to attributes_string
for k, v := range item.Attributes_bool {
item.Attributes_string[k] = fmt.Sprintf("%v", v)
}
for k, v := range item.Attributes_number {
item.Attributes_string[k] = strconv.FormatFloat(v, 'f', -1, 64)
}
for k, v := range item.Resources_string {
item.Attributes_string[k] = v
}
events := make([]model.Event, 0)
for _, event := range item.Events {
var eventMap model.Event
err = json.Unmarshal([]byte(event), &eventMap)
if err != nil {
r.logger.Error("Error unmarshalling events", errorsV2.Attr(err))
return nil, errorsV2.Newf(errorsV2.TypeInternal, errorsV2.CodeInternal, "getWaterfallSpansForTraceWithMetadata: error in unmarshalling events %s", err.Error())
}
events = append(events, eventMap)
}
startTimeUnixNano := uint64(item.TimeUnixNano.UnixNano())
jsonItem := model.Span{
SpanID: item.SpanID,
TraceID: item.TraceID,
ServiceName: item.ServiceName,
Name: item.Name,
Kind: int32(item.Kind),
DurationNano: item.DurationNano,
HasError: item.HasError,
StatusMessage: item.StatusMessage,
StatusCodeString: item.StatusCodeString,
SpanKind: item.SpanKind,
References: ref,
Events: events,
TagMap: item.Attributes_string,
Children: make([]*model.Span, 0),
TimeUnixNano: startTimeUnixNano, // Store nanoseconds temporarily
}
// metadata calculation
if startTime == 0 || startTimeUnixNano < startTime {
startTime = startTimeUnixNano
}
if endTime == 0 || (startTimeUnixNano+jsonItem.DurationNano) > endTime {
endTime = (startTimeUnixNano + jsonItem.DurationNano)
}
if durationNano == 0 || jsonItem.DurationNano > durationNano {
durationNano = jsonItem.DurationNano
}
if jsonItem.HasError {
totalErrorSpans = totalErrorSpans + 1
}
// collect the intervals for service for execution time calculation
serviceNameIntervalMap[jsonItem.ServiceName] =
append(serviceNameIntervalMap[jsonItem.ServiceName], tracedetail.Interval{StartTime: jsonItem.TimeUnixNano, Duration: jsonItem.DurationNano, Service: jsonItem.ServiceName})
// append to the span node map
spanIdToSpanNodeMap[jsonItem.SpanID] = &jsonItem
}
// traverse through the map and append each node to the children array of the parent node
// and add the missing spans
for _, spanNode := range spanIdToSpanNodeMap {
hasParentSpanNode := false
for _, reference := range spanNode.References {
if reference.RefType == "CHILD_OF" && reference.SpanId != "" {
hasParentSpanNode = true
if parentNode, exists := spanIdToSpanNodeMap[reference.SpanId]; exists {
parentNode.Children = append(parentNode.Children, spanNode)
} else {
// insert the missing span
missingSpan := model.Span{
SpanID: reference.SpanId,
TraceID: spanNode.TraceID,
ServiceName: "",
Name: "Missing Span",
TimeUnixNano: spanNode.TimeUnixNano,
Kind: 0,
DurationNano: spanNode.DurationNano,
HasError: false,
StatusMessage: "",
StatusCodeString: "",
SpanKind: "",
Events: make([]model.Event, 0),
Children: make([]*model.Span, 0),
}
missingSpan.Children = append(missingSpan.Children, spanNode)
spanIdToSpanNodeMap[missingSpan.SpanID] = &missingSpan
traceRoots = append(traceRoots, &missingSpan)
hasMissingSpans = true
}
}
}
if !hasParentSpanNode && !tracedetail.ContainsWaterfallSpan(traceRoots, spanNode) {
traceRoots = append(traceRoots, spanNode)
}
}
// sort the trace roots to add missing spans at the right order
sort.Slice(traceRoots, func(i, j int) bool {
if traceRoots[i].TimeUnixNano == traceRoots[j].TimeUnixNano {
return traceRoots[i].Name < traceRoots[j].Name
}
return traceRoots[i].TimeUnixNano < traceRoots[j].TimeUnixNano
})
serviceNameToTotalDurationMap = tracedetail.CalculateServiceTime(serviceNameIntervalMap)
// TODO: set the span data (model.GetWaterfallSpansForTraceWithMetadataCache) in cache here
// removed existing cache usage since it was not getting used due to this bug https://github.com/SigNoz/engineering-pod/issues/4648
// and was causing out of memory issues https://github.com/SigNoz/engineering-pod/issues/4638
}
processingPostCache := time.Now()
// When req.Limit is 0 (not set by the client), selectAllSpans is set to false
// preserving the old paged behaviour for backward compatibility
limit := min(req.Limit, tracedetail.MaxLimitToSelectAllSpans)
selectAllSpans := totalSpans <= uint64(limit)
var (
selectedSpans []*model.Span
uncollapsedSpans []string
rootServiceName, rootServiceEntryPoint string
)
if selectAllSpans {
selectedSpans, rootServiceName, rootServiceEntryPoint = tracedetail.GetAllSpans(traceRoots)
} else {
selectedSpans, uncollapsedSpans, rootServiceName, rootServiceEntryPoint = tracedetail.GetSelectedSpans(req.UncollapsedSpans, req.SelectedSpanID, traceRoots, spanIdToSpanNodeMap, req.IsSelectedSpanIDUnCollapsed)
}
r.logger.Info("getWaterfallSpansForTraceWithMetadata: processing post cache", "duration", time.Since(processingPostCache), "traceID", traceID)
// convert start timestamp to millis because right now frontend is expecting it in millis
for _, span := range selectedSpans {
span.TimeUnixNano = span.TimeUnixNano / 1000000
}
for serviceName, totalDuration := range serviceNameToTotalDurationMap {
serviceNameToTotalDurationMap[serviceName] = totalDuration / 1000000
}
response.Spans = selectedSpans
response.UncollapsedSpans = uncollapsedSpans // ignoring if all spans are returning
response.StartTimestampMillis = startTime / 1000000
response.EndTimestampMillis = endTime / 1000000
response.TotalSpansCount = totalSpans
response.TotalErrorSpansCount = totalErrorSpans
response.RootServiceName = rootServiceName
response.RootServiceEntryPoint = rootServiceEntryPoint
response.ServiceNameToTotalDurationMap = serviceNameToTotalDurationMap
response.HasMissingSpans = hasMissingSpans
response.HasMore = !selectAllSpans
return response, nil
}
func (r *ClickHouseReader) GetFlamegraphSpansForTraceCache(ctx context.Context, orgID valuer.UUID, traceID string) (*model.GetFlamegraphSpansForTraceCache, error) {
cachedTraceData := new(model.GetFlamegraphSpansForTraceCache)

View File

@@ -535,7 +535,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) {
router.HandleFunc("/api/v2/traces/fields", am.ViewAccess(aH.traceFields)).Methods(http.MethodGet)
router.HandleFunc("/api/v2/traces/fields", am.EditAccess(aH.updateTraceField)).Methods(http.MethodPost)
router.HandleFunc("/api/v2/traces/flamegraph/{traceId}", am.ViewAccess(aH.GetFlamegraphSpansForTrace)).Methods(http.MethodPost)
router.HandleFunc("/api/v2/traces/waterfall/{traceId}", am.ViewAccess(aH.GetWaterfallSpansForTraceWithMetadata)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/version", am.OpenAccess(aH.getVersion)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/features", am.ViewAccess(aH.getFeatureFlags)).Methods(http.MethodGet)
@@ -1446,39 +1446,6 @@ func (aH *APIHandler) SearchTraces(w http.ResponseWriter, r *http.Request) {
}
func (aH *APIHandler) GetWaterfallSpansForTraceWithMetadata(w http.ResponseWriter, r *http.Request) {
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {
render.Error(w, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(w, err)
return
}
traceID := mux.Vars(r)["traceId"]
if traceID == "" {
render.Error(w, errors.NewInvalidInputf(errors.CodeInvalidInput, "traceID is required"))
return
}
req := new(model.GetWaterfallSpansForTraceWithMetadataParams)
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
RespondError(w, model.BadRequest(err), nil)
return
}
result, apiErr := aH.reader.GetWaterfallSpansForTraceWithMetadata(r.Context(), orgID, traceID, req)
if apiErr != nil {
render.Error(w, apiErr)
return
}
aH.WriteJSON(w, r, result)
}
func (aH *APIHandler) GetFlamegraphSpansForTrace(w http.ResponseWriter, r *http.Request) {
claims, err := authtypes.ClaimsFromContext(r.Context())
if err != nil {

View File

@@ -1,287 +0,0 @@
package tracedetail
import (
"maps"
"slices"
"sort"
"github.com/SigNoz/signoz/pkg/query-service/model"
)
var (
SPAN_LIMIT_PER_REQUEST_FOR_WATERFALL float64 = 500
maxDepthForSelectedSpanChildren int = 5
MaxLimitToSelectAllSpans uint = 10_000
)
type Interval struct {
StartTime uint64
Duration uint64
Service string
}
func mergeIntervals(intervals []Interval) []Interval {
if len(intervals) == 0 {
return nil
}
var merged []Interval
current := intervals[0]
for i := 1; i < len(intervals); i++ {
next := intervals[i]
if current.StartTime+current.Duration >= next.StartTime {
endTime := max(current.StartTime+current.Duration, next.StartTime+next.Duration)
current.Duration = endTime - current.StartTime
} else {
merged = append(merged, current)
current = next
}
}
// Add the last interval
merged = append(merged, current)
return merged
}
func ContainsWaterfallSpan(slice []*model.Span, item *model.Span) bool {
for _, v := range slice {
if v.SpanID == item.SpanID {
return true
}
}
return false
}
func findIndexForSelectedSpanFromPreOrder(spans []*model.Span, selectedSpanId string) int {
var selectedSpanIndex = -1
for index, span := range spans {
if span.SpanID == selectedSpanId {
selectedSpanIndex = index
break
}
}
return selectedSpanIndex
}
func getPathFromRootToSelectedSpanId(node *model.Span, selectedSpanId string) (bool, []string) {
spansFromRootToNode := []string{}
spansFromRootToNode = append(spansFromRootToNode, node.SpanID)
if node.SpanID == selectedSpanId {
return true, spansFromRootToNode
}
isPresentInSubtreeForTheNode := false
for _, child := range node.Children {
isPresentInThisSubtree, _spansFromRootToNode := getPathFromRootToSelectedSpanId(child, selectedSpanId)
// if the interested node is present in the given subtree then add the span node to uncollapsed node list
if isPresentInThisSubtree {
isPresentInSubtreeForTheNode = true
spansFromRootToNode = append(spansFromRootToNode, _spansFromRootToNode...)
break
}
}
return isPresentInSubtreeForTheNode, spansFromRootToNode
}
// traverseOpts holds the traversal configuration that remains constant
// throughout the recursion. Per-call state (level, isPartOfPreOrder, etc.)
// is passed as direct arguments.
type traverseOpts struct {
uncollapsedSpans map[string]struct{}
selectedSpanID string
isSelectedSpanUncollapsed bool
selectAll bool
}
func traverseTrace(
span *model.Span,
opts traverseOpts,
level uint64,
isPartOfPreOrder bool,
hasSibling bool,
autoExpandDepth int,
) ([]*model.Span, []string) {
preOrderTraversal := []*model.Span{}
autoExpandedSpans := []string{}
// sort the children to maintain the order across requests
sort.Slice(span.Children, func(i, j int) bool {
if span.Children[i].TimeUnixNano == span.Children[j].TimeUnixNano {
return span.Children[i].Name < span.Children[j].Name
}
return span.Children[i].TimeUnixNano < span.Children[j].TimeUnixNano
})
span.SubTreeNodeCount = 0
nodeWithoutChildren := model.Span{
SpanID: span.SpanID,
TraceID: span.TraceID,
ServiceName: span.ServiceName,
TimeUnixNano: span.TimeUnixNano,
Name: span.Name,
Kind: int32(span.Kind),
DurationNano: span.DurationNano,
HasError: span.HasError,
StatusMessage: span.StatusMessage,
StatusCodeString: span.StatusCodeString,
SpanKind: span.SpanKind,
References: span.References,
Events: span.Events,
TagMap: span.TagMap,
Children: make([]*model.Span, 0),
HasChildren: len(span.Children) > 0,
Level: level,
HasSiblings: hasSibling,
SubTreeNodeCount: 0,
}
if isPartOfPreOrder {
preOrderTraversal = append(preOrderTraversal, &nodeWithoutChildren)
}
remainingAutoExpandDepth := 0
if span.SpanID == opts.selectedSpanID && opts.isSelectedSpanUncollapsed {
remainingAutoExpandDepth = maxDepthForSelectedSpanChildren
} else if autoExpandDepth > 0 {
remainingAutoExpandDepth = autoExpandDepth - 1
}
_, isAlreadyUncollapsed := opts.uncollapsedSpans[span.SpanID]
for index, child := range span.Children {
// A child is included in the pre-order output if its parent is uncollapsed
// OR if the child falls within MAX_DEPTH_FOR_SELECTED_SPAN_CHILDREN levels
// below the selected span.
isChildWithinMaxDepth := remainingAutoExpandDepth > 0
childIsPartOfPreOrder := opts.selectAll || (isPartOfPreOrder && (isAlreadyUncollapsed || isChildWithinMaxDepth))
if isPartOfPreOrder && isChildWithinMaxDepth && !isAlreadyUncollapsed {
if !slices.Contains(autoExpandedSpans, span.SpanID) {
autoExpandedSpans = append(autoExpandedSpans, span.SpanID)
}
}
_childTraversal, _autoExpanded := traverseTrace(child, opts, level+1, childIsPartOfPreOrder, index != (len(span.Children)-1), remainingAutoExpandDepth)
preOrderTraversal = append(preOrderTraversal, _childTraversal...)
autoExpandedSpans = append(autoExpandedSpans, _autoExpanded...)
nodeWithoutChildren.SubTreeNodeCount += child.SubTreeNodeCount + 1
span.SubTreeNodeCount += child.SubTreeNodeCount + 1
}
nodeWithoutChildren.SubTreeNodeCount += 1
return preOrderTraversal, autoExpandedSpans
}
func CalculateServiceTime(serviceIntervals map[string][]Interval) map[string]uint64 {
totalTimes := make(map[string]uint64)
for service, serviceIntervals := range serviceIntervals {
sort.Slice(serviceIntervals, func(i, j int) bool {
return serviceIntervals[i].StartTime < serviceIntervals[j].StartTime
})
mergedIntervals := mergeIntervals(serviceIntervals)
totalTime := uint64(0)
for _, interval := range mergedIntervals {
totalTime += interval.Duration
}
totalTimes[service] = totalTime
}
return totalTimes
}
func GetSelectedSpans(uncollapsedSpans []string, selectedSpanID string, traceRoots []*model.Span, spanIdToSpanNodeMap map[string]*model.Span, isSelectedSpanIDUnCollapsed bool) ([]*model.Span, []string, string, string) {
var preOrderTraversal = make([]*model.Span, 0)
var rootServiceName, rootServiceEntryPoint string
// create a map of uncollapsed spans for quick lookup
uncollapsedSpanMap := make(map[string]struct{})
for _, spanID := range uncollapsedSpans {
uncollapsedSpanMap[spanID] = struct{}{}
}
selectedSpanIndex := -1
for _, rootSpanID := range traceRoots {
if rootNode, exists := spanIdToSpanNodeMap[rootSpanID.SpanID]; exists {
present, spansFromRootToNode := getPathFromRootToSelectedSpanId(rootNode, selectedSpanID)
if present {
for _, spanID := range spansFromRootToNode {
if selectedSpanID == spanID && !isSelectedSpanIDUnCollapsed {
continue
}
uncollapsedSpanMap[spanID] = struct{}{}
}
}
opts := traverseOpts{
uncollapsedSpans: uncollapsedSpanMap,
selectedSpanID: selectedSpanID,
isSelectedSpanUncollapsed: isSelectedSpanIDUnCollapsed,
}
_preOrderTraversal, _autoExpanded := traverseTrace(rootNode, opts, 0, true, false, 0)
// Merge auto-expanded spans into updatedUncollapsedSpans for returning in response
for _, spanID := range _autoExpanded {
uncollapsedSpanMap[spanID] = struct{}{}
}
_selectedSpanIndex := findIndexForSelectedSpanFromPreOrder(_preOrderTraversal, selectedSpanID)
if _selectedSpanIndex != -1 {
selectedSpanIndex = _selectedSpanIndex + len(preOrderTraversal)
}
preOrderTraversal = append(preOrderTraversal, _preOrderTraversal...)
if rootServiceName == "" {
rootServiceName = rootNode.ServiceName
}
if rootServiceEntryPoint == "" {
rootServiceEntryPoint = rootNode.Name
}
}
}
// if we couldn't find the selectedSpan in the trace then defaulting the selected index to 0
if selectedSpanIndex == -1 && selectedSpanID != "" {
selectedSpanIndex = 0
}
// get the 0.4*[span limit] before the interested span index
startIndex := selectedSpanIndex - int(SPAN_LIMIT_PER_REQUEST_FOR_WATERFALL*0.4)
// get the 0.6*[span limit] after the intrested span index
endIndex := selectedSpanIndex + int(SPAN_LIMIT_PER_REQUEST_FOR_WATERFALL*0.6)
// adjust the sliding window according to the available left and right spaces.
if startIndex < 0 {
endIndex = endIndex - startIndex
startIndex = 0
}
if endIndex > len(preOrderTraversal) {
startIndex = startIndex - (endIndex - len(preOrderTraversal))
endIndex = len(preOrderTraversal)
}
if startIndex < 0 {
startIndex = 0
}
return preOrderTraversal[startIndex:endIndex], slices.Collect(maps.Keys(uncollapsedSpanMap)), rootServiceName, rootServiceEntryPoint
}
func GetAllSpans(traceRoots []*model.Span) (spans []*model.Span, rootServiceName, rootEntryPoint string) {
if len(traceRoots) > 0 {
rootServiceName = traceRoots[0].ServiceName
rootEntryPoint = traceRoots[0].Name
}
for _, root := range traceRoots {
childSpans, _ := traverseTrace(root, traverseOpts{selectAll: true}, 0, true, false, 0)
spans = append(spans, childSpans...)
}
return
}

View File

@@ -1,446 +0,0 @@
// Package tracedetail tests — waterfall
//
// # Background
//
// The waterfall view renders a trace as a scrollable list of spans in
// pre-order (parent before children, siblings left-to-right). Because a trace
// can have thousands of spans, only a window of ~500 is returned per request.
// The window is centred on the selected span.
//
// # Key concepts
//
// uncollapsedSpans
//
// The set of span IDs the user has manually expanded in the UI.
// Only the direct children of an uncollapsed span are included in the
// output; grandchildren stay hidden until their parent is also uncollapsed.
// When multiple spans are uncollapsed their children are all visible at once.
//
// selectedSpanID
//
// The span currently focused — set when the user clicks a span in the
// waterfall or selects one from the flamegraph. The output window is always
// centred on this span. The path from the trace root down to the selected
// span is automatically uncollapsed so ancestors are visible even if they are
// not in uncollapsedSpans.
//
// isSelectedSpanIDUnCollapsed
//
// Controls whether the selected span's own children are shown:
// true — user expanded the span (click-to-open in waterfall or flamegraph);
// direct children of the selected span are included.
// false — user selected without expanding;
// the span is visible but its children remain hidden.
//
// traceRoots
//
// Root spans of the trace — spans with no parent in the current dataset.
// Normally one, but multiple roots are common when upstream services are
// not instrumented or their spans were not sampled/exported.
package tracedetail
import (
"fmt"
"testing"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/stretchr/testify/assert"
)
// Pre-order traversal is preserved: parent before children, siblings left-to-right.
func TestGetSelectedSpans_PreOrderTraversal(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("child1", "svc", mkSpan("grandchild", "svc")),
mkSpan("child2", "svc"),
)
spanMap := buildSpanMap(root)
spans, _, _, _ := GetSelectedSpans([]string{"root", "child1"}, "root", []*model.Span{root}, spanMap, false)
assert.Equal(t, []string{"root", "child1", "grandchild", "child2"}, spanIDs(spans))
}
// Multiple roots: both trees are flattened into a single pre-order list with
// root1's subtree before root2's. Service/entry-point come from the first root.
//
// root1 svc-a ← selected
// └─ child1
// root2 svc-b
// └─ child2
//
// Expected output order: root1 → child1 → root2 → child2
func TestGetSelectedSpans_MultipleRoots(t *testing.T) {
root1 := mkSpan("root1", "svc-a", mkSpan("child1", "svc-a"))
root2 := mkSpan("root2", "svc-b", mkSpan("child2", "svc-b"))
spanMap := buildSpanMap(root1, root2)
spans, _, svcName, entryPoint := GetSelectedSpans([]string{"root1", "root2"}, "root1", []*model.Span{root1, root2}, spanMap, false)
assert.Equal(t, []string{"root1", "child1", "root2", "child2"}, spanIDs(spans), "root1 subtree must precede root2 subtree")
assert.Equal(t, "svc-a", svcName, "metadata comes from first root")
assert.Equal(t, "root1-op", entryPoint, "metadata comes from first root")
}
// Multiple spans uncollapsed simultaneously: children of all uncollapsed spans
// are visible at once.
//
// root
// ├─ childA (uncollapsed) → grandchildA ✓
// └─ childB (uncollapsed) → grandchildB ✓
func TestGetSelectedSpans_MultipleUncollapsed(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("childA", "svc", mkSpan("grandchildA", "svc")),
mkSpan("childB", "svc", mkSpan("grandchildB", "svc")),
)
spanMap := buildSpanMap(root)
spans, _, _, _ := GetSelectedSpans([]string{"root", "childA", "childB"}, "root", []*model.Span{root}, spanMap, false)
assert.Equal(t, []string{"root", "childA", "grandchildA", "childB", "grandchildB"}, spanIDs(spans))
}
// Collapsing a span with other uncollapsed spans
//
// root
// ├─ childA (previously expanded — in uncollapsedSpans)
// │ ├─ grandchild1 ✓
// │ │ └─ greatGrandchild ✗ (grandchild1 not in uncollapsedSpans)
// │ └─ grandchild2 ✓
// └─ childB ← selected (not expanded)
func TestGetSelectedSpans_ManualUncollapse(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("childA", "svc",
mkSpan("grandchild1", "svc", mkSpan("greatGrandchild", "svc")),
mkSpan("grandchild2", "svc"),
),
mkSpan("childB", "svc"),
)
spanMap := buildSpanMap(root)
// childA was expanded in a previous interaction; childB is now selected without expanding
spans, _, _, _ := GetSelectedSpans([]string{"childA"}, "childB", []*model.Span{root}, spanMap, false)
// path to childB auto-uncollpases root → childA and childB appear; childA is in
// uncollapsedSpans so its children appear; greatGrandchild stays hidden.
assert.Equal(t, []string{"root", "childA", "grandchild1", "grandchild2", "childB"}, spanIDs(spans))
}
// A collapsed span hides all children.
func TestGetSelectedSpans_CollapsedSpan(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("child1", "svc"),
mkSpan("child2", "svc"),
)
spanMap := buildSpanMap(root)
spans, _, _, _ := GetSelectedSpans([]string{}, "root", []*model.Span{root}, spanMap, false)
assert.Equal(t, []string{"root"}, spanIDs(spans))
}
// Selecting a span auto-uncollpases the path from root to that span so it is visible.
//
// root → parent → selected
func TestGetSelectedSpans_PathToSelectedIsUncollapsed(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("parent", "svc",
mkSpan("selected", "svc"),
),
)
spanMap := buildSpanMap(root)
// no manually uncollapsed spans — path should still be opened
spans, _, _, _ := GetSelectedSpans([]string{}, "selected", []*model.Span{root}, spanMap, false)
assert.Equal(t, []string{"root", "parent", "selected"}, spanIDs(spans))
}
// The path-to-selected spans are returned in updatedUncollapsedSpans.
func TestGetSelectedSpans_PathReturnedInUncollapsed(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("parent", "svc",
mkSpan("selected", "svc"),
),
)
spanMap := buildSpanMap(root)
spans, uncollapsed, _, _ := GetSelectedSpans([]string{}, "selected", []*model.Span{root}, spanMap, false)
assert.ElementsMatch(t, []string{"root", "parent"}, uncollapsed)
assert.Equal(t, []string{"root", "parent", "selected"}, spanIDs(spans))
}
// Siblings of ancestors are rendered as collapsed nodes but their subtrees
// must NOT be expanded.
//
// root
// ├─ unrelated → unrelated-child (✗)
// └─ parent → selected
func TestGetSelectedSpans_SiblingsNotExpanded(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("unrelated", "svc", mkSpan("unrelated-child", "svc")),
mkSpan("parent", "svc",
mkSpan("selected", "svc"),
),
)
spanMap := buildSpanMap(root)
spans, uncollapsed, _, _ := GetSelectedSpans([]string{}, "selected", []*model.Span{root}, spanMap, false)
// children of root sort alphabetically: parent < unrelated; unrelated-child stays hidden
assert.Equal(t, []string{"root", "parent", "selected", "unrelated"}, spanIDs(spans))
// only the path nodes are tracked as uncollapsed — unrelated is not
assert.ElementsMatch(t, []string{"root", "parent"}, uncollapsed)
}
// An unknown selectedSpanID must not panic; returns a window from index 0.
func TestGetSelectedSpans_UnknownSelectedSpan(t *testing.T) {
root := mkSpan("root", "svc", mkSpan("child", "svc"))
spanMap := buildSpanMap(root)
spans, _, _, _ := GetSelectedSpans([]string{}, "nonexistent", []*model.Span{root}, spanMap, false)
assert.Equal(t, []string{"root"}, spanIDs(spans))
}
// Test to check if Level, HasChildren, HasSiblings, and SubTreeNodeCount are populated correctly.
//
// root level=0, hasChildren=true, hasSiblings=false, subTree=4
// child1 level=1, hasChildren=true, hasSiblings=true, subTree=2
// grandchild level=2, hasChildren=false, hasSiblings=false, subTree=1
// child2 level=1, hasChildren=false, hasSiblings=false, subTree=1
func TestGetSelectedSpans_SpanMetadata(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("child1", "svc", mkSpan("grandchild", "svc")),
mkSpan("child2", "svc"),
)
spanMap := buildSpanMap(root)
spans, _, _, _ := GetSelectedSpans([]string{"root", "child1"}, "root", []*model.Span{root}, spanMap, false)
byID := map[string]*model.Span{}
for _, s := range spans {
byID[s.SpanID] = s
}
assert.Equal(t, uint64(0), byID["root"].Level)
assert.Equal(t, uint64(1), byID["child1"].Level)
assert.Equal(t, uint64(1), byID["child2"].Level)
assert.Equal(t, uint64(2), byID["grandchild"].Level)
assert.True(t, byID["root"].HasChildren)
assert.True(t, byID["child1"].HasChildren)
assert.False(t, byID["child2"].HasChildren)
assert.False(t, byID["grandchild"].HasChildren)
assert.False(t, byID["root"].HasSiblings, "root has no siblings")
assert.True(t, byID["child1"].HasSiblings, "child1 has sibling child2")
assert.False(t, byID["child2"].HasSiblings, "child2 is the last child")
assert.False(t, byID["grandchild"].HasSiblings, "grandchild has no siblings")
assert.Equal(t, uint64(4), byID["root"].SubTreeNodeCount)
assert.Equal(t, uint64(2), byID["child1"].SubTreeNodeCount)
assert.Equal(t, uint64(1), byID["grandchild"].SubTreeNodeCount)
assert.Equal(t, uint64(1), byID["child2"].SubTreeNodeCount)
}
// If the selected span is already in uncollapsedSpans AND isSelectedSpanIDUnCollapsed=true,
func TestGetSelectedSpans_DuplicateInUncollapsed(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("selected", "svc", mkSpan("child", "svc")),
)
spanMap := buildSpanMap(root)
_, uncollapsed, _, _ := GetSelectedSpans(
[]string{"selected"}, // already present
"selected",
[]*model.Span{root}, spanMap,
true,
)
count := 0
for _, id := range uncollapsed {
if id == "selected" {
count++
}
}
assert.Equal(t, 1, count, "should appear once")
}
// makeChain builds a linear trace: span0 → span1 → … → span(n-1).
// All span IDs are "span0", "span1", … so the caller can reference them by index.
func makeChain(n int) (*model.Span, map[string]*model.Span, []string) {
spans := make([]*model.Span, n)
for i := n - 1; i >= 0; i-- {
if i == n-1 {
spans[i] = mkSpan(fmt.Sprintf("span%d", i), "svc")
} else {
spans[i] = mkSpan(fmt.Sprintf("span%d", i), "svc", spans[i+1])
}
}
uncollapsed := make([]string, n)
for i := range spans {
uncollapsed[i] = fmt.Sprintf("span%d", i)
}
return spans[0], buildSpanMap(spans[0]), uncollapsed
}
// The selected span is centred: 200 spans before it, 300 after (0.4 / 0.6 split).
func TestGetSelectedSpans_WindowCentredOnSelected(t *testing.T) {
root, spanMap, uncollapsed := makeChain(600)
spans, _, _, _ := GetSelectedSpans(uncollapsed, "span300", []*model.Span{root}, spanMap, false)
assert.Equal(t, 500, len(spans), "window should be 500 spans")
// window is [100, 600): span300 lands at position 200 (300 - 100)
assert.Equal(t, "span100", spans[0].SpanID, "window starts 200 before selected")
assert.Equal(t, "span300", spans[200].SpanID, "selected span at position 200 in window")
assert.Equal(t, "span599", spans[499].SpanID, "window ends 300 after selected")
}
// When the selected span is near the start, the window shifts right so no
// negative index is used — the result is still 500 spans.
func TestGetSelectedSpans_WindowShiftsAtStart(t *testing.T) {
root, spanMap, uncollapsed := makeChain(600)
spans, _, _, _ := GetSelectedSpans(uncollapsed, "span10", []*model.Span{root}, spanMap, false)
assert.Equal(t, 500, len(spans))
assert.Equal(t, "span0", spans[0].SpanID, "window clamped to start of trace")
assert.Equal(t, "span10", spans[10].SpanID, "selected span still in window")
}
// Auto-expanded span IDs from ALL branches are returned in
// updatedUncollapsedSpans. Only internal nodes (spans with children) are
// tracked — leaf spans are never added.
//
// root (selected)
// ├─ childA (internal ✓)
// │ └─ grandchildA (internal ✓)
// │ └─ leafA (leaf ✗)
// └─ childB (internal ✓)
// └─ grandchildB (internal ✓)
// └─ leafB (leaf ✗)
func TestGetSelectedSpans_AutoExpandedSpansReturnedInUncollapsed(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("childA", "svc",
mkSpan("grandchildA", "svc",
mkSpan("leafA", "svc"),
),
),
mkSpan("childB", "svc",
mkSpan("grandchildB", "svc",
mkSpan("leafB", "svc"),
),
),
)
spanMap := buildSpanMap(root)
_, uncollapsed, _, _ := GetSelectedSpans([]string{}, "root", []*model.Span{root}, spanMap, true)
// all internal nodes across both branches must be tracked
assert.Contains(t, uncollapsed, "root")
assert.Contains(t, uncollapsed, "childA", "internal node depth 1, branch A")
assert.Contains(t, uncollapsed, "childB", "internal node depth 1, branch B")
assert.Contains(t, uncollapsed, "grandchildA", "internal node depth 2, branch A")
assert.Contains(t, uncollapsed, "grandchildB", "internal node depth 2, branch B")
// leaves have no children to show — never added to uncollapsedSpans
assert.NotContains(t, uncollapsed, "leafA", "leaf spans are never added to uncollapsedSpans")
assert.NotContains(t, uncollapsed, "leafB", "leaf spans are never added to uncollapsedSpans")
}
// ─────────────────────────────────────────────────────────────────────────────
// maxDepthForSelectedSpanChildren boundary tests
// ─────────────────────────────────────────────────────────────────────────────
// Depth is measured from the selected span, not the trace root.
// Ancestors appear via the path-to-root logic, not the depth limit.
// Each depth level has two children to confirm the limit is enforced on all
// branches, not just the first.
//
// root
// └─ A ancestor ✓ (path-to-root)
// └─ selected
// ├─ d1a depth 1 ✓
// │ ├─ d2a depth 2 ✓
// │ │ ├─ d3a depth 3 ✓
// │ │ │ ├─ d4a depth 4 ✓
// │ │ │ │ ├─ d5a depth 5 ✓
// │ │ │ │ │ └─ d6a depth 6 ✗
// │ │ │ │ └─ d5b depth 5 ✓
// │ │ │ └─ d4b depth 4 ✓
// │ │ └─ d3b depth 3 ✓
// │ └─ d2b depth 2 ✓
// └─ d1b depth 1 ✓
func TestGetSelectedSpans_DepthCountedFromSelectedSpan(t *testing.T) {
selected := mkSpan("selected", "svc",
mkSpan("d1a", "svc",
mkSpan("d2a", "svc",
mkSpan("d3a", "svc",
mkSpan("d4a", "svc",
mkSpan("d5a", "svc",
mkSpan("d6a", "svc"), // depth 6 — excluded
),
mkSpan("d5b", "svc"), // depth 5 — included
),
mkSpan("d4b", "svc"), // depth 4 — included
),
mkSpan("d3b", "svc"), // depth 3 — included
),
mkSpan("d2b", "svc"), // depth 2 — included
),
mkSpan("d1b", "svc"), // depth 1 — included
)
root := mkSpan("root", "svc", mkSpan("A", "svc", selected))
spanMap := buildSpanMap(root)
spans, _, _, _ := GetSelectedSpans([]string{}, "selected", []*model.Span{root}, spanMap, true)
ids := spanIDs(spans)
assert.Contains(t, ids, "root", "ancestor shown via path-to-root")
assert.Contains(t, ids, "A", "ancestor shown via path-to-root")
for _, id := range []string{"d1a", "d1b", "d2a", "d2b", "d3a", "d3b", "d4a", "d4b", "d5a", "d5b"} {
assert.Contains(t, ids, id, "depth ≤ 5 — must be included")
}
assert.NotContains(t, ids, "d6a", "depth 6 > limit — excluded")
}
func TestGetAllSpans(t *testing.T) {
root := mkSpan("root", "svc",
mkSpan("childA", "svc",
mkSpan("grandchildA", "svc",
mkSpan("leafA", "svc2"),
),
),
mkSpan("childB", "svc3",
mkSpan("grandchildB", "svc",
mkSpan("leafB", "svc2"),
),
),
)
spans, rootServiceName, rootEntryPoint := GetAllSpans([]*model.Span{root})
assert.ElementsMatch(t, spanIDs(spans), []string{"root", "childA", "grandchildA", "leafA", "childB", "grandchildB", "leafB"})
assert.Equal(t, rootServiceName, "svc")
assert.Equal(t, rootEntryPoint, "root-op")
}
func mkSpan(id, service string, children ...*model.Span) *model.Span {
return &model.Span{
SpanID: id,
ServiceName: service,
Name: id + "-op",
Children: children,
}
}
// spanIDs returns SpanIDs in order.
func spanIDs(spans []*model.Span) []string {
ids := make([]string, len(spans))
for i, s := range spans {
ids[i] = s.SpanID
}
return ids
}
// buildSpanMap indexes every span in a set of trees by SpanID.
func buildSpanMap(roots ...*model.Span) map[string]*model.Span {
m := map[string]*model.Span{}
var walk func(*model.Span)
walk = func(s *model.Span) {
m[s.SpanID] = s
for _, c := range s.Children {
walk(c)
}
}
for _, r := range roots {
walk(r)
}
return m
}

View File

@@ -43,7 +43,6 @@ type Reader interface {
// Search Interfaces
SearchTraces(ctx context.Context, params *model.SearchTracesParams) (*[]model.SearchSpansResult, error)
GetWaterfallSpansForTraceWithMetadata(ctx context.Context, orgID valuer.UUID, traceID string, req *model.GetWaterfallSpansForTraceWithMetadataParams) (*model.GetWaterfallSpansForTraceWithMetadataResponse, error)
GetFlamegraphSpansForTrace(ctx context.Context, orgID valuer.UUID, traceID string, req *model.GetFlamegraphSpansForTraceParams) (*model.GetFlamegraphSpansForTraceResponse, error)
// Setter Interfaces

View File

@@ -2,57 +2,10 @@ package model
import (
"encoding/json"
"maps"
"github.com/SigNoz/signoz/pkg/types/cachetypes"
)
type GetWaterfallSpansForTraceWithMetadataCache struct {
StartTime uint64 `json:"startTime"`
EndTime uint64 `json:"endTime"`
DurationNano uint64 `json:"durationNano"`
TotalSpans uint64 `json:"totalSpans"`
TotalErrorSpans uint64 `json:"totalErrorSpans"`
ServiceNameToTotalDurationMap map[string]uint64 `json:"serviceNameToTotalDurationMap"`
SpanIdToSpanNodeMap map[string]*Span `json:"spanIdToSpanNodeMap"`
TraceRoots []*Span `json:"traceRoots"`
HasMissingSpans bool `json:"hasMissingSpans"`
}
func (c *GetWaterfallSpansForTraceWithMetadataCache) Clone() cachetypes.Cacheable {
copyOfServiceNameToTotalDurationMap := make(map[string]uint64)
maps.Copy(copyOfServiceNameToTotalDurationMap, c.ServiceNameToTotalDurationMap)
copyOfSpanIdToSpanNodeMap := make(map[string]*Span)
maps.Copy(copyOfSpanIdToSpanNodeMap, c.SpanIdToSpanNodeMap)
copyOfTraceRoots := make([]*Span, len(c.TraceRoots))
copy(copyOfTraceRoots, c.TraceRoots)
return &GetWaterfallSpansForTraceWithMetadataCache{
StartTime: c.StartTime,
EndTime: c.EndTime,
DurationNano: c.DurationNano,
TotalSpans: c.TotalSpans,
TotalErrorSpans: c.TotalErrorSpans,
ServiceNameToTotalDurationMap: copyOfServiceNameToTotalDurationMap,
SpanIdToSpanNodeMap: copyOfSpanIdToSpanNodeMap,
TraceRoots: copyOfTraceRoots,
HasMissingSpans: c.HasMissingSpans,
}
}
func (c *GetWaterfallSpansForTraceWithMetadataCache) Cost() int64 {
const perSpanBytes = 256
return int64(c.TotalSpans) * perSpanBytes
}
func (c *GetWaterfallSpansForTraceWithMetadataCache) MarshalBinary() (data []byte, err error) {
return json.Marshal(c)
}
func (c *GetWaterfallSpansForTraceWithMetadataCache) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, c)
}
type GetFlamegraphSpansForTraceCache struct {
StartTime uint64 `json:"startTime"`
EndTime uint64 `json:"endTime"`

View File

@@ -331,13 +331,6 @@ type SearchTracesParams struct {
MaxSpansInTrace int `json:"maxSpansInTrace"`
}
type GetWaterfallSpansForTraceWithMetadataParams struct {
SelectedSpanID string `json:"selectedSpanId"`
IsSelectedSpanIDUnCollapsed bool `json:"isSelectedSpanIDUnCollapsed"`
UncollapsedSpans []string `json:"uncollapsedSpans"`
Limit uint `json:"limit"`
}
type GetFlamegraphSpansForTraceParams struct {
SelectedSpanID string `json:"selectedSpanId"`
Limit uint `json:"limit"`

View File

@@ -332,22 +332,6 @@ func (s *FlamegraphSpan) SetRequestedFields(item SpanItemV2, fields []telemetryt
}
}
type GetWaterfallSpansForTraceWithMetadataResponse struct {
StartTimestampMillis uint64 `json:"startTimestampMillis"`
EndTimestampMillis uint64 `json:"endTimestampMillis"`
DurationNano uint64 `json:"durationNano"`
RootServiceName string `json:"rootServiceName"`
RootServiceEntryPoint string `json:"rootServiceEntryPoint"`
TotalSpansCount uint64 `json:"totalSpansCount"`
TotalErrorSpansCount uint64 `json:"totalErrorSpansCount"`
ServiceNameToTotalDurationMap map[string]uint64 `json:"serviceNameToTotalDurationMap"`
Spans []*Span `json:"spans"`
HasMissingSpans bool `json:"hasMissingSpans"`
// this is needed for frontend and query service sync
UncollapsedSpans []string `json:"uncollapsedSpans"`
HasMore bool `json:"hasMore"`
}
type GetFlamegraphSpansForTraceResponse struct {
StartTimestampMillis uint64 `json:"startTimestampMillis"`
EndTimestampMillis uint64 `json:"endTimestampMillis"`

View File

@@ -211,6 +211,7 @@ func NewSQLMigrationProviderFactories(
sqlmigration.NewAddDashboardNameFactory(sqlstore, sqlschema),
sqlmigration.NewFixChangelogOperationTypeFactory(sqlstore, sqlschema),
sqlmigration.NewCloudIntegrationRemoveCascadeDeleteFactory(sqlschema),
sqlmigration.NewMigrateAuthDomainPayloadFactory(),
)
}

View File

@@ -0,0 +1,118 @@
package sqlmigration
import (
"context"
"encoding/json"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
type migrateAuthDomainPayload struct{}
type authDomainPayloadRaw struct {
bun.BaseModel `bun:"table:auth_domain"`
ID string `bun:"id"`
Data string `bun:"data"`
}
// auth config type -> old sso type.
var legacyConfigKeyByType = map[string]string{
"saml": "samlConfig",
"oidc": "oidcConfig",
"google_auth": "googleAuthConfig",
}
func NewMigrateAuthDomainPayloadFactory() factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(
factory.MustNewName("migrate_auth_domain_payload"),
func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
return &migrateAuthDomainPayload{}, nil
},
)
}
func (migration *migrateAuthDomainPayload) Register(migrations *migrate.Migrations) error {
return migrations.Register(migration.Up, migration.Down)
}
func (migration *migrateAuthDomainPayload) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
var rows []*authDomainPayloadRaw
if err := tx.NewSelect().Model(&rows).Scan(ctx); err != nil {
return err
}
for _, row := range rows {
var oldData map[string]json.RawMessage
if err := json.Unmarshal([]byte(row.Data), &oldData); err != nil {
return err
}
// idempotency - we skip the ones which already migrated.
if _, hasProvider := oldData["provider"]; hasProvider {
continue
}
if _, hasSSOType := oldData["ssoType"]; !hasSSOType {
continue
}
var ssoType string
if err := json.Unmarshal(oldData["ssoType"], &ssoType); err != nil {
return err
}
provider := map[string]json.RawMessage{
"type": oldData["ssoType"],
}
// get from old data and set config in provider.
if configKey, ok := legacyConfigKeyByType[ssoType]; ok {
if cfg, ok := oldData[configKey]; ok {
provider["config"] = cfg
}
}
providerRaw, err := json.Marshal(provider)
if err != nil {
return err
}
updatedData := map[string]json.RawMessage{
"provider": providerRaw,
}
if v, ok := oldData["ssoEnabled"]; ok {
updatedData["ssoEnabled"] = v
}
if v, ok := oldData["roleMapping"]; ok {
updatedData["roleMapping"] = v
}
updatedDataRaw, err := json.Marshal(updatedData)
if err != nil {
return err
}
row.Data = string(updatedDataRaw)
if _, err := tx.NewUpdate().Model(row).Column("data").Where("id = ?", row.ID).Exec(ctx); err != nil {
return err
}
}
return tx.Commit()
}
func (migration *migrateAuthDomainPayload) Down(ctx context.Context, db *bun.DB) error {
return nil
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/swaggest/jsonschema-go"
"github.com/uptrace/bun"
)
@@ -30,7 +31,7 @@ var (
type GettableAuthDomain struct {
StorableAuthDomain
Config AuthDomainConfig `json:"config"`
AuthDomainConfig
AuthNProviderInfo *AuthNProviderInfo `json:"authNProviderInfo"`
}
@@ -39,12 +40,12 @@ type AuthNProviderInfo struct {
}
type PostableAuthDomain struct {
Config AuthDomainConfig `json:"config"`
Name string `json:"name"`
Name string `json:"name"`
AuthDomainConfig
}
type UpdatableAuthDomain struct {
Config AuthDomainConfig `json:"config"`
AuthDomainConfig
}
type StorableAuthDomain struct {
@@ -57,22 +58,114 @@ type StorableAuthDomain struct {
types.TimeAuditable
}
// TODO: the oneOf emitted by JSONSchemaOneOf is not the shape OpenAPI wants
// for a discriminated union. OpenAPI's discriminator requires every oneOf
// branch to be a $ref to a named component and a sibling property whose value
// selects the variant. ssoType is already discriminator-shaped, but the
// variant payload lives in a sibling field (samlConfig / googleAuthConfig /
// oidcConfig) instead of being the payload itself, so no discriminator can
// be attached. Refactor AuthDomainConfig into an envelope (see
// ruletypes.RuleThresholdData for the pattern) where the chosen config is
// the payload and ssoType is the discriminator.
type AuthDomainConfig struct {
SSOEnabled bool `json:"ssoEnabled"`
AuthNProvider AuthNProvider `json:"ssoType"`
SAML *SamlConfig `json:"samlConfig"`
Google *GoogleConfig `json:"googleAuthConfig"`
OIDC *OIDCConfig `json:"oidcConfig"`
RoleMapping *RoleMapping `json:"roleMapping"`
SSOEnabled bool `json:"ssoEnabled"`
RoleMapping *RoleMapping `json:"roleMapping,omitempty"`
Provider AuthProviderEnvelope `json:"provider"`
}
func (config AuthDomainConfig) Saml() *SAMLConfig {
cfg, _ := config.Provider.Config.(*SAMLConfig)
return cfg
}
func (config AuthDomainConfig) Google() *GoogleConfig {
cfg, _ := config.Provider.Config.(*GoogleConfig)
return cfg
}
func (config AuthDomainConfig) Oidc() *OIDCConfig {
cfg, _ := config.Provider.Config.(*OIDCConfig)
return cfg
}
type AuthProviderEnvelope struct {
Type AuthNProvider `json:"type" required:"true"`
Config any `json:"config" required:"true"` // this can be either of SamlConfig, OIDCConfig and GoogleConfig
}
// internal - drives the oneOf thing in open api spec.
type authProviderSAML struct {
Type AuthNProvider `json:"type" required:"true"`
Config SAMLConfig `json:"config" required:"true"`
}
type authProviderOIDC struct {
Type AuthNProvider `json:"type" required:"true"`
Config OIDCConfig `json:"config" required:"true"`
}
type authProviderGoogle struct {
Type AuthNProvider `json:"type" required:"true"`
Config GoogleConfig `json:"config" required:"true"`
}
var (
_ jsonschema.OneOfExposer = AuthProviderEnvelope{}
_ jsonschema.Preparer = AuthProviderEnvelope{}
)
func (AuthProviderEnvelope) JSONSchemaOneOf() []any {
return []any{
authProviderSAML{},
authProviderOIDC{},
authProviderGoogle{},
}
}
func (AuthProviderEnvelope) PrepareJSONSchema(schema *jsonschema.Schema) error {
if schema.ExtraProperties == nil {
schema.ExtraProperties = map[string]any{}
}
schema.ExtraProperties["x-signoz-discriminator"] = map[string]any{
"propertyName": "type",
"mapping": map[string]string{
"saml": "#/components/schemas/AuthtypesAuthProviderSAML",
"oidc": "#/components/schemas/AuthtypesAuthProviderOIDC",
"google_auth": "#/components/schemas/AuthtypesAuthProviderGoogle",
},
}
return nil
}
func (envelop *AuthProviderEnvelope) UnmarshalJSON(data []byte) error {
var raw struct {
Type AuthNProvider `json:"type"`
Config json.RawMessage `json:"config"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "failed to unmarshal auth provider: %v", err)
}
envelop.Type = raw.Type
switch raw.Type {
case AuthNProviderSAML:
cfg := new(SAMLConfig)
if err := json.Unmarshal(raw.Config, cfg); err != nil {
return err
}
envelop.Config = cfg
case AuthNProviderOIDC:
cfg := new(OIDCConfig)
if err := json.Unmarshal(raw.Config, cfg); err != nil {
return err
}
envelop.Config = cfg
case AuthNProviderGoogleAuth:
cfg := new(GoogleConfig)
if err := json.Unmarshal(raw.Config, cfg); err != nil {
return err
}
envelop.Config = cfg
default:
return errors.NewInvalidInputf(errors.CodeInvalidInput, "unknown auth provider type: %s", raw.Type.StringValue())
}
return nil
}
type AuthDomain struct {
@@ -121,7 +214,7 @@ func NewAuthDomainFromStorableAuthDomain(storableAuthDomain *StorableAuthDomain)
func NewGettableAuthDomainFromAuthDomain(authDomain *AuthDomain, authNProviderInfo *AuthNProviderInfo) *GettableAuthDomain {
return &GettableAuthDomain{
StorableAuthDomain: *authDomain.StorableAuthDomain(),
Config: *authDomain.AuthDomainConfig(),
AuthDomainConfig: *authDomain.AuthDomainConfig(),
AuthNProviderInfo: authNProviderInfo,
}
}
@@ -158,51 +251,14 @@ func (typ *PostableAuthDomain) UnmarshalJSON(data []byte) error {
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthDomainInvalidName, "invalid domain name %s", temp.Name)
}
if temp.Provider.Config == nil {
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthDomainInvalidConfig, "provider config is required")
}
*typ = PostableAuthDomain(temp)
return nil
}
func (typ *AuthDomainConfig) UnmarshalJSON(data []byte) error {
type Alias AuthDomainConfig
var temp Alias
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
switch temp.AuthNProvider {
case AuthNProviderGoogleAuth:
if temp.Google == nil {
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthDomainInvalidConfig, "google auth config is required")
}
case AuthNProviderSAML:
if temp.SAML == nil {
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthDomainInvalidConfig, "saml config is required")
}
case AuthNProviderOIDC:
if temp.OIDC == nil {
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthDomainInvalidConfig, "oidc config is required")
}
default:
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthDomainInvalidConfig, "invalid authn provider %q", temp.AuthNProvider.StringValue())
}
*typ = AuthDomainConfig(temp)
return nil
}
func (AuthDomainConfig) JSONSchemaOneOf() []any {
return []any{
SamlConfig{},
GoogleConfig{},
OIDCConfig{},
}
}
type AuthDomainStore interface {
// Get by id.
Get(context.Context, valuer.UUID) (*AuthDomain, error)

View File

@@ -0,0 +1,154 @@
package authtypes
import (
"encoding/json"
"testing"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/stretchr/testify/require"
)
// Verifies the new flat wire shape: ssoType/provider configs collapse into a
// single discriminated `provider:{type,config}`, and the typed payload survives
// a marshal -> unmarshal round-trip. This fails if UnmarshalJSON forgets to
// assign envelop.Config (the decoded config would be lost).
func TestAuthDomainConfigWireRoundTrip(t *testing.T) {
tests := []struct {
name string
provider AuthNProvider
config any
wantType string
assertConfig func(t *testing.T, c AuthDomainConfig)
}{
{
name: "saml",
provider: AuthNProviderSAML,
config: &SAMLConfig{
SamlEntity: "https://idp.example.com",
SamlIdp: "https://idp.example.com/sso",
SamlCert: "cert-bytes",
},
wantType: "saml",
assertConfig: func(t *testing.T, c AuthDomainConfig) {
require.NotNil(t, c.Saml())
require.Equal(t, "https://idp.example.com", c.Saml().SamlEntity)
},
},
{
name: "google",
provider: AuthNProviderGoogleAuth,
config: &GoogleConfig{ClientID: "cid", ClientSecret: "secret"},
wantType: "google_auth",
assertConfig: func(t *testing.T, c AuthDomainConfig) {
require.NotNil(t, c.Google())
require.Equal(t, "cid", c.Google().ClientID)
},
},
{
name: "oidc",
provider: AuthNProviderOIDC,
config: &OIDCConfig{Issuer: "https://issuer", ClientID: "cid", ClientSecret: "secret"},
wantType: "oidc",
assertConfig: func(t *testing.T, c AuthDomainConfig) {
require.NotNil(t, c.Oidc())
require.Equal(t, "https://issuer", c.Oidc().Issuer)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
in := AuthDomainConfig{
SSOEnabled: true,
Provider: AuthProviderEnvelope{Type: tt.provider, Config: tt.config},
}
raw, err := json.Marshal(in)
require.NoError(t, err)
js := string(raw)
require.Contains(t, js, `"provider"`)
require.Contains(t, js, `"type":"`+tt.wantType+`"`)
// legacy keys must be gone
require.NotContains(t, js, "ssoType")
require.NotContains(t, js, "samlConfig")
require.NotContains(t, js, "googleAuthConfig")
require.NotContains(t, js, "oidcConfig")
var out AuthDomainConfig
require.NoError(t, json.Unmarshal(raw, &out))
require.True(t, out.SSOEnabled)
require.Equal(t, tt.provider, out.Provider.Type)
tt.assertConfig(t, out)
})
}
}
// Unknown discriminator values are rejected, and the nested provider config's
// own validators still run through the envelope.
func TestAuthDomainConfigUnmarshalRejects(t *testing.T) {
tests := []struct {
name string
json string
}{
{
name: "unknown provider type",
json: `{"ssoEnabled":true,"provider":{"type":"ldap","config":{}}}`,
},
{
name: "oidc config missing clientId",
json: `{"ssoEnabled":true,"provider":{"type":"oidc","config":{"issuer":"https://issuer","clientSecret":"secret"}}}`,
},
{
name: "saml config missing samlEntity",
json: `{"ssoEnabled":true,"provider":{"type":"saml","config":{"samlIdp":"https://idp/sso","samlCert":"abc"}}}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var c AuthDomainConfig
require.Error(t, json.Unmarshal([]byte(tt.json), &c))
})
}
}
// The config is marshaled into the `data` column and unmarshaled back when the
// domain is loaded, so the typed provider config must survive that round-trip.
func TestAuthDomainStorageRoundTrip(t *testing.T) {
cfg := AuthDomainConfig{
SSOEnabled: true,
Provider: AuthProviderEnvelope{
Type: AuthNProviderSAML,
Config: &SAMLConfig{SamlEntity: "https://idp", SamlIdp: "https://idp/sso", SamlCert: "abc"},
},
}
domain, err := NewAuthDomainFromConfig("example.com", &cfg, valuer.GenerateUUID())
require.NoError(t, err)
got := domain.AuthDomainConfig()
require.Equal(t, AuthNProviderSAML, got.Provider.Type)
require.NotNil(t, got.Saml())
require.Equal(t, "https://idp", got.Saml().SamlEntity)
}
// Postable keeps name-regex validation and still decodes the embedded provider.
func TestPostableAuthDomainUnmarshal(t *testing.T) {
valid := `{
"name":"example.com",
"ssoEnabled":true,
"provider":{"type":"saml","config":{"samlEntity":"https://idp","samlIdp":"https://idp/sso","samlCert":"abc"}}
}`
var p PostableAuthDomain
require.NoError(t, json.Unmarshal([]byte(valid), &p))
require.Equal(t, "example.com", p.Name)
require.True(t, p.SSOEnabled)
require.NotNil(t, p.Saml())
require.Equal(t, "https://idp", p.Saml().SamlEntity)
invalid := `{"name":"not a domain!","provider":{"type":"saml","config":{"samlEntity":"https://idp","samlIdp":"https://idp/sso","samlCert":"abc"}}}`
var bad PostableAuthDomain
require.Error(t, json.Unmarshal([]byte(invalid), &bad))
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
)
type SamlConfig struct {
type SAMLConfig struct {
// The entityID of the SAML identity provider. It can typically be found in the EntityID attribute of the EntityDescriptor element in the SAML metadata of the identity provider. Example: <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="{samlEntity}">
SamlEntity string `json:"samlEntity"`
@@ -25,8 +25,8 @@ type SamlConfig struct {
AttributeMapping AttributeMapping `json:"attributeMapping"`
}
func (config *SamlConfig) UnmarshalJSON(data []byte) error {
type Alias SamlConfig
func (config *SAMLConfig) UnmarshalJSON(data []byte) error {
type Alias SAMLConfig
var temp Alias
if err := json.Unmarshal(data, &temp); err != nil {
@@ -51,6 +51,6 @@ func (config *SamlConfig) UnmarshalJSON(data []byte) error {
}
}
*config = SamlConfig(temp)
*config = SAMLConfig(temp)
return nil
}

View File

@@ -1,251 +0,0 @@
package spantypes
import (
"testing"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/stretchr/testify/assert"
)
// mkASpan builds a WaterfallSpan with timing and field data for analytics tests.
func mkASpan(id string, resource map[string]string, attributes map[string]any, startNs, durationNs uint64) *WaterfallSpan {
return &WaterfallSpan{
SpanID: id,
Resource: resource,
Attributes: attributes,
TimeUnix: startNs,
DurationNano: durationNs,
Children: make([]*WaterfallSpan, 0),
}
}
func buildTraceFromSpans(spans ...*WaterfallSpan) *WaterfallTrace {
spanMap := make(map[string]*WaterfallSpan, len(spans))
var startTime, endTime uint64
initialized := false
for _, s := range spans {
spanMap[s.SpanID] = s
if !initialized || s.TimeUnix < startTime {
startTime = s.TimeUnix
initialized = true
}
if end := s.TimeUnix + s.DurationNano; end > endTime {
endTime = end
}
}
return NewWaterfallTrace(startTime, endTime, uint64(len(spanMap)), 0, spanMap, nil, false)
}
var (
fieldServiceName = telemetrytypes.TelemetryFieldKey{
Name: "service.name",
FieldContext: telemetrytypes.FieldContextResource,
}
fieldHTTPMethod = telemetrytypes.TelemetryFieldKey{
Name: "http.method",
FieldContext: telemetrytypes.FieldContextAttribute,
}
fieldCached = telemetrytypes.TelemetryFieldKey{
Name: "db.cached",
FieldContext: telemetrytypes.FieldContextAttribute,
}
)
func TestGetSpanAggregation_SpanCount(t *testing.T) {
tests := []struct {
name string
trace *WaterfallTrace
field telemetrytypes.TelemetryFieldKey
want map[string]uint64
}{
{
name: "counts by resource field",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "frontend"}, nil, 0, 10),
mkASpan("s2", map[string]string{"service.name": "frontend"}, nil, 10, 5),
mkASpan("s3", map[string]string{"service.name": "backend"}, nil, 20, 8),
),
field: fieldServiceName,
want: map[string]uint64{"frontend": 2, "backend": 1},
},
{
name: "counts by string attribute field",
trace: buildTraceFromSpans(
mkASpan("s1", nil, map[string]any{"http.method": "GET"}, 0, 10),
mkASpan("s2", nil, map[string]any{"http.method": "POST"}, 10, 5),
mkASpan("s3", nil, map[string]any{"http.method": "GET"}, 20, 8),
),
field: fieldHTTPMethod,
want: map[string]uint64{"GET": 2, "POST": 1},
},
{
name: "counts by boolean attribute field",
trace: buildTraceFromSpans(
mkASpan("s1", nil, map[string]any{"db.cached": true}, 0, 10),
mkASpan("s2", nil, map[string]any{"db.cached": false}, 10, 5),
mkASpan("s3", nil, map[string]any{"db.cached": true}, 20, 8),
),
field: fieldCached,
want: map[string]uint64{"true": 2, "false": 1},
},
{
name: "spans missing the field are excluded",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "frontend"}, nil, 0, 10),
mkASpan("s2", map[string]string{}, nil, 10, 5), // no service.name
mkASpan("s3", map[string]string{"service.name": "backend"}, nil, 20, 8),
),
field: fieldServiceName,
want: map[string]uint64{"frontend": 1, "backend": 1},
},
{
// empty string is a valid field value — counted under the "" key, unlike a missing field
name: "span with empty service.name is counted under empty string key",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "frontend"}, nil, 0, 10),
mkASpan("s2", map[string]string{"service.name": ""}, nil, 10, 5),
mkASpan("s3", map[string]string{"service.name": "backend"}, nil, 20, 8),
),
field: fieldServiceName,
want: map[string]uint64{"frontend": 1, "backend": 1, "": 1},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := tc.trace.GetSpanAggregation(SpanAggregationSpanCount, tc.field)
assert.Equal(t, tc.field, result.Field)
assert.Equal(t, SpanAggregationSpanCount, result.Aggregation)
assert.Equal(t, tc.want, result.Value)
})
}
}
func TestGetSpanAggregation_Duration(t *testing.T) {
tests := []struct {
name string
trace *WaterfallTrace
field telemetrytypes.TelemetryFieldKey
want map[string]uint64
}{
{
name: "non-overlapping spans — merged equals sum",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "frontend"}, nil, 0, 100),
mkASpan("s2", map[string]string{"service.name": "frontend"}, nil, 100, 50),
mkASpan("s3", map[string]string{"service.name": "backend"}, nil, 0, 80),
),
field: fieldServiceName,
want: map[string]uint64{"frontend": 150, "backend": 80},
},
{
name: "non-overlapping attribute groups — merged equals sum",
trace: buildTraceFromSpans(
mkASpan("s1", nil, map[string]any{"http.method": "GET"}, 0, 30),
mkASpan("s2", nil, map[string]any{"http.method": "GET"}, 50, 20),
mkASpan("s3", nil, map[string]any{"http.method": "POST"}, 0, 70),
),
field: fieldHTTPMethod,
want: map[string]uint64{"GET": 50, "POST": 70},
},
{
name: "overlapping spans — non-overlapping interval merge",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "svc"}, nil, 0, 10),
mkASpan("s2", map[string]string{"service.name": "svc"}, nil, 5, 10),
),
field: fieldServiceName,
want: map[string]uint64{"svc": 15}, // [0,10] [5,15] = [0,15]
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := tc.trace.GetSpanAggregation(SpanAggregationDuration, tc.field)
assert.Equal(t, tc.field, result.Field)
assert.Equal(t, SpanAggregationDuration, result.Aggregation)
assert.Equal(t, tc.want, result.Value)
})
}
}
func TestGetSpanAggregation_ExecutionTimePercentage(t *testing.T) {
tests := []struct {
name string
trace *WaterfallTrace
field telemetrytypes.TelemetryFieldKey
want map[string]uint64
}{
{
// trace [0,30]: svc occupies [0,10]+[20,30]=20 → 20*100/30 = 66%
name: "non-overlapping spans",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "svc"}, nil, 0, 10),
mkASpan("s2", map[string]string{"service.name": "svc"}, nil, 20, 10),
),
field: fieldServiceName,
want: map[string]uint64{"svc": 66},
},
{
// trace [0,15]: svc [0,15]=15 → 100%
name: "partially overlapping spans",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "svc"}, nil, 0, 10),
mkASpan("s2", map[string]string{"service.name": "svc"}, nil, 5, 10),
),
field: fieldServiceName,
want: map[string]uint64{"svc": 100},
},
{
// trace [0,20]: outer absorbs inner → 100%
name: "fully contained span",
trace: buildTraceFromSpans(
mkASpan("outer", map[string]string{"service.name": "svc"}, nil, 0, 20),
mkASpan("inner", map[string]string{"service.name": "svc"}, nil, 5, 5),
),
field: fieldServiceName,
want: map[string]uint64{"svc": 100},
},
{
// trace [0,30]: svc [0,15]+[20,30]=25 → 25*100/30 = 83%
name: "three spans with two merges",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "svc"}, nil, 0, 10),
mkASpan("s2", map[string]string{"service.name": "svc"}, nil, 5, 10),
mkASpan("s3", map[string]string{"service.name": "svc"}, nil, 20, 10),
),
field: fieldServiceName,
want: map[string]uint64{"svc": 83},
},
{
// trace [0,28]: frontend [0,15]=15 → 53%, backend [0,5]+[20,28]=13 → 46%
name: "independent groups are computed separately",
trace: buildTraceFromSpans(
mkASpan("a1", map[string]string{"service.name": "frontend"}, nil, 0, 10),
mkASpan("a2", map[string]string{"service.name": "frontend"}, nil, 5, 10),
mkASpan("b1", map[string]string{"service.name": "backend"}, nil, 0, 5),
mkASpan("b2", map[string]string{"service.name": "backend"}, nil, 20, 8),
),
field: fieldServiceName,
want: map[string]uint64{"frontend": 53, "backend": 46},
},
{
// trace [100,150]: svc [100,150]=50 → 100%
name: "single span",
trace: buildTraceFromSpans(
mkASpan("s1", map[string]string{"service.name": "svc"}, nil, 100, 50),
),
field: fieldServiceName,
want: map[string]uint64{"svc": 100},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := tc.trace.GetSpanAggregation(SpanAggregationExecutionTimePercentage, tc.field)
assert.Equal(t, tc.field, result.Field)
assert.Equal(t, SpanAggregationExecutionTimePercentage, result.Aggregation)
assert.Equal(t, tc.want, result.Value)
})
}
}

View File

@@ -21,28 +21,13 @@ const (
// ErrTraceNotFound is returned when a trace ID has no matching spans in ClickHouse.
var ErrTraceNotFound = errors.NewNotFoundf(errors.CodeNotFound, "trace not found")
// PostableWaterfall is the request body for the v3 waterfall API.
// PostableWaterfall is the request body for the waterfall API.
type PostableWaterfall struct {
SelectedSpanID string `json:"selectedSpanId"`
UncollapsedSpans []string `json:"uncollapsedSpans"`
Limit uint `json:"limit"`
Aggregations []SpanAggregation `json:"aggregations"`
SelectedSpanID string `json:"selectedSpanId"`
UncollapsedSpans []string `json:"uncollapsedSpans"`
}
func (p *PostableWaterfall) Validate() error {
if len(p.Aggregations) > maxAggregationItems {
return ErrTooManyAggregationItems
}
for _, a := range p.Aggregations {
if !a.Aggregation.isValid() {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "unknown aggregation type: %q", a.Aggregation)
}
fc := a.Field.FieldContext
if fc != telemetrytypes.FieldContextResource && fc != telemetrytypes.FieldContextAttribute {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "aggregation field context must be %q or %q, got %q",
telemetrytypes.FieldContextResource, telemetrytypes.FieldContextAttribute, fc)
}
}
return nil
}

View File

@@ -8,7 +8,6 @@ import (
"time"
"github.com/SigNoz/signoz/pkg/types/cachetypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
type TraceSummary struct {
@@ -29,19 +28,18 @@ type WaterfallTrace struct {
HasMissingSpans bool `json:"hasMissingSpans"`
}
// GettableWaterfallTrace is the response for the v3 waterfall API.
// GettableWaterfallTrace is the response for the waterfall API.
type GettableWaterfallTrace struct {
StartTimestampMillis uint64 `json:"startTimestampMillis"`
EndTimestampMillis uint64 `json:"endTimestampMillis"`
RootServiceName string `json:"rootServiceName"`
RootServiceEntryPoint string `json:"rootServiceEntryPoint"`
TotalSpansCount uint64 `json:"totalSpansCount"`
TotalErrorSpansCount uint64 `json:"totalErrorSpansCount"`
Spans []*WaterfallSpan `json:"spans"`
HasMissingSpans bool `json:"hasMissingSpans"`
UncollapsedSpans []string `json:"uncollapsedSpans"`
HasMore bool `json:"hasMore"`
Aggregations []SpanAggregationResult `json:"aggregations"`
StartTimestampMillis uint64 `json:"startTimestampMillis"`
EndTimestampMillis uint64 `json:"endTimestampMillis"`
RootServiceName string `json:"rootServiceName"`
RootServiceEntryPoint string `json:"rootServiceEntryPoint"`
TotalSpansCount uint64 `json:"totalSpansCount"`
TotalErrorSpansCount uint64 `json:"totalErrorSpansCount"`
Spans []*WaterfallSpan `json:"spans"`
HasMissingSpans bool `json:"hasMissingSpans"`
UncollapsedSpans []string `json:"uncollapsedSpans"`
HasMore bool `json:"hasMore"`
}
// NewWaterfallTrace constructs a WaterfallTrace from processed span data.
@@ -122,23 +120,6 @@ func NewWaterfallTraceFromSpans(nodes []*WaterfallSpan) *WaterfallTrace {
)
}
func (wt *WaterfallTrace) GetWaterfallSpans(uncollapsedSpanIDs []string, selectedSpanID string, limit uint, spanPageSize float64, maxDepthToAutoExpand int) ([]*WaterfallSpan, []string, bool) {
// Span selection decision: all spans or windowed
selectAllSpans := wt.TotalSpans <= uint64(limit)
var (
selectedSpans []*WaterfallSpan
uncollapsedSpans []string
)
if selectAllSpans {
selectedSpans = wt.GetAllSpans()
} else {
selectedSpans, uncollapsedSpans = wt.GetSelectedSpans(uncollapsedSpanIDs, selectedSpanID, spanPageSize, maxDepthToAutoExpand)
}
return selectedSpans, uncollapsedSpans, selectAllSpans
}
// GetAllSpans returns all spans with pre order traversal.
func (wt *WaterfallTrace) GetAllSpans() []*WaterfallSpan {
var preOrderedSpans []*WaterfallSpan
@@ -237,7 +218,6 @@ func NewGettableWaterfallTrace(
selectedSpans []*WaterfallSpan,
uncollapsedSpans []string,
selectAllSpans bool,
aggregations []SpanAggregationResult,
) *GettableWaterfallTrace {
var rootServiceName, rootServiceEntryPoint string
if len(traceData.TraceRoots) > 0 {
@@ -250,15 +230,6 @@ func NewGettableWaterfallTrace(
span.TimeUnix = span.TimeUnix / 1_000_000
}
// duration values are in nanoseconds; convert in-place to milliseconds.
for i := range aggregations {
if aggregations[i].Aggregation == SpanAggregationDuration {
for k, v := range aggregations[i].Value {
aggregations[i].Value[k] = v / 1_000_000
}
}
}
return &GettableWaterfallTrace{
Spans: selectedSpans,
UncollapsedSpans: uncollapsedSpans,
@@ -270,7 +241,6 @@ func NewGettableWaterfallTrace(
RootServiceEntryPoint: rootServiceEntryPoint,
HasMissingSpans: traceData.HasMissingSpans,
HasMore: !selectAllSpans,
Aggregations: aggregations,
}
}
@@ -292,78 +262,3 @@ func windowAroundIndex(selectedIndex, total int, spanLimitPerRequest float64) (s
start = max(start, 0)
return
}
// mergeSpanIntervals computes non-overlapping execution time for a set of spans.
func mergeSpanIntervals(spans []*WaterfallSpan) uint64 {
if len(spans) == 0 {
return 0
}
sort.Slice(spans, func(i, j int) bool {
return spans[i].TimeUnix < spans[j].TimeUnix
})
currentStart := spans[0].TimeUnix
currentEnd := currentStart + spans[0].DurationNano
total := uint64(0)
for _, span := range spans[1:] {
startNano := span.TimeUnix
endNano := startNano + span.DurationNano
if currentEnd >= startNano {
if endNano > currentEnd {
currentEnd = endNano
}
} else {
total += currentEnd - currentStart
currentStart = startNano
currentEnd = endNano
}
}
return total + (currentEnd - currentStart)
}
// GetSpanAggregation computes one aggregation result over all spans in the trace.
// Duration values are returned in nanoseconds; callers convert to milliseconds as needed.
func (wt *WaterfallTrace) GetSpanAggregation(aggregation SpanAggregationType, field telemetrytypes.TelemetryFieldKey) SpanAggregationResult {
result := SpanAggregationResult{
Field: field,
Aggregation: aggregation,
Value: make(map[string]uint64),
}
switch aggregation {
case SpanAggregationSpanCount:
for _, span := range wt.SpanIDToSpanNodeMap {
if key, ok := span.FieldValue(field); ok {
result.Value[key]++
}
}
case SpanAggregationDuration:
spansByField := make(map[string][]*WaterfallSpan)
for _, span := range wt.SpanIDToSpanNodeMap {
if key, ok := span.FieldValue(field); ok {
spansByField[key] = append(spansByField[key], span)
}
}
for key, spans := range spansByField {
result.Value[key] = mergeSpanIntervals(spans)
}
case SpanAggregationExecutionTimePercentage:
traceDuration := wt.EndTime - wt.StartTime
spansByField := make(map[string][]*WaterfallSpan)
for _, span := range wt.SpanIDToSpanNodeMap {
if key, ok := span.FieldValue(field); ok {
spansByField[key] = append(spansByField[key], span)
}
}
if traceDuration > 0 {
for key, spans := range spansByField {
result.Value[key] = mergeSpanIntervals(spans) * 100 / traceDuration
}
}
}
return result
}

View File

@@ -371,10 +371,11 @@ def idp_login(driver: webdriver.Chrome) -> Callable[[str, str], None]:
# Click the login button
login_button = wait.until(EC.element_to_be_clickable((By.ID, "kc-login")))
current_url = driver.current_url
login_button.click()
# Wait till kc-login element has vanished from the page, which means that a redirection is taking place.
wait.until(EC.invisibility_of_element((By.ID, "kc-login")))
# Wait till the page redirects away from the login form. We poll the URL.
wait.until(EC.url_changes(current_url))
return _idp_login

View File

@@ -0,0 +1,288 @@
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.node.uid": "web-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-prod-acc-c1-n1-p-uid", "k8s.pod.name": "web-gcp-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-prod-acc-c1-n1-p-uid", "k8s.pod.name": "web-gcp-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-prod-acc-c1-n1-p-uid", "k8s.pod.name": "web-gcp-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-prod-acc-c1-n1", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.node.uid": "web-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-prod-acc-c1-n2-p-uid", "k8s.pod.name": "web-gcp-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-prod-acc-c1-n2-p-uid", "k8s.pod.name": "web-gcp-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-prod-acc-c1-n2-p-uid", "k8s.pod.name": "web-gcp-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-prod-acc-c1-n2", "k8s.cluster.name": "web-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.node.uid": "web-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-dev-acc-c1-n1-p-uid", "k8s.pod.name": "web-gcp-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-dev-acc-c1-n1-p-uid", "k8s.pod.name": "web-gcp-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-dev-acc-c1-n1-p-uid", "k8s.pod.name": "web-gcp-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-dev-acc-c1-n1", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.node.uid": "web-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-dev-acc-c1-n2-p-uid", "k8s.pod.name": "web-gcp-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-dev-acc-c1-n2-p-uid", "k8s.pod.name": "web-gcp-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-gcp-dev-acc-c1-n2-p-uid", "k8s.pod.name": "web-gcp-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-gcp-dev-acc-c1-n2", "k8s.cluster.name": "web-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.node.uid": "api-gcp-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-prod-acc-c1-n1-p-uid", "k8s.pod.name": "api-gcp-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-prod-acc-c1-n1-p-uid", "k8s.pod.name": "api-gcp-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-prod-acc-c1-n1-p-uid", "k8s.pod.name": "api-gcp-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-prod-acc-c1-n1", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.node.uid": "api-gcp-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-prod-acc-c1-n2-p-uid", "k8s.pod.name": "api-gcp-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-prod-acc-c1-n2-p-uid", "k8s.pod.name": "api-gcp-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-prod-acc-c1-n2-p-uid", "k8s.pod.name": "api-gcp-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-prod-acc-c1-n2", "k8s.cluster.name": "api-gcp-prod", "cloud.provider": "gcp", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.node.uid": "api-gcp-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-dev-acc-c1-n1-p-uid", "k8s.pod.name": "api-gcp-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-dev-acc-c1-n1-p-uid", "k8s.pod.name": "api-gcp-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-dev-acc-c1-n1-p-uid", "k8s.pod.name": "api-gcp-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-dev-acc-c1-n1", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.node.uid": "api-gcp-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-dev-acc-c1-n2-p-uid", "k8s.pod.name": "api-gcp-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-dev-acc-c1-n2-p-uid", "k8s.pod.name": "api-gcp-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-gcp-dev-acc-c1-n2-p-uid", "k8s.pod.name": "api-gcp-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-gcp-dev-acc-c1-n2", "k8s.cluster.name": "api-gcp-dev", "cloud.provider": "gcp", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.node.uid": "web-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-prod-acc-c1-n1-p-uid", "k8s.pod.name": "web-aws-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-prod-acc-c1-n1-p-uid", "k8s.pod.name": "web-aws-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-prod-acc-c1-n1-p-uid", "k8s.pod.name": "web-aws-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-prod-acc-c1-n1", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.node.uid": "web-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-prod-acc-c1-n2-p-uid", "k8s.pod.name": "web-aws-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-prod-acc-c1-n2-p-uid", "k8s.pod.name": "web-aws-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-prod-acc-c1-n2-p-uid", "k8s.pod.name": "web-aws-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-prod-acc-c1-n2", "k8s.cluster.name": "web-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.node.uid": "web-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-dev-acc-c1-n1-p-uid", "k8s.pod.name": "web-aws-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-dev-acc-c1-n1-p-uid", "k8s.pod.name": "web-aws-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-dev-acc-c1-n1-p-uid", "k8s.pod.name": "web-aws-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-dev-acc-c1-n1", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.node.uid": "web-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-dev-acc-c1-n2-p-uid", "k8s.pod.name": "web-aws-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-dev-acc-c1-n2-p-uid", "k8s.pod.name": "web-aws-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "web-aws-dev-acc-c1-n2-p-uid", "k8s.pod.name": "web-aws-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "web-aws-dev-acc-c1-n2", "k8s.cluster.name": "web-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.node.uid": "api-aws-prod-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-prod-acc-c1-n1-p-uid", "k8s.pod.name": "api-aws-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-prod-acc-c1-n1-p-uid", "k8s.pod.name": "api-aws-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-prod-acc-c1-n1-p-uid", "k8s.pod.name": "api-aws-prod-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-prod-acc-c1-n1", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.node.uid": "api-aws-prod-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-prod-acc-c1-n2-p-uid", "k8s.pod.name": "api-aws-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-prod-acc-c1-n2-p-uid", "k8s.pod.name": "api-aws-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-prod-acc-c1-n2-p-uid", "k8s.pod.name": "api-aws-prod-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-prod-acc-c1-n2", "k8s.cluster.name": "api-aws-prod", "cloud.provider": "aws", "env": "prod"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.node.uid": "api-aws-dev-acc-c1-n1-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-dev-acc-c1-n1-p-uid", "k8s.pod.name": "api-aws-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-dev-acc-c1-n1-p-uid", "k8s.pod.name": "api-aws-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-dev-acc-c1-n1-p-uid", "k8s.pod.name": "api-aws-dev-acc-c1-n1-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-dev-acc-c1-n1", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.cpu.usage", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 0.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_cpu", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 4.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.memory.working_set", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.allocatable_memory", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 8000000000.0, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.node.condition_ready", "labels": {"k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.node.uid": "api-aws-dev-acc-c1-n2-uid", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-dev-acc-c1-n2-p-uid", "k8s.pod.name": "api-aws-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-dev-acc-c1-n2-p-uid", "k8s.pod.name": "api-aws-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
{"metric_name": "k8s.pod.phase", "labels": {"k8s.pod.uid": "api-aws-dev-acc-c1-n2-p-uid", "k8s.pod.name": "api-aws-dev-acc-c1-n2-p", "k8s.namespace.name": "ns-x", "k8s.node.name": "api-aws-dev-acc-c1-n2", "k8s.cluster.name": "api-aws-dev", "cloud.provider": "aws", "env": "dev"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}

View File

@@ -0,0 +1,72 @@
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-gcp-1","k8s.node.uid":"node-gb-gcp-1-uid","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-gcp-1-uid","k8s.pod.name":"pod-gb-gcp-1","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-gcp-1","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-gcp-1-uid","k8s.pod.name":"pod-gb-gcp-1","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-gcp-1","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-gcp-1-uid","k8s.pod.name":"pod-gb-gcp-1","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-gcp-1","k8s.cluster.name":"gb-gcp-1","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-gcp-2","k8s.node.uid":"node-gb-gcp-2-uid","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-gcp-2-uid","k8s.pod.name":"pod-gb-gcp-2","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-gcp-2","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-gcp-2-uid","k8s.pod.name":"pod-gb-gcp-2","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-gcp-2","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-gcp-2-uid","k8s.pod.name":"pod-gb-gcp-2","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-gcp-2","k8s.cluster.name":"gb-gcp-2","cloud.provider":"gcp"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-aws-1","k8s.node.uid":"node-gb-aws-1-uid","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-aws-1-uid","k8s.pod.name":"pod-gb-aws-1","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-aws-1","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-aws-1-uid","k8s.pod.name":"pod-gb-aws-1","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-aws-1","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-aws-1-uid","k8s.pod.name":"pod-gb-aws-1","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-aws-1","k8s.cluster.name":"gb-aws-1","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_cpu","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":4.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.memory.working_set","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":1000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.allocatable_memory","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":8000000000.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.condition_ready","labels":{"k8s.node.name":"node-gb-aws-2","k8s.node.uid":"node-gb-aws-2-uid","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-aws-2-uid","k8s.pod.name":"pod-gb-aws-2","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-aws-2","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:00:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-aws-2-uid","k8s.pod.name":"pod-gb-aws-2","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-aws-2","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:02:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.pod.phase","labels":{"k8s.pod.uid":"pod-gb-aws-2-uid","k8s.pod.name":"pod-gb-aws-2","k8s.namespace.name":"ns-x","k8s.node.name":"node-gb-aws-2","k8s.cluster.name":"gb-aws-2","cloud.provider":"aws"},"timestamp":"2025-01-10T10:04:00+00:00","value":2,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}

View File

@@ -0,0 +1,3 @@
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"miss-n","k8s.node.uid":"miss-n-uid","k8s.cluster.name":"miss-cluster"},"timestamp":"2025-01-10T10:00:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"miss-n","k8s.node.uid":"miss-n-uid","k8s.cluster.name":"miss-cluster"},"timestamp":"2025-01-10T10:02:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
{"metric_name":"k8s.node.cpu.usage","labels":{"k8s.node.name":"miss-n","k8s.node.uid":"miss-n-uid","k8s.cluster.name":"miss-cluster"},"timestamp":"2025-01-10T10:04:00+00:00","value":0.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}

Some files were not shown because too many files have changed in this diff Show More