Compare commits

..

7 Commits

Author SHA1 Message Date
Vinícius Lourenço
f20071dc19 feat(pnpm): migrate from yarn 2026-03-07 02:57:35 -03:00
Ashwin Bhatkal
08713cbf7d chore: support for merge queues (#10513)
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-03-06 14:22:17 +00:00
Ashwin Bhatkal
235dacf4a3 chore(frontend): separate out columnWidths from ResizeTable (#10510)
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(frontend): separate out columnWidths from ResizeTable

* chore: fix tests
2026-03-06 19:20:17 +05:30
Nityananda Gohain
3a82085eb4 chore: enrich clickhouse log_comment (#10446)
* chore: enchance clickhouse log_comment

* chore: enrich all clickhouse queries

* fix: remove is_meatadata

* fix: use the new func

* fix: use semconv keys

* fix: add more details

* fix: minor changes

* fix: address comments

* fix: address cursor comments

* fix: minor changes

* fix: addressed comments

* fix: address comments and move to module functions where possible

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2026-03-06 12:57:15 +00:00
SagarRajput-7
7691f3451a feat: revamped the settings nav and setting dropdown (#10494)
* feat: revamped the settings nav and setting dropdown

* feat: settings nav categorisation

* feat: added test cases

* feat: refactored code

* feat: updated test case

* feat: added keyboard shortcuts back into the settings nav at the bottom section
2026-03-06 10:32:25 +00:00
primus-bot[bot]
ded51c4379 chore(release): bump to v0.114.1 (#10515)
Co-authored-by: primus-bot[bot] <171087277+primus-bot[bot]@users.noreply.github.com>
2026-03-06 12:23:42 +05:30
Yunus M
ef8790b708 chore: remove search nav item from sidenav (#10512) 2026-03-06 06:13:30 +00:00
74 changed files with 26186 additions and 552 deletions

View File

@@ -7,10 +7,14 @@ on:
pull_request_target:
types:
- labeled
merge_group:
types:
- checks_requested
jobs:
refcheck:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest

View File

@@ -7,10 +7,14 @@ on:
pull_request_target:
types:
- labeled
merge_group:
types:
- checks_requested
jobs:
test:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/go-test.yaml@main
@@ -21,6 +25,7 @@ jobs:
GO_VERSION: 1.24
fmt:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/go-fmt.yaml@main
@@ -30,6 +35,7 @@ jobs:
GO_VERSION: 1.24
lint:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/go-lint.yaml@main
@@ -39,6 +45,7 @@ jobs:
GO_VERSION: 1.24
deps:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/go-deps.yaml@main
@@ -48,6 +55,7 @@ jobs:
GO_VERSION: 1.24
build:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest
@@ -79,6 +87,7 @@ jobs:
make docker-build-enterprise
openapi:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest

View File

@@ -7,10 +7,14 @@ on:
pull_request_target:
types:
- labeled
merge_group:
types:
- checks_requested
jobs:
tsc:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest
@@ -27,6 +31,7 @@ jobs:
run: cd frontend && yarn tsc
tsc2:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/js-tsc.yaml@main
@@ -36,6 +41,7 @@ jobs:
JS_SRC: frontend
test:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/js-test.yaml@main
@@ -45,6 +51,7 @@ jobs:
JS_SRC: frontend
fmt:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/js-fmt.yaml@main
@@ -54,6 +61,7 @@ jobs:
JS_SRC: frontend
lint:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
uses: signoz/primus.workflows/.github/workflows/js-lint.yaml@main
@@ -61,8 +69,53 @@ jobs:
with:
PRIMUS_REF: main
JS_SRC: frontend
lint-pnpm:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: setup node
uses: actions/setup-node@v6
with:
node-version: "22"
- name: install
run: cd frontend && pnpm install
- name: lint
run: echo 'done'
lint-pnpm-cache:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: setup node
uses: actions/setup-node@v6
with:
node-version: "22"
cache: 'pnpm'
cache-dependency-path: frontend/pnpm-lock.yaml
- name: install
run: cd frontend && pnpm install
- name: lint
run: echo 'done'
md-languages:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest
@@ -73,6 +126,7 @@ jobs:
run: bash frontend/scripts/validate-md-languages.sh
authz:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest

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.114.0
image: signoz/signoz:v0.114.1
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.114.0
image: signoz/signoz:v0.114.1
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.114.0}
image: signoz/signoz:${VERSION:-v0.114.1}
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.114.0}
image: signoz/signoz:${VERSION:-v0.114.1}
container_name: signoz
ports:
- "8080:8080" # signoz port

View File

@@ -1,216 +0,0 @@
# Query Range v5 — Design Principles & Architectural Contracts
## Purpose of This Document
This document defines the design principles, invariants, and architectural contracts of the Query Range v5 system. It is intended for the authors working on the querier and querier related parts codebase. Any change to the system must align with the principles described here. If a change would violate a principle, it must be flagged and discussed.
---
## Core Architectural Principle
**The user speaks OpenTelemetry. The storage speaks ClickHouse. The system translates between them. These two worlds must never leak into each other.**
Every design choice in Query Range flows from this separation. The user-facing API surface deals exclusively in `TelemetryFieldKey`: a representation of fields as they exist in the OpenTelemetry data model. The storage layer deals in ClickHouse column expressions, table names, and SQL fragments. The translation between them is mediated by a small set of composable abstractions with strict boundaries.
---
## The Central Type: `TelemetryFieldKey`
`TelemetryFieldKey` is the atomic unit of the entire query system. Every filter, aggregation, group-by, order-by, and select operation is expressed in terms of field keys. Understanding its design contracts is non-negotiable.
### Identity
A field key is identified by three dimensions:
- **Name** — the field name as the user knows it (`service.name`, `http.method`, `trace_id`)
- **FieldContext** — where the field lives in the OTel model (`resource`, `attribute`, `span`, `log`, `body`, `scope`, `event`, `metric`)
- **FieldDataType** — the data type (`string`, `bool`, `number`/`float64`/`int64`, array variants)
### Invariant: Same name does not mean same field
`status` as an attribute, `status` as a body JSON key, and `status` as a span-level field are **three different fields**. The context disambiguates. Code that resolves or compares field keys must always consider all three dimensions, never just the name.
### Invariant: Normalization happens once, at the boundary
`TelemetryFieldKey.Normalize()` is called during JSON unmarshaling. After normalization, the text representation `resource.service.name:string` and the programmatic construction `{Name: "service.name", FieldContext: Resource, FieldDataType: String}` are identical.
**Consequence:** Downstream code must never re-parse or re-normalize field keys. If you find yourself splitting on `.` or `:` deep in the pipeline, something is wrong — the normalization should have already happened.
### Invariant: The text format is `context.name:datatype`
Parsing rules (implemented in `GetFieldKeyFromKeyText` and `Normalize`):
1. Data type is extracted from the right, after the last `:`.
2. Field context is extracted from the left, before the first `.`, if it matches a known context prefix.
3. Everything remaining is the name.
Special case: `log.body.X` normalizes to `{FieldContext: body, Name: X}` — the `log.body.` prefix collapses because body fields under log are a nested context.
### Invariant: Historical aliases must be preserved
The `fieldContexts` map includes aliases (`tag` -> `attribute`, `spanfield` -> `span`, `logfield` -> `log`). These exist because older database entries use these names. Removing or changing these aliases will break existing saved queries and dashboard configurations.
---
## The Abstraction Stack
The query pipeline is built from four interfaces that compose vertically. Each layer has a single responsibility. Each layer depends only on the layers below it. This layering is intentional and must be preserved.
```
StatementBuilder <- Orchestrates everything into executable SQL
├── AggExprRewriter <- Rewrites aggregation expressions (maps field refs to columns)
├── ConditionBuilder <- Builds WHERE predicates (field + operator + value -> SQL)
└── FieldMapper <- Maps TelemetryFieldKey -> ClickHouse column expression
```
### FieldMapper
**Contract:** Given a `TelemetryFieldKey`, return a ClickHouse column expression that yields the value for that field when used in a SELECT.
**Principle:** This is the *only* place where field-to-column translation happens. No other layer should contain knowledge of how fields map to storage. If you need a column expression, go through the FieldMapper.
**Why:** The user says `http.request.method`. ClickHouse might store it as `attributes_string['http.request.method']`, or as a materialized column `` `attribute_string_http$$request$$method` ``, or via a JSON access path in a body column. This variation is entirely contained within the FieldMapper. Everything above it is storage-agnostic.
### ConditionBuilder
**Contract:** Given a field key, an operator, and a value, produce a valid SQL predicate for a WHERE clause.
**Dependency:** Uses FieldMapper for the left-hand side of the condition.
**Principle:** The ConditionBuilder owns all the complexity of operator semantics, i.e type casting, array operators (`hasAny`/`hasAll` vs `=`), existence checks, and negative operator behavior. This complexity must not leak upward into the StatementBuilder.
### AggExprRewriter
**Contract:** Given a user-facing aggregation expression like `sum(duration_nano)`, resolve field references within it and produce valid ClickHouse SQL.
**Dependency:** Uses FieldMapper to resolve field names within expressions.
**Principle:** Aggregation expressions are user-authored strings that contain field references. The rewriter parses them, identifies field references, resolves each through the FieldMapper, and reassembles the expression.
### StatementBuilder
**Contract:** Given a complete `QueryBuilderQuery`, a time range, and a request type, produces an executable SQL statement.
**Dependency:** Uses all three abstractions above.
**Principle:** This is the composition layer. It does not contain field mapping logic, condition building logic, or expression rewriting logic. It orchestrates the other abstractions. If you find storage-specific logic creeping into the StatementBuilder, push it down into the appropriate abstraction.
### Invariant: No layer skipping
The StatementBuilder must not call FieldMapper directly to build conditions, it goes through the ConditionBuilder. The AggExprRewriter must not hardcode column names, it goes through the FieldMapper. Skipping layers creates hidden coupling and makes the system fragile to storage changes.
---
## Design Decisions as Constraints
### Constraint: Formula evaluation happens in Go, not in ClickHouse
Formulas (`A + B`, `A / B`, `sqrt(A*A + B*B)`) are evaluated application-side by `FormulaEvaluator`, not via ClickHouse JOINs.
**Why this is a constraint, not just an implementation choice:** The original JOIN-based approach was abandoned because ClickHouse evaluates joins right-to-left, serializing execution unnecessarily. Running queries independently allows parallelism and caching of intermediate results. Any future optimization must not reintroduce the JOIN pattern without solving the serialization problem.
**Consequence:** Individual query results must be independently cacheable. Formula evaluation must handle label matching, timestamp alignment, and missing values without requiring the queries to coordinate at the SQL level.
### Constraint: Zero-defaulting is aggregation-dependent
Only additive/counting aggregations (`count`, `count_distinct`, `sum`, `rate`) default missing values to zero. Statistical aggregations (`avg`, `min`, `max`, percentiles) must show gaps.
**Why:** Absence of data has different meanings. No error requests in a time bucket means error count = 0. No requests at all means average latency is *unknown*, not 0. Conflating these is a correctness bug, not a display preference.
**Enforcement:** `GetQueriesSupportingZeroDefault` determines which queries can default to zero. The `FormulaEvaluator` consumes this via `canDefaultZero`. Changes to aggregation handling must preserve this distinction.
### Constraint: Existence semantics differ for positive vs negative operators
- **Positive operators** (`=`, `>`, `LIKE`, `IN`, etc.) implicitly assert field existence. `http.method = GET` means "the field exists AND equals GET".
- **Negative operators** (`!=`, `NOT IN`, `NOT LIKE`, etc.) do **not** add an existence check. `http.method != GET` includes records where the field doesn't exist at all.
**Why:** The user's intent with negative operators is ambiguous. Rather than guess, we take the broader interpretation. Users can add an explicit `EXISTS` filter if they want the narrower one. This is documented in `AddDefaultExistsFilter`.
**Consequence:** Any new operator must declare its existence behavior in `AddDefaultExistsFilter`. Do not add operators without considering this.
### Constraint: Post-processing functions operate on result sets, not in SQL
Functions like `cutOffMin`, `ewma`, `median`, `timeShift`, `fillZero`, `runningDiff`, and `cumulativeSum` are applied in Go on the returned time series, not pushed into ClickHouse SQL.
**Why:** These are sequential time-series transformations that require complete, ordered result sets. Pushing them into SQL would complicate query generation, prevent caching of raw results, and make the functions harder to test. They are applied via `ApplyFunctions` after query execution.
**Consequence:** New time-series transformation functions should follow this pattern i.e implement them as Go functions on `*TimeSeries`, not as SQL modifications.
### Constraint: The API surface rejects unknown fields with suggestions
All request types use custom `UnmarshalJSON` that calls `DisallowUnknownFields`. Unknown fields trigger error messages with Levenshtein-based suggestions ("did you mean: 'groupBy'?").
**Why:** Silent acceptance of unknown fields causes subtle bugs. A misspelled `groupBy` results in ungrouped data with no indication of what went wrong. Failing fast with suggestions turns errors into actionable feedback.
**Consequence:** Any new request type or query spec struct must implement custom unmarshaling with `UnmarshalJSONWithContext`. Do not use default `json.Unmarshal` for user-facing types.
### Constraint: Validation is context-sensitive to request type
What's valid depends on the `RequestType`. For aggregation requests (`time_series`, `scalar`, `distribution`), fields like `groupBy`, `aggregations`, `having`, and aggregation-referenced `orderBy` are validated. For non-aggregation requests (`raw`, `raw_stream`, `trace`), these fields are ignored.
**Why:** A raw log query doesn't have aggregations, so requiring `aggregations` would be wrong. But a time-series query without aggregations is meaningless. The validation rules are request-type-aware to avoid both false positives and false negatives.
**Consequence:** When adding new fields to query specs, consider which request types they apply to and gate validation accordingly.
---
## The Composite Query Model
### Structure
A `QueryRangeRequest` contains a `CompositeQuery` which holds `[]QueryEnvelope`. Each envelope is a discriminated union: a `Type` field determines how `Spec` is decoded.
### Invariant: Query names are unique within a composite query
Builder queries must have unique names. Formulas reference queries by name (`A`, `B`, `A.0`, `A.my_alias`). Duplicate names would make formula evaluation ambiguous.
### Invariant: Multi-aggregation uses indexed or aliased references
A single builder query can have multiple aggregations. They are accessed in formulas via:
- Index: `A.0`, `A.1` (zero-based)
- Alias: `A.total`, `A.error_count`
The default (just `A`) resolves to index 0. This is the formula evaluation contract and must be preserved.
### Invariant: Type-specific decoding through signal detection
Builder queries are decoded by first peeking at the `signal` field in the raw JSON, then unmarshaling into the appropriate generic type (`QueryBuilderQuery[TraceAggregation]`, `QueryBuilderQuery[LogAggregation]`, `QueryBuilderQuery[MetricAggregation]`). This two-pass decoding is intentional — it allows each signal to have its own aggregation schema while sharing the query structure.
---
## The Metadata Layer
### MetadataStore
The `MetadataStore` interface provides runtime field discovery and type resolution. It answers questions like "what fields exist for this signal?" and "what are the data types of field X?".
### Principle: Fields can be ambiguous until resolved
The same name can map to multiple `TelemetryFieldKey` variants (different contexts, different types). The metadata store returns *all* variants. Resolution to a single field happens during query building, using the query's signal and any explicit context/type hints from the user.
**Consequence:** Code that calls `GetKey` or `GetKeys` must handle multiple results. Do not assume a name maps to a single field.
### Principle: Materialized fields are a performance optimization, not a semantic distinction
A materialized field and its non-materialized equivalent represent the same logical field. The `Materialized` flag tells the FieldMapper to generate a simpler column expression. The user should never need to know whether a field is materialized.
### Principle: JSON body fields require access plans
Fields inside JSON body columns (`body.response.errors[].code`) need pre-computed `JSONAccessPlan` trees that encode the traversal path, including branching at array boundaries between `Array(JSON)` and `Array(Dynamic)` representations. These plans are computed during metadata resolution, not during query execution.
---
## Summary of Inviolable Rules
1. **User-facing types never contain ClickHouse column names or SQL fragments.**
2. **Field-to-column translation only happens in FieldMapper.**
3. **Normalization happens once at the API boundary, never deeper.**
4. **Historical aliases in fieldContexts and fieldDataTypes must not be removed.**
5. **Formula evaluation stays in Go — do not push it into ClickHouse JOINs.**
6. **Zero-defaulting is aggregation-type-dependent — do not universally default to zero.**
7. **Positive operators imply existence, negative operators do not.**
8. **Post-processing functions operate on Go result sets, not in SQL.**
9. **All user-facing types reject unknown JSON fields with suggestions.**
10. **Validation rules are gated by request type.**
11. **Query names must be unique within a composite query.**
12. **The four-layer abstraction stack (FieldMapper -> ConditionBuilder -> AggExprRewriter -> StatementBuilder) must not be bypassed or flattened.**

View File

@@ -8,6 +8,8 @@ import (
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
)
@@ -67,6 +69,10 @@ func (p *BaseSeasonalProvider) toTSResults(ctx context.Context, resp *qbtypes.Qu
}
func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID, params *anomalyQueryParams) (*anomalyQueryResults, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "anomaly",
instrumentationtypes.CodeFunctionName: "getResults",
})
// TODO(srikanthccv): parallelize this?
p.logger.InfoContext(ctx, "fetching results for current period", "anomaly_current_period_query", params.CurrentPeriodQuery)
currentPeriodResults, err := p.querier.QueryRange(ctx, orgID, &params.CurrentPeriodQuery)

View File

@@ -15,7 +15,9 @@ import (
"github.com/SigNoz/signoz/pkg/queryparser"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/roletypes"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -105,6 +107,10 @@ func (module *module) GetPublicDashboardSelectorsAndOrg(ctx context.Context, id
}
func (module *module) GetPublicWidgetQueryRange(ctx context.Context, id valuer.UUID, widgetIdx, startTime, endTime uint64) (*querybuildertypesv5.QueryRangeResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "dashboard",
instrumentationtypes.CodeFunctionName: "GetPublicWidgetQueryRange",
})
dashboard, err := module.GetDashboardByPublicID(ctx, id)
if err != nil {
return nil, err

View File

@@ -10,6 +10,8 @@ import (
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/query-service/utils/labels"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"go.uber.org/zap"
)
@@ -61,6 +63,10 @@ func (p *BaseSeasonalProvider) getQueryParams(req *GetAnomaliesRequest) *anomaly
}
func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID, params *anomalyQueryParams) (*anomalyQueryResults, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "anomaly",
instrumentationtypes.CodeFunctionName: "getResults",
})
zap.L().Info("fetching results for current period", zap.Any("currentPeriodQuery", params.CurrentPeriodQuery))
currentPeriodResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.CurrentPeriodQuery)
if err != nil {

View File

@@ -37,10 +37,10 @@
"@dnd-kit/modifiers": "7.0.0",
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@grafana/data": "^11.2.3",
"@grafana/data": "11.2.3",
"@mdx-js/loader": "2.3.0",
"@mdx-js/react": "2.3.0",
"@monaco-editor/react": "^4.3.1",
"@monaco-editor/react": "4.3.1",
"@playwright/test": "1.55.1",
"@radix-ui/react-tabs": "1.0.4",
"@radix-ui/react-tooltip": "1.0.7",
@@ -54,7 +54,7 @@
"@signozhq/combobox": "0.0.2",
"@signozhq/command": "0.0.0",
"@signozhq/design-tokens": "2.1.1",
"@signozhq/dialog": "^0.0.2",
"@signozhq/dialog": "0.0.2",
"@signozhq/icons": "0.1.0",
"@signozhq/input": "0.0.2",
"@signozhq/popover": "0.0.0",
@@ -63,7 +63,7 @@
"@signozhq/sonner": "0.1.0",
"@signozhq/switch": "0.0.2",
"@signozhq/table": "0.3.7",
"@signozhq/toggle-group": "^0.0.1",
"@signozhq/toggle-group": "0.0.1",
"@signozhq/tooltip": "0.0.2",
"@tanstack/react-table": "8.20.6",
"@tanstack/react-virtual": "3.11.2",
@@ -76,28 +76,28 @@
"@visx/shape": "3.5.0",
"@visx/tooltip": "3.3.0",
"@vitejs/plugin-react": "5.1.4",
"@xstate/react": "^3.0.0",
"@xstate/react": "3.0.0",
"ansi-to-html": "0.7.2",
"antd": "5.11.0",
"antd-table-saveas-excel": "2.2.1",
"antlr4": "4.13.2",
"axios": "1.12.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^29.6.4",
"babel-eslint": "10.1.0",
"babel-jest": "29.6.4",
"babel-loader": "9.1.3",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-minify": "^0.5.1",
"babel-preset-react-app": "^10.0.1",
"babel-plugin-named-asset-import": "0.3.7",
"babel-preset-minify": "0.5.1",
"babel-preset-react-app": "10.0.1",
"chart.js": "3.9.1",
"chartjs-adapter-date-fns": "^2.0.0",
"chartjs-plugin-annotation": "^1.4.0",
"chartjs-adapter-date-fns": "2.0.0",
"chartjs-plugin-annotation": "1.4.0",
"classnames": "2.3.2",
"color": "^4.2.1",
"color": "4.2.1",
"color-alpha": "2.0.0",
"cross-env": "^7.0.3",
"cross-env": "7.0.3",
"crypto-js": "4.2.0",
"d3-hierarchy": "3.1.2",
"dayjs": "^1.10.7",
"dayjs": "1.10.7",
"dompurify": "3.2.4",
"dotenv": "8.2.0",
"event-source-polyfill": "1.0.31",
@@ -106,19 +106,19 @@
"history": "4.10.1",
"http-proxy-middleware": "3.0.5",
"http-status-codes": "2.3.0",
"i18next": "^21.6.12",
"i18next-browser-languagedetector": "^6.1.3",
"i18next-http-backend": "^1.3.2",
"i18next": "21.6.12",
"i18next-browser-languagedetector": "6.1.3",
"i18next-http-backend": "1.3.2",
"immer": "11.1.3",
"jest": "30.2.0",
"js-base64": "^3.7.2",
"lodash-es": "^4.17.21",
"js-base64": "3.7.2",
"lodash-es": "4.17.21",
"lucide-react": "0.498.0",
"mini-css-extract-plugin": "2.4.5",
"motion": "12.4.13",
"nuqs": "2.8.8",
"overlayscrollbars": "^2.8.1",
"overlayscrollbars-react": "^0.5.6",
"overlayscrollbars": "2.8.1",
"overlayscrollbars-react": "0.5.6",
"papaparse": "5.4.1",
"posthog-js": "1.298.0",
"rc-tween-one": "3.0.6",
@@ -130,36 +130,36 @@
"react-dom": "18.2.0",
"react-drag-listview": "2.0.0",
"react-error-boundary": "4.0.11",
"react-force-graph-2d": "^1.29.1",
"react-force-graph-2d": "1.29.1",
"react-full-screen": "1.1.1",
"react-grid-layout": "^1.3.4",
"react-grid-layout": "1.3.4",
"react-helmet-async": "1.3.0",
"react-i18next": "^11.16.1",
"react-i18next": "11.16.1",
"react-lottie": "1.2.10",
"react-markdown": "8.0.7",
"react-query": "3.39.3",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-redux": "7.2.2",
"react-router-dom": "5.2.0",
"react-router-dom-v5-compat": "6.27.0",
"react-syntax-highlighter": "15.5.0",
"react-use": "^17.3.2",
"react-use": "17.3.2",
"react-virtuoso": "4.0.3",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"redux": "4.0.5",
"redux-thunk": "2.3.0",
"rehype-raw": "7.0.0",
"rollup-plugin-visualizer": "7.0.0",
"rrule": "2.8.1",
"stream": "^0.0.2",
"styled-components": "^5.3.11",
"timestamp-nano": "^1.0.0",
"ts-node": "^10.2.1",
"stream": "0.0.2",
"styled-components": "5.3.11",
"timestamp-nano": "1.0.0",
"ts-node": "10.2.1",
"typescript": "5.9.3",
"uplot": "1.6.31",
"uuid": "^8.3.2",
"uuid": "8.3.2",
"vite": "npm:rolldown-vite@7.3.1",
"vite-plugin-html": "3.2.2",
"web-vitals": "^0.2.4",
"xstate": "^4.31.0",
"web-vitals": "0.2.4",
"xstate": "4.31.0",
"zustand": "5.0.11"
},
"browserslist": {
@@ -175,69 +175,70 @@
]
},
"devDependencies": {
"@babel/core": "^7.22.11",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-syntax-jsx": "^7.12.13",
"@babel/preset-env": "^7.22.14",
"@babel/preset-react": "^7.12.13",
"@babel/preset-typescript": "^7.21.4",
"@commitlint/cli": "^20.4.2",
"@commitlint/config-conventional": "^20.4.2",
"@babel/core": "7.22.11",
"@babel/plugin-proposal-class-properties": "7.18.6",
"@babel/plugin-syntax-jsx": "7.12.13",
"@babel/preset-env": "7.22.14",
"@babel/preset-react": "7.12.13",
"@babel/preset-typescript": "7.21.4",
"@commitlint/cli": "20.4.2",
"@commitlint/config-conventional": "20.4.2",
"@faker-js/faker": "9.3.0",
"@jest/globals": "30.2.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
"@testing-library/user-event": "14.4.3",
"@types/color": "^3.0.3",
"@types/color": "3.0.3",
"@types/crypto-js": "4.2.2",
"@types/dompurify": "^2.4.0",
"@types/event-source-polyfill": "^1.0.0",
"@types/dompurify": "2.4.0",
"@types/event-source-polyfill": "1.0.0",
"@types/fontfaceobserver": "2.1.0",
"@types/jest": "30.0.0",
"@types/lodash-es": "^4.17.4",
"@types/mini-css-extract-plugin": "^2.5.1",
"@types/node": "^16.10.3",
"@types/lodash-es": "4.17.4",
"@types/mini-css-extract-plugin": "2.5.1",
"@types/node": "16.10.3",
"@types/papaparse": "5.3.7",
"@types/react": "18.0.26",
"@types/react-addons-update": "0.14.21",
"@types/react-beautiful-dnd": "13.1.8",
"@types/react-dom": "18.0.10",
"@types/react-grid-layout": "^1.1.2",
"@types/react-grid-layout": "1.1.2",
"@types/react-helmet-async": "1.0.3",
"@types/react-lottie": "1.2.10",
"@types/react-redux": "^7.1.11",
"@types/react-redux": "7.1.11",
"@types/react-resizable": "3.0.3",
"@types/react-router-dom": "^5.1.6",
"@types/react-router-dom": "5.1.6",
"@types/react-syntax-highlighter": "15.5.13",
"@types/redux-mock-store": "1.0.4",
"@types/styled-components": "^5.1.4",
"@types/uuid": "^8.3.1",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@types/styled-components": "5.1.4",
"@types/uuid": "8.3.1",
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"autoprefixer": "10.4.19",
"babel-plugin-styled-components": "^1.12.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-jest": "^29.15.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-sonarjs": "^0.12.0",
"husky": "^7.0.4",
"imagemin": "^8.0.1",
"imagemin-svgo": "^10.0.1",
"is-ci": "^3.0.1",
"babel-plugin-styled-components": "1.12.0",
"eslint": "7.32.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.28.1",
"eslint-plugin-jest": "29.15.0",
"eslint-plugin-jsx-a11y": "6.5.1",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-react": "7.24.0",
"eslint-plugin-react-hooks": "4.3.0",
"eslint-plugin-simple-import-sort": "7.0.0",
"eslint-plugin-sonarjs": "0.12.0",
"glob": "13.0.6",
"husky": "7.0.4",
"imagemin": "8.0.1",
"imagemin-svgo": "10.0.1",
"is-ci": "3.0.1",
"jest-environment-jsdom": "29.7.0",
"jest-environment-node": "29.7.0",
"jest-styled-components": "^7.2.0",
"lint-staged": "^12.5.0",
"jest-styled-components": "7.2.0",
"lint-staged": "12.5.0",
"msw": "1.3.2",
"npm-run-all": "latest",
"orval": "7.18.0",
"portfinder-sync": "^0.0.2",
"portfinder-sync": "0.0.2",
"postcss": "8.5.6",
"prettier": "2.2.1",
"prop-types": "15.8.1",
@@ -249,7 +250,7 @@
"svgo": "4.0.0",
"ts-api-utils": "2.4.0",
"ts-jest": "29.4.6",
"ts-node": "^10.2.1",
"ts-node": "10.2.1",
"typescript-plugin-css-modules": "5.2.0",
"vite-plugin-checker": "0.12.0",
"vite-plugin-compression": "0.5.1",
@@ -268,18 +269,40 @@
"debug": "4.3.4",
"semver": "7.5.4",
"xml2js": "0.5.0",
"phin": "^3.7.1",
"phin": "3.7.1",
"body-parser": "1.20.3",
"http-proxy-middleware": "3.0.5",
"cross-spawn": "7.0.5",
"cookie": "^0.7.1",
"cookie": "0.7.1",
"serialize-javascript": "6.0.2",
"prismjs": "1.30.0",
"got": "11.8.5",
"form-data": "4.0.4",
"brace-expansion": "^2.0.2",
"on-headers": "^1.1.0",
"brace-expansion": "2.0.2",
"on-headers": "1.1.0",
"tmp": "0.2.4",
"vite": "npm:rolldown-vite@7.3.1"
},
"pnpm": {
"overrides": {
"@types/react": "18.0.26",
"@types/react-dom": "18.0.10",
"debug": "4.3.4",
"semver": "7.5.4",
"xml2js": "0.5.0",
"phin": "3.7.1",
"body-parser": "1.20.3",
"http-proxy-middleware": "3.0.5",
"cross-spawn": "7.0.5",
"cookie": "0.7.1",
"serialize-javascript": "6.0.2",
"prismjs": "1.30.0",
"got": "11.8.5",
"form-data": "4.0.4",
"brace-expansion": "2.0.2",
"on-headers": "1.1.0",
"tmp": "0.2.4",
"vite": "npm:rolldown-vite@7.3.1"
}
}
}

24227
frontend/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -14,8 +14,6 @@ import cx from 'classnames';
import { dragColumnParams } from 'hooks/useDragColumns/configs';
import { getColumnWidth, RowData } from 'lib/query/createTableColumnsFromQuery';
import { debounce, set } from 'lodash-es';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { Widgets } from 'types/api/dashboard/getAll';
import ResizableHeader from './ResizableHeader';
import { DragSpanStyle } from './styles';
@@ -26,31 +24,26 @@ function ResizeTable({
columns,
onDragColumn,
pagination,
widgetId,
shouldPersistColumnWidths = false,
columnWidths,
onColumnWidthsChange,
...restProps
}: ResizeTableProps): JSX.Element {
const [columnsData, setColumns] = useState<ColumnsType>([]);
const { setColumnWidths, selectedDashboard } = useDashboard();
const columnWidths = shouldPersistColumnWidths
? (selectedDashboard?.data?.widgets?.find(
(widget) => widget.id === widgetId,
) as Widgets)?.columnWidths
: undefined;
const onColumnWidthsChangeRef = useRef(onColumnWidthsChange);
const updateAllColumnWidths = useRef(
debounce((widthsConfig: Record<string, number>) => {
if (!widgetId || !shouldPersistColumnWidths) {
if (!onColumnWidthsChangeRef.current) {
return;
}
setColumnWidths?.((prev) => ({
...prev,
[widgetId]: widthsConfig,
}));
onColumnWidthsChangeRef.current(widthsConfig);
}, 1000),
).current;
useEffect(() => {
onColumnWidthsChangeRef.current = onColumnWidthsChange;
}, [onColumnWidthsChange]);
const handleResize = useCallback(
(index: number) => (
e: SyntheticEvent<Element>,
@@ -75,7 +68,7 @@ function ResizeTable({
...col,
...(onDragColumn && {
title: (
<DragSpanStyle className="dragHandler">
<DragSpanStyle className="dragHandler" data-testid="drag-column-title">
{col?.title?.toString() || ''}
</DragSpanStyle>
),
@@ -106,31 +99,31 @@ function ResizeTable({
}, [mergedColumns, pagination, restProps]);
useEffect(() => {
if (columns) {
// Apply stored column widths from widget configuration
const columnsWithStoredWidths = columns.map((col) => {
const dataIndex = (col as RowData).dataIndex as string;
if (dataIndex && columnWidths) {
const width = getColumnWidth(dataIndex, columnWidths);
if (width) {
return {
...col,
width, // Apply stored width
};
}
}
return col;
});
setColumns(columnsWithStoredWidths);
}
}, [columns, columnWidths]);
useEffect(() => {
if (!shouldPersistColumnWidths) {
if (!columns) {
return;
}
// Collect all column widths in a single object
const columnsWithStoredWidths = columns.map((col) => {
const dataIndex = (col as RowData).dataIndex as string;
if (dataIndex && columnWidths) {
const width = getColumnWidth(dataIndex, columnWidths);
if (width) {
return { ...col, width };
}
}
return col;
});
setColumns(columnsWithStoredWidths);
}, [columns, columnWidths]);
const lastReportedWidthsRef = useRef<Record<string, number>>({});
useEffect(() => {
if (!onColumnWidthsChange) {
return;
}
const newColumnWidths: Record<string, number> = {};
mergedColumns.forEach((col) => {
@@ -140,11 +133,20 @@ function ResizeTable({
}
});
// Only update if there are actual widths to set
if (Object.keys(newColumnWidths).length > 0) {
if (Object.keys(newColumnWidths).length === 0) {
return;
}
const last = lastReportedWidthsRef.current;
const hasChange =
Object.keys(newColumnWidths).length !== Object.keys(last).length ||
Object.keys(newColumnWidths).some((k) => newColumnWidths[k] !== last[k]);
if (hasChange) {
lastReportedWidthsRef.current = newColumnWidths;
updateAllColumnWidths(newColumnWidths);
}
}, [mergedColumns, updateAllColumnWidths, shouldPersistColumnWidths]);
}, [mergedColumns, updateAllColumnWidths, onColumnWidthsChange]);
return onDragColumn ? (
<ReactDragListView.DragColumn {...dragColumnParams} onDragEnd={onDragColumn}>

View File

@@ -0,0 +1,244 @@
import { act } from '@testing-library/react';
import { render, screen, userEvent } from 'tests/test-utils';
import ResizeTable from '../ResizeTable';
jest.mock('react-resizable', () => ({
Resizable: ({
children,
onResize,
width,
}: {
children: React.ReactNode;
onResize: (
e: React.SyntheticEvent,
data: { size: { width: number } },
) => void;
width: number;
}): JSX.Element => (
<div>
{children}
<button
data-testid="resize-trigger"
type="button"
onClick={(e): void => onResize(e, { size: { width: width + 50 } })}
/>
</div>
),
}));
// Make debounce synchronous so onColumnWidthsChange fires immediately
jest.mock('lodash-es', () => ({
...jest.requireActual('lodash-es'),
debounce: (fn: (...args: any[]) => any): ((...args: any[]) => any) => fn,
}));
const baseColumns = [
{ dataIndex: 'name', title: 'Name', width: 100 },
{ dataIndex: 'value', title: 'Value', width: 100 },
];
const baseDataSource = [
{ key: '1', name: 'Alice', value: 42 },
{ key: '2', name: 'Bob', value: 99 },
];
describe('ResizeTable', () => {
it('renders column headers and data rows', () => {
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
/>,
);
expect(screen.getByText('Name')).toBeInTheDocument();
expect(screen.getByText('Value')).toBeInTheDocument();
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('Bob')).toBeInTheDocument();
});
it('overrides column widths from columnWidths prop and reports them via onColumnWidthsChange', () => {
const onColumnWidthsChange = jest.fn();
act(() => {
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
columnWidths={{ name: 250, value: 180 }}
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
});
expect(onColumnWidthsChange).toHaveBeenCalledWith(
expect.objectContaining({ name: 250, value: 180 }),
);
});
it('reports original column widths via onColumnWidthsChange when columnWidths prop is not provided', () => {
const onColumnWidthsChange = jest.fn();
act(() => {
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
});
expect(onColumnWidthsChange).toHaveBeenCalledWith(
expect.objectContaining({ name: 100, value: 100 }),
);
});
it('does not call onColumnWidthsChange when it is not provided', () => {
// Should render without errors and without attempting to call an undefined callback
expect(() => {
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
/>,
);
}).not.toThrow();
});
it('only overrides the column that has a stored width, leaving others at their original width', () => {
const onColumnWidthsChange = jest.fn();
act(() => {
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
columnWidths={{ name: 250 }}
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
});
expect(onColumnWidthsChange).toHaveBeenCalledWith(
expect.objectContaining({ name: 250, value: 100 }),
);
});
it('does not call onColumnWidthsChange on re-render when widths have not changed', () => {
const onColumnWidthsChange = jest.fn();
const { rerender } = render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
expect(onColumnWidthsChange).toHaveBeenCalledTimes(1);
onColumnWidthsChange.mockClear();
rerender(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
expect(onColumnWidthsChange).not.toHaveBeenCalled();
});
it('does not call onColumnWidthsChange when no column has a defined width', () => {
const onColumnWidthsChange = jest.fn();
render(
<ResizeTable
columns={[
{ dataIndex: 'name', title: 'Name' },
{ dataIndex: 'value', title: 'Value' },
]}
dataSource={baseDataSource}
rowKey="key"
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
expect(onColumnWidthsChange).not.toHaveBeenCalled();
});
it('calls onColumnWidthsChange with the new width after a column is resized', async () => {
const user = userEvent.setup();
const onColumnWidthsChange = jest.fn();
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
onColumnWidthsChange.mockClear();
// Click the first column's resize trigger — mock adds 50px to the current width (100 → 150)
const [firstResizeTrigger] = screen.getAllByTestId('resize-trigger');
await user.click(firstResizeTrigger);
expect(onColumnWidthsChange).toHaveBeenCalledWith(
expect.objectContaining({ name: 150, value: 100 }),
);
});
it('does not affect other columns when only one column is resized', async () => {
const user = userEvent.setup();
const onColumnWidthsChange = jest.fn();
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
onColumnWidthsChange={onColumnWidthsChange}
/>,
);
onColumnWidthsChange.mockClear();
// Resize only the second column (value: 100 → 150), name should stay at 100
const resizeTriggers = screen.getAllByTestId('resize-trigger');
await user.click(resizeTriggers[1]);
expect(onColumnWidthsChange).toHaveBeenCalledWith(
expect.objectContaining({ name: 100, value: 150 }),
);
});
it('wraps column titles in drag handler spans when onDragColumn is provided', () => {
const onDragColumn = jest.fn();
render(
<ResizeTable
columns={baseColumns}
dataSource={baseDataSource}
rowKey="key"
onDragColumn={onDragColumn}
/>,
);
const dragTitles = screen.getAllByTestId('drag-column-title');
expect(dragTitles).toHaveLength(baseColumns.length);
expect(dragTitles[0]).toHaveTextContent('Name');
expect(dragTitles[1]).toHaveTextContent('Value');
});
});

View File

@@ -6,10 +6,25 @@ import { LaunchChatSupportProps } from 'components/LaunchChatSupport/LaunchChatS
import { TableDataSource } from './contants';
type ColumnWidths = Record<string, number>;
export interface ResizeTableProps extends TableProps<any> {
onDragColumn?: (fromIndex: number, toIndex: number) => void;
widgetId?: string;
shouldPersistColumnWidths?: boolean;
/**
* Pre-resolved column widths for this table, keyed by column dataIndex.
* Use this to apply persisted widths on mount (e.g. from widget.columnWidths).
* Do NOT pass a value that updates reactively on every resize — that creates a
* feedback loop. Pass only stable / persisted values.
*/
columnWidths?: ColumnWidths;
/**
* Called (debounced) whenever the user finishes resizing a column.
* The widths object contains all current column widths keyed by dataIndex.
* Intended for persisting widths to an external store (e.g. dashboard context
* staging buffer). The caller owns the storage; ResizeTable does not read back
* whatever is written here.
*/
onColumnWidthsChange?: (widths: ColumnWidths) => void;
}
export interface DynamicColumnTableProps extends TableProps<any> {
tablesource: typeof TableDataSource[keyof typeof TableDataSource];

View File

@@ -674,7 +674,7 @@ function GeneralSettings({
return (
<div className="general-settings-page">
<div className="general-settings-header">
<span className="general-settings-title">General</span>
<span className="general-settings-title">Workspace</span>
<span className="general-settings-subtitle">
Manage your workspace settings.
</span>

View File

@@ -81,7 +81,18 @@ function FullView({
setCurrentGraphRef(fullViewRef);
}, [setCurrentGraphRef]);
const { selectedDashboard, isDashboardLocked } = useDashboard();
const {
selectedDashboard,
isDashboardLocked,
setColumnWidths,
} = useDashboard();
const onColumnWidthsChange = useCallback(
(widths: Record<string, number>) => {
setColumnWidths((prev) => ({ ...prev, [widget.id]: widths }));
},
[setColumnWidths, widget.id],
);
const { dashboardVariables } = useDashboardVariables();
const { user } = useAppContext();
@@ -381,6 +392,7 @@ function FullView({
onClickHandler={onClickHandler}
enableDrillDown={enableDrillDown}
selectedGraph={selectedPanelType}
onColumnWidthsChange={onColumnWidthsChange}
/>
</GraphContainer>
</div>

View File

@@ -168,6 +168,9 @@ jest.mock('providers/Dashboard/Dashboard', () => ({
variables: [],
},
},
setLayouts: jest.fn(),
setSelectedDashboard: jest.fn(),
setColumnWidths: jest.fn(),
}),
}));

View File

@@ -101,7 +101,19 @@ function WidgetGraphComponent({
const navigateToExplorerPages = useNavigateToExplorerPages();
const { setLayouts, selectedDashboard, setSelectedDashboard } = useDashboard();
const {
setLayouts,
selectedDashboard,
setSelectedDashboard,
setColumnWidths,
} = useDashboard();
const onColumnWidthsChange = useCallback(
(widths: Record<string, number>) => {
setColumnWidths((prev) => ({ ...prev, [widget.id]: widths }));
},
[setColumnWidths, widget.id],
);
const onToggleModal = useCallback(
(func: Dispatch<SetStateAction<boolean>>) => {
@@ -424,6 +436,7 @@ function WidgetGraphComponent({
customSeries={customSeries}
customOnRowClick={customOnRowClick}
enableDrillDown={enableDrillDown}
onColumnWidthsChange={onColumnWidthsChange}
/>
</div>
)}

View File

@@ -45,6 +45,8 @@ function GridTableComponent({
onOpenTraceBtnClick,
customOnRowClick,
widgetId,
columnWidths,
onColumnWidthsChange,
panelType,
queryRangeRequest,
decimalPrecision,
@@ -284,6 +286,8 @@ function GridTableComponent({
dataSource={dataSource}
sticky={sticky}
widgetId={widgetId}
columnWidths={columnWidths}
onColumnWidthsChange={onColumnWidthsChange}
panelType={panelType}
queryRangeRequest={queryRangeRequest}
onRow={

View File

@@ -24,6 +24,8 @@ export type GridTableComponentProps = {
onOpenTraceBtnClick?: (record: RowData) => void;
customOnRowClick?: (record: RowData) => void;
widgetId?: string;
columnWidths?: Record<string, number>;
onColumnWidthsChange?: (widths: Record<string, number>) => void;
renderColumnCell?: QueryTableProps['renderColumnCell'];
customColTitles?: Record<string, string>;
enableDrillDown?: boolean;

View File

@@ -33,6 +33,7 @@ function LogsPanelComponent({
widget,
setRequestData,
queryResponse,
onColumnWidthsChange,
}: LogsPanelComponentProps): JSX.Element {
const [pageSize, setPageSize] = useState<number>(10);
const [offset, setOffset] = useState<number>(0);
@@ -145,8 +146,8 @@ function LogsPanelComponent({
columns={columns}
onRow={handleRow}
rowKey={(record): string => record.id}
widgetId={widget.id}
shouldPersistColumnWidths
columnWidths={widget.columnWidths}
onColumnWidthsChange={onColumnWidthsChange}
/>
</OverlayScrollbar>
</div>
@@ -189,6 +190,7 @@ export type LogsPanelComponentProps = {
Error
>;
widget: Widgets;
onColumnWidthsChange?: (widths: Record<string, number>) => void;
};
export default LogsPanelComponent;

View File

@@ -161,7 +161,7 @@ function MySettings(): JSX.Element {
<div className="my-settings-container">
<div className="user-info-section">
<div className="user-info-section-header">
<div className="user-info-section-title">General </div>
<div className="user-info-section-title">Account </div>
<div className="user-info-section-subtitle">
Manage your account settings.

View File

@@ -8,6 +8,7 @@ function ListPanelWrapper({
widget,
queryResponse,
setRequestData,
onColumnWidthsChange,
}: PanelWrapperProps): JSX.Element {
const dataSource = widget.query.builder?.queryData[0]?.dataSource;
@@ -21,6 +22,7 @@ function ListPanelWrapper({
widget={widget}
queryResponse={queryResponse}
setRequestData={setRequestData}
onColumnWidthsChange={onColumnWidthsChange}
/>
);
}
@@ -29,6 +31,7 @@ function ListPanelWrapper({
widget={widget}
queryResponse={queryResponse}
setRequestData={setRequestData}
onColumnWidthsChange={onColumnWidthsChange}
/>
);
}

View File

@@ -24,6 +24,7 @@ function PanelWrapper({
customOnRowClick,
panelMode,
enableDrillDown = false,
onColumnWidthsChange,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
selectedGraph || widget.panelTypes
@@ -58,6 +59,7 @@ function PanelWrapper({
customOnRowClick={customOnRowClick}
customSeries={customSeries}
enableDrillDown={enableDrillDown}
onColumnWidthsChange={onColumnWidthsChange}
/>
);
}

View File

@@ -14,6 +14,7 @@ function TablePanelWrapper({
onOpenTraceBtnClick,
customOnRowClick,
enableDrillDown = false,
onColumnWidthsChange,
}: PanelWrapperProps): JSX.Element {
const panelData =
(queryResponse.data?.payload?.data?.result?.[0] as any)?.table || [];
@@ -34,6 +35,8 @@ function TablePanelWrapper({
onOpenTraceBtnClick={onOpenTraceBtnClick}
customOnRowClick={customOnRowClick}
widgetId={widget.id}
columnWidths={widget.columnWidths}
onColumnWidthsChange={onColumnWidthsChange}
renderColumnCell={widget.renderColumnCell}
customColTitles={widget.customColTitles}
contextLinks={widget.contextLinks}

View File

@@ -29,6 +29,7 @@ export type PanelWrapperProps = {
customSeries?: (data: QueryData[]) => uPlot.Series[];
enableDrillDown?: boolean;
panelMode: PanelMode;
onColumnWidthsChange?: (widths: Record<string, number>) => void;
};
export type TooltipData = {

View File

@@ -24,6 +24,8 @@ export type QueryTableProps = Omit<
sticky?: TableProps<RowData>['sticky'];
searchTerm?: string;
widgetId?: string;
columnWidths?: Record<string, number>;
onColumnWidthsChange?: (widths: Record<string, number>) => void;
enableDrillDown?: boolean;
contextLinks?: ContextLinksData;
panelType?: PANEL_TYPES;

View File

@@ -28,6 +28,8 @@ export function QueryTable({
sticky,
searchTerm,
widgetId,
columnWidths,
onColumnWidthsChange,
panelType,
...props
}: QueryTableProps): JSX.Element {
@@ -175,8 +177,8 @@ export function QueryTable({
dataSource={filterTable === null ? newDataSource : filterTable}
scroll={{ x: 'max-content' }}
pagination={paginationConfig}
widgetId={widgetId}
shouldPersistColumnWidths
columnWidths={columnWidths}
onColumnWidthsChange={onColumnWidthsChange}
sticky={sticky}
{...props}
/>

View File

@@ -1057,21 +1057,20 @@
gap: 8px;
.user-settings-dropdown-label-text {
color: var(--bg-slate-50, #62687c);
color: var(--l3-foreground);
font-family: Inter;
font-size: 10px;
font-family: Inter;
font-weight: 600;
font-size: var(--uppercase-small-500-font-size);
font-weight: var(--uppercase-small-500-font-weight);
font-style: normal;
line-height: 18px; /* 163.636% */
line-height: 18px;
letter-spacing: 0.88px;
text-transform: uppercase;
}
.user-settings-dropdown-label-email {
color: var(--bg-vanilla-400, #c0c1c3);
color: var(--l1-foreground);
font-family: Inter;
font-size: 12px;
font-size: var(--font-size-xs);
font-style: normal;
line-height: normal;
letter-spacing: 0.14px;
@@ -1079,7 +1078,7 @@
}
.ant-dropdown-menu-item-divider {
background-color: var(--bg-slate-500, #161922) !important;
background-color: var(--secondary) !important;
}
.ant-dropdown-menu-item-disabled {
@@ -1095,6 +1094,14 @@
.help-support-dropdown {
.ant-dropdown-menu-item {
min-height: 32px;
.ant-dropdown-menu-title-content {
color: var(--l1-foreground) !important;
}
.user-settings-dropdown-logout-section {
color: var(--danger-background);
}
}
}
@@ -1271,7 +1278,7 @@
}
.help-support-dropdown li.ant-dropdown-menu-item-divider {
background-color: var(--bg-slate-500, #161922) !important;
background-color: var(--secondary) !important;
}
.lightMode {
@@ -1431,22 +1438,6 @@
}
}
.settings-dropdown {
.user-settings-dropdown-logged-in-section {
.user-settings-dropdown-label-text {
color: var(--bg-ink-400);
}
.user-settings-dropdown-label-email {
color: var(--bg-ink-300);
}
}
.ant-dropdown-menu-item-divider {
background-color: var(--bg-vanilla-300) !important;
}
}
.reorder-shortcut-nav-items-modal {
.ant-modal-content {
border: 1px solid var(--bg-vanilla-300);
@@ -1503,10 +1494,6 @@
color: var(--bg-ink-400);
}
}
.help-support-dropdown li.ant-dropdown-menu-item-divider {
background-color: var(--bg-vanilla-300) !important;
}
}
.version-tooltip-overlay {

View File

@@ -69,6 +69,7 @@ import { routeConfig } from './config';
import { getQueryString } from './helper';
import {
defaultMoreMenuItems,
getUserSettingsDropdownMenuItems,
helpSupportDropdownMenuItems as DefaultHelpSupportDropdownMenuItems,
helpSupportMenuItem,
primaryMenuItems,
@@ -485,48 +486,12 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
const userSettingsDropdownMenuItems: MenuProps['items'] = useMemo(
() =>
[
{
key: 'label',
label: (
<div className="user-settings-dropdown-logged-in-section">
<span className="user-settings-dropdown-label-text">LOGGED IN AS</span>
<span className="user-settings-dropdown-label-email">{user.email}</span>
</div>
),
disabled: true,
dataTestId: 'logged-in-as-nav-item',
},
{ type: 'divider' as const },
{
key: 'account',
label: 'Account Settings',
dataTestId: 'account-settings-nav-item',
},
{
key: 'workspace',
label: 'Workspace Settings',
disabled: isWorkspaceBlocked,
dataTestId: 'workspace-settings-nav-item',
},
...(isEnterpriseSelfHostedUser || isCommunityEnterpriseUser
? [
{
key: 'license',
label: 'Manage License',
dataTestId: 'manage-license-nav-item',
},
]
: []),
{ type: 'divider' as const },
{
key: 'logout',
label: (
<span className="user-settings-dropdown-logout-section">Sign out</span>
),
dataTestId: 'logout-nav-item',
},
].filter(Boolean),
getUserSettingsDropdownMenuItems({
userEmail: user.email,
isWorkspaceBlocked,
isEnterpriseSelfHostedUser,
isCommunityEnterpriseUser,
}),
[
isEnterpriseSelfHostedUser,
isCommunityEnterpriseUser,
@@ -856,9 +821,6 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
});
switch (item.key) {
case ROUTES.SHORTCUTS:
history.push(ROUTES.SHORTCUTS);
break;
case 'invite-collaborators':
history.push(`${ROUTES.ORG_SETTINGS}#invite-team-members`);
break;
@@ -878,7 +840,7 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
};
const handleSettingsMenuItemClick = (info: SidebarItem): void => {
const item = userSettingsDropdownMenuItems.find(
const item = (userSettingsDropdownMenuItems ?? []).find(
(item) => item?.key === info.key,
);
let menuLabel = '';
@@ -904,6 +866,9 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
case 'license':
history.push(ROUTES.LIST_LICENSES);
break;
case 'keyboard-shortcuts':
history.push(ROUTES.SHORTCUTS);
break;
case 'logout':
Logout();
break;

View File

@@ -0,0 +1,74 @@
import { getUserSettingsDropdownMenuItems } from 'container/SideNav/menuItems';
const BASE_PARAMS = {
userEmail: 'test@signoz.io',
isWorkspaceBlocked: false,
isEnterpriseSelfHostedUser: false,
isCommunityEnterpriseUser: false,
};
describe('getUserSettingsDropdownMenuItems', () => {
it('always includes logged-in-as label, workspace, account, keyboard shortcuts, and sign out', () => {
const items = getUserSettingsDropdownMenuItems(BASE_PARAMS);
const keys = items?.map((item) => item?.key);
expect(keys).toContain('label');
expect(keys).toContain('workspace');
expect(keys).toContain('account');
expect(keys).toContain('keyboard-shortcuts');
expect(keys).toContain('logout');
// workspace item is enabled when workspace is not blocked
const workspaceItem = items?.find(
(item: any) => item.key === 'workspace',
) as any;
expect(workspaceItem?.disabled).toBe(false);
// does not include license item for regular cloud user
expect(keys).not.toContain('license');
});
it('includes manage license item for enterprise self-hosted users', () => {
const items = getUserSettingsDropdownMenuItems({
...BASE_PARAMS,
isEnterpriseSelfHostedUser: true,
});
const keys = items?.map((item) => item?.key);
expect(keys).toContain('license');
});
it('includes manage license item for community enterprise users', () => {
const items = getUserSettingsDropdownMenuItems({
...BASE_PARAMS,
isCommunityEnterpriseUser: true,
});
const keys = items?.map((item) => item?.key);
expect(keys).toContain('license');
});
it('workspace item is disabled when workspace is blocked', () => {
const items = getUserSettingsDropdownMenuItems({
...BASE_PARAMS,
isWorkspaceBlocked: true,
});
const workspaceItem = items?.find(
(item: any) => item.key === 'workspace',
) as any;
expect(workspaceItem?.disabled).toBe(true);
});
it('returns items in correct order: label, divider, workspace, account, ..., shortcuts, divider, logout', () => {
const items = getUserSettingsDropdownMenuItems(BASE_PARAMS) ?? [];
const keys = items.map((item: any) => item.key ?? item.type);
expect(keys[0]).toBe('label');
expect(keys[1]).toBe('divider');
expect(keys[2]).toBe('workspace');
expect(keys[3]).toBe('account');
expect(keys[keys.length - 1]).toBe('logout');
});
});

View File

@@ -1,4 +1,6 @@
import { RocketOutlined } from '@ant-design/icons';
import { Style } from '@signozhq/design-tokens';
import { MenuProps } from 'antd';
import ROUTES from 'constants/routes';
import {
ArrowUpRight,
@@ -8,6 +10,7 @@ import {
Book,
Boxes,
BugIcon,
Building2,
ChartArea,
Cloudy,
DraftingCompass,
@@ -20,12 +23,12 @@ import {
Layers2,
LayoutGrid,
ListMinus,
LogOut,
MessageSquareText,
Plus,
Receipt,
Route,
ScrollText,
Search,
Settings,
Shield,
Slack,
@@ -34,7 +37,11 @@ import {
UserPlus,
} from 'lucide-react';
import { SecondaryMenuItemKey, SidebarItem } from './sideNav.types';
import {
SecondaryMenuItemKey,
SettingsNavSection,
SidebarItem,
} from './sideNav.types';
export const getStartedMenuItem = {
key: ROUTES.GET_STARTED,
@@ -188,12 +195,6 @@ export const primaryMenuItems: SidebarItem[] = [
icon: <Home size={16} />,
itemKey: 'home',
},
{
key: 'quick-search',
label: 'Search',
icon: <Search size={16} />,
itemKey: 'quick-search',
},
{
key: ROUTES.LIST_ALL_ALERT,
label: 'Alerts',
@@ -296,77 +297,100 @@ export const defaultMoreMenuItems: SidebarItem[] = [
},
];
export const settingsMenuItems: SidebarItem[] = [
export const settingsNavSections: SettingsNavSection[] = [
{
key: ROUTES.SETTINGS,
label: 'General',
icon: <Settings size={16} />,
isEnabled: true,
itemKey: 'general',
},
{
key: ROUTES.BILLING,
label: 'Billing',
icon: <Receipt size={16} />,
isEnabled: false,
itemKey: 'billing',
},
{
key: ROUTES.ROLES_SETTINGS,
label: 'Roles',
icon: <Shield size={16} />,
isEnabled: false,
itemKey: 'roles',
},
{
key: ROUTES.ORG_SETTINGS,
label: 'Members & SSO',
icon: <User size={16} />,
isEnabled: false,
itemKey: 'members-sso',
key: 'general',
items: [
{
key: ROUTES.SETTINGS,
label: 'Workspace',
icon: <Settings size={16} />,
isEnabled: true,
itemKey: 'workspace',
},
{
key: ROUTES.MY_SETTINGS,
label: 'Account',
icon: <User size={16} />,
isEnabled: true,
itemKey: 'account',
},
{
key: ROUTES.ALL_CHANNELS,
label: 'Notification Channels',
icon: <FileKey2 size={16} />,
isEnabled: true,
itemKey: 'notification-channels',
},
{
key: ROUTES.BILLING,
label: 'Billing',
icon: <Receipt size={16} />,
isEnabled: false,
itemKey: 'billing',
},
{
key: ROUTES.INTEGRATIONS,
label: 'Integrations',
icon: <Unplug size={16} />,
isEnabled: false,
itemKey: 'integrations',
},
],
},
{
key: ROUTES.INTEGRATIONS,
label: 'Integrations',
icon: <Unplug size={16} />,
isEnabled: false,
itemKey: 'integrations',
key: 'identity-access',
title: 'Identity & Access',
items: [
{
key: ROUTES.ROLES_SETTINGS,
label: 'Roles',
icon: <Shield size={16} />,
isEnabled: false,
itemKey: 'roles',
},
{
key: ROUTES.API_KEYS,
label: 'API Keys',
icon: <Key size={16} />,
isEnabled: false,
itemKey: 'api-keys',
},
{
key: ROUTES.INGESTION_SETTINGS,
label: 'Ingestion',
icon: <RocketOutlined rotate={45} />,
isEnabled: false,
itemKey: 'ingestion',
},
],
},
{
key: ROUTES.ALL_CHANNELS,
label: 'Notification Channels',
icon: <FileKey2 size={16} />,
isEnabled: true,
itemKey: 'notification-channels',
key: 'authentication',
title: 'Authentication',
items: [
{
key: ROUTES.ORG_SETTINGS,
label: 'Members & SSO',
icon: <User size={16} />,
isEnabled: false,
itemKey: 'members-sso',
},
],
},
{
key: ROUTES.API_KEYS,
label: 'API Keys',
icon: <Key size={16} />,
isEnabled: false,
itemKey: 'api-keys',
},
{
key: ROUTES.INGESTION_SETTINGS,
label: 'Ingestion',
icon: <RocketOutlined rotate={45} />,
isEnabled: false,
itemKey: 'ingestion',
},
{
key: ROUTES.MY_SETTINGS,
label: 'Account Settings',
icon: <User size={16} />,
isEnabled: true,
itemKey: 'account-settings',
},
{
key: ROUTES.SHORTCUTS,
label: 'Keyboard Shortcuts',
icon: <Layers2 size={16} />,
isEnabled: true,
itemKey: 'keyboard-shortcuts',
key: 'shortcuts',
hasDivider: true,
items: [
{
key: ROUTES.SHORTCUTS,
label: 'Keyboard Shortcuts',
icon: <Keyboard size={16} />,
isEnabled: true,
itemKey: 'keyboard-shortcuts',
},
],
},
];
@@ -417,12 +441,6 @@ export const helpSupportDropdownMenuItems: SidebarItem[] = [
icon: <MessageSquareText size={14} />,
itemKey: 'chat-support',
},
{
key: ROUTES.SHORTCUTS,
label: 'Keyboard Shortcuts',
icon: <Keyboard size={14} />,
itemKey: 'keyboard-shortcuts',
},
{
key: 'invite-collaborators',
label: 'Invite a Team Member',
@@ -431,6 +449,78 @@ export const helpSupportDropdownMenuItems: SidebarItem[] = [
},
];
export interface UserSettingsMenuItemsParams {
userEmail: string;
isWorkspaceBlocked: boolean;
isEnterpriseSelfHostedUser: boolean;
isCommunityEnterpriseUser: boolean;
}
export const getUserSettingsDropdownMenuItems = ({
userEmail,
isWorkspaceBlocked,
isEnterpriseSelfHostedUser,
isCommunityEnterpriseUser,
}: UserSettingsMenuItemsParams): MenuProps['items'] =>
[
{
key: 'label',
label: (
<div className="user-settings-dropdown-logged-in-section">
<span className="user-settings-dropdown-label-text">LOGGED IN AS</span>
<span className="user-settings-dropdown-label-email">{userEmail}</span>
</div>
),
disabled: true,
dataTestId: 'logged-in-as-nav-item',
},
{ type: 'divider' as const },
{
key: 'workspace',
label: 'Workspace Settings',
icon: <Building2 size={14} color={Style.L1_FOREGROUND} />,
disabled: isWorkspaceBlocked,
dataTestId: 'workspace-settings-nav-item',
},
{
key: 'account',
label: 'Account Settings',
icon: <User size={14} color={Style.L1_FOREGROUND} />,
dataTestId: 'account-settings-nav-item',
},
...(isEnterpriseSelfHostedUser || isCommunityEnterpriseUser
? [
{
key: 'license',
label: 'Manage License',
icon: <Shield size={14} color={Style.L1_FOREGROUND} />,
dataTestId: 'manage-license-nav-item',
},
]
: []),
{
key: 'keyboard-shortcuts',
label: 'Keyboard Shortcuts',
icon: <Keyboard size={14} color={Style.L1_FOREGROUND} />,
dataTestId: 'keyboard-shortcuts-nav-item',
},
{ type: 'divider' as const },
{
key: 'logout',
label: (
<span className="user-settings-dropdown-logout-section">Sign out</span>
),
icon: (
<LogOut
size={14}
className="user-settings-dropdown-logout-section"
color={Style.DANGER_BACKGROUND}
/>
),
dataTestId: 'logout-nav-item',
},
].filter(Boolean);
/** Mapping of some newly added routes and their corresponding active sidebar menu key */
export const NEW_ROUTES_MENU_ITEM_KEY_MAP: Record<string, string> = {
[ROUTES.TRACE]: ROUTES.TRACES_EXPLORER,

View File

@@ -24,6 +24,13 @@ export interface SidebarItem {
export const CHANGELOG_LABEL = 'Full Changelog';
export interface SettingsNavSection {
title?: string;
items: SidebarItem[];
key: string;
hasDivider?: boolean;
}
export interface DropdownSeparator {
type: 'divider' | 'group';
label?: ReactNode;

View File

@@ -35,6 +35,7 @@ function TracesTableComponent({
widget,
queryResponse,
setRequestData,
onColumnWidthsChange,
}: TracesTableComponentProps): JSX.Element {
const [pagination, setPagination] = useState<Pagination>({
offset: 0,
@@ -131,8 +132,8 @@ function TracesTableComponent({
columns={columns}
onRow={handleRow}
sticky
widgetId={widget.id}
shouldPersistColumnWidths
columnWidths={widget.columnWidths}
onColumnWidthsChange={onColumnWidthsChange}
/>
</OverlayScrollbar>
</div>
@@ -175,6 +176,7 @@ export type TracesTableComponentProps = {
>;
widget: Widgets;
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
onColumnWidthsChange?: (widths: Record<string, number>) => void;
};
export default TracesTableComponent;

View File

@@ -31,9 +31,33 @@
.settings-page-sidenav {
width: 240px;
height: calc(100vh - 48px);
border-right: 1px solid var(--Slate-500, #161922);
background: var(--Ink-500, #0b0c0e);
margin-top: 4px;
border-right: 1px solid var(--secondary);
background: var(--sidebar-primary-foreground);
padding-top: var(--padding-1);
display: flex;
flex-direction: column;
gap: var(--spacing-12);
overflow-y: auto;
.settings-nav-section {
display: flex;
flex-direction: column;
}
.settings-nav-section--with-divider {
border-top: 1px solid var(--secondary);
padding-top: var(--padding-4);
}
.settings-nav-section-title {
font-size: var(--uppercase-small-600-font-size);
font-weight: var(--uppercase-small-600-font-weight);
letter-spacing: 0.88px;
text-transform: uppercase;
color: var(--l3-foreground);
margin-bottom: var(--margin-2);
padding: var(--padding-1) var(--padding-3);
}
.nav-item {
.nav-item-data {

View File

@@ -7,7 +7,7 @@ import { FeatureKeys } from 'constants/features';
import ROUTES from 'constants/routes';
import { routeConfig } from 'container/SideNav/config';
import { getQueryString } from 'container/SideNav/helper';
import { settingsMenuItems as defaultSettingsMenuItems } from 'container/SideNav/menuItems';
import { settingsNavSections } from 'container/SideNav/menuItems';
import NavItem from 'container/SideNav/NavItem/NavItem';
import { SidebarItem } from 'container/SideNav/sideNav.types';
import useComponentPermission from 'hooks/useComponentPermission';
@@ -33,7 +33,7 @@ function SettingsPage(): JSX.Element {
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
const [settingsMenuItems, setSettingsMenuItems] = useState<SidebarItem[]>(
defaultSettingsMenuItems,
settingsNavSections.flatMap((section) => section.items),
);
const isAdmin = user.role === USER_ROLES.ADMIN;
@@ -252,25 +252,45 @@ function SettingsPage(): JSX.Element {
<div className="settings-page-content-container">
<div className="settings-page-sidenav" data-testid="settings-page-sidenav">
{settingsMenuItems
.filter((item) => item.isEnabled)
.map((item) => (
<NavItem
key={item.key}
item={item}
isActive={isActiveNavItem(item.key as string)}
isDisabled={false}
showIcon={false}
onClick={(event): void => {
logEvent('Settings V2: Menu clicked', {
menuLabel: item.label,
menuRoute: item.key,
});
handleMenuItemClick((event as unknown) as MouseEvent, item);
}}
dataTestId={item.itemKey}
/>
))}
{settingsNavSections.map((section) => {
const enabledItems = section.items.filter((sectionItem) =>
settingsMenuItems.some(
(item) => item.key === sectionItem.key && item.isEnabled,
),
);
if (enabledItems.length === 0) {
return null;
}
return (
<div
key={section.key}
className={`settings-nav-section${
section.hasDivider ? ' settings-nav-section--with-divider' : ''
}`}
>
{section.title && (
<div className="settings-nav-section-title">{section.title}</div>
)}
{enabledItems.map((item) => (
<NavItem
key={item.key}
item={item}
isActive={isActiveNavItem(item.key as string)}
isDisabled={false}
showIcon={false}
onClick={(event): void => {
logEvent('Settings V2: Menu clicked', {
menuLabel: item.label,
menuRoute: item.key,
});
handleMenuItemClick((event as unknown) as MouseEvent, item);
}}
dataTestId={item.itemKey}
/>
))}
</div>
);
})}
</div>
<div className="settings-page-content">

View File

@@ -0,0 +1,137 @@
import React from 'react';
import SettingsPage from 'pages/Settings/Settings';
import { render, screen, within } from 'tests/test-utils';
import { LicensePlatform } from 'types/api/licensesV3/getActive';
import { USER_ROLES } from 'types/roles';
jest.mock('components/MarkdownRenderer/MarkdownRenderer', () => ({
__esModule: true,
default: ({ children }: { children: React.ReactNode }): React.ReactNode =>
children,
}));
jest.mock('api/common/logEvent', () => ({
__esModule: true,
default: jest.fn(),
}));
jest.mock('lib/history', () => ({
push: jest.fn(),
listen: jest.fn(() => jest.fn()),
location: { pathname: '/settings', search: '' },
}));
const getCloudAdminOverrides = (): any => ({
activeLicense: {
key: 'test-key',
platform: LicensePlatform.CLOUD,
},
});
const getSelfHostedAdminOverrides = (): any => ({
activeLicense: {
key: 'test-key',
platform: LicensePlatform.SELF_HOSTED,
},
});
describe('SettingsPage nav sections', () => {
describe('Cloud Admin', () => {
beforeEach(() => {
render(<SettingsPage />, undefined, {
role: USER_ROLES.ADMIN,
appContextOverrides: getCloudAdminOverrides(),
initialRoute: '/settings',
});
});
it.each([
'settings-page-sidenav',
'workspace',
'account',
'notification-channels',
'billing',
'roles',
'api-keys',
'members-sso',
'integrations',
'ingestion',
])('renders "%s" element', (id) => {
expect(screen.getByTestId(id)).toBeInTheDocument();
});
it.each(['Identity & Access', 'Authentication'])(
'renders "%s" section title',
(text) => {
expect(screen.getByText(text)).toBeInTheDocument();
},
);
});
describe('Cloud Viewer', () => {
beforeEach(() => {
render(<SettingsPage />, undefined, {
role: USER_ROLES.VIEWER,
appContextOverrides: getCloudAdminOverrides(),
initialRoute: '/settings',
});
});
it.each(['workspace', 'account'])('renders "%s" element', (id) => {
expect(screen.getByTestId(id)).toBeInTheDocument();
});
it.each(['billing', 'roles', 'api-keys'])(
'does not render "%s" element',
(id) => {
expect(screen.queryByTestId(id)).not.toBeInTheDocument();
},
);
});
describe('Self-hosted Admin', () => {
beforeEach(() => {
render(<SettingsPage />, undefined, {
role: USER_ROLES.ADMIN,
appContextOverrides: getSelfHostedAdminOverrides(),
initialRoute: '/settings',
});
});
it.each(['roles', 'api-keys', 'integrations', 'members-sso', 'ingestion'])(
'renders "%s" element',
(id) => {
expect(screen.getByTestId(id)).toBeInTheDocument();
},
);
});
describe('section structure', () => {
it('renders items in correct sections for cloud admin', () => {
const { container } = render(<SettingsPage />, undefined, {
role: USER_ROLES.ADMIN,
appContextOverrides: getCloudAdminOverrides(),
initialRoute: '/settings',
});
const sidenav = within(container).getByTestId('settings-page-sidenav');
const sections = sidenav.querySelectorAll('.settings-nav-section');
// Should have at least 2 sections (general + identity-access) for cloud admin
expect(sections.length).toBeGreaterThanOrEqual(2);
});
it('hides section entirely when all items in it are disabled', () => {
// Community user has very limited access — identity section should be hidden
render(<SettingsPage />, undefined, {
role: USER_ROLES.VIEWER,
appContextOverrides: {
activeLicense: null,
},
initialRoute: '/settings',
});
expect(screen.queryByText('IDENTITY & ACCESS')).not.toBeInTheDocument();
});
});
});

View File

@@ -18,6 +18,7 @@ import (
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/metricsexplorertypes"
"github.com/SigNoz/signoz/pkg/types/metrictypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
@@ -59,6 +60,8 @@ func NewModule(ts telemetrystore.TelemetryStore, telemetryMetadataStore telemetr
// TODO(srikanthccv): use metadata store to fetch metric metadata
func (m *module) ListMetrics(ctx context.Context, orgID valuer.UUID, params *metricsexplorertypes.ListMetricsParams) (*metricsexplorertypes.ListMetricsResponse, error) {
ctx = m.withMetricsExplorerContext(ctx, "ListMetrics")
if err := params.Validate(); err != nil {
return nil, err
}
@@ -288,6 +291,7 @@ func (m *module) GetTreemap(ctx context.Context, orgID valuer.UUID, req *metrics
}
func (m *module) GetMetricMetadataMulti(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
if len(metricNames) == 0 {
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
}
@@ -476,6 +480,8 @@ func (m *module) GetMetricAttributes(ctx context.Context, orgID valuer.UUID, req
}
func (m *module) CheckMetricExists(ctx context.Context, orgID valuer.UUID, metricName string) (bool, error) {
ctx = m.withMetricsExplorerContext(ctx, "CheckMetricExists")
sb := sqlbuilder.NewSelectBuilder()
sb.Select("count(*) > 0 as metricExists")
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
@@ -512,6 +518,8 @@ func (m *module) fetchMetadataFromCache(ctx context.Context, orgID valuer.UUID,
}
func (m *module) fetchUpdatedMetadata(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
ctx = m.withMetricsExplorerContext(ctx, "fetchUpdatedMetadata")
if len(metricNames) == 0 {
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
}
@@ -570,6 +578,8 @@ func (m *module) fetchUpdatedMetadata(ctx context.Context, orgID valuer.UUID, me
}
func (m *module) fetchTimeseriesMetadata(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
ctx = m.withMetricsExplorerContext(ctx, "fetchTimeseriesMetadata")
if len(metricNames) == 0 {
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
}
@@ -698,6 +708,8 @@ func (m *module) validateMetricLabels(ctx context.Context, req *metricsexplorert
}
func (m *module) checkForLabelInMetric(ctx context.Context, metricName string, label string) (bool, error) {
ctx = m.withMetricsExplorerContext(ctx, "checkForLabelInMetric")
sb := sqlbuilder.NewSelectBuilder()
sb.Select("count(*) > 0 AS has_label")
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
@@ -719,6 +731,7 @@ func (m *module) checkForLabelInMetric(ctx context.Context, metricName string, l
}
func (m *module) insertMetricsMetadata(ctx context.Context, orgID valuer.UUID, req *metricsexplorertypes.UpdateMetricMetadataRequest) error {
ctx = m.withMetricsExplorerContext(ctx, "insertMetricsMetadata")
createdAt := time.Now().UnixMilli()
ib := sqlbuilder.NewInsertBuilder()
@@ -812,6 +825,7 @@ func (m *module) fetchMetricsStatsWithSamples(
normalized bool,
orderBy *qbtypes.OrderBy,
) ([]metricsexplorertypes.Stat, uint64, error) {
ctx = m.withMetricsExplorerContext(ctx, "fetchMetricsStatsWithSamples")
start, end, distributedTsTable, localTsTable := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
samplesTable := telemetrymetrics.WhichSamplesTableToUse(uint64(req.Start), uint64(req.End), metrictypes.UnspecifiedType, metrictypes.TimeAggregationUnspecified, nil)
@@ -919,6 +933,8 @@ func (m *module) fetchMetricsStatsWithSamples(
}
func (m *module) computeTimeseriesTreemap(ctx context.Context, req *metricsexplorertypes.TreemapRequest, filterWhereClause *sqlbuilder.WhereClause) ([]metricsexplorertypes.TreemapEntry, error) {
ctx = m.withMetricsExplorerContext(ctx, "computeTimeseriesTreemap")
start, end, distributedTsTable, _ := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
totalTSBuilder := sqlbuilder.NewSelectBuilder()
@@ -983,6 +999,8 @@ func (m *module) computeTimeseriesTreemap(ctx context.Context, req *metricsexplo
}
func (m *module) computeSamplesTreemap(ctx context.Context, req *metricsexplorertypes.TreemapRequest, filterWhereClause *sqlbuilder.WhereClause) ([]metricsexplorertypes.TreemapEntry, error) {
ctx = m.withMetricsExplorerContext(ctx, "computeSamplesTreemap")
start, end, distributedTsTable, localTsTable := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
samplesTable := telemetrymetrics.WhichSamplesTableToUse(uint64(req.Start), uint64(req.End), metrictypes.UnspecifiedType, metrictypes.TimeAggregationUnspecified, nil)
countExp := telemetrymetrics.CountExpressionForSamplesTable(samplesTable)
@@ -1084,6 +1102,8 @@ func (m *module) computeSamplesTreemap(ctx context.Context, req *metricsexplorer
// getMetricDataPoints returns the total number of data points (samples) for a metric.
func (m *module) getMetricDataPoints(ctx context.Context, metricName string) (uint64, error) {
ctx = m.withMetricsExplorerContext(ctx, "getMetricDataPoints")
sb := sqlbuilder.NewSelectBuilder()
sb.Select("sum(count) AS data_points")
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.SamplesV4Agg30mTableName))
@@ -1104,6 +1124,8 @@ func (m *module) getMetricDataPoints(ctx context.Context, metricName string) (ui
// getMetricLastReceived returns the last received timestamp for a metric.
func (m *module) getMetricLastReceived(ctx context.Context, metricName string) (uint64, error) {
ctx = m.withMetricsExplorerContext(ctx, "getMetricLastReceived")
sb := sqlbuilder.NewSelectBuilder()
sb.Select("MAX(last_reported_unix_milli) AS last_received_time")
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
@@ -1127,6 +1149,8 @@ func (m *module) getMetricLastReceived(ctx context.Context, metricName string) (
// getTotalTimeSeriesForMetricName returns the total number of unique time series for a metric.
func (m *module) getTotalTimeSeriesForMetricName(ctx context.Context, metricName string) (uint64, error) {
ctx = m.withMetricsExplorerContext(ctx, "getTotalTimeSeriesForMetricName")
sb := sqlbuilder.NewSelectBuilder()
sb.Select("uniq(fingerprint) AS time_series_count")
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.TimeseriesV41weekTableName))
@@ -1147,6 +1171,8 @@ func (m *module) getTotalTimeSeriesForMetricName(ctx context.Context, metricName
// getActiveTimeSeriesForMetricName returns the number of active time series for a metric within the given duration.
func (m *module) getActiveTimeSeriesForMetricName(ctx context.Context, metricName string, duration time.Duration) (uint64, error) {
ctx = m.withMetricsExplorerContext(ctx, "getActiveTimeSeriesForMetricName")
milli := time.Now().Add(-duration).UnixMilli()
sb := sqlbuilder.NewSelectBuilder()
@@ -1168,6 +1194,8 @@ func (m *module) getActiveTimeSeriesForMetricName(ctx context.Context, metricNam
}
func (m *module) fetchMetricAttributes(ctx context.Context, metricName string, start, end *int64) ([]metricsexplorertypes.MetricAttribute, error) {
ctx = m.withMetricsExplorerContext(ctx, "fetchMetricAttributes")
// Build query using sqlbuilder
sb := sqlbuilder.NewSelectBuilder()
sb.Select(
@@ -1216,3 +1244,12 @@ func (m *module) fetchMetricAttributes(ctx context.Context, metricName string, s
return attributes, nil
}
func (m *module) withMetricsExplorerContext(ctx context.Context, functionName string) context.Context {
comments := map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metrics-explorer",
instrumentationtypes.CodeFunctionName: functionName,
}
return ctxtypes.NewContextWithCommentVals(ctx, comments)
}

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/promote"
"github.com/SigNoz/signoz/pkg/telemetrylogs"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/promotetypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
@@ -105,6 +107,11 @@ func (m *module) PromotePaths(ctx context.Context, paths []string) error {
// createIndexes creates string ngram + token filter indexes on JSON path subcolumns for LIKE queries.
func (m *module) createIndexes(ctx context.Context, indexes []schemamigrator.Index) error {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "promote",
instrumentationtypes.CodeFunctionName: "createIndexes",
})
if len(indexes) == 0 {
return nil
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/valuer"
)
@@ -22,6 +23,10 @@ func NewModule(querier querier.Querier) rawdataexport.Module {
}
func (m *Module) ExportRawData(ctx context.Context, orgID valuer.UUID, rangeRequest *qbtypes.QueryRangeRequest, doneChan chan any) (chan *qbtypes.RawRow, chan error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "rawdataexport",
instrumentationtypes.CodeFunctionName: "ExportRawData",
})
spec := rangeRequest.CompositeQuery.Queries[0].Spec.(qbtypes.QueryBuilderQuery[qbtypes.LogAggregation])
rowCountLimit := spec.Limit

View File

@@ -13,6 +13,8 @@ import (
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/telemetrytraces"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/servicetypes/servicetypesv1"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
@@ -34,6 +36,8 @@ func NewModule(q querier.Querier, ts telemetrystore.TelemetryStore) services.Mod
// FetchTopLevelOperations returns top-level operations per service using db query
func (m *module) FetchTopLevelOperations(ctx context.Context, start time.Time, services []string) (map[string][]string, error) {
ctx = m.withServicesContext(ctx, "FetchTopLevelOperations")
db := m.TelemetryStore.ClickhouseDB()
query := fmt.Sprintf("SELECT name, serviceName, max(time) as ts FROM %s.%s WHERE time >= @start", telemetrytraces.DBName, telemetrytraces.TopLevelOperationsTableName)
args := []any{clickhouse.Named("start", start)}
@@ -70,6 +74,7 @@ func (m *module) FetchTopLevelOperations(ctx context.Context, start time.Time, s
// Get implements services.Module
// Builds a QBv5 traces aggregation grouped by service.name and maps results to ResponseItem.
func (m *module) Get(ctx context.Context, orgUUID valuer.UUID, req *servicetypesv1.Request) ([]*servicetypesv1.ResponseItem, error) {
ctx = m.withServicesContext(ctx, "Get")
if req == nil {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
}
@@ -104,6 +109,7 @@ func (m *module) Get(ctx context.Context, orgUUID valuer.UUID, req *servicetypes
// GetTopOperations implements services.Module for QBV5 based top ops
func (m *module) GetTopOperations(ctx context.Context, orgUUID valuer.UUID, req *servicetypesv1.OperationsRequest) ([]servicetypesv1.OperationItem, error) {
ctx = m.withServicesContext(ctx, "GetTopOperations")
if req == nil {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
}
@@ -124,6 +130,7 @@ func (m *module) GetTopOperations(ctx context.Context, orgUUID valuer.UUID, req
// GetEntryPointOperations implements services.Module for QBV5 based entry point ops
func (m *module) GetEntryPointOperations(ctx context.Context, orgUUID valuer.UUID, req *servicetypesv1.OperationsRequest) ([]servicetypesv1.OperationItem, error) {
ctx = m.withServicesContext(ctx, "GetEntryPointOperations")
if req == nil {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
}
@@ -517,3 +524,12 @@ func (m *module) mapEntryPointOpsQueryRangeResp(resp *qbtypes.QueryRangeResponse
}
return out
}
func (m *module) withServicesContext(ctx context.Context, functionName string) context.Context {
comments := map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "services",
instrumentationtypes.CodeFunctionName: functionName,
}
return ctxtypes.NewContextWithCommentVals(ctx, comments)
}

View File

@@ -8,6 +8,8 @@ import (
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/spanpercentile"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/spanpercentiletypes"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -27,6 +29,10 @@ func NewModule(
}
func (m *module) GetSpanPercentile(ctx context.Context, orgID valuer.UUID, userID valuer.UUID, req *spanpercentiletypes.SpanPercentileRequest) (*spanpercentiletypes.SpanPercentileResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "spanpercentile",
instrumentationtypes.CodeFunctionName: "GetSpanPercentile",
})
queryRangeRequest, err := buildSpanPercentileQuery(ctx, req)
if err != nil {
return nil, err

View File

@@ -11,6 +11,9 @@ import (
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
promValue "github.com/prometheus/prometheus/model/value"
"github.com/prometheus/prometheus/prompb"
"github.com/prometheus/prometheus/storage"
@@ -137,6 +140,7 @@ func (client *client) queryToClickhouseQuery(_ context.Context, query *prompb.Qu
}
func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, query string, args []any) (map[uint64][]prompb.Label, error) {
ctx = client.withClickhousePrometheusContext(ctx, "getFingerprintsFromClickhouseQuery")
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
if err != nil {
return nil, err
@@ -168,6 +172,7 @@ func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, qu
}
func (client *client) querySamples(ctx context.Context, start int64, end int64, fingerprints map[uint64][]prompb.Label, metricName string, subQuery string, args []any) ([]*prompb.TimeSeries, error) {
ctx = client.withClickhousePrometheusContext(ctx, "querySamples")
argCount := len(args)
query := fmt.Sprintf(`
@@ -244,6 +249,8 @@ func (client *client) querySamples(ctx context.Context, start int64, end int64,
}
func (client *client) queryRaw(ctx context.Context, query string, ts int64) (*prompb.QueryResult, error) {
ctx = client.withClickhousePrometheusContext(ctx, "queryRaw")
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query)
if err != nil {
return nil, err
@@ -291,3 +298,12 @@ func (client *client) queryRaw(ctx context.Context, query string, ts int64) (*pr
return &res, nil
}
func (client *client) withClickhousePrometheusContext(ctx context.Context, functionName string) context.Context {
comments := map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-prometheus",
instrumentationtypes.CodeFunctionName: functionName,
}
return ctxtypes.NewContextWithCommentVals(ctx, comments)
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -33,6 +34,10 @@ func NewHandler(set factory.ProviderSettings, querier Querier, analytics analyti
func (handler *handler) QueryRange(rw http.ResponseWriter, req *http.Request) {
ctx := req.Context()
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "querier",
instrumentationtypes.CodeFunctionName: "QueryRange",
})
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {

View File

@@ -12,6 +12,8 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/telemetrylogs"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/bytedance/sonic"
@@ -212,6 +214,11 @@ func (q *builderQuery[T]) Execute(ctx context.Context) (*qbtypes.Result, error)
// executeWithContext executes the query with query window and step context for partial value detection
func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: q.spec.Signal.StringValue(),
instrumentationtypes.QueryDuration: instrumentationtypes.DurationBucket(q.fromMS, q.toMS),
})
totalRows := uint64(0)
totalBytes := uint64(0)
elapsed := time.Duration(0)

View File

@@ -14,6 +14,8 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
)
@@ -98,6 +100,9 @@ func (q *chSQLQuery) renderVars(query string, vars map[string]qbtypes.VariableIt
}
func (q *chSQLQuery) Execute(ctx context.Context) (*qbtypes.Result, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.QueryDuration: instrumentationtypes.DurationBucket(q.fromMS, q.toMS),
})
totalRows := uint64(0)
totalBytes := uint64(0)

View File

@@ -14,6 +14,8 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbv5 "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/prometheus/prometheus/promql"
@@ -187,6 +189,11 @@ func (q *promqlQuery) renderVars(query string, vars map[string]qbv5.VariableItem
func (q *promqlQuery) Execute(ctx context.Context) (*qbv5.Result, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.QueryDuration: instrumentationtypes.DurationBucket(q.tr.From, q.tr.To),
})
start := int64(querybuilder.ToNanoSecs(q.tr.From))
end := int64(querybuilder.ToNanoSecs(q.tr.To))

View File

@@ -16,6 +16,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/utils"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/metrictypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"golang.org/x/exp/maps"
@@ -526,6 +528,11 @@ func (q *querier) run(
steps map[string]qbtypes.Step,
qbEvent *qbtypes.QBEvent,
) (*qbtypes.QueryRangeResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.PanelType: qbEvent.PanelType,
instrumentationtypes.QueryType: qbEvent.QueryType,
})
results := make(map[string]any)
warnings := make([]string, 0)
warningsDocURL := ""

View File

@@ -6,7 +6,10 @@ import (
"github.com/ClickHouse/clickhouse-go/v2"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
type traceOperatorQuery struct {
@@ -52,6 +55,11 @@ func (q *traceOperatorQuery) Execute(ctx context.Context) (*qbtypes.Result, erro
}
func (q *traceOperatorQuery) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.QueryDuration: instrumentationtypes.DurationBucket(q.fromMS, q.toMS),
})
totalRows := uint64(0)
totalBytes := uint64(0)
elapsed := time.Duration(0)

View File

@@ -20,6 +20,9 @@ import (
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
@@ -269,6 +272,12 @@ func (r *ClickHouseReader) GetQueryRangeResult(ctx context.Context, query *model
}
func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetServicesList",
})
services := []string{}
rows, err := r.db.Query(ctx, fmt.Sprintf(`SELECT DISTINCT resource_string_service$$name FROM %s.%s WHERE ts_bucket_start > (toUnixTimestamp(now() - INTERVAL 1 DAY) - 1800) AND toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName))
if err != nil {
@@ -288,6 +297,12 @@ func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, erro
}
func (r *ClickHouseReader) GetTopLevelOperations(ctx context.Context, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTopLevelOperations",
})
start = start.In(time.UTC)
// The `top_level_operations` that have `time` >= start
@@ -383,6 +398,12 @@ func (r *ClickHouseReader) buildResourceSubQuery(tags []model.TagQueryParam, svc
func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.GetServicesParams) (*[]model.ServiceItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetServices",
})
if r.indexTable == "" {
return nil, &model.ApiError{Typ: model.ErrorExec, Err: ErrNoIndexTable}
}
@@ -739,6 +760,11 @@ func (r *ClickHouseReader) GetEntryPointOperations(ctx context.Context, queryPar
func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTopOperations",
})
namedArgs := []interface{}{
clickhouse.Named("start", strconv.FormatInt(queryParams.Start.UnixNano(), 10)),
clickhouse.Named("end", strconv.FormatInt(queryParams.End.UnixNano(), 10)),
@@ -794,6 +820,11 @@ func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *mo
func (r *ClickHouseReader) GetUsage(ctx context.Context, queryParams *model.GetUsageParams) (*[]model.UsageItem, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetUsage",
})
var usageItems []model.UsageItem
namedArgs := []interface{}{
clickhouse.Named("interval", queryParams.StepHour),
@@ -829,6 +860,13 @@ func (r *ClickHouseReader) GetUsage(ctx context.Context, queryParams *model.GetU
}
func (r *ClickHouseReader) GetSpansForTrace(ctx context.Context, traceID string, traceDetailsQuery string) ([]model.SpanItemV2, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetSpansForTrace",
})
var traceSummary model.TraceSummary
summaryQuery := fmt.Sprintf("SELECT trace_id, min(start) AS start, max(end) AS end, sum(num_spans) AS num_spans FROM %s.%s WHERE trace_id=$1 GROUP BY trace_id", r.TraceDB, r.traceSummaryTable)
err := r.db.QueryRow(ctx, summaryQuery, traceID).Scan(&traceSummary.TraceID, &traceSummary.Start, &traceSummary.End, &traceSummary.NumSpans)
@@ -1227,6 +1265,11 @@ func (r *ClickHouseReader) GetFlamegraphSpansForTrace(ctx context.Context, orgID
func (r *ClickHouseReader) GetDependencyGraph(ctx context.Context, queryParams *model.GetServicesParams) (*[]model.ServiceMapDependencyResponseItem, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetDependencyGraph",
})
response := []model.ServiceMapDependencyResponseItem{}
args := []interface{}{}
@@ -1281,6 +1324,11 @@ func getLocalTableName(tableName string) string {
}
func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "setTTLLogs",
})
hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
if hasCustomRetention {
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("SetTTLV2 only supported")}
@@ -1444,6 +1492,11 @@ func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params
}
func (r *ClickHouseReader) setTTLTraces(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "setTTLTraces",
})
// uuid is used as transaction id
uuidWithHyphen := uuid.New()
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
@@ -1589,6 +1642,12 @@ func (r *ClickHouseReader) setTTLTraces(ctx context.Context, orgID string, param
}
func (r *ClickHouseReader) hasCustomRetentionColumn(ctx context.Context) (bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "hasCustomRetentionColumn",
})
// Directly query for the _retention_days column existence
query := fmt.Sprintf("SELECT 1 FROM system.columns WHERE database = '%s' AND table = '%s' AND name = '_retention_days' LIMIT 1", r.logsDB, r.logsLocalTableV2)
@@ -1610,6 +1669,11 @@ func (r *ClickHouseReader) hasCustomRetentionColumn(ctx context.Context) (bool,
func (r *ClickHouseReader) SetTTLV2(ctx context.Context, orgID string, params *model.CustomRetentionTTLParams) (*model.CustomRetentionTTLResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "SetTTLV2",
})
hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
if err != nil {
return nil, errorsV2.Wrapf(err, errorsV2.TypeInternal, errorsV2.CodeInternal, "custom retention not supported")
@@ -1999,6 +2063,10 @@ func (r *ClickHouseReader) updateCustomRetentionTTLStatus(ctx context.Context, o
// Enhanced validation function with duplicate detection and efficient key validation
func (r *ClickHouseReader) validateTTLConditions(ctx context.Context, ttlConditions []model.CustomRetentionRule) error {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "validateTTLConditions",
})
if len(ttlConditions) == 0 {
return nil
}
@@ -2116,6 +2184,11 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, orgID string, params *mod
}
func (r *ClickHouseReader) setTTLMetrics(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "setTTLMetrics",
})
// uuid is used as transaction id
uuidWithHyphen := uuid.New()
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
@@ -2324,6 +2397,10 @@ func (r *ClickHouseReader) getTTLQueryStatus(ctx context.Context, orgID string,
func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string, coldStorageVolume string) *model.ApiError {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "setColdStorage",
})
// Set the storage policy for the required table. If it is already set, then setting it again
// will not a problem.
if len(coldStorageVolume) > 0 {
@@ -2340,6 +2417,10 @@ func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string,
// GetDisks returns a list of disks {name, type} configured in clickhouse DB.
func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetDisks",
})
diskItems := []model.DiskItem{}
query := "SELECT name,type FROM system.disks"
@@ -2363,6 +2444,10 @@ func getLocalTableNameArray(tableNames []string) []string {
// GetTTL returns current ttl, expected ttl and past setTTL status for metrics/traces.
func (r *ClickHouseReader) GetTTL(ctx context.Context, orgID string, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTTL",
})
parseTTL := func(queryResp string) (int, int) {
zap.L().Info("Parsing TTL from: ", zap.String("queryResp", queryResp))
@@ -2532,6 +2617,11 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, orgID string, ttlParams *
func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.ListErrorsParams) (*[]model.Error, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "ListErrors",
})
var getErrorResponses []model.Error
query := "SELECT any(exceptionMessage) as exceptionMessage, count() AS exceptionCount, min(timestamp) as firstSeen, max(timestamp) as lastSeen, groupID"
@@ -2604,6 +2694,12 @@ func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.Li
func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.CountErrorsParams) (uint64, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "CountErrors",
})
var errorCount uint64
query := fmt.Sprintf("SELECT count(distinct(groupID)) FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", r.TraceDB, r.errorTable)
@@ -2641,6 +2737,11 @@ func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.C
func (r *ClickHouseReader) GetErrorFromErrorID(ctx context.Context, queryParams *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetErrorFromErrorID",
})
if queryParams.ErrorID == "" {
zap.L().Error("errorId missing from params")
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("ErrorID missing from params")}
@@ -2668,6 +2769,11 @@ func (r *ClickHouseReader) GetErrorFromErrorID(ctx context.Context, queryParams
func (r *ClickHouseReader) GetErrorFromGroupID(ctx context.Context, queryParams *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetErrorFromGroupID",
})
var getErrorWithSpanReponse []model.ErrorWithSpan
query := fmt.Sprintf("SELECT errorID, exceptionType, exceptionStacktrace, exceptionEscaped, exceptionMessage, timestamp, spanID, traceID, serviceName, groupID FROM %s.%s WHERE timestamp = @timestamp AND groupID = @groupID LIMIT 1", r.TraceDB, r.errorTable)
@@ -2716,6 +2822,11 @@ func (r *ClickHouseReader) GetNextPrevErrorIDs(ctx context.Context, queryParams
func (r *ClickHouseReader) getNextErrorID(ctx context.Context, queryParams *model.GetErrorParams) (string, time.Time, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "getNextErrorID",
})
var getNextErrorIDReponse []model.NextPrevErrorIDsDBResponse
query := fmt.Sprintf("SELECT errorID as nextErrorID, timestamp as nextTimestamp FROM %s.%s WHERE groupID = @groupID AND timestamp >= @timestamp AND errorID != @errorID ORDER BY timestamp ASC LIMIT 2", r.TraceDB, r.errorTable)
@@ -2785,6 +2896,11 @@ func (r *ClickHouseReader) getNextErrorID(ctx context.Context, queryParams *mode
func (r *ClickHouseReader) getPrevErrorID(ctx context.Context, queryParams *model.GetErrorParams) (string, time.Time, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "getPrevErrorID",
})
var getPrevErrorIDReponse []model.NextPrevErrorIDsDBResponse
query := fmt.Sprintf("SELECT errorID as prevErrorID, timestamp as prevTimestamp FROM %s.%s WHERE groupID = @groupID AND timestamp <= @timestamp AND errorID != @errorID ORDER BY timestamp DESC LIMIT 2", r.TraceDB, r.errorTable)
@@ -2876,6 +2992,11 @@ func (r *ClickHouseReader) FetchTemporality(ctx context.Context, orgID valuer.UU
}
func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLogFields",
})
// response will contain top level fields from the otel log model
response := model.GetFieldsResponse{
Selected: constants.StaticSelectedLogFields,
@@ -2912,6 +3033,11 @@ func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsRe
}
func (r *ClickHouseReader) GetLogFieldsFromNames(ctx context.Context, fieldNames []string) (*model.GetFieldsResponse, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLogFieldsFromNames",
})
// response will contain top level fields from the otel log model
response := model.GetFieldsResponse{
Selected: constants.StaticSelectedLogFields,
@@ -2962,6 +3088,10 @@ func (r *ClickHouseReader) extractSelectedAndInterestingFields(tableStatement st
}
func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.UpdateField) *model.ApiError {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "UpdateLogField",
})
if !field.Selected {
return model.ForbiddenError(errors.New("removing a selected field is not allowed, please reach out to support."))
}
@@ -3028,6 +3158,10 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda
}
func (r *ClickHouseReader) GetTraceFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTraceFields",
})
// response will contain top level fields from the otel trace model
response := model.GetFieldsResponse{
Selected: []model.Field{},
@@ -3083,6 +3217,11 @@ func (r *ClickHouseReader) GetTraceFields(ctx context.Context) (*model.GetFields
}
func (r *ClickHouseReader) UpdateTraceField(ctx context.Context, field *model.UpdateField) *model.ApiError {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "UpdateTraceField",
})
if !field.Selected {
return model.ForbiddenError(errors.New("removing a selected field is not allowed, please reach out to support."))
}
@@ -3174,6 +3313,10 @@ func (r *ClickHouseReader) UpdateTraceField(ctx context.Context, field *model.Up
return nil
}
func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "QueryDashboardVars",
})
var result = model.DashboardVar{VariableValues: make([]interface{}, 0)}
rows, err := r.db.Query(ctx, query)
@@ -3210,6 +3353,11 @@ func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string)
}
func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, orgID valuer.UUID, req *v3.AggregateAttributeRequest, skipSignozMetrics bool) (*v3.AggregateAttributeResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricAggregateAttributes",
})
var response v3.AggregateAttributeResponse
normalized := true
if constants.IsDotMetricsEnabled {
@@ -3288,6 +3436,11 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, org
}
func (r *ClickHouseReader) GetMeterAggregateAttributes(ctx context.Context, orgID valuer.UUID, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMeterAggregateAttributes",
})
var response v3.AggregateAttributeResponse
// Query all relevant metric names from time_series_v4, but leave metadata retrieval to cache/db
query := fmt.Sprintf(
@@ -3336,6 +3489,11 @@ func (r *ClickHouseReader) GetMeterAggregateAttributes(ctx context.Context, orgI
}
func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricAttributeKeys",
})
var query string
var err error
var rows driver.Rows
@@ -3376,6 +3534,11 @@ func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.F
}
func (r *ClickHouseReader) GetMeterAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMeterAttributeKeys",
})
var query string
var err error
var rows driver.Rows
@@ -3412,6 +3575,11 @@ func (r *ClickHouseReader) GetMeterAttributeKeys(ctx context.Context, req *v3.Fi
func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricAttributeValues",
})
var query string
var err error
var rows driver.Rows
@@ -3452,6 +3620,11 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3
func (r *ClickHouseReader) GetMetricMetadata(ctx context.Context, orgID valuer.UUID, metricName, serviceName string) (*v3.MetricMetadataResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricMetadata",
})
unixMilli := common.PastDayRoundOff()
// 1. Fetch metadata from cache/db using unified function
@@ -3533,6 +3706,10 @@ func (r *ClickHouseReader) GetMetricMetadata(ctx context.Context, orgID valuer.U
// GetCountOfThings returns the count of things in the query
// This is a generic function that can be used to check if any data exists for a given query
func (r *ClickHouseReader) GetCountOfThings(ctx context.Context, query string) (uint64, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetCountOfThings",
})
var count uint64
err := r.db.QueryRow(ctx, query).Scan(&count)
if err != nil {
@@ -3583,6 +3760,11 @@ func (r *ClickHouseReader) GetActiveHostsFromMetricMetadata(ctx context.Context,
func (r *ClickHouseReader) GetLatestReceivedMetric(
ctx context.Context, metricNames []string, labelValues map[string]string,
) (*model.MetricStatus, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLatestReceivedMetric",
})
// at least 1 metric name must be specified.
// this query can be too slow otherwise.
if len(metricNames) < 1 {
@@ -3667,6 +3849,11 @@ func isColumn(tableStatement, attrType, field, datType string) bool {
func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLogAggregateAttributes",
})
var query string
var err error
var rows driver.Rows
@@ -3751,6 +3938,11 @@ func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v
}
func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLogAttributeKeys",
})
var query string
var err error
var rows driver.Rows
@@ -3817,6 +4009,10 @@ func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.Filt
}
func (r *ClickHouseReader) FetchRelatedValues(ctx context.Context, req *v3.FilterAttributeValueRequest) ([]string, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "FetchRelatedValues",
})
var andConditions []string
andConditions = append(andConditions, fmt.Sprintf("unix_milli >= %d", req.StartTimeMillis))
@@ -3908,6 +4104,11 @@ func (r *ClickHouseReader) FetchRelatedValues(ctx context.Context, req *v3.Filte
}
func (r *ClickHouseReader) GetLogAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLogAttributeValues",
})
var err error
var filterValueColumn string
var rows driver.Rows
@@ -4225,6 +4426,10 @@ func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNam
// GetTimeSeriesResultV3 runs the query and returns list of time series
func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query string) ([]*v3.Series, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTimeSeriesResultV3",
})
// Hook up query progress reporting if requested.
queryId := ctx.Value("queryId")
if queryId != nil {
@@ -4288,6 +4493,10 @@ func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query stri
// GetListResultV3 runs the query and returns list of rows
func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetListResultV3",
})
rows, err := r.db.Query(ctx, query)
if err != nil {
zap.L().Error("error while reading time series result", zap.Error(err))
@@ -4350,6 +4559,11 @@ func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([
// GetHostMetricsExistenceAndEarliestTime returns (count, minFirstReportedUnixMilli, error) for the given host metric names
// from distributed_metadata. When count is 0, minFirstReportedUnixMilli is 0.
func (r *ClickHouseReader) GetMetricsExistenceAndEarliestTime(ctx context.Context, metricNames []string) (uint64, uint64, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricsExistenceAndEarliestTime",
})
if len(metricNames) == 0 {
return 0, 0, nil
}
@@ -4385,6 +4599,10 @@ func getPersonalisedError(err error) error {
}
func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "CheckClickHouse",
})
rows, err := r.db.Query(ctx, "SELECT 1")
if err != nil {
return err
@@ -4395,6 +4613,11 @@ func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
}
func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTraceAggregateAttributes",
})
var query string
var err error
var rows driver.Rows
@@ -4488,6 +4711,11 @@ func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req
func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTraceAttributeKeys",
})
var query string
var err error
var rows driver.Rows
@@ -4556,6 +4784,11 @@ func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.Fi
}
func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTraceAttributeValues",
})
var query string
var filterValueColumn string
var err error
@@ -4649,6 +4882,11 @@ func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.
}
func (r *ClickHouseReader) GetSpanAttributeKeysByNames(ctx context.Context, names []string) (map[string]v3.AttributeKey, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetSpanAttributeKeysByNames",
})
var query string
var err error
var rows driver.Rows
@@ -4697,6 +4935,10 @@ func (r *ClickHouseReader) GetSpanAttributeKeysByNames(ctx context.Context, name
}
func (r *ClickHouseReader) AddRuleStateHistory(ctx context.Context, ruleStateHistory []model.RuleStateHistory) error {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "AddRuleStateHistory",
})
var statement driver.Batch
var err error
@@ -4728,6 +4970,10 @@ func (r *ClickHouseReader) AddRuleStateHistory(ctx context.Context, ruleStateHis
}
func (r *ClickHouseReader) GetLastSavedRuleStateHistory(ctx context.Context, ruleID string) ([]model.RuleStateHistory, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetLastSavedRuleStateHistory",
})
query := fmt.Sprintf("SELECT * FROM %s.%s WHERE rule_id = '%s' AND state_changed = true ORDER BY unix_milli DESC LIMIT 1 BY fingerprint",
signozHistoryDBName, ruleStateHistoryTableName, ruleID)
@@ -4742,6 +4988,10 @@ func (r *ClickHouseReader) GetLastSavedRuleStateHistory(ctx context.Context, rul
func (r *ClickHouseReader) ReadRuleStateHistoryByRuleID(
ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (*model.RuleStateTimeline, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "ReadRuleStateHistoryByRuleID",
})
var conditions []string
conditions = append(conditions, fmt.Sprintf("rule_id = '%s'", ruleID))
@@ -4856,6 +5106,10 @@ func (r *ClickHouseReader) ReadRuleStateHistoryByRuleID(
func (r *ClickHouseReader) ReadRuleStateHistoryTopContributorsByRuleID(
ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) ([]model.RuleStateHistoryContributor, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "ReadRuleStateHistoryTopContributorsByRuleID",
})
query := fmt.Sprintf(`SELECT
fingerprint,
any(labels) as labels,
@@ -4880,6 +5134,10 @@ func (r *ClickHouseReader) ReadRuleStateHistoryTopContributorsByRuleID(
func (r *ClickHouseReader) GetOverallStateTransitions(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) ([]model.ReleStateItem, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetOverallStateTransitions",
})
tmpl := `WITH firing_events AS (
SELECT
rule_id,
@@ -5007,6 +5265,10 @@ ORDER BY firing_time ASC;`
func (r *ClickHouseReader) GetAvgResolutionTime(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (float64, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAvgResolutionTime",
})
tmpl := `
WITH firing_events AS (
SELECT
@@ -5118,6 +5380,10 @@ ORDER BY ts ASC;`
}
func (r *ClickHouseReader) GetTotalTriggers(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (uint64, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTotalTriggers",
})
query := fmt.Sprintf("SELECT count(*) FROM %s.%s WHERE rule_id = '%s' AND (state_changed = true) AND (state = '%s') AND unix_milli >= %d AND unix_milli <= %d",
signozHistoryDBName, ruleStateHistoryTableName, ruleID, model.StateFiring.String(), params.Start, params.End)
@@ -5146,6 +5412,11 @@ func (r *ClickHouseReader) GetTriggersByInterval(ctx context.Context, ruleID str
}
func (r *ClickHouseReader) GetMinAndMaxTimestampForTraceID(ctx context.Context, traceID []string) (int64, int64, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMinAndMaxTimestampForTraceID",
})
var minTime, maxTime time.Time
query := fmt.Sprintf("SELECT min(timestamp), max(timestamp) FROM %s.%s WHERE traceID IN ('%s')",
@@ -5183,6 +5454,11 @@ func (r *ClickHouseReader) SubscribeToQueryProgress(
}
func (r *ClickHouseReader) GetAllMetricFilterAttributeKeys(ctx context.Context, req *metrics_explorer.FilterKeyRequest) (*[]v3.AttributeKey, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAllMetricFilterAttributeKeys",
})
var rows driver.Rows
var response []v3.AttributeKey
normalized := true
@@ -5220,6 +5496,11 @@ func (r *ClickHouseReader) GetAllMetricFilterAttributeKeys(ctx context.Context,
}
func (r *ClickHouseReader) GetAllMetricFilterAttributeValues(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAllMetricFilterAttributeValues",
})
var query string
var err error
var rows driver.Rows
@@ -5256,6 +5537,11 @@ func (r *ClickHouseReader) GetAllMetricFilterAttributeValues(ctx context.Context
}
func (r *ClickHouseReader) GetAllMetricFilterUnits(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAllMetricFilterUnits",
})
var rows driver.Rows
var response []string
query := fmt.Sprintf("SELECT DISTINCT unit FROM %s.%s WHERE unit ILIKE $1 AND unit IS NOT NULL ORDER BY unit", signozMetricDBName, signozTSTableNameV41Day)
@@ -5283,6 +5569,11 @@ func (r *ClickHouseReader) GetAllMetricFilterUnits(ctx context.Context, req *met
return response, nil
}
func (r *ClickHouseReader) GetAllMetricFilterTypes(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAllMetricFilterTypes",
})
var rows driver.Rows
var response []string
query := fmt.Sprintf("SELECT DISTINCT type FROM %s.%s WHERE type ILIKE $1 AND type IS NOT NULL ORDER BY type", signozMetricDBName, signozTSTableNameV41Day)
@@ -5310,6 +5601,11 @@ func (r *ClickHouseReader) GetAllMetricFilterTypes(ctx context.Context, req *met
}
func (r *ClickHouseReader) GetMetricsDataPoints(ctx context.Context, metricName string) (uint64, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricsDataPoints",
})
query := fmt.Sprintf(`SELECT
sum(count) as data_points
FROM %s.%s
@@ -5325,6 +5621,11 @@ WHERE metric_name = ?
}
func (r *ClickHouseReader) GetMetricsLastReceived(ctx context.Context, metricName string) (int64, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricsLastReceived",
})
query := fmt.Sprintf(`SELECT
MAX(unix_milli) AS last_received_time
FROM %s.%s
@@ -5350,6 +5651,11 @@ WHERE metric_name = ? and unix_milli > ?
}
func (r *ClickHouseReader) GetTotalTimeSeriesForMetricName(ctx context.Context, metricName string) (uint64, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetTotalTimeSeriesForMetricName",
})
query := fmt.Sprintf(`SELECT
uniq(fingerprint) AS timeSeriesCount
FROM %s.%s
@@ -5364,6 +5670,11 @@ WHERE metric_name = ?;`, signozMetricDBName, signozTSTableNameV41Week)
}
func (r *ClickHouseReader) GetAttributesForMetricName(ctx context.Context, metricName string, start, end *int64, filters *v3.FilterSet) (*[]metrics_explorer.Attribute, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAttributesForMetricName",
})
whereClause := ""
if filters != nil {
conditions, _ := utils.BuildFilterConditions(filters, "t")
@@ -5431,6 +5742,11 @@ WHERE metric_name = ? AND __normalized=? %s`
}
func (r *ClickHouseReader) GetActiveTimeSeriesForMetricName(ctx context.Context, metricName string, duration time.Duration) (uint64, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetActiveTimeSeriesForMetricName",
})
milli := time.Now().Add(-duration).UnixMilli()
query := fmt.Sprintf("SELECT uniq(fingerprint) FROM %s.%s WHERE metric_name = '%s' and unix_milli >= ?", signozMetricDBName, signozTSTableNameV4, metricName)
var timeSeries uint64
@@ -5444,6 +5760,11 @@ func (r *ClickHouseReader) GetActiveTimeSeriesForMetricName(ctx context.Context,
}
func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.UUID, req *metrics_explorer.SummaryListMetricsRequest) (*metrics_explorer.SummaryListMetricsResponse, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "ListSummaryMetrics",
})
var args []interface{}
// Build filter conditions (if any)
@@ -5662,6 +5983,11 @@ func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.
}
func (r *ClickHouseReader) GetMetricsTimeSeriesPercentage(ctx context.Context, req *metrics_explorer.TreeMapMetricsRequest) (*[]metrics_explorer.TreeMapResponseItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricsTimeSeriesPercentage",
})
var args []interface{}
normalized := true
@@ -5742,6 +6068,11 @@ func (r *ClickHouseReader) GetMetricsTimeSeriesPercentage(ctx context.Context, r
func (r *ClickHouseReader) GetMetricsSamplesPercentage(ctx context.Context, req *metrics_explorer.TreeMapMetricsRequest) (*[]metrics_explorer.TreeMapResponseItem, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricsSamplesPercentage",
})
conditions, _ := utils.BuildFilterConditions(&req.Filters, "ts")
whereClause := ""
if conditions != nil {
@@ -5901,6 +6232,11 @@ func (r *ClickHouseReader) GetMetricsSamplesPercentage(ctx context.Context, req
}
func (r *ClickHouseReader) GetNameSimilarity(ctx context.Context, req *metrics_explorer.RelatedMetricsRequest) (map[string]metrics_explorer.RelatedMetricsScore, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetNameSimilarity",
})
start, end, tsTable, _ := utils.WhichTSTableToUse(req.Start, req.End)
normalized := true
@@ -5954,6 +6290,11 @@ func (r *ClickHouseReader) GetNameSimilarity(ctx context.Context, req *metrics_e
}
func (r *ClickHouseReader) GetAttributeSimilarity(ctx context.Context, req *metrics_explorer.RelatedMetricsRequest) (map[string]metrics_explorer.RelatedMetricsScore, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetAttributeSimilarity",
})
start, end, tsTable, _ := utils.WhichTSTableToUse(req.Start, req.End)
normalized := true
@@ -6112,6 +6453,11 @@ func (r *ClickHouseReader) GetAttributeSimilarity(ctx context.Context, req *metr
}
func (r *ClickHouseReader) GetMetricsAllResourceAttributes(ctx context.Context, start int64, end int64) (map[string]uint64, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetMetricsAllResourceAttributes",
})
start, end, attTable, _ := utils.WhichAttributesTableToUse(start, end)
query := fmt.Sprintf(`SELECT
key,
@@ -6148,6 +6494,11 @@ ORDER BY distinct_value_count DESC;`, signozMetadataDbName, attTable)
}
func (r *ClickHouseReader) GetInspectMetrics(ctx context.Context, req *metrics_explorer.InspectMetricsRequest, fingerprints []string) (*metrics_explorer.InspectMetricsResponse, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetInspectMetrics",
})
start, end, _, localTsTable := utils.WhichTSTableToUse(req.Start, req.End)
fingerprintsString := strings.Join(fingerprints, ",")
query := fmt.Sprintf(`SELECT
@@ -6242,6 +6593,11 @@ func (r *ClickHouseReader) GetInspectMetrics(ctx context.Context, req *metrics_e
}
func (r *ClickHouseReader) GetInspectMetricsFingerprints(ctx context.Context, attributes []string, req *metrics_explorer.InspectMetricsRequest) ([]string, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetInspectMetricsFingerprints",
})
// Build dynamic key selections and JSON extracts
var jsonExtracts []string
var groupBys []string
@@ -6323,6 +6679,11 @@ LIMIT 40`, // added rand to get diff value every time we run this query
}
func (r *ClickHouseReader) UpdateMetricsMetadata(ctx context.Context, orgID valuer.UUID, req *model.UpdateMetricsMetadata) *model.ApiError {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "UpdateMetricsMetadata",
})
if req.MetricType == v3.MetricTypeHistogram {
labels := []string{"le"}
hasLabels, apiError := r.CheckForLabelsInMetric(ctx, req.MetricName, labels)
@@ -6367,6 +6728,11 @@ VALUES ( ?, ?, ?, ?, ?, ?, ?);`, signozMetricDBName, signozUpdatedMetricsMetadat
}
func (r *ClickHouseReader) CheckForLabelsInMetric(ctx context.Context, metricName string, labels []string) (bool, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "CheckForLabelsInMetric",
})
if len(labels) == 0 {
return true, nil
}
@@ -6401,6 +6767,11 @@ func (r *ClickHouseReader) CheckForLabelsInMetric(ctx context.Context, metricNam
}
func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID valuer.UUID, metricNames ...string) (map[string]*model.UpdateMetricsMetadata, *model.ApiError) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetUpdatedMetricsMetadata",
})
cachedMetadata := make(map[string]*model.UpdateMetricsMetadata)
var missingMetrics []string
@@ -6510,6 +6881,11 @@ func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID
}
func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.SearchTracesParams) (*[]model.SearchSpansResult, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "SearchTraces",
})
searchSpansResult := []model.SearchSpansResult{
{
Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError", "StatusMessage", "StatusCodeString", "SpanKind"},
@@ -6621,6 +6997,11 @@ func (r *ClickHouseReader) GetNormalizedStatus(
metricNames []string,
) (map[string]bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "clickhouse-reader",
instrumentationtypes.CodeFunctionName: "GetNormalizedStatus",
})
if len(metricNames) == 0 {
return map[string]bool{}, nil
}

View File

@@ -62,8 +62,10 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/licensetypes"
"github.com/SigNoz/signoz/pkg/types/opamptypes"
"github.com/SigNoz/signoz/pkg/types/pipelinetypes"
@@ -2412,7 +2414,12 @@ func (aH *APIHandler) onboardKafka(w http.ResponseWriter, r *http.Request) {
return
}
results, errQueriesByName, err := aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "onboardKafka",
})
results, errQueriesByName, err := aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2524,7 +2531,12 @@ func (aH *APIHandler) getNetworkData(w http.ResponseWriter, r *http.Request) {
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getNetworkData",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2560,7 +2572,7 @@ func (aH *APIHandler) getNetworkData(w http.ResponseWriter, r *http.Request) {
return
}
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByNameFetchLatency)
@@ -2616,7 +2628,11 @@ func (aH *APIHandler) getProducerData(w http.ResponseWriter, r *http.Request) {
}
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
kafkaSpanEval := aH.Signoz.Flagger.BooleanOrEmpty(r.Context(), flagger.FeatureKafkaSpanEval, evalCtx)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getProducerData",
})
kafkaSpanEval := aH.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureKafkaSpanEval, evalCtx)
queryRangeParams, err := kafka.BuildQueryRangeParams(messagingQueue, "producer", kafkaSpanEval)
if err != nil {
@@ -2634,7 +2650,7 @@ func (aH *APIHandler) getProducerData(w http.ResponseWriter, r *http.Request) {
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2687,7 +2703,11 @@ func (aH *APIHandler) getConsumerData(w http.ResponseWriter, r *http.Request) {
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getConsumerData",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2741,7 +2761,11 @@ func (aH *APIHandler) getPartitionOverviewLatencyData(w http.ResponseWriter, r *
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getPartitionOverviewLatencyData",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2795,7 +2819,11 @@ func (aH *APIHandler) getConsumerPartitionLatencyData(w http.ResponseWriter, r *
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getConsumerPartitionLatencyData",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2852,7 +2880,11 @@ func (aH *APIHandler) getProducerThroughputOverview(w http.ResponseWriter, r *ht
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, producerQueryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getProducerThroughputOverview",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, producerQueryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -2886,7 +2918,7 @@ func (aH *APIHandler) getProducerThroughputOverview(w http.ResponseWriter, r *ht
return
}
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByNameFetchLatency)
@@ -2963,7 +2995,11 @@ func (aH *APIHandler) getProducerThroughputDetails(w http.ResponseWriter, r *htt
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getProducerThroughputDetails",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -3017,7 +3053,11 @@ func (aH *APIHandler) getConsumerThroughputOverview(w http.ResponseWriter, r *ht
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getConsumerThroughputOverview",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -3071,7 +3111,11 @@ func (aH *APIHandler) getConsumerThroughputDetails(w http.ResponseWriter, r *htt
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getConsumerThroughputDetails",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -3131,7 +3175,11 @@ func (aH *APIHandler) getProducerConsumerEval(w http.ResponseWriter, r *http.Req
var result []*v3.Result
var errQueriesByName map[string]error
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getProducerConsumerEval",
})
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
RespondError(w, apiErrObj, errQueriesByName)
@@ -3392,6 +3440,10 @@ func (aH *APIHandler) calculateLogsConnectionStatus(ctx context.Context, orgID v
},
},
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "calculateLogsConnectionStatus",
})
queryRes, _, err := aH.querier.QueryRange(ctx, orgID, qrParams)
if err != nil {
return nil, model.InternalError(fmt.Errorf(
@@ -3944,6 +3996,10 @@ func (aH *APIHandler) calculateAWSIntegrationSvcLogsConnectionStatus(
},
},
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "calculateLogsConnectionStatus",
})
queryRes, _, err := aH.querier.QueryRange(
ctx, orgID, qrParams,
)
@@ -4492,6 +4548,10 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "QueryRange",
})
result, errQueriesByName, err = aH.querier.QueryRange(ctx, orgID, queryRangeParams)
if err != nil {
@@ -4886,7 +4946,11 @@ func (aH *APIHandler) QueryRangeV4(w http.ResponseWriter, r *http.Request) {
return
}
aH.queryRangeV4(r.Context(), queryRangeParams, w, r)
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "QueryRangeV4",
})
aH.queryRangeV4(ctx, queryRangeParams, w, r)
}
func (aH *APIHandler) traceFields(w http.ResponseWriter, r *http.Request) {
@@ -4979,8 +5043,12 @@ func (aH *APIHandler) getDomainList(w http.ResponseWriter, r *http.Request) {
return
}
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getDomainList",
})
// Execute the query using the v5 querier
result, err := aH.Signoz.Querier.QueryRange(r.Context(), orgID, queryRangeRequest)
result, err := aH.Signoz.Querier.QueryRange(ctx, orgID, queryRangeRequest)
if err != nil {
zap.L().Error("Query execution failed", zap.Error(err))
apiErrObj := errorsV2.New(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
@@ -5035,8 +5103,12 @@ func (aH *APIHandler) getDomainInfo(w http.ResponseWriter, r *http.Request) {
return
}
ctx := ctxtypes.NewContextWithCommentVals(r.Context(), map[string]string{
instrumentationtypes.CodeNamespace: "app",
instrumentationtypes.CodeFunctionName: "getDomainInfo",
})
// Execute the query using the v5 querier
result, err := aH.Signoz.Querier.QueryRange(r.Context(), orgID, queryRangeRequest)
result, err := aH.Signoz.Querier.QueryRange(ctx, orgID, queryRangeRequest)
if err != nil {
zap.L().Error("Query execution failed", zap.Error(err))
apiErrObj := errorsV2.New(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -163,6 +165,10 @@ func (p *ClustersRepo) getTopClusterGroups(ctx context.Context, orgID valuer.UUI
topClusterGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopClusterGroups",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topClusterGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -278,6 +284,10 @@ func (p *ClustersRepo) GetClusterList(ctx context.Context, orgID valuer.UUID, re
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetClusterList",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -230,6 +232,10 @@ func (d *DaemonSetsRepo) getTopDaemonSetGroups(ctx context.Context, orgID valuer
topDaemonSetGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopDaemonSetGroups",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topDaemonSetGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -355,6 +361,10 @@ func (d *DaemonSetsRepo) GetDaemonSetList(ctx context.Context, orgID valuer.UUID
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetDaemonSetList",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -230,6 +232,10 @@ func (d *DeploymentsRepo) getTopDeploymentGroups(ctx context.Context, orgID valu
topDeploymentGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopDeploymentGroups",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topDeploymentGroupsQueryRangeParams)
if err != nil {
return nil, nil, err

View File

@@ -16,6 +16,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"go.uber.org/zap"
"golang.org/x/exp/maps"
@@ -272,6 +274,10 @@ func (h *HostsRepo) getTopHostGroups(ctx context.Context, orgID valuer.UUID, req
topHostGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopHostGroups",
})
queryResponse, _, err := h.querierV2.QueryRange(ctx, orgID, topHostGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -482,6 +488,10 @@ func (h *HostsRepo) GetHostList(ctx context.Context, orgID valuer.UUID, req mode
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetHostList",
})
queryResponse, _, err := h.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -274,6 +276,10 @@ func (d *JobsRepo) getTopJobGroups(ctx context.Context, orgID valuer.UUID, req m
topJobGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopJobGroups",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topJobGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -399,6 +405,10 @@ func (d *JobsRepo) GetJobList(ctx context.Context, orgID valuer.UUID, req model.
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetJobList",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -157,6 +159,10 @@ func (p *NamespacesRepo) getTopNamespaceGroups(ctx context.Context, orgID valuer
topNamespaceGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopNamespaceGroups",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topNamespaceGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -277,6 +283,10 @@ func (p *NamespacesRepo) GetNamespaceList(ctx context.Context, orgID valuer.UUID
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetNamespaceList",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -14,6 +14,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -187,6 +189,10 @@ func (p *NodesRepo) getTopNodeGroups(ctx context.Context, orgID valuer.UUID, req
topNodeGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopNodeGroups",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topNodeGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -302,6 +308,10 @@ func (p *NodesRepo) GetNodeList(ctx context.Context, orgID valuer.UUID, req mode
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetNodeList",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -14,6 +14,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -332,6 +334,10 @@ func (p *PodsRepo) getTopPodGroups(ctx context.Context, orgID valuer.UUID, req m
topPodGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopPodGroups",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topPodGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -447,6 +453,10 @@ func (p *PodsRepo) GetPodList(ctx context.Context, orgID valuer.UUID, req model.
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetPodList",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -171,6 +173,10 @@ func (p *ProcessesRepo) getTopProcessGroups(ctx context.Context, orgID valuer.UU
topProcessGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopProcessGroups",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topProcessGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -284,6 +290,10 @@ func (p *ProcessesRepo) GetProcessList(ctx context.Context, orgID valuer.UUID, r
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetProcessList",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -190,6 +192,10 @@ func (p *PvcsRepo) getTopVolumeGroups(ctx context.Context, orgID valuer.UUID, re
topVolumeGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopVolumeGroups",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topVolumeGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -305,6 +311,10 @@ func (p *PvcsRepo) GetPvcList(ctx context.Context, orgID valuer.UUID, req model.
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetPvcList",
})
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"golang.org/x/exp/slices"
)
@@ -230,6 +232,10 @@ func (d *StatefulSetsRepo) getTopStatefulSetGroups(ctx context.Context, orgID va
topStatefulSetGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "getTopStatefulSetGroups",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topStatefulSetGroupsQueryRangeParams)
if err != nil {
return nil, nil, err
@@ -355,6 +361,10 @@ func (d *StatefulSetsRepo) GetStatefulSetList(ctx context.Context, orgID valuer.
}
}
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "inframetrics",
instrumentationtypes.CodeFunctionName: "GetStatefulSetList",
})
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, query)
if err != nil {
return resp, err

View File

@@ -17,6 +17,8 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/transition"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/ruletypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -433,7 +435,10 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID,
var results []*v3.Result
var queryErrors map[string]error
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "rules",
instrumentationtypes.CodeFunctionName: "buildAndRunQuery",
})
if r.version == "v4" {
results, queryErrors, err = r.querierV2.QueryRange(ctx, orgID, params)
} else {
@@ -501,6 +506,11 @@ func (r *ThresholdRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUI
var results []*v3.Result
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "rules",
instrumentationtypes.CodeFunctionName: "buildAndRunQueryV5",
})
v5Result, err := r.querierV5.QueryRange(ctx, orgID, params)
if err != nil {
r.logger.ErrorContext(ctx, "failed to get alert query result", "rule_name", r.Name(), "error", err)

View File

@@ -176,9 +176,10 @@ func NewSQLMigrationProviderFactories(
func NewTelemetryStoreProviderFactories() factory.NamedMap[factory.ProviderFactory[telemetrystore.TelemetryStore, telemetrystore.Config]] {
return factory.MustNewNamedMap(
clickhousetelemetrystore.NewFactory(
telemetrystorehook.NewSettingsFactory(),
telemetrystorehook.NewLoggingFactory(),
// adding instrumentation factory before settings as we are starting the query span here
telemetrystorehook.NewInstrumentationFactory(),
telemetrystorehook.NewSettingsFactory(),
),
)
}

View File

@@ -14,6 +14,8 @@ import (
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/tokenizer"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/SigNoz/signoz/pkg/version"
"go.opentelemetry.io/otel/attribute"
@@ -201,6 +203,10 @@ func (provider *provider) Stop(ctx context.Context) error {
}
func (provider *provider) collectOrg(ctx context.Context, orgID valuer.UUID) map[string]any {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.CodeNamespace: "statsreporter",
instrumentationtypes.CodeFunctionName: "collectOrg",
})
var wg sync.WaitGroup
wg.Add(len(provider.collectors))

View File

@@ -13,6 +13,8 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/telemetrylogs"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/huandu/go-sqlbuilder"
)
@@ -47,6 +49,11 @@ var (
// searchOperator: LIKE for pattern matching, EQUAL for exact match
func (t *telemetryMetaStore) fetchBodyJSONPaths(ctx context.Context,
fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, []string, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "fetchBodyJSONPaths",
})
query, args, limit := buildGetBodyJSONPathsQuery(fieldKeySelectors)
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
if err != nil {
@@ -267,6 +274,7 @@ func buildListLogsJSONIndexesQuery(cluster string, filters ...string) (string, [
}
func (t *telemetryMetaStore) ListLogsJSONIndexes(ctx context.Context, filters ...string) (map[string][]schemamigrator.Index, error) {
ctx = withTelemetryContext(ctx, "ListLogsJSONIndexes")
query, args := buildListLogsJSONIndexesQuery(t.telemetrystore.Cluster(), filters...)
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
if err != nil {
@@ -296,6 +304,7 @@ func (t *telemetryMetaStore) ListLogsJSONIndexes(ctx context.Context, filters ..
// TODO(Piyush): Remove this if not used in future
func (t *telemetryMetaStore) ListJSONValues(ctx context.Context, path string, limit int) (*telemetrytypes.TelemetryFieldValues, bool, error) {
ctx = withTelemetryContext(ctx, "ListJSONValues")
path = CleanPathPrefixes(path)
if strings.Contains(path, telemetrytypes.ArraySep) || strings.Contains(path, telemetrytypes.ArrayAnyIndex) {
@@ -458,6 +467,7 @@ func derefValue(v any) any {
// IsPathPromoted checks if a specific path is promoted (Column Evolution table: field_name for logs body).
func (t *telemetryMetaStore) IsPathPromoted(ctx context.Context, path string) (bool, error) {
ctx = withTelemetryContext(ctx, "IsPathPromoted")
split := strings.Split(path, telemetrytypes.ArraySep)
pathSegment := split[0]
query := fmt.Sprintf("SELECT 1 FROM %s.%s WHERE signal = ? AND column_name = ? AND field_context = ? AND field_name = ? LIMIT 1", DBName, PromotedPathsTableName)
@@ -472,6 +482,7 @@ func (t *telemetryMetaStore) IsPathPromoted(ctx context.Context, path string) (b
// GetPromotedPaths returns promoted paths from the Column Evolution table (field_name for logs body).
func (t *telemetryMetaStore) GetPromotedPaths(ctx context.Context, paths ...string) (map[string]bool, error) {
ctx = withTelemetryContext(ctx, "GetPromotedPaths")
sb := sqlbuilder.Select("field_name").From(fmt.Sprintf("%s.%s", DBName, PromotedPathsTableName))
conditions := []string{
sb.Equal("signal", telemetrytypes.SignalLogs),
@@ -518,6 +529,7 @@ func CleanPathPrefixes(path string) string {
// PromotePaths inserts promoted paths into the Column Evolution table (same schema as signoz-otel-collector metadata_migrations).
func (t *telemetryMetaStore) PromotePaths(ctx context.Context, paths ...string) error {
ctx = withTelemetryContext(ctx, "PromotePaths")
batch, err := t.telemetrystore.ClickhouseDB().PrepareBatch(ctx,
fmt.Sprintf("INSERT INTO %s.%s (signal, column_name, column_type, field_context, field_name, version, release_time) VALUES", DBName,
PromotedPathsTableName))
@@ -542,3 +554,11 @@ func (t *telemetryMetaStore) PromotePaths(ctx context.Context, paths ...string)
}
return nil
}
func withTelemetryContext(ctx context.Context, functionName string) context.Context {
return ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: functionName,
})
}

View File

@@ -13,6 +13,8 @@ import (
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/telemetrytraces"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/metrictypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
@@ -118,6 +120,11 @@ func NewTelemetryMetaStore(
// tracesTblStatementToFieldKeys returns materialised attribute/resource/scope keys from the traces table
func (t *telemetryMetaStore) tracesTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "tracesTblStatementToFieldKeys",
})
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.tracesDBName, t.indexV3TblName)
statements := []telemetrytypes.ShowCreateTableStatement{}
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
@@ -139,6 +146,12 @@ func (t *telemetryMetaStore) tracesTblStatementToFieldKeys(ctx context.Context)
// getTracesKeys returns the keys from the spans that match the field selection criteria
func (t *telemetryMetaStore) getTracesKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getTracesKeys",
})
if len(fieldKeySelectors) == 0 {
return nil, true, nil
}
@@ -313,6 +326,11 @@ func (t *telemetryMetaStore) getTracesKeys(ctx context.Context, fieldKeySelector
// logsTblStatementToFieldKeys returns materialised attribute/resource/scope keys from the logs table
func (t *telemetryMetaStore) logsTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "logsTblStatementToFieldKeys",
})
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.logsDBName, t.logsV2TblName)
statements := []telemetrytypes.ShowCreateTableStatement{}
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
@@ -334,6 +352,12 @@ func (t *telemetryMetaStore) logsTblStatementToFieldKeys(ctx context.Context) ([
// getLogsKeys returns the keys from the spans that match the field selection criteria
func (t *telemetryMetaStore) getLogsKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getLogsKeys",
})
if len(fieldKeySelectors) == 0 {
return nil, true, nil
}
@@ -583,6 +607,11 @@ func getPriorityForContext(ctx telemetrytypes.FieldContext) int {
// getMetricsKeys returns the keys from the metrics that match the field selection criteria
func (t *telemetryMetaStore) getMetricsKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getMetricsKeys",
})
if len(fieldKeySelectors) == 0 {
return nil, true, nil
}
@@ -685,6 +714,12 @@ func (t *telemetryMetaStore) getMetricsKeys(ctx context.Context, fieldKeySelecto
// getMeterKeys returns the keys from the meter metrics that match the field selection criteria
func (t *telemetryMetaStore) getMeterSourceMetricKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getMeterSourceMetricKeys",
})
if len(fieldKeySelectors) == 0 {
return nil, true, nil
}
@@ -973,6 +1008,11 @@ func (t *telemetryMetaStore) GetKey(ctx context.Context, fieldKeySelector *telem
}
func (t *telemetryMetaStore) getRelatedValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) ([]string, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: fieldValueSelector.Signal.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getRelatedValues",
})
// nothing to return as "related" value if there is nothing to filter on
if fieldValueSelector.ExistingQuery == "" {
@@ -1118,6 +1158,11 @@ func (t *telemetryMetaStore) GetRelatedValues(ctx context.Context, fieldValueSel
}
func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getSpanFieldValues",
})
// build the query to get the keys from the spans that match the field selection criteria
limit := fieldValueSelector.Limit
if limit == 0 {
@@ -1202,6 +1247,11 @@ func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, fieldValueS
}
func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getLogFieldValues",
})
// build the query to get the keys from the spans that match the field selection criteria
limit := fieldValueSelector.Limit
if limit == 0 {
@@ -1285,6 +1335,11 @@ func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSe
// getMetricFieldValues returns field values and whether the result is complete
func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getMetricFieldValues",
})
limit := fieldValueSelector.Limit
if limit == 0 {
limit = 50
@@ -1373,6 +1428,11 @@ func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValu
// getIntrinsicMetricFieldValues returns values, isSearchComplete, error
func (t *telemetryMetaStore) getIntrinsicMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector, limit int) (*telemetrytypes.TelemetryFieldValues, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getIntrinsicMetricFieldValues",
})
key, ok := telemetrymetrics.IntrinsicMetricFieldDefinitions[fieldValueSelector.Name]
if !ok {
return &telemetrytypes.TelemetryFieldValues{}, nil
@@ -1446,6 +1506,11 @@ func (t *telemetryMetaStore) getIntrinsicMetricFieldValues(ctx context.Context,
}
func (t *telemetryMetaStore) getMeterSourceMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "getMeterSourceMetricFieldValues",
})
sb := sqlbuilder.Select("DISTINCT arrayJoin(JSONExtractKeysAndValues(labels, 'String')) AS attr").
From(t.meterDBName + "." + t.meterFieldsTblName)
@@ -1662,6 +1727,11 @@ func (t *telemetryMetaStore) FetchTemporalityAndTypeMulti(ctx context.Context, q
}
func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string][]metrictypes.Temporality, map[string]metrictypes.Type, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "fetchMetricsTemporalityAndType",
})
temporalities := make(map[string][]metrictypes.Temporality)
types := make(map[string]metrictypes.Type)
@@ -1723,6 +1793,11 @@ func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context,
}
func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporalityAndType(ctx context.Context, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "fetchMeterSourceMetricsTemporalityAndType",
})
temporalities := make(map[string]metrictypes.Temporality)
types := make(map[string]metrictypes.Type)
@@ -1789,6 +1864,11 @@ const chunkSizeFirstSeenMetricMetadata = 1600
// for each metric-attribute-value combination.
// Returns a map where key is `telemetrytypes.MetricMetadataLookupKey` and value is first_seen in milliseconds.
func (t *telemetryMetaStore) GetFirstSeenFromMetricMetadata(ctx context.Context, lookupKeys []telemetrytypes.MetricMetadataLookupKey) (map[telemetrytypes.MetricMetadataLookupKey]int64, error) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
instrumentationtypes.CodeNamespace: "metadata",
instrumentationtypes.CodeFunctionName: "GetFirstSeenFromMetricMetadata",
})
result := make(map[telemetrytypes.MetricMetadataLookupKey]int64)
for i := 0; i < len(lookupKeys); i += chunkSizeFirstSeenMetricMetadata {

View File

@@ -5,6 +5,7 @@ import (
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
@@ -35,7 +36,14 @@ func NewInstrumentation(ctx context.Context, providerSettings factory.ProviderSe
}
func (hook *instrumentation) BeforeQuery(ctx context.Context, event *telemetrystore.QueryEvent) context.Context {
ctx, _ = hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
ctx, span := hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
// add trace_id and span_id to the log_comment
comment := ctxtypes.CommentFromContext(ctx)
comment.Set("trace_id", span.SpanContext().TraceID().String())
comment.Set("span_id", span.SpanContext().SpanID().String())
ctx = ctxtypes.NewContextWithComment(ctx, comment)
return ctx
}

View File

@@ -6,6 +6,9 @@ import (
"strings"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
type TraceTimeRangeFinder struct {
@@ -24,6 +27,11 @@ func (f *TraceTimeRangeFinder) GetTraceTimeRange(ctx context.Context, traceID st
}
func (f *TraceTimeRangeFinder) GetTraceTimeRangeMulti(ctx context.Context, traceIDs []string) (startNano, endNano int64, ok bool) {
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
instrumentationtypes.CodeNamespace: "trace-time-range",
instrumentationtypes.CodeFunctionName: "GetTraceTimeRangeMulti",
})
if len(traceIDs) == 0 {
return 0, 0, false
}

View File

@@ -104,6 +104,13 @@ func CommentFromHTTPRequest(req *http.Request) map[string]string {
return comments
}
// AddCommentsToContext returns a new context with all key-value pairs merged into the Comment in the context.
func NewContextWithCommentVals(ctx context.Context, vals map[string]string) context.Context {
comment := CommentFromContext(ctx)
comment.Merge(vals)
return NewContextWithComment(ctx, comment)
}
// NewComment creates a new Comment with an empty map. It is safe to use concurrently.
func NewComment() *Comment {
return &Comment{vals: map[string]string{}}

View File

@@ -0,0 +1,53 @@
package instrumentationtypes
import (
"math"
"time"
)
// DurationBucket returns a human-readable bucket label for the duration between fromMS and toMS.
// fromMS and toMS are Unix timestamps (same unit as used by time.Unix).
// Returns labels like "<1h", "<6h", "<24h", "<3D", "<1W", "<2W", "<1M", or ">=1M".
func DurationBucket(from, to uint64) string {
// make sure it's nanoseconds regardless of the unit
fromNS := ToNanoSecs(from)
toNS := ToNanoSecs(to)
diff := time.Unix(0, int64(toNS)).Sub(time.Unix(0, int64(fromNS)))
buckets := []struct {
d time.Duration
l string
}{
{1 * time.Hour, "<1h"},
{6 * time.Hour, "<6h"},
{24 * time.Hour, "<24h"},
{3 * 24 * time.Hour, "<3D"},
{7 * 24 * time.Hour, "<1W"},
{14 * 24 * time.Hour, "<2W"},
{30 * 24 * time.Hour, "<1M"},
}
for _, b := range buckets {
if diff < b.d {
return b.l
}
}
return ">=1M"
}
// ToNanoSecs takes epoch and returns it in ns
func ToNanoSecs(epoch uint64) uint64 {
temp := epoch
count := 0
if epoch == 0 {
count = 1
} else {
for epoch != 0 {
epoch /= 10
count++
}
}
return temp * uint64(math.Pow(10, float64(19-count)))
}

View File

@@ -0,0 +1,21 @@
package instrumentationtypes
import semconv "go.opentelemetry.io/collector/semconv/v1.6.1"
// Log comment / context keys for query observability.
// Names align with OpenTelemetry semantic conventions where applicable
// (https://pkg.go.dev/go.opentelemetry.io/otel/semconv); custom keys are namespaced.
const (
// CodeFunctionName is the fully-qualified function or method name (OTel code.function.name).
CodeFunctionName = semconv.AttributeCodeFunction
// CodeNamespace is the logical module or component name (e.g. "dashboard", "anomaly").
CodeNamespace = semconv.AttributeCodeNamespace
// TelemetrySignal is the telemetry signal type: "traces", "logs", or "metrics".
TelemetrySignal = "telemetry.signal"
// QueryDuration is the query time-range bucket label (e.g. "<1h", "<24h").
QueryDuration = "query.duration"
// PanelType is the panel type: "timeseries", "list", "value"
PanelType = "panel.type"
// QueryType is the query type: "promql", "clickhouse_sql", "builder_query".
QueryType = "query.type"
)