MongoDB slow queries stay empty until you enable the profiler
The panel names the command to run instead of showing a blank table, and the metrics beside it are omitted rather than reported as zero.
Open the monitoring Queries tab on a fresh MongoDB connection and there is nothing
in it. Not because the read failed, and not because the server is idle. The
MongoDB slow query log is a collection, system.profile, and it does not exist
until somebody switches the profiler on. getSlowQueries() reads that collection
and nothing else, so before the profiler runs there is genuinely nothing to list.
The panel does not draw an empty table and leave you to work out whether the tool is broken. It prints the sentence that names the missing step:
Query stats come from the database profiler - run db.setProfilingLevel() to start recording into system.profile.
That string is slowQueriesEmptyState, a per-engine label on the MongoDB
provider. It used to be PostgreSQL’s pg_stat_statements advice, hardcoded into
the panel for every engine including this one, and pg_stat_statements is a
PostgreSQL extension that a MongoDB server has no equivalent of.
Where each MongoDB monitoring read gets its numbers
Each monitoring read is a separate call to the server, and the provider doc names the commands behind every one. Knowing which read stands behind a panel tells you what a blank panel means.
| Monitoring read | Commands it issues |
|---|---|
| Health | serverStatus, db.stats(), currentOp, system.profile |
| Overview | serverStatus, buildInfo, db.stats(), listCollections |
| Performance metrics | serverStatus (WiredTiger cache + opcounters) |
| Slow queries | system.profile |
| Active sessions | currentOp |
| Table stats | collStats per collection |
| Index stats | $indexStats plus indexes() |
| Storage stats | db.stats() plus the WiredTiger section |
Two consequences fall out of that table. First, the slow-query read has no second
source: no opcounter, no currentOp sample, no client-side timing stands in for
the profiler. Second, the reads divide along privilege lines. serverStatus,
currentOp, $indexStats and the profiler want clusterMonitor or an equivalent
role; listCollections and collStats do not. A user without the monitoring role
gets a populated Tables tab and an empty Performance tab, and that asymmetry is a
permissions answer, not a bug. The Overview read is the exception, because its
listCollections sits in the same try as serverStatus, so a refused
serverStatus takes the whole overview into the catch with it.
Every one of those methods is wrapped in try/catch, so one refusal costs one panel rather than the dashboard. The same principle runs through the rest of the monitoring surface: what a panel shows is bounded by what the engine reports.
Why the MongoDB slow query log, system.profile, is empty by default
Switching the profiler on is a mongosh command against the database you want profiled - the same one the empty state names:
db.setProfilingLevel(1)
Run it in mongosh or in your own shell session, not in the Studio editor. The editor here parses a JSON command object and only that:
{ "collection": "orders", "operation": "find", "filter": { "status": "open" } }
A statement beginning db. cannot be executed through this provider at all. That
is the same boundary the engine grid states on
the databases page - queries here are MongoDB queries, with no SQL
translation layer faked over them - and it cuts the other way too: mongosh syntax
is not the language the editor accepts either.
Once the profiler is on, the Queries tab lists per-operation time and documents
returned, sorted by millis, slowest first. The slow-query block inside the
Health read sorts the same collection by ts instead, most recent first, because
health is a question about now and the Queries tab is a question about what hurts.
One honesty note about that Health block: when the whole health read fails it
still emits a placeholder row reading Error fetching health info in the query
column. That is a fabricated row in a list of measurements, one size down from the
numbers described below, and it is still there because removing it requires
HealthInfo.slowQueries to become optional across all fifteen engine ids. It is
tracked, not defended.
Omitted versus zero, and why the difference matters
The rule the rest of this dashboard follows is one sentence long. A number the server never published is left out of the payload entirely. A number the server published as zero is kept and rendered as zero.
They are different facts and they used to be the same value. The provider once
returned a hardcoded cacheHitRatio: 99 when it could not read WiredTiger, and
activeConnections: 0 and databaseSizeBytes: 0 in the catch blocks around a
failed serverStatus. At the panel, an invented 99 is indistinguishable from a
measurement. A fabricated 0 is worse, because it also flattens a genuinely idle
server’s real 0 into the same value, so the two can no longer be told apart
downstream.
Absence is now spelled explicitly - a measuredNumber(...) reading plus a
conditional spread - rather than || 0, which invented a figure, or
|| undefined, which would have discarded a real one.
Cache ratio, connections and database size
Three readings carry the rule, and each has its own reason to go missing.
WiredTiger cache hit ratio. Computed from pages read into cache over
pages requested from the cache. It is omitted when the deployment publishes no
wiredTiger section at all, when pages requested from the cache is 0 on a
server that has served nothing yet, and when serverStatus fails outright. The
Overview and Performance tabs then render Cache Hit as N/A beside Not
measured, and the card border stays neutral rather than being rated. A measured
0 - a genuinely cold cache - renders as 0.0%. bufferPoolUsage follows the same
section and the same rule.
Connection count. From serverStatus.connections.current. The
serverStatus manual page promises no top-level field on every deployment, so a
serverStatus that answers without a connections section is treated as an
ordinary answer rather than an error. With the key absent, the Connections card
reads N/A above not published, and - this is the part a blank would not have
achieved - the sample is dropped from the connection sparkline instead of adding a
real 0 point to a trend line on every refresh.
Database size. From dbStats.dataSize. The Storage tab keys its entire
breakdown off whether databaseSizeBytes is defined. With the key present as a
fabricated 0 it treated the total as known and drew a breakdown over it: Tables
and Indexes at 0.0%, and an Other (unattributed) row computed as
total - tables - indexes. Because the table read does not share the failure -
collStats needs no monitoring role - real per-collection byte figures arrive
into that subtraction and the row renders a negative byte count as a measurement.
A 1 KB collection with 512 B of indexes printed the literal string -1536 B. With
the key omitted the tab draws No storage size information available instead.
So, plainly: slow queries come from system.profile and are empty until the
profiler is enabled, and the cache-hit ratio, connection count and database size
are omitted rather than reported as zero when their source published nothing.
Two counts that still report zero, and why that is recorded as a limitation
The rule is not applied everywhere yet, and the exception is published rather than
quietly excluded. tableCount and indexCount on the overview object are
required numbers. They have no absence to spell, so a failed overview read - the
one an unprivileged user gets - reports 0 collections and 0 indexes on a database
that has plenty of both.
They are the one place in that object that states more than it read. Fixing them is the same change the connection count and the byte figure already received, and it is larger than it looks: both fields would have to become optional across all fifteen engine type-ids, and every reader of them adjusted. Until that happens the two zeros stand, listed in the provider’s known limitations beside the readings that were fixed.
A dashboard’s job is to tell you what the server said. Where the server said nothing, the least useful thing to print is a number.