SigNoz monitoring with Topologix
Monitoring & Observability
SigNoz APM on the canvas — chart any PromQL query against SigNoz’s Prometheus-compatible endpoint with 6 trace-derived presets, or run an All-Services latency alarm across every service SigNoz reports. Works with SigNoz Cloud and self-hosted alike.
Requirements
- Reach from the Topologix server or a proxy agent to the SigNoz query service — self-hosted default port
3301, orhttps://<region>.signoz.cloudfor SigNoz Cloud. - An API key for SigNoz Cloud (Settings → Workspace Settings → API Keys → Create Key with the Viewer role; sent as the
SIGNOZ-API-KEYheader). Self-hosted deployments without auth configured can leave it empty. - Self-signed HTTPS is accepted with the Allow self-signed certificate toggle.
1 · Set up the integration
- Open the Integrations pageClick Integrations in the toolbar and pick SigNoz from the catalogue.
- Enter the base URLThe query service URL — e.g.
http://signoz:3301self-hosted, or your region’ssignoz.cloudURL. - Add the API key (optional)Required for SigNoz Cloud, optional self-hosted — the Viewer role is all Topologix needs.
- Create, then verifyClick Create, then reopen the integration and click Test — it runs the query
upthrough/api/v1/queryand reports how many targets responded.


2 · Add it to a flow
- Select the service nodeClick the node, open Metrics, then + Add and choose the SigNoz integration.
- Pick a modeThree tabs: Preset (HTTP Request Rate, Error Rate %, P99 Latency (ms), P50 Latency (ms), Active Operations, Span Rate (/min) — each shows its underlying PromQL), Custom PromQL for any query, or All Services Alarm to watch the whole fleet.
- SaveSet the interval and click Add Source. Preset and custom cards chart the query’s instant value; the alarm card counts breaching services.
3 · Beyond presets: custom queries & the All-Services alarm
Custom PromQL
SigNoz’s query endpoint is PromQL-compatible, so any Prometheus knowledge transfers directly — the trace-derived series (signoz_latency_count, signoz_latency_bucket…) are queried exactly like scrape metrics, and the first series’ value lands on the card each poll.
- A query that returns nothing surfaces as an error on the card, not a silent 0 — broken queries never fake health.
- Errors carry SigNoz’s real message, so a typo in a metric name reads as exactly that.
All Services Alarm
One source that watches every service SigNoz knows about. Each tick polls /api/v1/services for the lookback window (default 300 s), compares each service’s p99 latency against your millisecond threshold, and the card’s value is the count of currently-breaching services.
- No preset and no service selection — services appearing in SigNoz join the alarm automatically, and breach state auto-clears after sustained clean polls.
- SigNoz reports p99 in nanoseconds; Topologix converts to milliseconds (0.1 ms resolution) before comparing.
- Per-service trace drill-down is intentionally not attached — SigNoz’s trace-by-id format isn’t Zipkin-compatible — but breach state, Ack and auto-clear all work without it.
Discover from Traces
Import → Discover from Traces builds the full architecture from SigNoz’s Service Map: /api/v1/dependency_graph supplies parent → child edges with call and error rates, /api/v1/services the per-service call counts, average and p99 latency (shown as p50 / p95 on the nodes). Older SigNoz releases that only serve the GET variants of those endpoints are handled automatically.
- Full architecture only — SigNoz exposes no per-trace walk, so “From a service” is not offered for it.
- Selected Kubernetes clusters and vCenters place the services on namespaces, VMs and ESXi hosts like any other source; nodes are created without per-service metric sources because SigNoz’s presets are instance-wide PromQL.
4 · Instrument services & track queries
Instrument your services
SigNoz is an OpenTelemetry backend, so every service is instrumented the same way as for the OpenTelemetry guide — only the destination differs: SigNoz’s own collector (self-hosted, ports 4317 gRPC / 4318 HTTP) or SigNoz Cloud’s ingest endpoint with an ingestion key. Topologix then reads the service list and the Service Map that SigNoz builds from those spans.
- Set the service name
OTEL_SERVICE_NAMEis the name SigNoz reports in/api/v1/servicesand on the Service Map, and the name Topologix shows and matches against Kubernetesapplabels. - Point the exporter at SigNozSelf-hosted:
http://signoz-otel-collector:4318(HTTP) or:4317(gRPC). SigNoz Cloud:https://ingest.<region>.signoz.cloud:443with the headersignoz-ingestion-key=<key>(Settings → Ingestion Settings). - Attach the agentThe per-language recipes in the OpenTelemetry guide, section 4 — Java agent, Node.js auto-instrumentations,
opentelemetry-instrumentfor Python, and so on. An existing OpenTelemetry Collector can fan out: add anotlp/otlphttpexporter for SigNoz next to the one for Topologix. - Confirm it workedThe service appears under Services in SigNoz within a minute; in Topologix, reopen the integration → Test, then Import → Discover from Traces with the SigNoz integration as the source.
Self-hosted SigNoz — environment block for any SDK or agent
OTEL_SERVICE_NAME=checkout-api
OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=productionSigNoz Cloud
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.eu.signoz.cloud:443
OTEL_EXPORTER_OTLP_HEADERS=signoz-ingestion-key=<your-ingestion-key>Query tracking
SigNoz shows database calls per service in its own UI from the db.system / db.statement attributes your instrumentation puts on database spans (see “Capture SQL on database spans” in the OpenTelemetry guide). Topologix reads SigNoz’s aggregates, not its spans — the Service Map’s database children (a redis or postgresql child) show up as external nodes in Discover from Traces, but statement text and fingerprints do not travel through SigNoz.
- For statement-level tracking in Topologix, turn on statistics on the database itself (
pg_stat_statements, Query Store, HANA plan cache — each database guide has the exact commands) and use the database integration’s Statements view; the top statements, calls and timings come straight from the database. - To correlate a trace with a statement inside Topologix, send the same spans to a source it reads spans from — OTLP ingest (direct) or Zipkin — in addition to SigNoz; an OpenTelemetry Collector with two exporters does that without touching the services.
Dependencies and blast radius
Every trace backend you connect also feeds one persisted call graph: which service calls which, how often, with how many errors. The Topology tab draws a service's callers and callees at +1 or +2 hops, the Catalogue shows “Calls →” / “Called by ←” on each service, and Blast radius… (on services, externals, VMs, hosts, datastores, devices, databases and business processes) lists everything a change or a maintenance there might touch.
What is collected, and from where
A background dependency sweep reads the service-to-service graph that Discover from Traces already computes, from every enabled Zipkin, OpenTelemetry (OTLP ingest), Dynatrace, Elastic APM and SigNoz integration — through the integration's proxy agent when it has one — and stores one edge per caller → callee pair per owner: calls, errors, a p95 only when a backend measured one, and which integrations reported it. A count nobody measured stays empty, never 0 (Dynatrace's Smartscape edges carry no per-edge counts, so they read “—”). A call to something that is not an instrumented service becomes an external node — an API host, an unmatched database, a queue — unless it matches a monitored database instance, in which case it folds into that instance's node. Externals never enter a runs-on chain. Up to 5,000 edges are kept per owner (the busiest).
Which instance a database call folds into
A database span is matched by its host first (one monitored instance of that engine on the host), then by its db.name — names compare ignoring case and punctuation (crocobetCasinoCommerce, crocobetcasinocommerce and crocobet_casino_commerce are one name; a suffix such as _prod is never stripped). A database held by a primary and its replicas is not ambiguous: the call folds into the primary, both by name and when several instances share the host — a replica is one a primary's replication block names, or a monitored source whose own sample reports it replicates (linked to its primary by that upstream, else by sharing its integration and engine). The external node an earlier sweep drew for that name is then marked stale, its edges with it, as an alias would. Two unrelated instances holding the same name, or a span of another engine, are never guessed: the call stays an external node, and the Catalogue's “database known only by name” gap names the other instance (ambiguous: also on …).
Cadence
Admin → System → Background workers: Dependency sweep interval (default 900 s — 15 min), Dependency lookback (default 3600 s; OTLP ingest ignores it and uses its own in-memory window) and Dependency stale window (default 7 days). The same block shows the sweep's last run — when it started, how long it took, how many integrations it read, each one that failed by name with its error, the edges stored / written / stale — when the next run is due, and a Run now button. The Topology's Calls button starts a sweep too; its tooltip says when the last one ran and how many of your trace sources failed in it. An integration whose sealed credentials cannot be opened is skipped (its stored edges are kept), never read without its secret.
Aliases — when a trace name is really something else
Backends name things their own way: a renamed deployment, an ingress host, a database seen only from its callers. Two ways to say what a name really is — both need edit rights on the integrations' owner (an admin, or the owner); everyone else sees the aliases read-only.
- Catalogue → a service group → Link a trace name…: pick a name from the call graph that is really this service. The group then lists it under Also known as, with × to remove it.
- Topology → an external node → This is actually…: pick the monitored service or database instance (engine and host shown) it really is.
- Aliases are applied first, on every sweep, to every edge — not only new ones. After you save one, a sweep starts; its edges fold into the target and the old edges and the external node are marked stale at once (never deleted). Remove the alias and the next sweep brings the original edges back.
Blast radius — both directions
For a service (or an external or a business process) the blast radius goes both ways across the call graph at 1 or 2 hops: Depends on me (its callers — what breaks if it changes) and I depend on (its callees — what it needs), then down to the pods, K8s nodes, VMs, hosts, datastores and devices they run on, and the business processes any of them belongs to. For a VM, host, datastore, device, database or schema the default is callers only: the services running there and whoever calls them — what a maintenance window touches. Every item carries its path and the reason it is listed, and a confidence: 100 for a call seen by two or more backends, 90 for one, 50 for a stale edge (stale edges are left out unless you tick include stale). Results are capped at 500 services and 500 hardware items, and say so when they are. Export CSV and Open as business process are on the panel.
Stale rules
Nothing here is deleted automatically. A call edge no backend has reported for the stale window is flagged stale — drawn dimmed, lower confidence, kept with its last numbers — and comes back to life the moment a trace shows it again. Removing an integration drops its share of each edge; an edge only it reported stays, as history. An external that no live edge points at any more (an alias folded it into a service, or all its edges aged out) is flagged stale the same way.
Where OTLP- and Dynatrace-only services run
A service seen only through OpenTelemetry or Dynatrace still gets its own node, and its runs-on placement: for OTLP, from the host.ip, host.name, k8s.pod.name and k8s.node.name resource attributes your SDK or collector sends (only those four are kept); for Dynatrace, from the hosts its process groups run on. Each is matched to your pods, K8s nodes and VMs by IP first, then by name, by the same background probe that places Zipkin, Prometheus, Graylog, Elastic APM and SigNoz services (Probe batch size / Probe interval). A placement that has not changed is not rewritten.
The Catalogue — every service in one place
Infrastructure → Catalogue binds everything known about a service together: the integrations that report it (metrics, logs, traces), where it runs, who it calls and who calls it, the databases it uses, its owner and its health. The trace backends are where most services first appear, so this guide carries it; the same page lists every service your other integrations report too.
The list and the service page
The list shows one card per service — names are grouped case-insensitively, so “Checkout” and “checkout” are one card — with its data classes, a health dot, the tier chip and owner, whether it is placed on infrastructure and whether it has live calls. Group by service or by integration; a card loads its detail only when you expand it. The service name (or Open) opens the service page, which has its own link: ?view=catalogue&service=svc:<name> (Copy link in its header). Its sections:
- Identity — every source per integration and data class, with gap chips (“no logs”, “no traces”) and the trace names aliased into it.
- Ownership and Health — below; health with three sparklines (average latency, p95 latency, errors) from the learned baselines, and inbound requests from trace edges (“trace edges, last sweep”). A series nobody measured says “not measured”, never 0.
- Interfaces — the routes it is called on, who calls each one, and what it calls (below).
- Runs on, Calls / Called by, Databases used (the instances it calls, with their catalogued databases), Business processes it belongs to (those you can open), Findings on its VMs, hosts and datastores, Recent vCenter events (last 7 days), Cost share (loaded when opened).
- Change timeline and What changed this week — below.
- The header: favourite star, Blast radius…, Open in Topology, Export ▾. Open in Catalogue reaches the same page from Hypervisors VM rows, the Databases page, the Topology popover, the Business Process editor and blast-radius rows.
Interfaces — what it is called on and what it calls
The service page's Interfaces section is built from traces, not configuration: every inbound route (method + templated path — /wallet/3f2504e0-…/withdraw becomes /wallet/{uuid}/withdraw; also {hex}, {id}, {token}; a path the framework already templated, like /api/{session}/balance, is kept) with its calls, error share, p95, status-code mix and callers (the service whose span called it; a request with no parent span is shown as external ingress), and every outbound peer — another service, a database (its operations keyed on the normalised statement's fingerprint, the same key the database's own statistics use), an HTTP host or a queue — with its top operations. Both instrumentation dialects are read: OpenTelemetry semantic conventions and Spring Micrometer's method / uri / status / outcome / exception tags.
- Sources. OTLP pushes count every span as it arrives. Zipkin is sampled: every 5 minutes the next 25 services (round-robin; Interface inventory: Zipkin services per pass) each get up to 500 traces looking back to their previous visit (5–60 min), so the section says “seen in N traces since …”. A Zipkin reached through a proxy needs proxy ≥ 1.208.0 — with an older one the section says to update it. Dynatrace, Elastic APM and SigNoz do not feed the inventory yet.
- Stored once per flush (Interface inventory: flush interval, default 300 s) in one database transaction; an hourly history is kept 7 days (hourly buckets, on by default). Raw path samples (3 per route) go through your organisation's redaction rules before they are stored; a client's own IP address is never read.
- Health probes — routes matching
/health,/healthz,/livez,/readyz,/liveness,/readiness… (also under/actuator/) — are folded into one line and never enter a change contract. - Not seen recently. A route unseen for 7 days moves to this list (with its last-seen time, calls and callers) and is left out of change contracts; it is deleted 30 days after it was last seen (7 on the built-in SQLite store).
- Bounds. Per service 200 routes and 200 outbound operations (the least recently seen is dropped and counted), 20 callers per route (the rest are counted as “+N”). p95 comes from a sample of 64 durations per route (±2.7 percentage points of rank at 1σ; 32 for outbound, ±3.9) — good for a contract, not an alarm threshold. A value nobody measured shows “—”, never 0.
- The same inventory drives the change page's Contracts card (the routes each caller in a blast radius uses) — see Change management. API:
GET /api/catalog/service/{key}/interfaces?probes=1&stale=1,GET /api/catalog/contract?from=svc:a&to=svc:b(topologix/interface-contract/1).
Ownership
Owner, team, tier (critical / important / standard), environment, description, a runbook link (http or https only) and up to 20 tags. Everyone who sees the service reads it; only the integrations' owner or an admin can change it (the same rule as trace-name aliases) — Edit ownership on the card or the page, and Clear ownership in the editor removes the record. Every change is audited (service_meta.set / service_meta.delete) and shows on the service's change timeline. The tier is shown and sorted on — critical services first in a blast radius, “Affects N critical services” on a Hypervisors finding — but it does not change any alert channel.
Health — what the dot means
A background health fold (Admin → System → Background workers → Service health interval, default 300 s) folds what is already measured into one status per service, with up to 8 reasons. The worst input wins; nothing new is polled. Stale placements and stale call edges never change a status, and a snoozed finding is no breach. A status change is recorded once on the timeline, with a 10-minute flap guard.
| Status | When | Shown as |
|---|---|---|
| Error | a metric source in error · an open critical finding on its VM, host or datastore · a latched breach in error · an inbound trace error rate ≥ 25 % with ≥ 20 calls | red dot; a red ring in the Topology |
| Warning | a source warning · a warning finding · a latched warning · an inbound error rate ≥ 5 % (≥ 20 calls) | amber dot; an amber ring |
| Unusual | only seasonal-unusual sources (outside the learned band, not over a threshold) | violet dot; a violet ring |
| Healthy | at least one measured input, none breached | green dot; no ring |
| Not measured | no source, finding or trace edge reports on it yet (or the fold has not run) | grey dot; no ring — never shown as healthy |
- Where it shows: the card dot (reasons in its tooltip), the Unhealthy first sort and the health facet, the service page header (“Error · since 15m ago”) with every reason, ⌘K hits, and a thin ring on service and database-instance cards in the Topology (reasons in the card popover; the sidebar has a Health legend).
- Health is display only in this version: no alert channel reads it.
⌘K search, facets and saved views
⌘K (Ctrl+K) anywhere opens the command palette: services, database instances, databases and schemas, VMs, ESXi hosts, IP addresses (a VM's guest IPs, a host's management address, K8s node IPs), externals and integrations — up to 50 hits, exact names first, then prefixes, word starts, substrings and typo-tolerant matches. Enter opens the hit; Tab lists its actions (Open, Blast radius of…, Show in Topology, Copy link, Favourite). With an empty query it shows your favourites, team favourites and recent service pages. The index is built per account on demand and refreshed after each catalogue, infrastructure or dependency sweep (at most 1 min old); you only ever find what your integrations can see.
- Facets over the list: kind (service / instance / has databases / has schemas), engine, data class, environment, tier, health and flags (no placement, no owner, stale, no calls). Values inside a facet are OR-ed, facets are AND-ed, and each count is taken over what the other facets keep. The filter lives in the URL (
?f=,&sort=,&group=), so a filtered list can be linked. - Views ▾ saves the search, facets, grouping and order under a name (up to 50 per user), applies, renames and deletes them. Ticking shared lists a view under Team views for everyone in your organisation — read-only for members, editable by its author or an org admin.
Favourites and team sharing
The star on a card, on the service page or on a ⌘K hit favourites a service, instance, database, schema, VM, host, datastore or external (up to 200). The list opens with a Favourites · Team favourites · Recent strip: a favourite marked Share with team appears under Team favourites for everyone in your organisation (only its author or an org admin changes it), and Recent keeps the last 12 service pages you opened.
Gaps — what the map does not know yet
The Gaps (N) badge in the header (or ?view=catalogue&gaps=1) lists what the Catalogue cannot place or name, one rule per section, each row with the one action that fixes it. Up to 50 rows per rule are shown, then “Show all”.
| Rule | Detected when | Fix |
|---|---|---|
| Services without a placement | a service has no runs-on edge to a pod, K8s node, VM or host (a name seen only in traces counts only while it still has live calls) | Re-probe (front of the classifier queue) · This is an alias of… |
| Instances not on a VM | a database instance whose host matched no VM, or several (an instance whose link you switched off is left alone) | Pin to VM… → the Databases page, under “Where it runs” |
| Externals with a private address | a called host is a private, loopback or link-local address (IPv4 or IPv6) — something inside the estate | This is actually…, prefilled with the services on the VM, node or pod holding that address (≤ 8) |
| Unmonitored database hosts | traces reach a database on a host no monitored instance runs on | Add integration → the integration form with the type and host filled in |
| Databases known only by name | traces name a database but never its host (the spans carry db.name without server.address) | This is actually…, prefilled with the instances holding a database of that name, then those of the same engine (≤ 8) |
| Seen in network flows, not monitored | the hourly scan of the ClickHouse flow warehouse (24 h lookback) finds a private address answering on a database or queue port, scored low / medium / high from its clients, traffic shape and name, and no monitored instance runs there (see Network flows → Inferred databases and services) | Monitor this → the integration form as a draft prefilled from the flows · Not a database / Ignore (remembered across runs; owners and admins) |
- Re-probe and the alias fixes need edit rights on the integrations' owner; everyone else sees the candidates read-only. Pin to VM and Add integration open pages that check their own rights.
Change timeline and the weekly diff
The service page's Change timeline merges, newest first, the last 30 days (Load more reads 90; at most 200 entries): ownership edits and clears, health changes with their reasons, aliases set or removed, snoozes on findings of its hardware, vCenter events on its VMs and hosts, calls that started (“Started calling X”) and calls that went stale. Each row links to where it happened.
- What changed this week compares its call edges now with a daily snapshot of the call graph 7 days ago (or the nearest earlier day, which it names): new and gone edges, edges gone stale, error-rate changes (≥ 2× and ≥ 5 points) and call-volume changes (≥ 50 %). The dependency sweep writes the snapshot once a UTC day and keeps 90 days; before the first one it says so (“no snapshot yet — first one on …”) instead of showing zeros.
- The Catalogue header shows “N new edges, M stale this week” across everything you can see.
Exports and the ServiceNow CMDB import
Export ▾ in the header downloads services, instances or databases as CSV, or everything as JSON (topologix/catalogue/1); the service page's menu exports that one service with the instances it calls. A value nobody measured is an empty cell, never 0, and a cell that would start a spreadsheet formula is quoted. Every download is audited (catalog.export), and database names of an integration you cannot see are never in it.
- ServiceNow CMDB… downloads import-ready files for
cmdb_ci_service,cmdb_ci_db_instance,cmdb_ci_vmware_instance,cmdb_ci_esx_server,cmdb_rel_ci— one CSV per class, or all of them as one JSON. Every row carriessys_class_nameandcorrelation_id(our key), so an Import Set with a transform map that coalesces on correlation_id updates the same CI on every re-import instead of creating duplicates. Tier maps to business criticality, health to operational status; relationships (Runs on, Virtualized by, Depends on, Uses) are written only between CIs in the same export — load the CI classes first andcmdb_rel_cilast. - VMs and ESXi hosts ServiceNow Discovery already knows need a second coalesce (VM name, host serial number) or the Identification and Reconciliation Engine. Topologix does not write to the CMDB itself in this version.
Good to know
- The service list endpoint also powers ranking against richer trace backends: SigNoz contributes call volume per service even though it exposes no host/pod identity.
More Monitoring & Observability integrations
See it on your own infrastructure
Book a demo and we will walk you through Topologix with your systems in mind: on our cloud, or self-hosted in your own network.