Compare commits

...

4 Commits

Author SHA1 Message Date
aks07
5ca7baca9f feat(frontend): add analytics events to span percentile flow
Instrument the span percentile widget with product analytics: panel
toggle, time-range change, resource-attributes selector toggle, and
attribute selection change. Events go through the existing
useTraceDetailLogEvent hook so view and traceId are injected.
2026-08-13 12:33:53 +05:30
Vinicius Lourenço
55ef5fbc3c fix(infrastructure-monitoring-namespaces): wrong division for available/desired and use lastest instead of avg (#12429)
Some checks are pending
build-staging / staging (push) Blocked by required conditions
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
cacheci / tests (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
## Pull Request

---

### 📄 Summary
> Why does this change exist?  
> What problem does it solve, and why is this the right approach?

This follows the same pattern as
https://github.com/SigNoz/signoz/pull/11681 to use `latest` instead of
`avg`, and also fixes the calculation of `util %` that was suppose to be
`desired/available * 100` instead of current value `available/desired`.

#### Screenshots / Screen Recordings (if applicable)
> Include screenshots or screen recordings that clearly show the
behavior before the change and the result after the change. This helps
reviewers quickly understand the impact and verify the update.

Before:

<img width="857" height="364" alt="image"
src="https://github.com/user-attachments/assets/949db1a8-c27d-41da-8573-398a4d53af24"
/>

After:

<img width="851" height="336" alt="image"
src="https://github.com/user-attachments/assets/5181849a-28e4-45f8-b7a5-0d611b5ae02e"
/>

#### Issues closed by this PR
> Reference issues using `Closes #issue-number` to enable automatic
closure on merge.

Closes https://github.com/SigNoz/pulse-pod/issues/210

---

###  Change Type
_Select all that apply_

- [ ]  Feature
- [x] 🐛 Bug fix
- [ ] ♻️ Refactor
- [ ] 🛠️ Infra / Tooling
- [ ] 🧪 Test-only

---

### 🧪 Testing Strategy
> How was this change validated?

- Tests added/updated: No
- Manual verification: Yes
- Edge cases covered: -

---

### ⚠️ Risk & Impact Assessment
> What could break? How do we recover?

- Blast radius: Infrastructure Monitoring - Namespaces
- Potential regressions: None
- Rollback plan: Revert this commit

---

### 📝 Changelog
> Fill only if this affects users, APIs, UI, or documented behavior  
> Use **N/A** for internal or non-user-facing changes

| Field | Value |
|------|-------|
| Deployment Type | Cloud / OSS / Enterprise |
| Change Type | Bug Fix |
| Description | We updated the table for Desired (pods) inside the
Namespace Details on Infrastructure Monitoring to correctly show the
`util %`. |

---

### 📋 Checklist
- [x] Tests added or explicitly not required
- [x] Manually tested
- [ ] Breaking changes documented
- [ ] Backward compatibility considered
2026-08-13 05:52:36 +00:00
Shivam Gupta
49749626dc fix(onboarding): list multi-signal data sources under every signal they support (#12522)
#### Description

- Some data sources ship a single doc that sets up two or three signals,
but carried only one tag, so they showed up in exactly one section of
the picker. Searching `temporal` surfaced it only under APM/Traces even
though both Temporal docs configure traces, metrics and logs.
- Tagged them with every signal their doc actually configures, so they
list under each matching section — the same way `Deno` already does. No
UI changes needed: `groupDataSourcesByTags` already fans an entry out
across its tags.

| entry | was | now |
| --- | --- | --- |
| Temporal | `apm/traces` | `apm/traces`, `logs`, `metrics` |
| Nginx - OpenTelemetry (was "Nginx - Tracing") | `apm/traces` |
`apm/traces`, `logs`, `metrics` |
| OpenTelemetry eBPF (OBI) | `apm/traces` | `apm/traces`, `metrics` |
| DBOS | `apm/traces` | `apm/traces`, `logs` |
| Cloudflare Workers | `apm/traces` | `apm/traces`, `logs` |

- "Nginx - Tracing" is renamed to "Nginx - OpenTelemetry" since it no
longer lists only under traces, and to stay distinct from the existing
built-in Nginx integration entry.

#### Additional Information

- All 82 docs behind the 70 single-signal-tagged entries were read to
decide this; the other 77 are genuinely single-signal. Every language
APM doc explicitly sets `OTEL_METRICS_EXPORTER=none` /
`OTEL_LOGS_EXPORTER=none`, and the matching metrics docs set
`OTEL_TRACES_EXPORTER=none` — so splits like `Java` / `Java logs` /
`Java Metrics` are correct as they stand.
- Left unchanged, but worth a second opinion: the logs docs for Java,
Python, Node.js (Pino/Winston/Bunyan) and Golang (Logrus/Zerolog) run
auto-instrumentation that emits traces, but only ever mention traces to
tell you how to switch them off. Read as logs-only here.
2026-08-13 04:03:15 +00:00
Pandey
061eb1f867 chore(deps): bump clickhouse-sql-parser to v0.5.6 (#12536)
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
cacheci / tests (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
### Description

Bumps `github.com/AfterShip/clickhouse-sql-parser` from v0.5.5 to
v0.5.6.

- v0.5.6 parses a parenthesized left operand of a set operator (upstream
https://github.com/AfterShip/clickhouse-sql-parser/pull/312), e.g.
`(SELECT 1) UNION ALL (SELECT 2)`.
- Moves the three now-passing parenthesized set-operation cases into the
pass table in `clickhouse_sql_test.go` as regression canaries.
- Records the outstanding `NULLS FIRST|LAST` ORDER BY gap in the
known-gap table — the parser still rejects it, so it stays tracked until
fixed upstream.
2026-08-12 19:25:17 +00:00
8 changed files with 77 additions and 27 deletions

View File

@@ -1617,13 +1617,13 @@ export const getNamespaceMetricsQueryPayload = (
reduceTo: ReduceOperators.LAST,
spaceAggregation: 'max',
stepInterval: 60,
timeAggregation: 'avg',
timeAggregation: 'latest',
},
],
queryFormulas: [
{
disabled: false,
expression: 'A/B',
expression: '(B/A) * 100',
legend: 'util %',
queryName: 'F1',
},

View File

@@ -1521,9 +1521,9 @@ const onboardingConfigWithLinks = [
},
{
dataSource: 'nginx-tracing',
label: 'Nginx - Tracing',
label: 'Nginx - OpenTelemetry',
imgUrl: nginxUrl,
tags: ['apm/traces'],
tags: ['apm/traces', 'logs', 'metrics'],
module: 'apm',
relatedSearchKeywords: [
'apm',
@@ -1626,7 +1626,7 @@ const onboardingConfigWithLinks = [
dataSource: 'cloudflare-workers',
label: 'Cloudflare Workers',
imgUrl: cloudflareUrl,
tags: ['apm/traces'],
tags: ['apm/traces', 'logs'],
module: 'apm',
relatedSearchKeywords: [
'cloudflare',
@@ -5346,13 +5346,17 @@ const onboardingConfigWithLinks = [
dataSource: 'temporal',
label: 'Temporal',
imgUrl: temporalUrl,
tags: ['apm/traces'],
tags: ['apm/traces', 'logs', 'metrics'],
module: 'apm',
relatedSearchKeywords: [
'apm',
'application performance monitoring',
'integrations',
'logs',
'metrics',
'temporal',
'temporal logs',
'temporal metrics',
'temporal traces',
'traces',
'tracing',
@@ -5478,7 +5482,7 @@ const onboardingConfigWithLinks = [
dataSource: 'dbos',
label: 'DBOS',
imgUrl: dbosUrl,
tags: ['apm/traces'],
tags: ['apm/traces', 'logs'],
module: 'apm',
relatedSearchKeywords: [
'database oriented',
@@ -6622,7 +6626,7 @@ const onboardingConfigWithLinks = [
dataSource: 'opentelemetry-ebpf',
label: 'OpenTelemetry eBPF (OBI)',
imgUrl: opentelemetryUrl,
tags: ['apm/traces'],
tags: ['apm/traces', 'metrics'],
module: 'apm',
relatedSearchKeywords: [
'auto instrumentation',

View File

@@ -42,7 +42,7 @@ function SpanPercentilePanel({
selectedTimeRange,
setSelectedTimeRange,
showResourceAttributesSelector,
setShowResourceAttributesSelector,
toggleResourceAttributesSelector,
resourceAttributesSearchQuery,
setResourceAttributesSearchQuery,
spanResourceAttributes,
@@ -72,9 +72,7 @@ function SpanPercentilePanel({
variant="link"
color="secondary"
size="icon"
onClick={(): void =>
setShowResourceAttributesSelector(!showResourceAttributesSelector)
}
onClick={toggleResourceAttributesSelector}
prefix={
showResourceAttributesSelector ? <Check size={16} /> : <Plus size={16} />
}

View File

@@ -8,6 +8,11 @@ import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { USER_PREFERENCES } from 'constants/userPreferences';
import dayjs from 'dayjs';
import useClickOutside from 'hooks/useClickOutside';
import {
TraceDetailEventKeys,
TraceDetailEvents,
} from 'pages/TraceDetailsV3/events';
import { useTraceDetailLogEvent } from 'pages/TraceDetailsV3/hooks/useTraceDetailLogEvent';
import { SpanV3 } from 'types/api/trace/getTraceV3';
export interface IResourceAttribute {
@@ -37,7 +42,7 @@ export interface UseSpanPercentileReturn {
selectedTimeRange: number;
setSelectedTimeRange: (range: number) => void;
showResourceAttributesSelector: boolean;
setShowResourceAttributesSelector: (show: boolean) => void;
toggleResourceAttributesSelector: () => void;
resourceAttributesSearchQuery: string;
setResourceAttributesSearchQuery: (query: string) => void;
spanResourceAttributes: IResourceAttribute[];
@@ -76,6 +81,8 @@ function useSpanPercentile(selectedSpan: SpanV3): UseSpanPercentileReturn {
const resourceAttributesSelectorRef = useRef<HTMLDivElement | null>(null);
const logTraceEvent = useTraceDetailLogEvent('v3', selectedSpan.trace_id);
useClickOutside({
ref: resourceAttributesSelectorRef,
onClickOutside: () => {
@@ -257,6 +264,12 @@ function useSpanPercentile(selectedSpan: SpanV3): UseSpanPercentileReturn {
const handleResourceAttributeChange = useCallback(
(key: string, value: string, isSelected: boolean): void => {
logTraceEvent(TraceDetailEvents.SpanPercentileAttributeChanged, {
[TraceDetailEventKeys.SpanId]: selectedSpan.span_id,
[TraceDetailEventKeys.ResourceAttributeKey]: key,
[TraceDetailEventKeys.Selected]: isSelected,
});
updateSpanResourceAttributes((prev) =>
prev.map((attr) => (attr.key === key ? { ...attr, isSelected } : attr)),
);
@@ -271,7 +284,7 @@ function useSpanPercentile(selectedSpan: SpanV3): UseSpanPercentileReturn {
setShouldFetchData(true);
setShouldUpdateUserPreference(true);
},
[selectedResourceAttributes],
[selectedResourceAttributes, logTraceEvent, selectedSpan.span_id],
);
useEffect(() => {
@@ -293,12 +306,37 @@ function useSpanPercentile(selectedSpan: SpanV3): UseSpanPercentileReturn {
'ms',
);
const toggleOpen = useCallback(() => setIsOpen((prev) => !prev), []);
const toggleOpen = useCallback(() => {
const nextOpen = !isOpen;
setIsOpen(nextOpen);
logTraceEvent(TraceDetailEvents.SpanPercentileToggled, {
[TraceDetailEventKeys.SpanId]: selectedSpan.span_id,
[TraceDetailEventKeys.Open]: nextOpen,
[TraceDetailEventKeys.PercentileValue]: percentileValue,
});
}, [isOpen, logTraceEvent, selectedSpan.span_id, percentileValue]);
const handleTimeRangeChange = useCallback((range: number): void => {
setShouldFetchData(true);
setSelectedTimeRange(range);
}, []);
const toggleResourceAttributesSelector = useCallback(() => {
const nextOpen = !showResourceAttributesSelector;
setShowResourceAttributesSelector(nextOpen);
logTraceEvent(TraceDetailEvents.SpanPercentileAttributesSelectorToggled, {
[TraceDetailEventKeys.SpanId]: selectedSpan.span_id,
[TraceDetailEventKeys.Open]: nextOpen,
});
}, [showResourceAttributesSelector, logTraceEvent, selectedSpan.span_id]);
const handleTimeRangeChange = useCallback(
(range: number): void => {
logTraceEvent(TraceDetailEvents.SpanPercentileTimeRangeChanged, {
[TraceDetailEventKeys.SpanId]: selectedSpan.span_id,
[TraceDetailEventKeys.From]: selectedTimeRange,
[TraceDetailEventKeys.To]: range,
});
setShouldFetchData(true);
setSelectedTimeRange(range);
},
[logTraceEvent, selectedSpan.span_id, selectedTimeRange],
);
return {
isOpen,
@@ -312,7 +350,7 @@ function useSpanPercentile(selectedSpan: SpanV3): UseSpanPercentileReturn {
selectedTimeRange,
setSelectedTimeRange: handleTimeRangeChange,
showResourceAttributesSelector,
setShowResourceAttributesSelector,
toggleResourceAttributesSelector,
resourceAttributesSearchQuery,
setResourceAttributesSearchQuery,
spanResourceAttributes,

View File

@@ -8,6 +8,10 @@ export enum TraceDetailEvents {
SpanPanelTabChanged = 'Trace Detail: Span panel tab changed',
DownloadTriggered = 'Trace Detail: Download triggered',
DownloadCancelled = 'Trace Detail: Download cancelled',
SpanPercentileToggled = 'Trace Detail: Span percentile toggled',
SpanPercentileTimeRangeChanged = 'Trace Detail: Span percentile time range changed',
SpanPercentileAttributesSelectorToggled = 'Trace Detail: Span percentile attributes selector toggled',
SpanPercentileAttributeChanged = 'Trace Detail: Span percentile attribute changed',
}
export enum TraceDetailEventKeys {
@@ -36,6 +40,10 @@ export enum TraceDetailEventKeys {
SpanId = 'spanId',
// Download triggered (reuses TotalSpansCount for trace size)
Format = 'format',
// Span percentile (reuses Open, SpanId, From, To)
PercentileValue = 'percentileValue',
ResourceAttributeKey = 'resourceAttributeKey',
Selected = 'selected',
}
export type TraceDetailView = 'v2' | 'v3';

2
go.mod
View File

@@ -4,7 +4,7 @@ go 1.25.7
require (
dario.cat/mergo v1.0.2
github.com/AfterShip/clickhouse-sql-parser v0.5.5
github.com/AfterShip/clickhouse-sql-parser v0.5.6
github.com/ClickHouse/clickhouse-go/v2 v2.44.0
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/SigNoz/clickhouse-go-mock v0.14.0

4
go.sum
View File

@@ -66,8 +66,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/AfterShip/clickhouse-sql-parser v0.5.5 h1:LCA23yAA4GgF73PoYXb67yzCdC4sXsj4geQz1Oij3U8=
github.com/AfterShip/clickhouse-sql-parser v0.5.5/go.mod h1:Qi3qvPTfZb/aFwI5V4WFOahgjsLJa4MzVijIAfwOhDw=
github.com/AfterShip/clickhouse-sql-parser v0.5.6 h1:njgRLxQz/pE16ZO1MSjWadIzabwqsjDMMX8RR5Dbv7Y=
github.com/AfterShip/clickhouse-sql-parser v0.5.6/go.mod h1:Qi3qvPTfZb/aFwI5V4WFOahgjsLJa4MzVijIAfwOhDw=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=

View File

@@ -25,6 +25,10 @@ func TestErrIfStatementIsNotValid_Pass(t *testing.T) {
{"GlobalNotIn", "SELECT a FROM t WHERE a GLOBAL NOT IN (SELECT b FROM t2)"},
{"Union", "SELECT * FROM t UNION ALL SELECT * FROM t2"},
{"Intersect", "SELECT * FROM t INTERSECT SELECT * FROM t2"},
// A parenthesised left operand of a set operator. https://github.com/AfterShip/clickhouse-sql-parser/pull/312
{"ParenthesisedUnionLeftOperand", "SELECT a FROM ((SELECT 1 AS a) UNION ALL (SELECT 2 AS a))"},
{"ParenthesisedExceptLeftOperand", "SELECT a FROM ((SELECT 1 AS a) EXCEPT (SELECT 2 AS a))"},
{"ParenthesisedUnionLeftOperandAtStatementLevel", "(SELECT 1 AS a) UNION ALL (SELECT 2 AS a)"},
{"WindowFunction", "SELECT sum(v) OVER (PARTITION BY a ORDER BY t) FROM t"},
{"UnrelatedSetting", "SELECT * FROM t SETTINGS max_threads = 4"},
{"TerminatedBlockComment", "SELECT /* keep me */ count() FROM t"},
@@ -183,12 +187,10 @@ func TestErrIfStatementIsNotValid_ShouldPassButFails(t *testing.T) {
query string
expectedCode errors.Code
}{
// The left operand commits the parser to a subquery, leaving the operator nowhere to bind. Parenthesising only the right operand is fine.
{"ParenthesisedUnionLeftOperand", "SELECT a FROM ((SELECT 1 AS a) UNION ALL (SELECT 2 AS a))", CodeClickHouseSQLUnparseable},
{"ParenthesisedExceptLeftOperand", "SELECT a FROM ((SELECT 1 AS a) EXCEPT (SELECT 2 AS a))", CodeClickHouseSQLUnparseable},
{"ParenthesisedUnionLeftOperandAtStatementLevel", "(SELECT 1 AS a) UNION ALL (SELECT 2 AS a)", CodeClickHouseSQLUnparseable},
// The one keyword PR 305 left behind, because ON also opens a join condition.
{"UnquotedOnAsColumnName", "SELECT on + 1 FROM t", CodeClickHouseSQLUnparseable},
// ClickHouse accepts NULLS FIRST|LAST as an ORDER BY modifier; the parser's grammar has no rule for it.
{"OrderByNullsLast", "SELECT x FROM t ORDER BY x DESC NULLS LAST", CodeClickHouseSQLUnparseable},
}
for _, testCase := range testCases {