> ## Documentation Index
> Fetch the complete documentation index at: https://otel.fyi/llms.txt
> Use this file to discover all available pages before exploring further.

# Sqlserver

> OpenTelemetry receiver for Sqlserver

# Sqlserver Receiver

![Status](https://img.shields.io/badge/status-beta-yellow)

**Available in:** `contrib`

**Maintainers:** [@sincejune](https://github.com/sincejune), [@crobert-1](https://github.com/crobert-1), [@sv-splunk](https://github.com/sv-splunk), [@ebrdarSplunk](https://github.com/ebrdarSplunk), [@XSAM](https://github.com/XSAM), [@akshays-19](https://github.com/akshays-19), [@splunk-shanu](https://github.com/splunk-shanu)

**Source:** [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/sqlserverreceiver)

## Supported Telemetry

![Logs](https://img.shields.io/badge/logs-development-blue) ![Metrics](https://img.shields.io/badge/metrics-beta-green)

## Overview

## Required Permissions

### Windows Performance Counters

Make sure to run the collector as administrator in order to collect all performance counters for metrics.

### Direct Connection

When configured to directly connect to the SQL Server instance, the user must have the following permissions:

1. At least one of the following permissions:

* `CREATE DATABASE`
* `ALTER ANY DATABASE`
* `VIEW ANY DATABASE`

2. Permission to view server state:
   * SQL Server pre-2022: `VIEW SERVER STATE`
   * SQL Server 2022 and later: `VIEW SERVER PERFORMANCE STATE`

3. To collect the per-index physical stats metrics (`sqlserver.index.*`), the following two
   permissions are also required:
   * `CONNECT ANY DATABASE` — the receiver enters each user database to read its index
     physical stats; this grant lets the login connect to all current and future databases.
   * `VIEW ANY DEFINITION` — makes the index, object, and schema catalog views visible in
     every database so the physical stats query can resolve index metadata.

4. To collect the `db.server.top_procedure` event, `VIEW ANY DEFINITION` is also required.
   The query resolves each procedure's schema and name with `OBJECT_SCHEMA_NAME` and
   `OBJECT_NAME` across databases, and both return `NULL` without metadata visibility in the
   target database. Rows that cannot be resolved are skipped, so a login missing this grant
   sees procedures silently absent rather than an error. Procedures in the system databases
   (`master`, `tempdb`, `model`, `msdb`) are never reported.

> \[!NOTE]
> `db.server.top_procedure` requires SQL Server 2017 CU3 or later, where
> `sys.dm_exec_procedure_stats.total_spills` became available. On older builds the event
> reports nothing and logs an error; the other events are unaffected.

## Configuration

The following is a generic configuration that can be used for the default logs and metrics scraped
by the SQL Server receiver. A basic explanation on some of the fields has also been provided. For more
information, please reference the following section.

```yaml theme={null}
sqlserver:
  collection_interval: 10s                     # interval for overall collection
  instance_name: CustomInstance
  username: myusername
  password: mypassword
  server: sqlserver.address
  port: 1433
  events:
    db.server.query_sample:
      enabled: true
    db.server.top_query:
      enabled: true
    db.server.top_procedure:
      enabled: true
    db.server.query_plan:
      enabled: true
  top_query_collection:                        # this collection exports the most expensive queries as logs
    lookback_time: 60s                         # which time window should we look for the top queries
    max_query_sample_count: 1000               # maximum number query we store in cache for top queries.
    top_query_count: 250                       # The maximum number of active queries to report in a single run.
    collection_interval: 60s                   # collection interval for top query collection specifically
  query_sample_collection:                     # this collection exports the currently (relate to the query time) executing queries as logs
    max_rows_per_query: 100                    # the maximum number of samples to return for one single query.
  top_procedure_collection:                    # this collection exports aggregated stored procedure statistics as logs
    max_procedure_sample_count: 1000           # maximum number of procedures to consider as candidates in a single run.
    top_procedure_count: 250                   # The maximum number of procedures to report in a single run.
    collection_interval: 60s                   # collection interval for top procedure collection specifically
```

The following settings are optional:

* `collection_interval` (default = `10s`): The interval at which metrics should be emitted by this receiver.
* `instance_name` (optional): The instance name identifies the specific SQL Server instance being monitored.
  If unspecified, metrics will be scraped from all instances. If configured, the `computer_name` must also be set
  when running on Windows.
* `top_procedure_collection` (optional): Tunes the `db.server.top_procedure` event.
  * `max_procedure_sample_count` (default = `1000`, max `10000`): How many procedures to read from
    `sys.dm_exec_procedure_stats` as candidates each run.
  * `top_procedure_count` (default = `250`): How many of those candidates to report, chosen by the
    elapsed time each procedure accrued since the previous run. Must not exceed
    `max_procedure_sample_count`.
  * `collection_interval` (default = `60s`): The interval at which top procedures should be
    emitted by this receiver, independently of the global `collection_interval`. As with
    `top_query_collection.collection_interval`, this only guarantees the event is collected at most
    once per interval: the scraper still runs on the global interval and skips the collection until
    this much time has passed since the last one, so a value below the global interval has no
    effect. It also sets the window the reported deltas cover, so raising it rolls the counters up
    over a longer period. Candidates are also bounded to procedures that actually ran within that
    same window (derived automatically from the time since the last successful scrape, with no
    separate setting to tune), so a procedure is only sampled if it executed recently, not merely
    because it has a large lifetime total.

Direct connection options (optional, but all must be specified to enable):

* `username`: The username used to connect to the SQL Server instance.
* `password`: The password used to connect to the SQL Server instance.
* `server`: IP Address or hostname of SQL Server instance to connect to.
* `port`: Port of the SQL Server instance to connect to.

For finer control over the direct connection use the `datasource`, a.k.a. the "connection string", instead.
Note: it can't be used in conjunction with the `username`, `password`, `server` and `port` options.

When a direct connection is used, all scrapers created for a signal share a single database connection pool
instead of each opening its own. The pool is scoped per receiver instance: if this receiver is used in both a
metrics and a logs pipeline, the metrics and logs receivers each own a separate pool. The pool can be tuned
with the `connection_pool` options (all optional):

* `max_open` (default = number of scrapers): The maximum number of open connections to the database. `0` means unlimited.
* `max_idle` (default = number of scrapers): The maximum number of idle connections kept in the pool.
* `max_lifetime` (optional, example = `5m`, default = unset): The maximum amount of time a connection may be reused. `0` means connections are reused forever.
* `max_idle_time` (optional, example = `1m`, default = unset): The maximum amount of time a connection may be idle before being closed. `0` means idle connections are not closed due to idle time.

The defaults are derived from the number of enabled scrapers so that every scraper can query concurrently
while keeping the total number of connections bounded. Most deployments do not need to set these; tune them
only when connecting to an instance with strict connection limits or a large number of enabled scrapers.

Windows-specific options:

* `computer_name` (optional): The computer name identifies the SQL Server name or IP address of the computer being monitored.
  If specified, `instance_name` is also required to be defined. This option is ignored in non-Windows environments.

Top-Query collection specific options (only useful when top-query collection are enabled):

* `lookback_time` (optional, example = `60s`, default = `2 * collection_interval`): The time window (in second) in which to query for top queries.
  * Queries that were finished execution outside the lookback window are not included in the collection. Increasing the lookback window (in seconds) will be useful for capturing long-running queries.
* `max_query_sample_count` (optional, example = `5000`, default = `1000`): The maximum number of records to fetch in a single run.
* `top_query_count`: (optional, example = `100`, default = `250`): The maximum number of active queries to report (to the next consumer) in a single run.
* `collection_interval`: (optional, default = `60s`): The interval at which top queries should be emitted by this receiver.
  * This value can only guarantee that the top queries are collected at most once in this interval.
    * For instance, you have global `collection_interval` as `10s` and `top_query_collection.collection_interval` as `60s`.
      * In this case, the default receiver scraper will still try to run in every 10 seconds.
      * However, the top queries collection will only run after 60 seconds have passed since the last collection.
    * For instance, you have global `collection_interval` as `10s` and `top_query_collection.collection_interval` as `5s`.
      * In this case, `top_query_collection.collection_internal` will make no effects to the collection

By default, `db.server.top_query` carries the query's execution plan in its `sqlserver.query_plan`
attribute. Execution plans can be large, so an oversized plan can push its record past a
transport/buffer limit and take the lightweight query statistics down with it. Enabling the
`db.server.query_plan` event (disabled by default, like the other events above) moves the plan onto
its own record: `db.server.top_query` is then emitted without `sqlserver.query_plan`, and the plan
is reported on `db.server.query_plan` instead, joined back via the composite key
`sqlserver.query_hash` + `sqlserver.query_plan_hash` (the plan hash alone can collide across
distinct queries with a similar shape). Leaving `db.server.query_plan` disabled preserves the
previous behavior exactly.

`db.server.query_plan` is sourced from the same query as `db.server.top_query` and only splits the
plan out of it, so it collects nothing unless `db.server.top_query` is enabled too.

Query sample collection related options (only useful when query sample is enabled)

* `max_rows_per_query`: (optional, default = `100`) use this to limit rows returned by the sampling query.
  Example:

```yaml theme={null}
    receivers:
      sqlserver:
        collection_interval: 10s
      sqlserver/1:
        collection_interval: 5s
        username: sa
        password: securepassword
        server: 0.0.0.0
        port: 1433
```

When a named instance is used on Windows, a computer name and an instance name must be specified.
Example with named instance:

```yaml theme={null}
    receivers:
      sqlserver:
        collection_interval: 10s
        computer_name: CustomServer
        instance_name: CustomInstance
        resource_attributes:
          sqlserver.computer.name:
            enabled: true
          sqlserver.instance.name:
            enabled: true
```

The full list of settings exposed for this receiver are documented in [config.go](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/sqlserverreceiver/config.go) with detailed sample configurations in [testdata/config.yaml](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/sqlserverreceiver/testdata/config.yaml).

Top query collection enabled:

```yaml theme={null}
    receivers:
      sqlserver:
        collection_interval: 5s
        username: sa
        password: securepassword
        server: 0.0.0.0
        port: 1433
        top_query_collection:
          lookback_time: 60s
          max_query_sample_count: 1000
          top_query_count: 200
        query_sample_collection:
          max_rows_per_query: 1450
```

## Resource attributes

`server.address` and `server.port` identify the monitored SQL Server instance and are emitted by default.
When the receiver connects over loopback (for example `server: localhost` or `server: 127.0.0.1`),
`server.address` reports the host name of the machine running the collector, because the monitored
instance is co-located with it and `localhost` would otherwise be shared by every monitored host.
`service.instance.id` resolves its host the same way, so it reports the collector host name rather than
`localhost` for a loopback target.

To stop emitting the server attributes, disable them individually:

```yaml theme={null}
sqlserver:
  resource_attributes:
    server.address:
      enabled: false
    server.port:
      enabled: false
```

`db.system.version` reports the SQL Server product version (e.g. `15.0.4261.1`). It is disabled by
default and only available in direct connection mode. Enable it via:

```yaml theme={null}
sqlserver:
  resource_attributes:
    db.system.version:
      enabled: true
```

> **Note:** On Azure SQL Database and Azure SQL Managed Instance, `SERVERPROPERTY('ProductVersion')`
> returns a fixed legacy version string rather than the actual engine version.

## Metrics

Details about the metrics produced by this receiver can be found in [documentation.md](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/sqlserverreceiver/documentation.md)

## Logs

Details about the logs produced by this receiver can be found in [logs-documentation.md](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/sqlserverreceiver/logs-documentation.md)

## Known issues

### `x509: negative serial number` with SQL Server in Docker

SQL Server docker users may run into an issue that the collector fails to parse certificate from server due to `x509: negative serial number`. That's because we adopted Go `1.23` starting from contrib `v0.121.0`:

> Before Go 1.23, ParseCertificate accepted certificates with negative serial numbers.
> This behavior can be restored by including "x509negativeserial=1" in the GODEBUG environment variable.

references:

1. [https://pkg.go.dev/crypto/x509#ParseCertificate](https://pkg.go.dev/crypto/x509#ParseCertificate)
2. [https://github.com/microsoft/mssql-docker/issues/895](https://github.com/microsoft/mssql-docker/issues/895)

### TLS handshake failures on Windows Server 2012 R2 and older

Direct connections to SQL Server hosted on Windows Server 2012 R2 or older may fail during the TLS handshake. See [`TLS Handshake failed: cannot read handshake packet: EOF`](#tls-handshake-failed-cannot-read-handshake-packet-eof) in Troubleshooting.

## Troubleshooting

### `service.instance.id` is `unknown:1433`

In a rare case, the `service.instance.id` resource attribute is set to `unknown:1433`. This is because the receiver is unable to parse and compute the `service.instance.id` resource attribute.

You can file an issue that includes your configuration to help us investigate the issue.

### `TLS Handshake failed: cannot read handshake packet: EOF`

This error has been reported for SQL Server on Windows Server 2012 R2 and older. It occurs even when `ForceEncryption` is set to `0`, because by default the driver still uses TLS to encrypt the login packet.

The root cause is unconfirmed. Setting the minimum TLS version (`tlsmin`) and re-enabling RSA key exchange (`tlsrsakex`) through `GODEBUG` did not resolve it. See [#50774](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/50774) for the investigation so far.

Options, in order of preference:

1. Upgrade the host to a newer Windows Server release.
2. Review the certificate and the TLS cipher suite configuration on the SQL Server host.
3. As a last resort, disable encryption for the connection by adding `encrypt=disable` to the `datasource`.

> \[!WARNING]
> With the default setting only the login packet is encrypted, and `encrypt=disable` removes that too. SQL Server authentication credentials are then sent in cleartext each time the receiver opens a connection. Only use this on a trusted network.

`encrypt=disable` can only be set through `datasource`, which can't be combined with `server`, `port`, `username` or `password`. If you use those options, move the connection details into the connection string instead, for example:

```yaml theme={null}
receivers:
  sqlserver:
    datasource: "sqlserver://${env:SQL_USER}:${env:SQL_PASSWORD}@<host>:1433?encrypt=disable"
```

Special characters in the username or password must be URL-encoded in this form.

### Scrapes appear to succeed but collect nothing on a failover cluster instance

On a SQL Server failover cluster instance (FCI), the instance listens on the cluster IP address rather than on the loopback address. Pointing the receiver at `localhost` therefore fails before any TLS handshake starts, with an error like:

```
dial tcp 127.0.0.1:1433: connectex: No connection could be made because the target machine actively refused it.
```

This is easy to miss: after applying the TLS workaround above, the TLS errors stop, which looks like success while every scrape is actually failing. Point the receiver at the FCI's network name or cluster IP address instead.

## Metrics

| Metric Name | Description | Unit | Type | Attributes |
| - | - | - | - | - |
| ❌ `sqlserver.access.scan.rate` | Rate of access method scans. | \{scan}/s | Gauge | sqlserver.access.scan.type |
| ❌ `sqlserver.attention.rate` | Number of SQL attentions (client cancellation interrupts) received per second. | \{attentions}/s | Gauge | |
| ❌ `sqlserver.availability_group.database_replica.queue.rate` | Rate at which log data is being sent or redone on a database replica, broken down by queue type (log\_send for primary-to-secondary transmission, redo for applying log records on the secondary). | By/s | Gauge | sqlserver.availability\_group.name, sqlserver.replica.name, sqlserver.availability\_group.queue.type |
| ❌ `sqlserver.availability_group.database_replica.queue.size` | Amount of log data waiting to be processed on a database replica, broken down by queue type (log\_send for data not yet sent to the secondary, redo for data received but not yet applied). | By | Gauge | sqlserver.availability\_group.name, sqlserver.replica.name, sqlserver.availability\_group.queue.type |
| ❌ `sqlserver.availability_group.database_replica.secondary_lag` | Number of seconds the secondary replica is lagging behind the primary replica, measured as the time between the most recent hardened log block on the primary and on the secondary. | s | Gauge | sqlserver.availability\_group.name, sqlserver.replica.name |
| ✅ `sqlserver.batch.request.rate` | Number of batch requests received by SQL Server. | \{requests}/s | Gauge | |
| ✅ `sqlserver.batch.sql_compilation.rate` | Number of SQL compilations needed. | \{compilations}/s | Gauge | |
| ✅ `sqlserver.batch.sql_recompilation.rate` | Number of SQL recompilations needed. | \{compilations}/s | Gauge | |
| ❌ `sqlserver.clr.execution.time` | Total time spent executing in the CLR. Only non-zero when CLR integration is enabled and CLR code has been executed. | s | Counter | |
| ❌ `sqlserver.computer.uptime` | Computer uptime. | \{seconds} | Gauge | |
| ❌ `sqlserver.connection.reset.rate` | Number of logical connections reset per second. | \{connection}/s | Gauge | |
| ❌ `sqlserver.cpu.count` | Number of CPUs. | \{CPUs} | Gauge | |
| ❌ `sqlserver.cpu.utilization` | System-wide CPU utilization on the host that SQL Server is running on, expressed as a fraction between 0 and 1. | 1 | Gauge | |
| ❌ `sqlserver.cursor.count` | Number of cursors by state. | \{cursor} | Gauge | cursor.state |
| ❌ `sqlserver.cursor.memory.usage` | Memory used by cursors. | By | Gauge | |
| ❌ `sqlserver.cursor.plan.count` | Number of active cursor plans. | \{plan} | Gauge | |
| ❌ `sqlserver.cursor.request.rate` | Rate of cursor requests per second. | \{request}/s | Gauge | |
| ❌ `sqlserver.database.backup_or_restore.rate` | Total number of backups/restores. | \{backups\_or\_restores}/s | Gauge | |
| ❌ `sqlserver.database.count` | The number of databases | \{databases} | Gauge | database.status |
| ❌ `sqlserver.database.execution.errors` | Number of execution errors. | \{errors} | Gauge | |
| ❌ `sqlserver.database.full_scan.rate` | The number of unrestricted full table or index scans. | \{scans}/s | Gauge | |
| ❌ `sqlserver.database.io` | The number of bytes of I/O on this file. | By | Counter | physical\_filename, logical\_filename, file\_type, direction |
| ❌ `sqlserver.database.latency` | Total time that the users waited for I/O issued on this file. | s | Counter | physical\_filename, logical\_filename, file\_type, direction |
| ❌ `sqlserver.database.operations` | The number of operations issued on the file. | \{operations} | Counter | physical\_filename, logical\_filename, file\_type, direction |
| ❌ `sqlserver.database.tempdb.space` | Total free space in temporary DB. | kBy | UpDownCounter | tempdb.state |
| ❌ `sqlserver.database.tempdb.version_store.size` | TempDB version store size. | kBy | Gauge | |
| ❌ `sqlserver.deadlock.rate` | Total number of deadlocks. | \{deadlocks}/s | Gauge | |
| ❌ `sqlserver.disk.io` | Cumulative bytes transferred to or from files backing SQL Server databases, per I/O direction. Counters reset when the file becomes unavailable (detached, taken offline, or database restart). | By | Counter | disk.io.direction, sqlserver.file.path.prefix |
| ❌ `sqlserver.disk.operations` | Cumulative I/O operation count on files backing SQL Server databases, per I/O direction. Counters reset when the file becomes unavailable (detached, taken offline, or database restart). | \{operation} | Counter | disk.io.direction, sqlserver.file.path.prefix |
| ❌ `sqlserver.error.rate` | Number of errors raised per second. | \{error}/s | Gauge | sqlserver.error.category |
| ❌ `sqlserver.extent.operation.rate` | Rate of extent operations. | \{extent}/s | Gauge | sqlserver.extent.operation.type |
| ❌ `sqlserver.ghost_record.skipped.rate` | Rate of ghosted records skipped during scans. | \{record}/s | Gauge | |
| ❌ `sqlserver.host.memory.limit` | Total physical memory available to SQL Server on the host. | By | Gauge | |
| ❌ `sqlserver.host.memory.usage` | Physical memory usage available to SQL Server on the host, broken down by state. | By | Gauge | system.memory.state |
| ❌ `sqlserver.index.fragmentation` | Average fragmentation percentage of the index. | % | Gauge | db.namespace, sqlserver.index.id, sqlserver.object.name, sqlserver.schema.name |
| ❌ `sqlserver.index.page.count` | Number of pages in the index. | \{page} | Gauge | db.namespace, sqlserver.index.id, sqlserver.object.name, sqlserver.schema.name |
| ❌ `sqlserver.index.page.utilization` | Average percentage of available data storage space used in all pages of the index. | % | Gauge | db.namespace, sqlserver.index.id, sqlserver.object.name, sqlserver.schema.name |
| ❌ `sqlserver.index.record.count` | Total number of records in the index. | \{record} | Gauge | db.namespace, sqlserver.index.id, sqlserver.object.name, sqlserver.schema.name |
| ❌ `sqlserver.index.search.rate` | Total number of index searches. | \{searches}/s | Gauge | |
| ❌ `sqlserver.index.size` | Total size of the index. | By | Gauge | db.namespace, sqlserver.index.id, sqlserver.object.name, sqlserver.schema.name |
| ❌ `sqlserver.latch.superlatch.count` | Number of superlatches currently active. | \{superlatch} | Gauge | |
| ❌ `sqlserver.latch.superlatch.transition.rate` | Rate of superlatch promotions or demotions. | \{transition}/s | Gauge | transition.direction |
| ❌ `sqlserver.latch.wait.rate` | Number of latch waits per second. | \{wait}/s | Gauge | |
| ❌ `sqlserver.latch.wait_time.avg` | Average latch wait time. | s | Gauge | |
| ❌ `sqlserver.latch.wait_time.total` | Total latch wait time. | s | Counter | |
| ❌ `sqlserver.lock.block.count` | Number of lock blocks tracked by the lock manager. | \{block} | Gauge | sqlserver.lock.block.type |
| ❌ `sqlserver.lock.escalation.rate` | Number of lock escalations per second (locks on a table escalated to a larger granularity). | \{escalation}/s | Gauge | |
| ❌ `sqlserver.lock.memory` | Total amount of memory used for locks. | By | Gauge | |
| ❌ `sqlserver.lock.request.rate` | Number of new locks and lock conversions per second requested from the lock manager. | \{request}/s | Gauge | |
| ❌ `sqlserver.lock.timeout.rate` | Number of lock timeouts per second. | \{timeout}/s | Gauge | sqlserver.lock.timeout.type |
| ❌ `sqlserver.lock.wait.count` | Cumulative count of lock waits that occurred. | \{wait} | Counter | |
| ✅ `sqlserver.lock.wait.rate` | Number of lock requests resulting in a wait. | \{requests}/s | Gauge | |
| ✅ `sqlserver.lock.wait_time.avg` | Average wait time for all lock requests that had to wait. | ms | Gauge | |
| ❌ `sqlserver.lock.wait_time.total` | Total wait time in seconds for locks since the last server restart. | s | Counter | |
| ❌ `sqlserver.login.rate` | Total number of logins. | \{logins}/s | Gauge | |
| ❌ `sqlserver.logout.rate` | Total number of logouts. | \{logouts}/s | Gauge | |
| ❌ `sqlserver.memory.area` | Amount of memory used by the SQL Server memory pool. | By | Gauge | memory.pool |
| ❌ `sqlserver.memory.cache.object.count` | Number of cache objects in the SQL Server cache. | \{object} | Gauge | cache.state |
| ❌ `sqlserver.memory.grants.pending.count` | Total number of memory grants pending. | \{grants} | UpDownCounter | |
| ❌ `sqlserver.memory.page.count` | Number of pages in the SQL Server buffer pool. | \{page} | Gauge | page.pool |
| ❌ `sqlserver.memory.usage` | Total memory in use. | kBy | UpDownCounter | |
| ❌ `sqlserver.os.wait.duration` | Total wait time for this wait type | s | Counter | wait.category, wait.type |
| ❌ `sqlserver.page.allocation.rate` | Rate of page allocation operations. | \{page}/s | Gauge | sqlserver.page.allocation.type |
| ❌ `sqlserver.page.buffer_cache.free_list.stalls.rate` | Number of free list stalls. | \{stalls}/s | Gauge | |
| ✅ `sqlserver.page.buffer_cache.hit_ratio` | Pages found in the buffer pool without having to read from disk. | % | Gauge | |
| ✅ `sqlserver.page.checkpoint.flush.rate` | Number of pages flushed by operations requiring dirty pages to be flushed. | \{pages}/s | Gauge | |
| ❌ `sqlserver.page.compression.rate` | Rate of page compression operations. | \{page}/s | Gauge | sqlserver.page.compression.type |
| ✅ `sqlserver.page.lazy_write.rate` | Number of lazy writes moving dirty pages to disk. | \{writes}/s | Gauge | |
| ✅ `sqlserver.page.life_expectancy` | Time a page will stay in the buffer pool. | s | Gauge | performance\_counter.object\_name |
| ❌ `sqlserver.page.lookup.rate` | Total number of page lookups. | \{lookups}/s | Gauge | |
| ✅ `sqlserver.page.operation.rate` | Number of physical database page operations issued. | \{operations}/s | Gauge | page.operations |
| ❌ `sqlserver.page.read_ahead.rate` | Rate of pages read from disk by the read-ahead manager. | \{page}/s | Gauge | |
| ✅ `sqlserver.page.split.rate` | Number of pages split as a result of overflowing index pages. | \{pages}/s | Gauge | |
| ❌ `sqlserver.parameterization.rate` | Rate of auto-parameterization activity, broken down by result. | \{params}/s | Gauge | sqlserver.parameterization.result |
| ❌ `sqlserver.plan.execution.rate` | Rate of plan executions, classified by plan guide result. | \{executions}/s | Gauge | sqlserver.plan.guidance.result |
| ❌ `sqlserver.processes.blocked` | The number of processes that are currently blocked | \{processes} | Gauge | |
| ❌ `sqlserver.recompilation.ratio` | Ratio of SQL recompilations to compilations, expressed as a percentage. | % | Gauge | |
| ❌ `sqlserver.replica.data.rate` | Throughput rate of replica data. | By/s | Gauge | replica.direction |
| ❌ `sqlserver.resource_pool.disk.operations` | The rate of operations issued. | \{operations}/s | Gauge | direction |
| ❌ `sqlserver.resource_pool.disk.throttled.read.rate` | The number of read operations that were throttled in the last second | \{reads}/s | Gauge | |
| ❌ `sqlserver.resource_pool.disk.throttled.write.rate` | The number of write operations that were throttled in the last second | \{writes}/s | Gauge | |
| ❌ `sqlserver.scan_point.revalidation.rate` | Rate at which scan points needed to be revalidated. | \{revalidate}/s | Gauge | |
| ❌ `sqlserver.stored_procedure.invocation.rate` | Rate of Service Broker activated stored procedure invocations per second. | \{invocation}/s | Gauge | |
| ❌ `sqlserver.table.count` | The number of tables. | \{tables} | UpDownCounter | table.state, table.status |
| ❌ `sqlserver.task.count` | Number of Service Broker activation tasks by state. | \{task} | Gauge | task.state |
| ❌ `sqlserver.task.rate` | Rate of Service Broker activation tasks by type per second. | \{task}/s | Gauge | task.result |
| ❌ `sqlserver.transaction.delay` | Time consumed in transaction delays. | ms | UpDownCounter | |
| ❌ `sqlserver.transaction.mirror_write.rate` | Total number of mirror write transactions. | \{transactions}/s | Gauge | |
| ✅ `sqlserver.transaction.rate` | Number of transactions started for the database (not including XTP-only transactions). | \{transactions}/s | Gauge | |
| ✅ `sqlserver.transaction.write.rate` | Number of transactions that wrote to the database and committed. | \{transactions}/s | Gauge | |
| ✅ `sqlserver.transaction_log.flush.data.rate` | Total number of log bytes flushed. | By/s | Gauge | |
| ✅ `sqlserver.transaction_log.flush.rate` | Number of log flushes. | \{flushes}/s | Gauge | |
| ✅ `sqlserver.transaction_log.flush.wait.rate` | Number of commits waiting for a transaction log flush. | \{commits}/s | Gauge | |
| ✅ `sqlserver.transaction_log.growth.count` | Total number of transaction log expansions for a database. | \{growths} | Counter | |
| ✅ `sqlserver.transaction_log.shrink.count` | Total number of transaction log shrinks for a database. | \{shrinks} | Counter | |
| ✅ `sqlserver.transaction_log.usage` | Percent of transaction log space used. | % | Gauge | |
| ✅ `sqlserver.user.connection.count` | Number of users connected to the SQL Server. | \{connections} | Gauge | |
| ❌ `sqlserver.worker.request.count` | Number of worker requests by state. | \{request} | Gauge | request.state |
| ❌ `sqlserver.worker.thread.count` | Number of worker threads by state. | \{thread} | Gauge | worker.state |
| ❌ `sqlserver.worktable.cache.hit_ratio` | Fraction of worktables that did not require initialization because they were retrieved from the worktable cache. | 1 | Gauge | |

## Attributes

| Attribute Name | Description | Type | Values |
| - | - | - | - |
| `cache.state` | The state of the cache objects. | string | `in_use`, `total` |
| `client.address` | Hostname or address of the client. | string | |
| `client.port` | TCP port used by the client. | int | |
| `cursor.state` | The state of the cursor. | string | `active`, `cached` |
| `database.status` | The current status of a database | string | `online`, `restoring`, `recovering`, `pending_recovery`, `suspect`, `offline` |
| `db.namespace` | The database name. | string | |
| `db.query.text` | The text of the database query being executed. | string | |
| `db.system.name` | The database management system (DBMS) product as identified by the client instrumentation. | string | |
| `direction` | The direction of flow of bytes or operations. | string | `read`, `write` |
| `disk.io.direction` | The direction of disk I/O operations. | string | `read`, `write` |
| `file_type` | The type of file being monitored. | string | |
| `logical_filename` | The logical filename of the file being monitored. | string | |
| `memory.pool` | The functional area of SQL Server memory. | string | `target`, `total`, `sql_cache`, `optimizer`, `connection`, `granted_workspace`, `max_workspace` |
| `network.peer.address` | IP address of the peer client. | string | |
| `network.peer.port` | TCP port used by the peer client. | int | |
| `type` | The page operation types. | string | `read`, `write` |
| `page.pool` | The type of page pool in the SQL Server buffer manager. | string | `cache`, `total`, `target`, `database`, `stolen`, `reserved`, `free` |
| `performance_counter.object_name` | Category to which this counter belongs | string | |
| `physical_filename` | The physical filename of the file being monitored. | string | |
| `replica.direction` | The direction of flow of bytes for replica. | string | `transmit`, `receive` |
| `request.state` | The state of the worker request. | string | `waiting` |
| `sqlserver.access.scan.type` | The type of access method scan being performed. | string | `free_space`, `probe`, `range` |
| `sqlserver.availability_group.name` | The name of the SQL Server Availability Group. | string | |
| `sqlserver.availability_group.queue.type` | The type of availability group queue. | string | `log_send`, `redo` |
| `sqlserver.blocking.start_time` | Timestamp of when the current blocking wait began (ISO 8601 format). | string | |
| `sqlserver.blocking_session_id` | Session ID that is blocking the current session. 0 if none. | int | |
| `sqlserver.client.app.name` | Name of the client application that initiated the session. | string | |
| `sqlserver.command` | SQL command type being executed. | string | |
| `sqlserver.context_info` | Context information for the session, represented as a hexadecimal string. | string | |
| `sqlserver.cpu_time` | CPU time consumed by the query, in seconds. | double | |
| `sqlserver.deadlock_priority` | Deadlock priority value for the session. | int | |
| `sqlserver.error.category` | The category of the SQL Server error. | string | `db_offline`, `info`, `kill_connection`, `user` |
| `sqlserver.estimated_completion_time` | Estimated time remaining for the request to complete, in seconds. | double | |
| `sqlserver.execution_count` | Number of times that the plan has been executed since it was last compiled, reported in delta value. | int | |
| `sqlserver.extent.operation.type` | The type of extent allocation operation. | string | `allocated`, `deallocated` |
| `sqlserver.file.path.prefix` | The path prefix identifying the storage location of SQL Server database files. | string | |
| `sqlserver.index.id` | The identifier of the index. | int | |
| `sqlserver.lock.block.type` | The type of lock block tracked by the lock manager. | string | `allocated`, `blocks`, `owner`, `owner_allocated` |
| `sqlserver.lock.timeout.type` | The type of lock timeout being reported. `all` includes immediate (zero-wait) timeouts; `nonzero` excludes them. | string | `all`, `nonzero` |
| `sqlserver.lock_timeout` | Lock timeout value in seconds. | double | |
| `sqlserver.logical_reads` | Number of logical reads (data read from cache/memory). | int | |
| `sqlserver.object.name` | The name of the database object. | string | |
| `sqlserver.open_transaction_count` | Number of transactions currently open in the session. | int | |
| `sqlserver.page.allocation.type` | The type of page allocation operation. | string | `allocated`, `deallocated`, `mixed` |
| `sqlserver.page.compression.type` | The type of page compression operation. | string | `attempted`, `succeeded` |
| `sqlserver.parameterization.result` | The result of an auto-parameterization attempt by the SQL Server query optimizer. | string | `auto_attempted`, `safe`, `unsafe`, `failed`, `forced` |
| `sqlserver.percent_complete` | Percentage of work completed. | double | |
| `sqlserver.plan.guidance.result` | Whether a SQL plan execution successfully used a matching plan guide (guided) or did not (misguided). | string | `guided`, `misguided` |
| `sqlserver.procedure.last_execution_time` | ISO 8601 timestamp of the last execution of the procedure. | string | |
| `sqlserver.procedure.max_duration` | Longest elapsed time for a single execution of the procedure, in seconds. Covers the whole period the plan has been cached, so unlike the other durations it is not a delta. | double | |
| `sqlserver.procedure.min_duration` | Shortest elapsed time for a single execution of the procedure, in seconds. Covers the whole period the plan has been cached, so unlike the other durations it is not a delta. | double | |
| `sqlserver.procedure.tempdb.spilled_pages` | Pages spilled to tempdb by the procedure over the collection interval, reported as a delta. | int | |
| `sqlserver.procedure_execution_count` | Number of times that the procedure has been executed since it was last compiled, reported in delta value. | int | |
| `sqlserver.procedure_id` | The SQL Server ID of the stored procedure, if any | string | |
| `sqlserver.procedure_name` | The name of the stored procedure, if any | string | |
| `sqlserver.query.last_started` | Timestamp of when the SQL query last started executing (ISO 8601 format). | string | |
| `sqlserver.query.plan.creation_time` | Timestamp of when the SQL query execution plan was compiled (ISO 8601 format). | string | |
| `sqlserver.query_hash` | Binary hash value calculated on the query and used to identify queries with similar logic, reported in the HEX format. | string | |
| `sqlserver.query_plan` | The query execution plan used by the SQL Server. | string | |
| `sqlserver.query_plan_hash` | Binary hash value calculated on the query execution plan and used to identify similar query execution plans, reported in the HEX format. | string | |
| `sqlserver.query_start` | Timestamp of when the SQL query started (ISO 8601 format). | string | |
| `sqlserver.reads` | Number of physical reads performed by the query. | int | |
| `sqlserver.replica.name` | The name of the SQL Server Availability Group replica. | string | |
| `sqlserver.request_status` | Status of the request (e.g., running, suspended). | string | |
| `sqlserver.row_count` | Number of rows affected or returned by the query. | int | |
| `sqlserver.schema.name` | The name of the database schema. | string | |
| `sqlserver.session.duration` | Total elapsed time in seconds since the session was established (login time). | double | |
| `sqlserver.session.start_time` | Timestamp when the session was established (ISO 8601 format). | string | |
| `sqlserver.session_id` | ID of the SQL Server session. | int | |
| `sqlserver.session_status` | Status of the session (e.g., running, sleeping). | string | |
| `sqlserver.total_elapsed_time` | Total elapsed time for completed executions of this plan, reported in delta seconds. | double | |
| `sqlserver.total_grant_kb` | The total amount of reserved memory grant in KB this plan received since it was compiled, reported in delta value. | int | |
| `sqlserver.total_logical_reads` | Total number of logical reads performed by executions of this plan since it was compiled, reported in delta value. | int | |
| `sqlserver.total_logical_writes` | Total number of logical writes performed by executions of this plan since it was compiled, reported in delta value. | int | |
| `sqlserver.total_physical_reads` | Total number of physical reads performed by executions of this plan since it was compiled, reported in delta value. | int | |
| `sqlserver.total_rows` | Total number of rows returned by the query, reported in delta value. | int | |
| `sqlserver.total_worker_time` | Total amount of CPU time that was consumed by executions of this plan since it was compiled, reported in delta seconds. | double | |
| `sqlserver.transaction_id` | Unique ID of the active transaction. | int | |
| `sqlserver.transaction_isolation_level` | Transaction isolation level used in the session. Represented as numeric constant. | int | |
| `sqlserver.wait.resource.id` | SQL Server identifier for the locked or waited-on resource, if available. | string | |
| `sqlserver.wait.resource.type` | SQL Server type of the locked or waited-on resource, if available. | string | |
| `sqlserver.wait_resource` | The resource for which the session is waiting. | string | |
| `sqlserver.wait_time` | Duration in seconds the request has been waiting. | double | |
| `sqlserver.wait_type` | Type of wait encountered by the request. Empty if none. | string | |
| `sqlserver.writes` | Number of writes performed by the query. | int | |
| `system.memory.state` | The state of the physical memory on the host. | string | `used`, `free` |
| `table.state` | The state of the table. | string | `active`, `inactive` |
| `table.status` | The status of the table. | string | `temporary`, `permanent` |
| `task.result` | The result of the task activation. | string | `started`, `aborted` |
| `task.state` | The state of the task. | string | `running`, `limit_reached` |
| `tempdb.state` | The status of the tempdb space usage. | string | `free`, `used` |
| `transition.direction` | The direction of a superlatch transition. | string | `promotion`, `demotion` |
| `user.name` | Login name associated with the SQL Server session. | string | |
| `wait.category` | Category of the reason for a wait. | string | |
| `wait.type` | Type of the wait, view [WaitTypes documentation](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-os-wait-stats-transact-sql?view=sql-server-ver16#WaitTypes) for more information. | string | |
| `worker.state` | The state of the worker thread. | string | `maximum`, `active`, `available`, `waiting_for_cpu` |

## Resource Attributes

| Attribute Name | Description | Type | Enabled |
| - | - | - | - |
| `db.system.version` | The database version of the instance. Examples include "15.0.4261.1". | string | ❌ |
| `host.name` | The host name of SQL Server | string | ✅ |
| `server.address` | The address of the monitored SQL Server instance. A loopback target (for example localhost or 127.0.0.1) is reported as the host name of the machine running the collector, since the instance is co-located with it. | string | ✅ |
| `server.port` | The port of the monitored SQL Server instance, defaulting to 1433 when not configured. | int | ✅ |
| `service.instance.id` | A unique identifier of the SQL Server instance. When using direct connection settings, the format is host:port. When using a datasource, the format is host\instance for a named instance and host:port otherwise; the port defaults to 1433 when omitted. In Windows Performance Counter mode, the format remains host:1433; host is computer\_name for remote monitoring, or the local collector host when monitoring locally. | string | ✅ |
| `service.name` | Logical name of the service. When enabled, defaults to unknown\_service:microsoft.sql\_server. | string | ❌ |
| `service.namespace` | Logical namespace for the service (for example team or environment). When enabled, defaults to an empty string until set via configuration. | string | ❌ |
| `sqlserver.computer.name` | The name of the SQL Server instance being monitored. | string | ❌ |
| `sqlserver.database.name` | The name of the SQL Server database. | string | ✅ |
| `sqlserver.instance.name` | The name of the SQL Server instance being monitored. | string | ❌ |

## Configuration

### Example Configuration

```yaml theme={null}
sqlserver:
  collection_interval: 10s

sqlserver/named:
  collection_interval: 10s
  computer_name: CustomServer
  instance_name: CustomInstance
  resource_attributes:
    sqlserver.computer.name:
      enabled: true
    sqlserver.instance.name:
      enabled: true
    server.port:
      enabled: true
    server.address:
      enabled: true
    service.name:
      enabled: true
    service.instance.id:
      enabled: false
  top_query_collection:
    lookback_time: 60s
    max_query_sample_count: 1000
    top_query_count: 200
    collection_interval: 80s
  query_sample_collection:
    max_rows_per_query: 1450
  events:
    db.server.query_sample:
      enabled: true
    db.server.top_query:
      enabled: true
```

***

*Last generated: 2026-10-05*
