Compare commits

...

20 Commits

Author SHA1 Message Date
swapnil-signoz
28f10b3567 chore: generating openapi spec 2026-07-29 20:28:55 +05:30
swapnil-signoz
5d6ee1b97a Merge branch 'main' of https://github.com/SigNoz/signoz into issue_5737 2026-07-29 20:16:24 +05:30
swapnil-signoz
a61cb1fc15 feat: adding gcp gke service 2026-07-29 20:15:24 +05:30
Ashwin Bhatkal
e51c464417 fix(frontend): alias lodash to lodash-es to survive a page-level AMD loader (#12332)
@grafana/data's ESM build imports bare CJS `lodash` in ~28 modules.
lodash's UMD footer checks for an AMD loader before assigning
module.exports, so when a third-party script has already defined
window.define.amd, lodash takes that branch and never exports. rolldown
can't statically detect lodash's dynamic named exports, so those imports
compile to runtime namespace reads against an empty object and every
method is undefined.

lodash-es is the same version, real ESM, and tree-shakes.
2026-07-29 14:03:32 +00:00
Tushar Vats
04637bd960 test(querier): assert rendered SQL in skip_resource_fingerprint tests (#12334)
Identical rows can't distinguish the optimized instance from the
fingerprint baseline, so the tests now also diff the rendered ClickHouse
statement via /query_range/preview: the baseline must join the fingerprint
CTE and the optimized instance must not.

The assertion is pinned to the "resource_fingerprint GLOBAL IN (SELECT
fingerprint FROM __resource_filter)" clause, a literal in the statement
builder. The resource condition the fallback path renders on the main
table is deliberately not asserted: its column form follows the resource
JSON evolution boundary, rendering as a multiIf over resources_string
while the query window straddles release_time and collapsing to
resource.`key`::String once the window sits past it.

Also fixes the fixture env prefix. skip_resource_fingerprint moved out of
the querier config namespace into its own, so
SIGNOZ_QUERIER_SKIP__RESOURCE__FINGERPRINT_* was being ignored and both
instances ran with the optimization disabled.
2026-07-29 12:51:03 +00:00
Swapnil Nakade
0703fbf961 feat: adding compute engine service in GCP integration (#12166)
* feat: adding gcp memorystore redis service

* refactor: updating dashboard title

* refactor: extending width of uptime gauge panel

* refactor: updating cpu utilization panel

* refactor: updating dashboard panel to use rate function instead of hack

* feat: adding compute engine service

* refactor: updating dashboard panels to use rate aggregation

* fix: correct typo and unit in compute engine dashboard

* refactor: migrating dashboard to v6
2026-07-29 11:28:58 +00:00
swapnil-signoz
642c49db37 refactor: migrating dashboard to v6 2026-07-29 01:32:45 +05:30
swapnil-signoz
f98d2291fd Merge branch 'main' into issue_5721 2026-07-28 23:01:05 +05:30
swapnil-signoz
22833ea8fe Merge branch 'issue_5721' of https://github.com/SigNoz/signoz into issue_5721 2026-07-22 17:30:14 +05:30
swapnil-signoz
8eb0a24492 fix: correct typo and unit in compute engine dashboard 2026-07-22 17:29:22 +05:30
swapnil-signoz
7e0b19800c Merge branch 'main' into issue_5721 2026-07-22 17:13:37 +05:30
Swapnil Nakade
3bc476c2ed Merge branch 'main' into issue_5721 2026-07-22 16:32:23 +05:30
swapnil-signoz
6a834ee944 Merge branch 'main' into issue_5721 2026-07-22 12:57:39 +05:30
swapnil-signoz
ba6a78aea7 refactor: updating dashboard panels to use rate aggregation 2026-07-22 10:38:46 +05:30
swapnil-signoz
ca98231b18 feat: adding compute engine service 2026-07-22 10:10:59 +05:30
swapnil-signoz
1644ecd6fc refactor: updating dashboard panel to use rate function instead of hack 2026-07-22 01:32:13 +05:30
swapnil-signoz
f1e2e9f4f7 refactor: updating cpu utilization panel 2026-07-19 22:25:47 +05:30
swapnil-signoz
a59e372f07 refactor: extending width of uptime gauge panel 2026-07-14 22:59:52 +05:30
swapnil-signoz
ca368a5b38 refactor: updating dashboard title 2026-07-14 22:51:25 +05:30
swapnil-signoz
dab5ceee0d feat: adding gcp memorystore redis service 2026-07-14 22:40:31 +05:30
15 changed files with 3842 additions and 8 deletions

View File

@@ -1496,6 +1496,8 @@ components:
- redis
- cloudsql_postgres
- memorystore_redis
- computeengine
- gke
type: string
CloudintegrationtypesServiceMetadata:
properties:

View File

@@ -2815,6 +2815,8 @@ export enum CloudintegrationtypesServiceIDDTO {
redis = 'redis',
cloudsql_postgres = 'cloudsql_postgres',
memorystore_redis = 'memorystore_redis',
computeengine = 'computeengine',
gke = 'gke',
}
export type CloudintegrationtypesCloudIntegrationServiceDTOAnyOf = {
/**

View File

@@ -143,6 +143,12 @@ export default defineConfig(({ mode }): UserConfig => {
plugins,
resolve: {
alias: {
// @grafana/data imports bare CJS `lodash`, whose UMD footer checks for an
// AMD loader before assigning module.exports. Any third-party script that
// defines window.define.amd first leaves the bundled namespace empty, so
// every lodash method reached through it is undefined at runtime. lodash-es
// is the same version, real ESM, and tree-shakes.
lodash: 'lodash-es',
'@': resolve(__dirname, './src'),
utils: resolve(__dirname, './src/utils'),
types: resolve(__dirname, './src/types'),

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#aecbfa;}.cls-2{fill:#669df6;}.cls-3{fill:#4285f4;}</style></defs><title>Icon_24px_ComputeEngine_Color</title><g data-name="Product Icons"><rect class="cls-1" x="9" y="9" width="6" height="6"/><rect class="cls-2" x="11" y="2" width="2" height="4"/><rect class="cls-2" x="7" y="2" width="2" height="4"/><rect class="cls-2" x="15" y="2" width="2" height="4"/><rect class="cls-3" x="11" y="18" width="2" height="4"/><rect class="cls-3" x="7" y="18" width="2" height="4"/><rect class="cls-3" x="15" y="18" width="2" height="4"/><rect class="cls-3" x="19" y="10" width="2" height="4" transform="translate(8 32) rotate(-90)"/><rect class="cls-3" x="19" y="14" width="2" height="4" transform="translate(4 36) rotate(-90)"/><rect class="cls-3" x="19" y="6" width="2" height="4" transform="translate(12 28) rotate(-90)"/><rect class="cls-2" x="3" y="10" width="2" height="4" transform="translate(-8 16) rotate(-90)"/><rect class="cls-2" x="3" y="14" width="2" height="4" transform="translate(-12 20) rotate(-90)"/><rect class="cls-2" x="3" y="6" width="2" height="4" transform="translate(-4 12) rotate(-90)"/><path class="cls-1" d="M5,5V19H19V5ZM17,17H7V7H17Z"/><polygon class="cls-2" points="9 15 15 15 12 12 9 15"/><polygon class="cls-3" points="12 12 15 15 15 9 12 12"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,124 @@
{
"id": "computeengine",
"title": "GCP Compute Engine",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "compute.googleapis.com/instance/uptime_total",
"unit": "Seconds",
"type": "Gauge",
"description": ""
},
{
"name": "compute.googleapis.com/instance/cpu/utilization",
"unit": "Percent",
"type": "Gauge",
"description": ""
},
{
"name": "compute.googleapis.com/guest/memory/bytes_used",
"unit": "Bytes",
"type": "Gauge",
"description": ""
},
{
"name": "compute.googleapis.com/guest/memory/percent_used",
"unit": "Percent",
"type": "Gauge",
"description": ""
},
{
"name": "agent.googleapis.com/memory/percent_used",
"unit": "Percent",
"type": "Gauge",
"description": ""
},
{
"name": "compute.googleapis.com/instance/disk/average_io_latency",
"unit": "Microseconds",
"type": "Gauge",
"description": ""
},
{
"name": "compute.googleapis.com/instance/disk/read_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/disk/write_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/disk/read_ops_count",
"unit": "Count",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/disk/write_ops_count",
"unit": "Count",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/disk/performance_status",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "compute.googleapis.com/instance/network/received_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/network/sent_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/network/received_packets_count",
"unit": "Count",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/instance/network/sent_packets_count",
"unit": "Count",
"type": "Sum",
"description": ""
},
{
"name": "compute.googleapis.com/firewall/dropped_packets_count",
"unit": "Count",
"type": "Sum",
"description": ""
}
],
"logs": []
},
"telemetryCollectionStrategy": {
"gcp": {}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "GCP Compute Engine Overview",
"description": "Overview of GCP Compute Engine metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -0,0 +1,3 @@
### Monitor GCP Compute Engine with SigNoz
Collect key GCP Compute Engine metrics and view them with an out of the box dashboard.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#4285f4;}.cls-1,.cls-2,.cls-4{fill-rule:evenodd;}.cls-2{fill:#669df6;}.cls-3,.cls-4{fill:#aecbfa;}</style></defs><title>Icon_24px_K8Engine_Color</title><g data-name="Product Icons"><g ><polygon class="cls-1" points="14.68 13.06 19.23 15.69 19.23 16.68 14.29 13.83 14.68 13.06"/><polygon class="cls-2" points="9.98 13.65 4.77 16.66 4.45 15.86 9.53 12.92 9.98 13.65"/><rect class="cls-3" x="11.55" y="3.29" width="0.86" height="5.78"/><path class="cls-4" d="M3.25,7V17L12,22l8.74-5V7L12,2Zm15.63,8.89L12,19.78,5.12,15.89V8.11L12,4.22l6.87,3.89v7.78Z"/><polygon class="cls-4" points="11.98 11.5 15.96 9.21 11.98 6.91 8.01 9.21 11.98 11.5"/><polygon class="cls-2" points="11.52 12.3 7.66 10.01 7.66 14.6 11.52 16.89 11.52 12.3"/><polygon class="cls-1" points="12.48 12.3 12.48 16.89 16.34 14.6 16.34 10.01 12.48 12.3"/></g></g></svg>

After

Width:  |  Height:  |  Size: 941 B

View File

@@ -0,0 +1,124 @@
{
"id": "gke",
"title": "GCP Kubernetes Engine",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "kubernetes.io/container/cpu/limit_utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/container/cpu/request_utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/container/memory/limit_utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/container/memory/request_utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/container/restart_count",
"unit": "Count",
"type": "Sum",
"description": ""
},
{
"name": "kubernetes.io/pod/latencies/pod_first_ready",
"unit": "Seconds",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/pod/network/received_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "kubernetes.io/pod/network/sent_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "kubernetes.io/pod/volume/utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/node/cpu/allocatable_utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/node/memory/allocatable_utilization",
"unit": "None",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/node/ephemeral_storage/used_bytes",
"unit": "Bytes",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/node/ephemeral_storage/allocatable_bytes",
"unit": "Bytes",
"type": "Gauge",
"description": ""
},
{
"name": "kubernetes.io/node/network/received_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "kubernetes.io/node/network/sent_bytes_count",
"unit": "Bytes",
"type": "Sum",
"description": ""
},
{
"name": "kubernetes.io/node/status_condition",
"unit": "None",
"type": "Gauge",
"description": ""
}
],
"logs": []
},
"telemetryCollectionStrategy": {
"gcp": {}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "GCP Kubernetes Engine Overview",
"description": "Overview of GCP Kubernetes Engine metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -0,0 +1,3 @@
### Monitor GCP Kubernetes Engine with SigNoz
Collect key GCP Kubernetes Engine metrics and view them with an out of the box dashboard.

View File

@@ -43,6 +43,8 @@ var (
// GCP services.
GCPServiceCloudSQLPostgres = ServiceID{valuer.NewString("cloudsql_postgres")}
GCPServiceMemorystoreRedis = ServiceID{valuer.NewString("memorystore_redis")}
GCPServiceComputeEngine = ServiceID{valuer.NewString("computeengine")}
GCPServiceGKE = ServiceID{valuer.NewString("gke")}
)
func (ServiceID) Enum() []any {
@@ -76,6 +78,8 @@ func (ServiceID) Enum() []any {
AzureServiceRedis,
GCPServiceCloudSQLPostgres,
GCPServiceMemorystoreRedis,
GCPServiceComputeEngine,
GCPServiceGKE,
}
}
@@ -115,6 +119,8 @@ var SupportedServices = map[CloudProviderType][]ServiceID{
CloudProviderTypeGCP: {
GCPServiceCloudSQLPostgres,
GCPServiceMemorystoreRedis,
GCPServiceComputeEngine,
GCPServiceGKE,
},
}

View File

@@ -192,6 +192,59 @@ def make_query_request(
)
def make_preview_query_request(
signoz: types.SigNoz,
token: str,
start_ms: int,
end_ms: int,
queries: list[dict],
*,
request_type: str = RequestType.TIME_SERIES,
format_options: dict | None = None,
variables: dict | None = None,
verbose: bool = True,
timeout: int = QUERY_TIMEOUT,
) -> requests.Response:
"""Dry-run the same payload as make_query_request against /query_range/preview.
Verbose (the default) renders the underlying ClickHouse statement per query."""
if format_options is None:
format_options = {"formatTableResultForUI": False, "fillGaps": False}
payload = {
"schemaVersion": "v1",
"start": start_ms,
"end": end_ms,
"requestType": request_type,
"compositeQuery": {"queries": queries},
"formatOptions": format_options,
}
if variables:
payload["variables"] = variables
return requests.post(
signoz.self.host_configs["8080"].get("/api/v5/query_range/preview"),
params={"verbose": str(verbose).lower()},
timeout=timeout,
headers={"authorization": f"Bearer {token}"},
json=payload,
)
def get_preview_statements(response: requests.Response, name: str) -> list[dict[str, Any]]:
"""The rendered statements for the named query in a preview response."""
assert response.status_code == HTTPStatus.OK, response.text
preview = response.json()["data"]["compositeQuery"][name]
assert preview["valid"], f"preview for query {name} is invalid: {preview['error']}"
return preview["statements"]
def get_preview_sql(response: requests.Response, name: str) -> str:
"""The single rendered ClickHouse statement for the named query in a preview response."""
statements = get_preview_statements(response, name)
assert len(statements) == 1, f"expected 1 statement for query {name}, got {len(statements)}"
return statements[0]["db.statement.query"]
def aligned_epoch(ago: timedelta, step_seconds: int = DEFAULT_STEP_INTERVAL) -> int:
"""Epoch seconds for `now - ago`, floored to a step boundary so seeded
points land exactly on the query's toStartOfInterval buckets."""

View File

@@ -5,7 +5,8 @@ At or above the configured fingerprint threshold the optimization pushes resourc
conditions onto the main spans/logs table instead of the fingerprint CTE. That
rewrite must change ClickHouse performance, never the rows: each test runs the same
query against the primary instance (optimization on, threshold=2) and
`signoz_fingerprint` (optimization off) and asserts the responses are identical.
`signoz_fingerprint` (optimization off) and asserts the responses are identical, then
diffs the rendered SQL to prove the two really did take different paths.
"""
from collections.abc import Callable
@@ -17,13 +18,20 @@ from fixtures.logs import Logs
from fixtures.querier import (
BuilderQuery,
OrderBy,
RequestType,
TelemetryFieldKey,
assert_identical_query_response,
get_preview_sql,
get_rows,
make_preview_query_request,
make_query_request,
)
from fixtures.traces import Traces
# The clause the baseline joins the fingerprint CTE with; the fallback path renders the
# resource conditions on the main table and drops it.
FINGERPRINT_CTE_FILTER = "resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)"
def test_skip_resource_fingerprint_traces_fallback_matches_fingerprint(
signoz: types.SigNoz,
@@ -58,12 +66,21 @@ def test_skip_resource_fingerprint_traces_fallback_matches_fingerprint(
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query])
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query])
assert len(get_rows(optimized)) == 3
assert_identical_query_response(optimized, fingerprint)
# Identical rows alone can't tell the two instances apart; the rendered SQL can.
# The baseline resolves the env filter through the fingerprint CTE, the optimized
# instance pushes it onto the spans table.
optimized_sql = get_preview_sql(make_preview_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query]), "A")
fingerprint_sql = get_preview_sql(make_preview_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query]), "A")
assert FINGERPRINT_CTE_FILTER in fingerprint_sql, fingerprint_sql
assert FINGERPRINT_CTE_FILTER not in optimized_sql, optimized_sql
def test_skip_resource_fingerprint_logs_fallback_matches_fingerprint(
signoz: types.SigNoz,
@@ -98,8 +115,15 @@ def test_skip_resource_fingerprint_logs_fallback_matches_fingerprint(
start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=5)).timestamp() * 1000)
end_ms = int(datetime.now(tz=UTC).timestamp() * 1000)
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type="raw", queries=[query])
optimized = make_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query])
fingerprint = make_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query])
assert len(get_rows(optimized)) == 3
assert_identical_query_response(optimized, fingerprint)
# Same transparency check as above, on the logs table.
optimized_sql = get_preview_sql(make_preview_query_request(signoz, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query]), "A")
fingerprint_sql = get_preview_sql(make_preview_query_request(signoz_fingerprint, token, start_ms=start_ms, end_ms=end_ms, request_type=RequestType.RAW, queries=[query]), "A")
assert FINGERPRINT_CTE_FILTER in fingerprint_sql, fingerprint_sql
assert FINGERPRINT_CTE_FILTER not in optimized_sql, optimized_sql

View File

@@ -31,8 +31,8 @@ def signoz_skip_resource_fingerprint(
pytestconfig=pytestconfig,
cache_key="signoz-skip-resource-fingerprint",
env_overrides={
"SIGNOZ_QUERIER_SKIP__RESOURCE__FINGERPRINT_ENABLED": True,
"SIGNOZ_QUERIER_SKIP__RESOURCE__FINGERPRINT_THRESHOLD": 2,
"SIGNOZ_STATEMENTBUILDER_SKIP__RESOURCE__FINGERPRINT_ENABLED": True,
"SIGNOZ_STATEMENTBUILDER_SKIP__RESOURCE__FINGERPRINT_THRESHOLD": 2,
},
)
@@ -66,6 +66,6 @@ def signoz_fingerprint(
pytestconfig=pytestconfig,
cache_key="signoz-skip-resource-fingerprint-baseline",
env_overrides={
"SIGNOZ_QUERIER_SKIP__RESOURCE__FINGERPRINT_ENABLED": False,
"SIGNOZ_STATEMENTBUILDER_SKIP__RESOURCE__FINGERPRINT_ENABLED": False,
},
)