Custom Dashboards

Build your own Grafana dashboards on top of QMonitor’s Metrics API.

Custom Dashboards let you create your own Grafana dashboards and charts, using the metrics QMonitor already collects for your organization. They live only in your organization, read only your organization’s data and are only visible to your users.

Creating a custom dashboard

  1. In the QMonitor side menu, open Custom Dashboards → Manage Dashboards (available to Premium users; the other users can view the custom dashboards).
  2. Type a name and click New dashboard. The dashboard opens in edit mode.
  3. Add a panel. The data source is already set to Metrics and cannot be changed: every custom dashboard reads from this single, read-only source.
  4. In the panel query, keep the Infinity query type and set the URL to / followed by a measurement name (see below).

Manage Dashboards lists your custom dashboards and lets you:

  • Export one as a JSON file;
  • Delete one;
  • Import a JSON file exported from QMonitor as a new dashboard, for example to reuse a dashboard of another organization: export it there, switch organization, import it here.

An import fails if a dashboard with the same name already exists.

To see what you can query, open Custom Dashboards → Metrics catalog: it lists every measurement with its fields and tags, the monitored instances, and a preview of the data.

How the Metrics API works

You never write a raw database query. You pick a measurement (one of the standard ones below, or one of your organization’s own Custom Queries) and pass structured parameters. QMonitor builds a safe, read-only query against your organization’s data.

The data source already points at the API, so in a panel the URL is just / and the measurement name:

/custom_db_size

Use Grafana’s time macros so the panel follows the dashboard time picker:

/sqlserver_cpu?timeFrom=${__from}&timeTo=${__to}&aggregation=mean

Query parameters

ParameterDescription
timeFrom, timeToTime range: epoch milliseconds or ISO-8601. With ${__from} and ${__to} Grafana passes the dashboard time range and handles the time zone for you. Dates written by hand are read as UTC unless they carry an offset. from / to also work. Without them the last hour is returned; a value that cannot be read, or a timeFrom after timeTo, returns an error.
aggregationAggregation applied to every field: mean (default), max, min, sum, last, first, count, median. Only one per request: to get, say, mean and max, add two queries to the panel.
intervalAggregation bucket, in seconds. Optional; by default it follows the time range (1 h: 1 min, 24 h: 5 min, 3 days: 10 min, 7 days: 30 min, longer: 1 h). It is never below the measurement’s interval shown in the catalog, nor below 60 seconds.
fieldsComma-separated list of fields to return. Optional; defaults to all fields of the measurement.
tagsComma-separated list of tags to group by. Optional; defaults to all tags of the measurement, so every instance gets its own series.
filter.<tag>Keep only some tag values, e.g. filter.sql_instance=SRV01:MSSQL. Several values are comma-separated (filter.sql_instance=SRV01:MSSQL,SRV02:MSSQL) and match any of them; repeat the parameter for more tags. An empty value is ignored.

You can write the parameters directly in the URL, as in the examples below, or add them one by one in the Infinity query under Headers, Request params → URL Query Param. Always pass timeFrom=${__from}&timeTo=${__to}, otherwise the panel shows only the last hour.

Examples

CPU trend of every instance: one line per instance, one point every few minutes depending on the time range.

/sqlserver_cpu?timeFrom=${__from}&timeTo=${__to}&fields=sqlserver_process_cpu&tags=sql_instance

Rows come back one per instance and time bucket. To get one line per instance in a Time series panel, add the Partition by values transformation on sql_instance.

Instances chosen from a dashboard variable: create a variable named instance of type Query on the Metrics data source with URL /instances (single or multi-select, “All” included), then filter on it. Use the :csv format so a multi-select passes all selected values.

/sqlserver_cpu?timeFrom=${__from}&timeTo=${__to}&fields=sqlserver_process_cpu&filter.sql_instance=${instance:csv}

Azure SQL elastic pools: the values of any tag can feed a variable with URL /<measurement>/tags/<tag>, for example /sqlazure_resource_stats/tags/elastic_pool_name for a pool variable, then:

/sqlazure_resource_stats?timeFrom=${__from}&timeTo=${__to}&fields=avg_cpu_percent&tags=database_name&filter.elastic_pool_name=${pool:csv}

Current value for a Stat or Gauge panel: last returns the latest value of each bucket; set the panel calculation to Last *.

/sqlserver_server_properties?timeFrom=${__from}&timeTo=${__to}&fields=uptime,db_online&tags=sql_instance&aggregation=last

Peaks instead of averages: with mean short spikes disappear on long ranges; max keeps them. Here every bucket is 5 minutes long.

/sqlserver_cpu?timeFrom=${__from}&timeTo=${__to}&fields=sqlserver_process_cpu&filter.sql_instance=${instance:csv}&aggregation=max&interval=300

One performance counter: sqlserver_performance contains hundreds of counters, so narrow it with filters, otherwise the query returns too many series.

/sqlserver_performance?timeFrom=${__from}&timeTo=${__to}&fields=value&tags=sql_instance&filter.sql_instance=${instance:csv}&filter.object=SQLServer:Buffer Manager&filter.counter=Page life expectancy&aggregation=last

Wait statistics of one category, split by wait type:

/sqlserver_waitstats?timeFrom=${__from}&timeTo=${__to}&fields=wait_time_ms&filter.sql_instance=${instance}&filter.wait_category=CPU&tags=wait_type&aggregation=max

One of your Custom Queries: same parameters, with the fields and tags you declared. Here the size of each database of an instance:

/custom_db_usage?timeFrom=${__from}&timeTo=${__to}&fields=data_mb,log_mb&tags=database_name&filter.sql_instance=${instance}&aggregation=last

ISO dates: useful when the time range comes from another source. Use UTC (Z): a + in an offset such as +02:00 must be written as %2B in the URL.

/sqlserver_cpu?timeFrom=2026-10-01T00:00:00Z&timeTo=2026-10-02T00:00:00Z&fields=sqlserver_process_cpu&tags=sql_instance

If a request is not valid the panel shows the reason, for example an unknown field or tag, an unreadable date, a time range longer than 30 days, or too many series.

Endpoints

URLReturns
/<measurement>The data of a measurement, with the parameters above.
/catalogEvery measurement available to your organization, with fields and tags.
/instancesThe enabled instances of your organization (sql_instance), for dashboard variables.
/<measurement>/tags/<tag>The values of one tag of a measurement, for dashboard variables.

All of them return a JSON array (Infinity “table” format).

A measurement query returns one object per time bucket, per series: the tags you grouped by, organization, a time field (ISO-8601, UTC) and each requested field already aggregated. If a Time series panel reports that the data has no time field, add the Convert field type transformation and set time to Time.

[
  { "sql_instance": "SRV01:MSSQL", "organization": "acme", "time": "2026-10-20T10:00:00Z", "sqlserver_process_cpu": 2.3 },
  { "sql_instance": "SRV01:MSSQL", "organization": "acme", "time": "2026-10-20T10:01:00Z", "sqlserver_process_cpu": 2.1 }
]

Which tag columns appear depends on tags (by default, all of the measurement’s tags); which value columns appear depends on fields (by default, all of the measurement’s fields).

The catalog returns one object per available measurement:

[
  {
    "source": "standard",
    "measurement": "sqlserver_cpu",
    "description": "SQL Server vs other-process CPU usage (%).",
    "interval": "60s",
    "fields": ["sqlserver_process_cpu", "other_process_cpu"],
    "tags": ["sql_instance", "host"]
  }
]

source is standard for native measurements or custom for your organization’s own Custom Queries; interval is how often the measurement is collected: buckets are never smaller than this, nor than 60 seconds.

/instances and /<measurement>/tags/<tag> return one object per value, named after the tag:

[
  { "sql_instance": "SRV01:MSSQL" },
  { "sql_instance": "SRV02:MSSQL" }
]

Standard measurements

These native QMonitor measurements are available to every organization. In a panel, type / and the measurement name as the URL.

MeasurementDescriptionFieldsTags
sqlserver_cpuSQL Server vs other-process CPU usage (%).sqlserver_process_cpu, other_process_cpusql_instance, host
sqlserver_waitstatsWait statistics by wait type.wait_time_ms, waiting_tasks_count, max_wait_time_mssql_instance, wait_type, wait_category
sqlserver_database_ioPer-file I/O: reads, writes, bytes and latency.reads, writes, read_bytes, write_bytes, read_latency_ms, write_latency_mssql_instance, database_name, logical_filename, file_type
sqlserver_volume_spaceDisk volume space (bytes).total_space_bytes, available_space_bytes, used_space_bytessql_instance, volume_mount_point
sqlserver_performanceRaw performance counters (filter by the counter/instance tags).valuesql_instance, object, counter, instance
sqlserver_memory_clerksMemory usage by memory clerk type (KB).size_kbsql_instance, clerk_type
sqlserver_server_propertiesInstance properties: uptime, cpu count, memory, database counts.uptime, cpu_count, server_memory, db_online, db_offlinesql_instance, host
sqlazure_resource_statsAzure SQL resource usage (%).avg_cpu_percent, avg_data_io_percent, avg_log_write_percent, dtu_consumption_percentsql_instance, database_name, elastic_pool_name

Your organization’s own Custom Queries appear in the catalog alongside these, with the measurement, fields and tags you defined for them.

Limits

To keep the shared platform healthy, each query is capped: the time range cannot exceed 30 days, each query must complete within 3 seconds, data is always aggregated into buckets of at least 60 seconds, and a single query returns at most 10,000 points per series and at most 50 series. When a range would exceed 10,000 points the bucket is widened automatically, so the data comes back aggregated instead of cut. More than 50 series returns an error: narrow it with filter.<tag> or group by fewer tags.

Custom Queries for dashboards

A Custom Query appears in the catalog as soon as it is enabled; it starts producing data once it is assigned to an agent, after one collection interval. A few rules keep it working:

  • Return numbers as integer or float: wrap decimals in CAST(... AS float), otherwise they are stored as text.
  • Never change the type of an existing column: rows with a different type are discarded. To change it, give the column a new name.
  • Name the instance like the standard measurements: REPLACE(@@SERVERNAME, CHAR(92), ':') AS sql_instance.
  • On Azure SQL Database objects such as sys.master_files are not available: run that part only on other editions with IF SERVERPROPERTY('EngineEdition') <> 5 EXEC('...').