Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
537
plugins/inputs/sqlserver/README.md
Normal file
537
plugins/inputs/sqlserver/README.md
Normal file
|
@ -0,0 +1,537 @@
|
|||
# SQL Server Input Plugin
|
||||
|
||||
The `sqlserver` plugin provides metrics for your SQL Server instance.
|
||||
Recorded metrics are lightweight and use Dynamic Management Views
|
||||
supplied by SQL Server.
|
||||
|
||||
## The SQL Server plugin supports the following editions/versions of SQL Server
|
||||
|
||||
- SQL Server
|
||||
- 2012 or newer (Plugin support aligned with the [official Microsoft SQL Server support](https://docs.microsoft.com/en-us/sql/sql-server/end-of-support/sql-server-end-of-life-overview?view=sql-server-ver15#lifecycle-dates))
|
||||
- End-of-life SQL Server versions are not guaranteed to be supported by Telegraf. Any issues with the SQL Server plugin for these EOL versions will need to be addressed by the community.
|
||||
- Azure SQL Database (Single)
|
||||
- Azure SQL Managed Instance
|
||||
- Azure SQL Elastic Pool
|
||||
- Azure Arc-enabled SQL Managed Instance
|
||||
|
||||
## Additional Setup
|
||||
|
||||
You have to create a login on every SQL Server instance or Azure SQL
|
||||
Managed instance you want to monitor, with following script:
|
||||
|
||||
```sql
|
||||
USE master;
|
||||
GO
|
||||
CREATE LOGIN [telegraf] WITH PASSWORD = N'mystrongpassword';
|
||||
GO
|
||||
GRANT VIEW SERVER STATE TO [telegraf];
|
||||
GO
|
||||
GRANT VIEW ANY DEFINITION TO [telegraf];
|
||||
GO
|
||||
```
|
||||
|
||||
For Azure SQL Database, you require the View Database State permission
|
||||
and can create a user with a password directly in the database.
|
||||
|
||||
```sql
|
||||
CREATE USER [telegraf] WITH PASSWORD = N'mystrongpassword';
|
||||
GO
|
||||
GRANT VIEW DATABASE STATE TO [telegraf];
|
||||
GO
|
||||
```
|
||||
|
||||
For Azure SQL Elastic Pool, please follow the following instructions
|
||||
to collect metrics.
|
||||
|
||||
On master logical database, create an SQL login 'telegraf' and assign
|
||||
it to the server-level role ##MS_ServerStateReader##.
|
||||
|
||||
```sql
|
||||
CREATE LOGIN [telegraf] WITH PASSWORD = N'mystrongpassword';
|
||||
GO
|
||||
ALTER SERVER ROLE ##MS_ServerStateReader##
|
||||
ADD MEMBER [telegraf];
|
||||
GO
|
||||
```
|
||||
|
||||
Elastic pool metrics can be collected from any database in the pool if a user
|
||||
for the `telegraf` login is created in that database. For collection to work,
|
||||
this database must remain in the pool, and must not be renamed. If you plan
|
||||
to add/remove databases from this pool, create a separate database for
|
||||
monitoring purposes that will remain in the pool.
|
||||
|
||||
> Note: To avoid duplicate monitoring data, do not collect elastic pool metrics
|
||||
from more than one database in the same pool.
|
||||
|
||||
```sql
|
||||
GO
|
||||
CREATE USER [telegraf] FOR LOGIN telegraf;
|
||||
```
|
||||
|
||||
For Service SID authentication to SQL Server (Windows service installations
|
||||
only).
|
||||
|
||||
- [More information about using service SIDs to grant permissions in SQL Server](https://docs.microsoft.com/en-us/sql/relational-databases/security/using-service-sids-to-grant-permissions-to-services-in-sql-server)
|
||||
|
||||
In an administrative command prompt configure the telegraf service for use
|
||||
with a service SID
|
||||
|
||||
```Batchfile
|
||||
sc.exe sidtype "telegraf" unrestricted
|
||||
```
|
||||
|
||||
To create the login for the telegraf service run the following script:
|
||||
|
||||
```sql
|
||||
USE master;
|
||||
GO
|
||||
CREATE LOGIN [NT SERVICE\telegraf] FROM WINDOWS;
|
||||
GO
|
||||
GRANT VIEW SERVER STATE TO [NT SERVICE\telegraf];
|
||||
GO
|
||||
GRANT VIEW ANY DEFINITION TO [NT SERVICE\telegraf];
|
||||
GO
|
||||
```
|
||||
|
||||
Remove User Id and Password keywords from the connection string in your
|
||||
config file to use windows authentication.
|
||||
|
||||
```toml
|
||||
[[inputs.sqlserver]]
|
||||
servers = ["Server=192.168.1.10;Port=1433;app name=telegraf;log=1;",]
|
||||
```
|
||||
|
||||
To set up a configurable timeout, add timeout to the connections string
|
||||
in your config file.
|
||||
|
||||
```toml
|
||||
servers = [
|
||||
"Server=192.168.1.10;Port=1433;User Id=<user>;Password=<pw>;app name=telegraf;log=1;dial timeout=30",
|
||||
]
|
||||
```
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Secret-store support
|
||||
|
||||
This plugin supports secrets from secret-stores for the `servers` option.
|
||||
See the [secret-store documentation][SECRETSTORE] for more details on how
|
||||
to use them.
|
||||
|
||||
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Read metrics from Microsoft SQL Server
|
||||
[[inputs.sqlserver]]
|
||||
## Specify instances to monitor with a list of connection strings.
|
||||
## All connection parameters are optional.
|
||||
## By default, the host is localhost, listening on default port, TCP 1433.
|
||||
## for Windows, the user is the currently running AD user (SSO).
|
||||
## See https://github.com/microsoft/go-mssqldb for detailed connection
|
||||
## parameters, in particular, tls connections can be created like so:
|
||||
## "encrypt=true;certificate=<cert>;hostNameInCertificate=<SqlServer host fqdn>"
|
||||
servers = [
|
||||
"Server=192.168.1.10;Port=1433;User Id=<user>;Password=<pw>;app name=telegraf;log=1;",
|
||||
]
|
||||
|
||||
## Timeout for query execution operation
|
||||
## Note that the timeout for queries is per query not per gather.
|
||||
## 0 value means no timeout
|
||||
# query_timeout = "0s"
|
||||
|
||||
## Authentication method
|
||||
## valid methods: "connection_string", "AAD"
|
||||
# auth_method = "connection_string"
|
||||
|
||||
## ClientID is the is the client ID of the user assigned identity of the VM
|
||||
## that should be used to authenticate to the Azure SQL server.
|
||||
# client_id = ""
|
||||
|
||||
## "database_type" enables a specific set of queries depending on the database type. If specified, it replaces azuredb = true/false and query_version = 2
|
||||
## In the config file, the sql server plugin section should be repeated each with a set of servers for a specific database_type.
|
||||
## Possible values for database_type are - "SQLServer" or "AzureSQLDB" or "AzureSQLManagedInstance" or "AzureSQLPool"
|
||||
database_type = "SQLServer"
|
||||
|
||||
## A list of queries to include. If not specified, all the below listed queries are used.
|
||||
include_query = []
|
||||
|
||||
## A list of queries to explicitly ignore.
|
||||
exclude_query = ["SQLServerAvailabilityReplicaStates", "SQLServerDatabaseReplicaStates"]
|
||||
|
||||
## Force using the deprecated ADAL authentication method instead of the recommended
|
||||
## MSAL method. Setting this option is not recommended and only exists for backward
|
||||
## compatibility.
|
||||
# use_deprecated_adal_authentication = false
|
||||
|
||||
## Queries enabled by default for database_type = "SQLServer" are -
|
||||
## SQLServerPerformanceCounters, SQLServerWaitStatsCategorized, SQLServerDatabaseIO, SQLServerProperties, SQLServerMemoryClerks,
|
||||
## SQLServerSchedulers, SQLServerRequests, SQLServerVolumeSpace, SQLServerCpu, SQLServerAvailabilityReplicaStates, SQLServerDatabaseReplicaStates,
|
||||
## SQLServerRecentBackups
|
||||
|
||||
## Queries enabled by default for database_type = "AzureSQLDB" are -
|
||||
## AzureSQLDBResourceStats, AzureSQLDBResourceGovernance, AzureSQLDBWaitStats, AzureSQLDBDatabaseIO, AzureSQLDBServerProperties,
|
||||
## AzureSQLDBOsWaitstats, AzureSQLDBMemoryClerks, AzureSQLDBPerformanceCounters, AzureSQLDBRequests, AzureSQLDBSchedulers
|
||||
|
||||
## Queries enabled by default for database_type = "AzureSQLManagedInstance" are -
|
||||
## AzureSQLMIResourceStats, AzureSQLMIResourceGovernance, AzureSQLMIDatabaseIO, AzureSQLMIServerProperties, AzureSQLMIOsWaitstats,
|
||||
## AzureSQLMIMemoryClerks, AzureSQLMIPerformanceCounters, AzureSQLMIRequests, AzureSQLMISchedulers
|
||||
|
||||
## Queries enabled by default for database_type = "AzureSQLPool" are -
|
||||
## AzureSQLPoolResourceStats, AzureSQLPoolResourceGovernance, AzureSQLPoolDatabaseIO, AzureSQLPoolWaitStats,
|
||||
## AzureSQLPoolMemoryClerks, AzureSQLPoolPerformanceCounters, AzureSQLPoolSchedulers
|
||||
|
||||
## Queries enabled by default for database_type = "AzureArcSQLManagedInstance" are -
|
||||
## AzureSQLMIDatabaseIO, AzureSQLMIServerProperties, AzureSQLMIOsWaitstats,
|
||||
## AzureSQLMIMemoryClerks, AzureSQLMIPerformanceCounters, AzureSQLMIRequests, AzureSQLMISchedulers
|
||||
|
||||
## Following are old config settings
|
||||
## You may use them only if you are using the earlier flavor of queries, however it is recommended to use
|
||||
## the new mechanism of identifying the database_type there by use it's corresponding queries
|
||||
|
||||
## Optional parameter, setting this to 2 will use a new version
|
||||
## of the collection queries that break compatibility with the original
|
||||
## dashboards.
|
||||
## Version 2 - is compatible from SQL Server 2012 and later versions and also for SQL Azure DB
|
||||
# query_version = 2
|
||||
|
||||
## If you are using AzureDB, setting this to true will gather resource utilization metrics
|
||||
# azuredb = false
|
||||
|
||||
## Toggling this to true will emit an additional metric called "sqlserver_telegraf_health".
|
||||
## This metric tracks the count of attempted queries and successful queries for each SQL instance specified in "servers".
|
||||
## The purpose of this metric is to assist with identifying and diagnosing any connectivity or query issues.
|
||||
## This setting/metric is optional and is disabled by default.
|
||||
# health_metric = false
|
||||
|
||||
## Possible queries across different versions of the collectors
|
||||
## Queries enabled by default for specific Database Type
|
||||
|
||||
## database_type = AzureSQLDB by default collects the following queries
|
||||
## - AzureSQLDBWaitStats
|
||||
## - AzureSQLDBResourceStats
|
||||
## - AzureSQLDBResourceGovernance
|
||||
## - AzureSQLDBDatabaseIO
|
||||
## - AzureSQLDBServerProperties
|
||||
## - AzureSQLDBOsWaitstats
|
||||
## - AzureSQLDBMemoryClerks
|
||||
## - AzureSQLDBPerformanceCounters
|
||||
## - AzureSQLDBRequests
|
||||
## - AzureSQLDBSchedulers
|
||||
|
||||
## database_type = AzureSQLManagedInstance by default collects the following queries
|
||||
## - AzureSQLMIResourceStats
|
||||
## - AzureSQLMIResourceGovernance
|
||||
## - AzureSQLMIDatabaseIO
|
||||
## - AzureSQLMIServerProperties
|
||||
## - AzureSQLMIOsWaitstats
|
||||
## - AzureSQLMIMemoryClerks
|
||||
## - AzureSQLMIPerformanceCounters
|
||||
## - AzureSQLMIRequests
|
||||
## - AzureSQLMISchedulers
|
||||
|
||||
## database_type = AzureSQLPool by default collects the following queries
|
||||
## - AzureSQLPoolResourceStats
|
||||
## - AzureSQLPoolResourceGovernance
|
||||
## - AzureSQLPoolDatabaseIO
|
||||
## - AzureSQLPoolOsWaitStats,
|
||||
## - AzureSQLPoolMemoryClerks
|
||||
## - AzureSQLPoolPerformanceCounters
|
||||
## - AzureSQLPoolSchedulers
|
||||
|
||||
## database_type = SQLServer by default collects the following queries
|
||||
## - SQLServerPerformanceCounters
|
||||
## - SQLServerWaitStatsCategorized
|
||||
## - SQLServerDatabaseIO
|
||||
## - SQLServerProperties
|
||||
## - SQLServerMemoryClerks
|
||||
## - SQLServerSchedulers
|
||||
## - SQLServerRequests
|
||||
## - SQLServerVolumeSpace
|
||||
## - SQLServerCpu
|
||||
## - SQLServerRecentBackups
|
||||
## and following as optional (if mentioned in the include_query list)
|
||||
## - SQLServerAvailabilityReplicaStates
|
||||
## - SQLServerDatabaseReplicaStates
|
||||
```
|
||||
|
||||
## Support for Azure Active Directory (AAD) authentication using [Managed Identity](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview)
|
||||
|
||||
- Azure SQL Database supports 2 main methods of authentication: [SQL authentication and AAD authentication](https://docs.microsoft.com/en-us/azure/azure-sql/database/security-overview#authentication).
|
||||
- The recommended practice is to [use AAD authentication when possible](https://docs.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-overview).
|
||||
|
||||
AAD is a more modern authentication protocol, allows for easier
|
||||
credential/role management, and can eliminate the need to include passwords
|
||||
in a connection string.
|
||||
|
||||
To enable support for AAD authentication, we leverage the existing AAD
|
||||
authentication support.
|
||||
|
||||
If more then one managed identity is assigned to the VM. You need specify the
|
||||
client_id of the identity you wish to use to authenticate with the SQL Server.
|
||||
If only one is assigned you don't need so specify this value.
|
||||
|
||||
- Please see [SQL Server driver for Go](https://github.com/microsoft/go-mssqldb#azure-active-directory-authentication)
|
||||
|
||||
### How to use AAD Auth with MSI
|
||||
|
||||
- Please note AAD based auth is currently only supported for Azure SQL Database and Azure SQL Managed Instance (but not for SQL Server), as described [here](https://docs.microsoft.com/en-us/azure/azure-sql/database/security-overview#authentication).
|
||||
|
||||
- Configure "system-assigned managed identity" for Azure resources on the Monitoring VM (the VM that'd connect to the SQL server/database) [using the Azure portal](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm).
|
||||
- On the database being monitored, create/update a USER with the name of the Monitoring VM as the principal using the below script. This might require allow-listing the client machine's IP address (from where the below SQL script is being run) on the SQL Server resource.
|
||||
|
||||
In case of multiple assigned identities on one VM you can use the parameter
|
||||
user_assigned_id to specify the client_id.
|
||||
|
||||
```sql
|
||||
EXECUTE ('IF EXISTS(SELECT * FROM sys.database_principals WHERE name = ''<Monitoring_VM_Name>'')
|
||||
BEGIN
|
||||
DROP USER [<Monitoring_VM_Name>]
|
||||
END')
|
||||
EXECUTE ('CREATE USER [<Monitoring_VM_Name>] FROM EXTERNAL PROVIDER')
|
||||
EXECUTE ('GRANT VIEW DATABASE STATE TO [<Monitoring_VM_Name>]')
|
||||
```
|
||||
|
||||
- On the SQL Server resource of the database(s) being monitored, go to "Firewalls and Virtual Networks" tab and allowlist the monitoring VM IP address.
|
||||
- On the Monitoring VM, update the telegraf config file with the database connection string in the following format. The connection string only provides the server and database name, but no password (since the VM's system-assigned managed identity would be used for authentication). The auth method must be set to "AAD"
|
||||
|
||||
```toml
|
||||
servers = [
|
||||
"Server=<Azure_SQL_Server_Name>.database.windows.net;Port=1433;Database=<Azure_SQL_Database_Name>;app name=telegraf;log=1;",
|
||||
]
|
||||
auth_method = "AAD"
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
To provide backwards compatibility, this plugin support two versions of
|
||||
metrics queries.
|
||||
|
||||
**Note**: Version 2 queries are not backwards compatible with the old queries.
|
||||
Any dashboards or queries based on the old query format will not work with
|
||||
the new format. The version 2 queries only report raw metrics, no math has
|
||||
been done to calculate deltas. To graph this data you must calculate deltas
|
||||
in your dashboarding software.
|
||||
|
||||
### Version 1 (query_version=1): This is Deprecated in 1.16, all future development will be under configuration option database_type
|
||||
|
||||
The original metrics queries provide:
|
||||
|
||||
- *Performance counters*: 1000+ metrics from `sys.dm_os_performance_counters`
|
||||
- *Performance metrics*: special performance and ratio metrics
|
||||
- *Wait stats*: wait tasks categorized from `sys.dm_os_wait_stats`
|
||||
- *Memory clerk*: memory breakdown from `sys.dm_os_memory_clerks`
|
||||
- *Database size*: databases size trend from `sys.dm_io_virtual_file_stats`
|
||||
- *Database IO*: databases I/O from `sys.dm_io_virtual_file_stats`
|
||||
- *Database latency*: databases latency from `sys.dm_io_virtual_file_stats`
|
||||
- *Database properties*: databases properties, state and recovery model, from `sys.databases`
|
||||
- *OS Volume*: available, used and total space from `sys.dm_os_volume_stats`
|
||||
- *CPU*: cpu usage from `sys.dm_os_ring_buffers`
|
||||
|
||||
If you are using the original queries all stats have the following tags:
|
||||
|
||||
- `servername`: hostname:instance
|
||||
- `type`: type of stats to easily filter measurements
|
||||
|
||||
### Version 2 (query_version=2): This is Deprecated in 1.16, all future development will be under configuration option database_type
|
||||
|
||||
The new (version 2) metrics provide:
|
||||
|
||||
- *Database IO*: IO stats from `sys.dm_io_virtual_file_stats`.
|
||||
- *Memory Clerk*: Memory clerk breakdown from `sys.dm_os_memory_clerks`, most clerks have been given a friendly name.
|
||||
- *Performance Counters*: A select list of performance counters from `sys.dm_os_performance_counters`. Some of the important metrics included:
|
||||
- *Activity*: Transactions/sec/database, Batch requests/sec, blocked processes, + more
|
||||
- *Availability Groups*: Bytes sent to replica, Bytes received from replica, Log bytes received, Log send queue, transaction delay, + more
|
||||
- *Log activity*: Log bytes flushed/sec, Log flushes/sec, Log Flush Wait Time
|
||||
- *Memory*: PLE, Page reads/sec, Page writes/sec, + more
|
||||
- *TempDB*: Free space, Version store usage, Active temp tables, temp table creation rate, + more
|
||||
- *Resource Governor*: CPU Usage, Requests/sec, Queued Requests, and Blocked tasks per workload group + more
|
||||
- *Server properties*: Number of databases in all possible states (online, offline, suspect, etc.), cpu count, total physical memory, available physical memory, SQL Server service uptime, SQL Server SPID, and SQL Server version. In the case of Azure SQL relevant properties such as Tier, #Vcores, Memory etc.
|
||||
- *Wait stats*: Wait time in ms, number of waiting tasks, resource wait time, signal wait time, max wait time in ms, wait type, and wait category. The waits are categorized using the same categories used in Query Store.
|
||||
- *Schedulers* - This captures `sys.dm_os_schedulers`.
|
||||
- *SqlRequests* - This captures a snapshot of `sys.dm_exec_requests` and `sys.dm_exec_sessions` that gives you running requests as well as wait types and
|
||||
blocking sessions. Telegraf's monitoring request is omitted unless it is a heading blocker. Also includes sleeping sessions with open transactions.
|
||||
- *VolumeSpace* - uses `sys.dm_os_volume_stats` to get total, used and occupied space on every disk that contains a data or log file. (Note that even if enabled it won't get any data from Azure SQL Database or SQL Managed Instance). It is pointless to run this with high frequency (ie: every 10s), but it won't cause any problem.
|
||||
- *Cpu* - uses the buffer ring (`sys.dm_os_ring_buffers`) to get CPU data, the table is updated once per minute. (Note that even if enabled it won't get any data from Azure SQL Database or SQL Managed Instance).
|
||||
|
||||
In order to allow tracking on a per statement basis this query produces a
|
||||
unique tag for each query. Depending on the database workload, this may
|
||||
result in a high cardinality series. Reference the FAQ for tips on
|
||||
[managing series cardinality][cardinality].
|
||||
|
||||
- *Azure Managed Instances*
|
||||
- Stats from `sys.server_resource_stats`
|
||||
- Resource governance stats from `sys.dm_instance_resource_governance`
|
||||
- *Azure SQL Database* in addition to other stats
|
||||
- Stats from `sys.dm_db_wait_stats`
|
||||
- Resource governance stats from `sys.dm_user_db_resource_governance`
|
||||
- Stats from `sys.dm_db_resource_stats`
|
||||
|
||||
### database_type = "AzureSQLDB"
|
||||
|
||||
These are metrics for Azure SQL Database (single database) and are very
|
||||
similar to version 2 but split out for maintenance reasons, better ability
|
||||
to test,differences in DMVs:
|
||||
|
||||
- *AzureSQLDBDatabaseIO*: IO stats from `sys.dm_io_virtual_file_stats` including resource governance time, RBPEX, IO for Hyperscale.
|
||||
- *AzureSQLDBMemoryClerks*: Memory clerk breakdown from `sys.dm_os_memory_clerks`.
|
||||
- *AzureSQLDBResourceGovernance*: Relevant properties indicatign resource limits from `sys.dm_user_db_resource_governance`
|
||||
- *AzureSQLDBPerformanceCounters*: A select list of performance counters from `sys.dm_os_performance_counters` including cloud specific counters for SQL Hyperscale.
|
||||
- *AzureSQLDBServerProperties*: Relevant Azure SQL relevant properties from such as Tier, #Vcores, Memory etc, storage, etc.
|
||||
- *AzureSQLDBWaitstats*: Wait time in ms from `sys.dm_db_wait_stats`, number of waiting tasks, resource wait time, signal wait time, max wait time in ms, wait type, and wait category. The waits are categorized using the same categories used in Query Store. These waits are collected only as of the end of the a statement. and for a specific database only.
|
||||
- *AzureSQLOsWaitstats*: Wait time in ms from `sys.dm_os_wait_stats`, number of waiting tasks, resource wait time, signal wait time, max wait time in ms, wait type, and wait category. The waits are categorized using the same categories used in Query Store. These waits are collected as they occur and instance wide
|
||||
- *AzureSQLDBRequests*: Requests which are blocked or have a wait type from `sys.dm_exec_sessions` and `sys.dm_exec_requests`. Telegraf's monitoring request is omitted unless it is a heading blocker
|
||||
- *AzureSQLDBSchedulers* - This captures `sys.dm_os_schedulers` snapshots.
|
||||
|
||||
### database_type = "AzureSQLManagedInstance"
|
||||
|
||||
These are metrics for Azure SQL Managed instance, are very similar to version
|
||||
2 but split out for maintenance reasons, better ability to test, differences
|
||||
in DMVs:
|
||||
|
||||
- *AzureSQLMIDatabaseIO*: IO stats from `sys.dm_io_virtual_file_stats` including resource governance time, RBPEX, IO for Hyperscale.
|
||||
- *AzureSQLMIMemoryClerks*: Memory clerk breakdown from `sys.dm_os_memory_clerks`.
|
||||
- *AzureSQLMIResourceGovernance*: Relevant properties indicatign resource limits from `sys.dm_instance_resource_governance`
|
||||
- *AzureSQLMIPerformanceCounters*: A select list of performance counters from `sys.dm_os_performance_counters` including cloud specific counters for SQL Hyperscale.
|
||||
- *AzureSQLMIServerProperties*: Relevant Azure SQL relevant properties such as Tier, #Vcores, Memory etc, storage, etc.
|
||||
- *AzureSQLMIOsWaitstats*: Wait time in ms from `sys.dm_os_wait_stats`, number of waiting tasks, resource wait time, signal wait time, max wait time in ms, wait type, and wait category. The waits are categorized using the same categories used in Query Store. These waits are collected as they occur and instance wide
|
||||
- *AzureSQLMIRequests*: Requests which are blocked or have a wait type from `sys.dm_exec_sessions` and `sys.dm_exec_requests`. Telegraf's monitoring request is omitted unless it is a heading blocker
|
||||
- *AzureSQLMISchedulers*: This captures `sys.dm_os_schedulers` snapshots.
|
||||
|
||||
### database_type = "AzureSQLPool"
|
||||
|
||||
These are metrics for Azure SQL to monitor resources usage at Elastic Pool
|
||||
level. These metrics require additional permissions to be collected, please
|
||||
ensure to check additional setup section in this documentation.
|
||||
|
||||
- *AzureSQLPoolResourceStats*: Returns resource usage statistics for the current elastic pool in a SQL Database server. Queried from `sys.dm_resource_governor_resource_pools_history_ex`.
|
||||
- *AzureSQLPoolResourceGovernance*: Returns actual configuration and capacity settings used by resource governance mechanisms in the current elastic pool. Queried from `sys.dm_user_db_resource_governance`.
|
||||
- *AzureSQLPoolDatabaseIO*: Returns I/O statistics for data and log files for each database in the pool. Queried from `sys.dm_io_virtual_file_stats`.
|
||||
- *AzureSQLPoolOsWaitStats*: Returns information about all the waits encountered by threads that executed. Queried from `sys.dm_os_wait_stats`.
|
||||
- *AzureSQLPoolMemoryClerks*: Memory clerk breakdown from `sys.dm_os_memory_clerks`.
|
||||
- *AzureSQLPoolPerformanceCounters*: A selected list of performance counters from `sys.dm_os_performance_counters`. Note: Performance counters where the cntr_type column value is 537003264 are already returned with a percentage format between 0 and 100. For other counters, please check [sys.dm_os_performance_counters](https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-os-performance-counters-transact-sql?view=azuresqldb-current) documentation.
|
||||
- *AzureSQLPoolSchedulers*: This captures `sys.dm_os_schedulers` snapshots.
|
||||
|
||||
### database_type = "SQLServer"
|
||||
|
||||
- *SQLServerDatabaseIO*: IO stats from `sys.dm_io_virtual_file_stats`
|
||||
- *SQLServerMemoryClerks*: Memory clerk breakdown from `sys.dm_os_memory_clerks`, most clerks have been given a friendly name.
|
||||
- *SQLServerPerformanceCounters*: A select list of performance counters from `sys.dm_os_performance_counters`. Some of the important metrics included:
|
||||
- *Activity*: Transactions/sec/database, Batch requests/sec, blocked processes, + more
|
||||
- *Availability Groups*: Bytes sent to replica, Bytes received from replica, Log bytes received, Log send queue, transaction delay, + more
|
||||
- *Log activity*: Log bytes flushed/sec, Log flushes/sec, Log Flush Wait Time
|
||||
- *Memory*: PLE, Page reads/sec, Page writes/sec, + more
|
||||
- *TempDB*: Free space, Version store usage, Active temp tables, temp table creation rate, + more
|
||||
- *Resource Governor*: CPU Usage, Requests/sec, Queued Requests, and Blocked tasks per workload group + more
|
||||
- *SQLServerProperties*: Number of databases in all possible states (online, offline, suspect, etc.), cpu count, total physical memory, available physical memory, SQL Server service uptime, SQL Server SPID and SQL Server version. In the case of Azure SQL relevant properties such as Tier, #Vcores, Memory etc.
|
||||
- *SQLServerWaitStatsCategorized*: Wait time in ms, number of waiting tasks, resource wait time, signal wait time, max wait time in ms, wait type, and wait category. The waits are categorized using the same categories used in Query Store.
|
||||
- *SQLServerSchedulers*: This captures `sys.dm_os_schedulers`.
|
||||
- *SQLServerRequests*: This captures a snapshot of `sys.dm_exec_requests` and `sys.dm_exec_sessions` that gives you running requests as well as wait types and
|
||||
blocking sessions.
|
||||
- *SQLServerVolumeSpace*: Uses `sys.dm_os_volume_stats` to get total, used and occupied space on every disk that contains a data or log file. (Note that even if enabled it won't get any data from Azure SQL Database or SQL Managed Instance). It is pointless to run this with high frequency (ie: every 10s), but it won't cause any problem.
|
||||
- SQLServerCpu: Uses the buffer ring (`sys.dm_os_ring_buffers`) to get CPU data, the table is updated once per minute. (Note that even if enabled it won't get any data from Azure SQL Database or SQL Managed Instance).
|
||||
- SQLServerAvailabilityReplicaStates: Collects availability replica state information from `sys.dm_hadr_availability_replica_states` for a High Availability / Disaster Recovery (HADR) setup
|
||||
- SQLServerDatabaseReplicaStates: Collects database replica state information from `sys.dm_hadr_database_replica_states` for a High Availability / Disaster Recovery (HADR) setup
|
||||
- SQLServerRecentBackups: Collects latest full, differential and transaction log backup date and size from `msdb.dbo.backupset`
|
||||
- SQLServerPersistentVersionStore: Collects persistent version store information from `sys.dm_tran_persistent_version_store_stats` for databases with Accelerated Database Recovery enabled
|
||||
|
||||
### Output Measures
|
||||
|
||||
The guiding principal is that all data collected from the same primary DMV ends
|
||||
up in the same measure irrespective of database_type.
|
||||
|
||||
- `sqlserver_database_io` - Used by AzureSQLDBDatabaseIO, AzureSQLMIDatabaseIO, SQLServerDatabaseIO, DatabaseIO given the data is from `sys.dm_io_virtual_file_stats`
|
||||
- `sqlserver_waitstats` - Used by WaitStatsCategorized,AzureSQLDBOsWaitstats,AzureSQLMIOsWaitstats
|
||||
- `sqlserver_server_properties` - Used by SQLServerProperties, AzureSQLDBServerProperties , AzureSQLMIServerProperties,ServerProperties
|
||||
- `sqlserver_memory_clerks` - Used by SQLServerMemoryClerks, AzureSQLDBMemoryClerks, AzureSQLMIMemoryClerks,MemoryClerk
|
||||
- `sqlserver_performance` - Used by SQLServerPerformanceCounters, AzureSQLDBPerformanceCounters, AzureSQLMIPerformanceCounters,PerformanceCounters
|
||||
- `sys.dm_os_schedulers` - Used by SQLServerSchedulers,AzureSQLDBServerSchedulers, AzureSQLMIServerSchedulers
|
||||
|
||||
The following Performance counter metrics can be used directly, with no delta
|
||||
calculations:
|
||||
|
||||
- SQLServer:Buffer Manager\Buffer cache hit ratio
|
||||
- SQLServer:Buffer Manager\Page life expectancy
|
||||
- SQLServer:Buffer Node\Page life expectancy
|
||||
- SQLServer:Database Replica\Log Apply Pending Queue
|
||||
- SQLServer:Database Replica\Log Apply Ready Queue
|
||||
- SQLServer:Database Replica\Log Send Queue
|
||||
- SQLServer:Database Replica\Recovery Queue
|
||||
- SQLServer:Databases\Data File(s) Size (KB)
|
||||
- SQLServer:Databases\Log File(s) Size (KB)
|
||||
- SQLServer:Databases\Log File(s) Used Size (KB)
|
||||
- SQLServer:Databases\XTP Memory Used (KB)
|
||||
- SQLServer:General Statistics\Active Temp Tables
|
||||
- SQLServer:General Statistics\Processes blocked
|
||||
- SQLServer:General Statistics\Temp Tables For Destruction
|
||||
- SQLServer:General Statistics\User Connections
|
||||
- SQLServer:Memory Broker Clerks\Memory broker clerk size
|
||||
- SQLServer:Memory Manager\Memory Grants Pending
|
||||
- SQLServer:Memory Manager\Target Server Memory (KB)
|
||||
- SQLServer:Memory Manager\Total Server Memory (KB)
|
||||
- SQLServer:Resource Pool Stats\Active memory grant amount (KB)
|
||||
- SQLServer:Resource Pool Stats\Disk Read Bytes/sec
|
||||
- SQLServer:Resource Pool Stats\Disk Read IO Throttled/sec
|
||||
- SQLServer:Resource Pool Stats\Disk Read IO/sec
|
||||
- SQLServer:Resource Pool Stats\Disk Write Bytes/sec
|
||||
- SQLServer:Resource Pool Stats\Disk Write IO Throttled/sec
|
||||
- SQLServer:Resource Pool Stats\Disk Write IO/sec
|
||||
- SQLServer:Resource Pool Stats\Used memory (KB)
|
||||
- SQLServer:Transactions\Free Space in tempdb (KB)
|
||||
- SQLServer:Transactions\Version Store Size (KB)
|
||||
- SQLServer:User Settable\Query
|
||||
- SQLServer:Workload Group Stats\Blocked tasks
|
||||
- SQLServer:Workload Group Stats\CPU usage %
|
||||
- SQLServer:Workload Group Stats\Queued requests
|
||||
- SQLServer:Workload Group Stats\Requests completed/sec
|
||||
|
||||
Version 2 queries have the following tags:
|
||||
|
||||
- `sql_instance`: Physical host and instance name (hostname:instance)
|
||||
- `database_name`: For Azure SQLDB, database_name denotes the name of the Azure SQL Database as server name is a logical construct.
|
||||
|
||||
### Health Metric
|
||||
|
||||
All collection versions (version 1, version 2, and database_type) support an
|
||||
optional plugin health metric called `sqlserver_telegraf_health`. This metric
|
||||
tracks if connections to SQL Server are succeeding or failing. Users can
|
||||
leverage this metric to detect if their SQL Server monitoring is not working
|
||||
as intended.
|
||||
|
||||
In the configuration file, toggling `health_metric` to `true` will enable
|
||||
collection of this metric. By default, this value is set to `false` and
|
||||
the metric is not collected. The health metric emits one record for each
|
||||
connection specified by `servers` in the configuration file.
|
||||
|
||||
The health metric emits the following tags:
|
||||
|
||||
- `sql_instance` - Name of the server specified in the connection string. This value is emitted as-is in the connection string. If the server could not be parsed from the connection string, a constant placeholder value is emitted
|
||||
- `database_name` - Name of the database or (initial catalog) specified in the connection string. This value is emitted as-is in the connection string. If the database could not be parsed from the connection string, a constant placeholder value is emitted
|
||||
|
||||
The health metric emits the following fields:
|
||||
|
||||
- `attempted_queries` - Number of queries that were attempted for this connection
|
||||
- `successful_queries` - Number of queries that completed successfully for this connection
|
||||
- `database_type` - Type of database as specified by `database_type`. If `database_type` is empty, the `QueryVersion` and `AzureDB` fields are concatenated instead
|
||||
|
||||
If `attempted_queries` and `successful_queries` are not equal for
|
||||
a given connection, some metrics were not successfully gathered for
|
||||
that connection. If `successful_queries` is 0, no metrics were successfully
|
||||
gathered.
|
||||
|
||||
[cardinality]: /docs/FAQ.md#user-content-q-how-can-i-manage-series-cardinality
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
sqlserver_cpu_other_process_cpu{host="servername",measurement_db_type="SQLServer",sql_instance="SERVERNAME:INST"} 9
|
||||
sqlserver_performance{counter="Log File(s) Size (KB)",counter_type="65792",host="servername",instance="instance_name",measurement_db_type="SQLServer",object="MSSQL$INSTANCE_NAME:Databases",sql_instance="SERVERNAME:INSTANCE_NAME"} 1.048568e+06
|
||||
```
|
515
plugins/inputs/sqlserver/azurearcsqlmiqueries.go
Normal file
515
plugins/inputs/sqlserver/azurearcsqlmiqueries.go
Normal file
|
@ -0,0 +1,515 @@
|
|||
//nolint:lll // conditionally long lines allowed
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
_ "github.com/microsoft/go-mssqldb" // go-mssqldb initialization
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------ Azure Arc Managed Instance ------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
const sqlAzureArcMIProperties = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT TOP 1
|
||||
'sqlserver_server_properties' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,(SELECT [cpu_count] FROM sys.dm_os_sys_info) AS [cpu_count]
|
||||
,(SELECT [physical_memory_kb] FROM sys.dm_os_sys_info) AS [server_memory]
|
||||
,(SELECT [host_sku] FROM sys.dm_os_host_info) AS [sku]
|
||||
,SERVERPROPERTY('EngineEdition') AS [engine_edition]
|
||||
,(SELECT [container_type_desc] FROM sys.dm_os_sys_info) AS [hardware_type]
|
||||
,(SELECT DATEDIFF(MINUTE,[sqlserver_start_time],GETDATE()) FROM sys.dm_os_sys_info) as [uptime]
|
||||
,SERVERPROPERTY('ProductVersion') AS [sql_version]
|
||||
,LEFT(@@VERSION,CHARINDEX(' - ',@@VERSION)) AS [sql_version_desc]
|
||||
,(SELECT SUM( CASE WHEN [state] = 0 THEN 1 ELSE 0 END ) FROM sys.databases) AS [db_online]
|
||||
,(SELECT SUM( CASE WHEN [state] = 1 THEN 1 ELSE 0 END ) FROM sys.databases) AS [db_restoring]
|
||||
,(SELECT SUM( CASE WHEN [state] = 2 THEN 1 ELSE 0 END ) FROM sys.databases) AS [db_recovering]
|
||||
,(SELECT SUM( CASE WHEN [state] = 3 THEN 1 ELSE 0 END ) FROM sys.databases) AS [db_recoveryPending]
|
||||
,(SELECT SUM( CASE WHEN [state] = 4 THEN 1 ELSE 0 END ) FROM sys.databases) AS [db_suspect]
|
||||
,(SELECT SUM( CASE WHEN [state] IN (6,10) THEN 1 ELSE 0 END) FROM sys.databases) AS [db_offline]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
`
|
||||
|
||||
const sqlAzureArcMIDatabaseIO = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_database_io' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME(mf.[database_id]) AS [database_name]
|
||||
,COALESCE(mf.[physical_name],'RBPEX') AS [physical_filename] --RPBEX = Resilient Buffer Pool Extension
|
||||
,COALESCE(mf.[name],'RBPEX') AS [logical_filename] --RPBEX = Resilient Buffer Pool Extension
|
||||
,mf.[type_desc] AS [file_type]
|
||||
,vfs.[io_stall_read_ms] AS [read_latency_ms]
|
||||
,vfs.[num_of_reads] AS [reads]
|
||||
,vfs.[num_of_bytes_read] AS [read_bytes]
|
||||
,vfs.[io_stall_write_ms] AS [write_latency_ms]
|
||||
,vfs.[num_of_writes] AS [writes]
|
||||
,vfs.[num_of_bytes_written] AS [write_bytes]
|
||||
,vfs.io_stall_queued_read_ms AS [rg_read_stall_ms]
|
||||
,vfs.io_stall_queued_write_ms AS [rg_write_stall_ms]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
|
||||
LEFT OUTER JOIN sys.master_files AS mf WITH (NOLOCK)
|
||||
ON vfs.[database_id] = mf.[database_id]
|
||||
AND vfs.[file_id] = mf.[file_id]
|
||||
WHERE
|
||||
vfs.[database_id] < 32760
|
||||
`
|
||||
|
||||
const sqlAzureArcMIMemoryClerks = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_memory_clerks' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,mc.[type] AS [clerk_type]
|
||||
,SUM(mc.[pages_kb]) AS [size_kb]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.[dm_os_memory_clerks] AS mc WITH (NOLOCK)
|
||||
GROUP BY
|
||||
mc.[type]
|
||||
HAVING
|
||||
SUM(mc.[pages_kb]) >= 1024
|
||||
OPTION(RECOMPILE);
|
||||
`
|
||||
|
||||
const sqlAzureArcMIOsWaitStats = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_waitstats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,ws.[wait_type]
|
||||
,[wait_time_ms]
|
||||
,[wait_time_ms] - [signal_wait_time_ms] AS [resource_wait_ms]
|
||||
,[signal_wait_time_ms]
|
||||
,[max_wait_time_ms]
|
||||
,[waiting_tasks_count]
|
||||
,CASE
|
||||
WHEN ws.[wait_type] LIKE 'SOS_SCHEDULER_YIELD' then 'CPU'
|
||||
WHEN ws.[wait_type] = 'THREADPOOL' THEN 'Worker Thread'
|
||||
WHEN ws.[wait_type] LIKE 'LCK[_]%' THEN 'Lock'
|
||||
WHEN ws.[wait_type] LIKE 'LATCH[_]%' THEN 'Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGELATCH[_]%' THEN 'Buffer Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGEIOLATCH[_]%' THEN 'Buffer IO'
|
||||
WHEN ws.[wait_type] LIKE 'RESOURCE_SEMAPHORE_QUERY_COMPILE%' THEN 'Compilation'
|
||||
WHEN ws.[wait_type] LIKE 'CLR[_]%' or ws.[wait_type] like 'SQLCLR%' THEN 'SQL CLR'
|
||||
WHEN ws.[wait_type] LIKE 'DBMIRROR_%' THEN 'Mirroring'
|
||||
WHEN ws.[wait_type] LIKE 'DTC[_]%' or ws.[wait_type] LIKE 'DTCNEW%' or ws.[wait_type] LIKE 'TRAN_%'
|
||||
or ws.[wait_type] LIKE 'XACT%' or ws.[wait_type] like 'MSQL_XACT%' THEN 'Transaction'
|
||||
WHEN ws.[wait_type] LIKE 'SLEEP[_]%'
|
||||
or ws.[wait_type] IN (
|
||||
'LAZYWRITER_SLEEP', 'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
'SQLTRACE_WAIT_ENTRIES', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT',
|
||||
'REQUEST_FOR_DEADLOCK_SEARCH', 'LOGMGR_QUEUE', 'ONDEMAND_TASK_QUEUE',
|
||||
'CHECKPOINT_QUEUE', 'XE_TIMER_EVENT') THEN 'Idle'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_IO_COMPLETION','BACKUPIO','CHKPT','WRITE_COMPLETION',
|
||||
'IO_QUEUE_LIMIT', 'IO_RETRY') THEN 'Other Disk IO'
|
||||
WHEN ws.[wait_type] LIKE 'PREEMPTIVE_%' THEN 'Preemptive'
|
||||
WHEN ws.[wait_type] LIKE 'BROKER[_]%' THEN 'Service Broker'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'WRITELOG','LOGBUFFER','LOGMGR_RESERVE_APPEND',
|
||||
'LOGMGR_FLUSH', 'LOGMGR_PMM_LOG') THEN 'Tran Log IO'
|
||||
WHEN ws.[wait_type] LIKE 'LOG_RATE%' then 'Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'HADR_THROTTLE[_]%'
|
||||
or ws.[wait_type] = 'THROTTLE_LOG_RATE_LOG_STORAGE' THEN 'HADR Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO_RG%' or ws.[wait_type] like 'WAIT_RBIO_RG%' then 'VLDB Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO[_]%' or ws.[wait_type] like 'WAIT_RBIO[_]%' then 'VLDB RBIO'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_NETWORK_IO','EXTERNAL_SCRIPT_NETWORK_IOF',
|
||||
'NET_WAITFOR_PACKET','PROXY_NETWORK_IO') THEN 'Network IO'
|
||||
WHEN ws.[wait_type] IN ( 'CXPACKET', 'CXCONSUMER')
|
||||
or ws.[wait_type] like 'HT%' or ws.[wait_type] like 'BMP%'
|
||||
or ws.[wait_type] like 'BP%' THEN 'Parallelism'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'CMEMTHREAD','CMEMPARTITIONED','EE_PMOLOCK','EXCHANGE',
|
||||
'RESOURCE_SEMAPHORE','MEMORY_ALLOCATION_EXT',
|
||||
'RESERVED_MEMORY_ALLOCATION_EXT', 'MEMORY_GRANT_UPDATE') THEN 'Memory'
|
||||
WHEN ws.[wait_type] IN ('WAITFOR','WAIT_FOR_RESULTS') THEN 'User Wait'
|
||||
WHEN ws.[wait_type] LIKE 'HADR[_]%' or ws.[wait_type] LIKE 'PWAIT_HADR%'
|
||||
or ws.[wait_type] LIKE 'REPLICA[_]%' or ws.[wait_type] LIKE 'REPL_%'
|
||||
or ws.[wait_type] LIKE 'SE_REPL[_]%'
|
||||
or ws.[wait_type] LIKE 'FCB_REPLICA%' THEN 'Replication'
|
||||
WHEN ws.[wait_type] LIKE 'SQLTRACE[_]%'
|
||||
or ws.[wait_type] IN (
|
||||
'TRACEWRITE', 'SQLTRACE_LOCK', 'SQLTRACE_FILE_BUFFER', 'SQLTRACE_FILE_WRITE_IO_COMPLETION',
|
||||
'SQLTRACE_FILE_READ_IO_COMPLETION', 'SQLTRACE_PENDING_BUFFER_WRITERS', 'SQLTRACE_SHUTDOWN',
|
||||
'QUERY_TRACEOUT', 'TRACE_EVTNOTIF') THEN 'Tracing'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'FT_RESTART_CRAWL', 'FULLTEXT GATHERER', 'MSSEARCH', 'FT_METADATA_MUTEX',
|
||||
'FT_IFTSHC_MUTEX', 'FT_IFTSISM_MUTEX', 'FT_IFTS_RWLOCK', 'FT_COMPROWSET_RWLOCK',
|
||||
'FT_MASTER_MERGE', 'FT_PROPERTYLIST_CACHE', 'FT_MASTER_MERGE_COORDINATOR',
|
||||
'PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC') THEN 'Full Text Search'
|
||||
ELSE 'Other'
|
||||
END as [wait_category]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_os_wait_stats AS ws WITH (NOLOCK)
|
||||
WHERE
|
||||
ws.[wait_type] NOT IN (
|
||||
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
|
||||
N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
|
||||
N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
|
||||
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_QUEUE',
|
||||
N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
|
||||
N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
|
||||
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT',
|
||||
N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
|
||||
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE',
|
||||
N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
|
||||
N'PARALLEL_REDO_WORKER_WAIT_WORK',
|
||||
N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
|
||||
N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
|
||||
N'PREEMPTIVE_OS_PIPEOPS','PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
|
||||
N'PREEMPTIVE_OS_DEVICEOPS',
|
||||
N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
|
||||
N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
|
||||
N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
|
||||
N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
|
||||
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
|
||||
N'QDS_ASYNC_QUEUE',
|
||||
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
|
||||
N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
|
||||
N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
|
||||
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
|
||||
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
|
||||
N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
N'SQLTRACE_WAIT_ENTRIES',
|
||||
N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
|
||||
N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE',
|
||||
N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
|
||||
N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT',
|
||||
N'SOS_WORK_DISPATCHER','RESERVED_MEMORY_ALLOCATION_EXT','SQLTRACE_WAIT_ENTRIES',
|
||||
N'RBIO_COMM_RETRY')
|
||||
AND [waiting_tasks_count] > 10
|
||||
AND [wait_time_ms] > 100;
|
||||
`
|
||||
|
||||
const sqlAzureArcMIPerformanceCounters = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
DECLARE @PCounters TABLE
|
||||
(
|
||||
[object_name] nvarchar(128),
|
||||
[counter_name] nvarchar(128),
|
||||
[instance_name] nvarchar(128),
|
||||
[cntr_value] bigint,
|
||||
[cntr_type] INT ,
|
||||
Primary Key([object_name],[counter_name],[instance_name])
|
||||
);
|
||||
|
||||
WITH PerfCounters AS (
|
||||
SELECT DISTINCT
|
||||
RTrim(spi.[object_name]) [object_name]
|
||||
,RTrim(spi.[counter_name]) [counter_name]
|
||||
,CASE WHEN (
|
||||
RTRIM(spi.[object_name]) LIKE '%:Databases'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Database Replica'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Catalog Metadata'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Query Store'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Columnstore'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Advanced Analytics')
|
||||
AND TRY_CONVERT([uniqueidentifier], spi.[instance_name]) IS NOT NULL -- for cloud only
|
||||
THEN ISNULL(d.[name],RTRIM(spi.instance_name)) -- Elastic Pools counters exist for all databases but sys.databases only has current DB value
|
||||
WHEN
|
||||
RTRIM([object_name]) LIKE '%:Availability Replica'
|
||||
AND TRY_CONVERT([uniqueidentifier], spi.[instance_name]) IS NOT NULL -- for cloud only
|
||||
THEN ISNULL(d.[name],RTRIM(spi.[instance_name])) + RTRIM(SUBSTRING(spi.[instance_name], 37, LEN(spi.[instance_name])))
|
||||
ELSE RTRIM(spi.instance_name)
|
||||
END AS [instance_name]
|
||||
,CAST(spi.[cntr_value] AS BIGINT) AS [cntr_value]
|
||||
,spi.[cntr_type]
|
||||
FROM sys.dm_os_performance_counters AS spi
|
||||
LEFT JOIN sys.databases AS d
|
||||
ON LEFT(spi.[instance_name], 36) -- some instance_name values have an additional identifier appended after the GUID
|
||||
= CASE
|
||||
/*in SQL DB standalone, physical_database_name for master is the GUID of the user database*/
|
||||
WHEN d.[name] = 'master' AND TRY_CONVERT([uniqueidentifier], d.[physical_database_name]) IS NOT NULL
|
||||
THEN d.[name]
|
||||
ELSE d.[physical_database_name]
|
||||
END
|
||||
WHERE
|
||||
counter_name IN (
|
||||
'SQL Compilations/sec'
|
||||
,'SQL Re-Compilations/sec'
|
||||
,'User Connections'
|
||||
,'Batch Requests/sec'
|
||||
,'Logouts/sec'
|
||||
,'Logins/sec'
|
||||
,'Processes blocked'
|
||||
,'Latch Waits/sec'
|
||||
,'Full Scans/sec'
|
||||
,'Index Searches/sec'
|
||||
,'Page Splits/sec'
|
||||
,'Page lookups/sec'
|
||||
,'Page reads/sec'
|
||||
,'Page writes/sec'
|
||||
,'Readahead pages/sec'
|
||||
,'Lazy writes/sec'
|
||||
,'Checkpoint pages/sec'
|
||||
,'Table Lock Escalations/sec'
|
||||
,'Page life expectancy'
|
||||
,'Log File(s) Size (KB)'
|
||||
,'Log File(s) Used Size (KB)'
|
||||
,'Data File(s) Size (KB)'
|
||||
,'Transactions/sec'
|
||||
,'Write Transactions/sec'
|
||||
,'Active Transactions'
|
||||
,'Log Growths'
|
||||
,'Active Temp Tables'
|
||||
,'Logical Connections'
|
||||
,'Temp Tables Creation Rate'
|
||||
,'Temp Tables For Destruction'
|
||||
,'Free Space in tempdb (KB)'
|
||||
,'Version Store Size (KB)'
|
||||
,'Memory Grants Pending'
|
||||
,'Memory Grants Outstanding'
|
||||
,'Free list stalls/sec'
|
||||
,'Buffer cache hit ratio'
|
||||
,'Buffer cache hit ratio base'
|
||||
,'Backup/Restore Throughput/sec'
|
||||
,'Total Server Memory (KB)'
|
||||
,'Target Server Memory (KB)'
|
||||
,'Log Flushes/sec'
|
||||
,'Log Flush Wait Time'
|
||||
,'Memory broker clerk size'
|
||||
,'Log Bytes Flushed/sec'
|
||||
,'Bytes Sent to Replica/sec'
|
||||
,'Log Send Queue'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Replica/sec'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Transport/sec'
|
||||
,'Bytes Received from Replica/sec'
|
||||
,'Receives from Replica/sec'
|
||||
,'Flow Control Time (ms/sec)'
|
||||
,'Flow Control/sec'
|
||||
,'Resent Messages/sec'
|
||||
,'Redone Bytes/sec'
|
||||
,'XTP Memory Used (KB)'
|
||||
,'Transaction Delay'
|
||||
,'Log Bytes Received/sec'
|
||||
,'Log Apply Pending Queue'
|
||||
,'Redone Bytes/sec'
|
||||
,'Recovery Queue'
|
||||
,'Log Apply Ready Queue'
|
||||
,'CPU usage %'
|
||||
,'CPU usage % base'
|
||||
,'Queued requests'
|
||||
,'Requests completed/sec'
|
||||
,'Blocked tasks'
|
||||
,'Active memory grant amount (KB)'
|
||||
,'Disk Read Bytes/sec'
|
||||
,'Disk Read IO Throttled/sec'
|
||||
,'Disk Read IO/sec'
|
||||
,'Disk Write Bytes/sec'
|
||||
,'Disk Write IO Throttled/sec'
|
||||
,'Disk Write IO/sec'
|
||||
,'Used memory (KB)'
|
||||
,'Forwarded Records/sec'
|
||||
,'Background Writer pages/sec'
|
||||
,'Percent Log Used'
|
||||
,'Log Send Queue KB'
|
||||
,'Redo Queue KB'
|
||||
,'Mirrored Write Transactions/sec'
|
||||
,'Group Commit Time'
|
||||
,'Group Commits/Sec'
|
||||
,'Workfiles Created/sec'
|
||||
,'Worktables Created/sec'
|
||||
,'Distributed Query'
|
||||
,'DTC calls'
|
||||
,'Query Store CPU usage'
|
||||
) OR (
|
||||
spi.[object_name] LIKE '%User Settable%'
|
||||
OR spi.[object_name] LIKE '%SQL Errors%'
|
||||
OR spi.[object_name] LIKE '%Batch Resp Statistics%'
|
||||
) OR (
|
||||
spi.[instance_name] IN ('_Total')
|
||||
AND spi.[counter_name] IN (
|
||||
'Lock Timeouts/sec'
|
||||
,'Lock Timeouts (timeout > 0)/sec'
|
||||
,'Number of Deadlocks/sec'
|
||||
,'Lock Waits/sec'
|
||||
,'Latch Waits/sec'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
INSERT INTO @PCounters select * from PerfCounters
|
||||
|
||||
SELECT
|
||||
'sqlserver_performance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,pc.[object_name] AS [object]
|
||||
,pc.[counter_name] AS [counter]
|
||||
,CASE pc.[instance_name]
|
||||
WHEN '_Total' THEN 'Total'
|
||||
ELSE ISNULL(pc.[instance_name],'')
|
||||
END AS [instance]
|
||||
,CAST(CASE WHEN pc.[cntr_type] = 537003264 AND pc1.[cntr_value] > 0 THEN (pc.[cntr_value] * 1.0) / (pc1.[cntr_value] * 1.0) * 100 ELSE pc.[cntr_value] END AS float(10)) AS [value]
|
||||
,cast(pc.[cntr_type] as varchar(25)) as [counter_type]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
from @PCounters pc
|
||||
LEFT OUTER JOIN @PCounters AS pc1
|
||||
ON (
|
||||
pc.[counter_name] = REPLACE(pc1.[counter_name],' base','')
|
||||
OR pc.[counter_name] = REPLACE(pc1.[counter_name],' base',' (ms)')
|
||||
)
|
||||
AND pc.[object_name] = pc1.[object_name]
|
||||
AND pc.[instance_name] = pc1.[instance_name]
|
||||
AND pc1.[counter_name] LIKE '%base'
|
||||
WHERE
|
||||
pc.[counter_name] NOT LIKE '% base'
|
||||
OPTION (RECOMPILE);
|
||||
`
|
||||
|
||||
const sqlAzureArcMIRequests string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
|
||||
|
||||
BEGIN TRY
|
||||
SELECT
|
||||
[measurement],[sql_instance],[database_name],[session_id]
|
||||
,ISNULL([request_id],0) AS [request_id]
|
||||
,[blocking_session_id],[status],[cpu_time_ms]
|
||||
,[total_elapsed_time_ms],[logical_reads],[writes]
|
||||
,[command],[wait_time_ms],[wait_type]
|
||||
,[wait_resource],[program_name]
|
||||
,[host_name],[nt_user_name],[login_name]
|
||||
,[transaction_isolation_level],[granted_query_memory_pages],[percent_complete]
|
||||
,[statement_text],[objectid],[stmt_object_name]
|
||||
,[stmt_db_name],[query_hash],[query_plan_hash]
|
||||
,replica_updateability
|
||||
,[session_db_name],[open_transaction]
|
||||
FROM (
|
||||
SELECT
|
||||
'sqlserver_requests' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,s.[session_id]
|
||||
,r.[request_id]
|
||||
,DB_NAME(COALESCE(r.[database_id], s.[database_id])) AS [session_db_name]
|
||||
,COALESCE(r.[status], s.[status]) AS [status]
|
||||
,COALESCE(r.[cpu_time], s.[cpu_time]) AS [cpu_time_ms]
|
||||
,COALESCE(r.[total_elapsed_time], s.[total_elapsed_time]) AS [total_elapsed_time_ms]
|
||||
,COALESCE(r.[logical_reads], s.[logical_reads]) AS [logical_reads]
|
||||
,COALESCE(r.[writes], s.[writes]) AS [writes]
|
||||
,r.[command]
|
||||
,r.[wait_time] AS [wait_time_ms]
|
||||
,r.[wait_type]
|
||||
,r.[wait_resource]
|
||||
,NULLIF(r.[blocking_session_id],0) AS [blocking_session_id]
|
||||
,s.[program_name]
|
||||
,s.[host_name]
|
||||
,s.[nt_user_name]
|
||||
,s.[login_name]
|
||||
,COALESCE(r.[open_transaction_count], s.[open_transaction_count]) AS [open_transaction]
|
||||
,LEFT (CASE COALESCE(r.[transaction_isolation_level], s.[transaction_isolation_level])
|
||||
WHEN 0 THEN '0-Read Committed'
|
||||
WHEN 1 THEN '1-Read Uncommitted (NOLOCK)'
|
||||
WHEN 2 THEN '2-Read Committed'
|
||||
WHEN 3 THEN '3-Repeatable Read'
|
||||
WHEN 4 THEN '4-Serializable'
|
||||
WHEN 5 THEN '5-Snapshot'
|
||||
ELSE CONVERT (varchar(30), r.[transaction_isolation_level]) + '-UNKNOWN'
|
||||
END, 30) AS [transaction_isolation_level]
|
||||
,r.[granted_query_memory] AS [granted_query_memory_pages]
|
||||
,r.[percent_complete]
|
||||
,SUBSTRING(
|
||||
qt.[text],
|
||||
r.[statement_start_offset] / 2 + 1,
|
||||
(CASE WHEN r.[statement_end_offset] = -1
|
||||
THEN DATALENGTH(qt.[text])
|
||||
ELSE r.[statement_end_offset]
|
||||
END - r.[statement_start_offset]) / 2 + 1
|
||||
) AS [statement_text]
|
||||
,qt.[objectid]
|
||||
,QUOTENAME(OBJECT_SCHEMA_NAME(qt.[objectid], qt.[dbid])) + '.' + QUOTENAME(OBJECT_NAME(qt.[objectid], qt.[dbid])) as [stmt_object_name]
|
||||
,DB_NAME(qt.[dbid]) AS [stmt_db_name]
|
||||
,CONVERT(varchar(20),r.[query_hash],1) AS [query_hash]
|
||||
,CONVERT(varchar(20),r.[query_plan_hash],1) AS [query_plan_hash]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
,s.[is_user_process]
|
||||
,[blocking_or_blocked] = COUNT(*) OVER(PARTITION BY ISNULL(NULLIF(r.[blocking_session_id], 0),s.[session_id]))
|
||||
FROM sys.dm_exec_sessions AS s
|
||||
LEFT OUTER JOIN sys.dm_exec_requests AS r
|
||||
ON s.[session_id] = r.[session_id]
|
||||
OUTER APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
|
||||
) AS data
|
||||
WHERE
|
||||
[blocking_or_blocked] > 1 --Always include blocking or blocked sessions/requests
|
||||
OR [open_transaction] >= 1 --Always include sessions with open transactions
|
||||
OR (
|
||||
[request_id] IS NOT NULL --A request must exists
|
||||
AND ( --Always fetch user process (in any state), fetch system process only if active
|
||||
[is_user_process] = 1
|
||||
OR [status] COLLATE Latin1_General_BIN NOT IN ('background', 'sleeping')
|
||||
)
|
||||
AND [session_id] <> @@SPID
|
||||
)
|
||||
OPTION(MAXDOP 1);
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF (ERROR_NUMBER() <> 976) --Avoid possible errors from secondary replica
|
||||
THROW;
|
||||
END CATCH
|
||||
`
|
||||
|
||||
const sqlAzureArcMISchedulers string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 10 BEGIN /*not Azure Arc-enabled Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Arc-enabled Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_schedulers' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,CAST(s.[scheduler_id] AS VARCHAR(4)) AS [scheduler_id]
|
||||
,CAST(s.[cpu_id] AS VARCHAR(4)) AS [cpu_id]
|
||||
,s.[is_online]
|
||||
,s.[is_idle]
|
||||
,s.[preemptive_switches_count]
|
||||
,s.[context_switches_count]
|
||||
,s.[current_tasks_count]
|
||||
,s.[runnable_tasks_count]
|
||||
,s.[current_workers_count]
|
||||
,s.[active_workers_count]
|
||||
,s.[work_queue_count]
|
||||
,s.[pending_disk_io_count]
|
||||
,s.[load_factor]
|
||||
,s.[yield_count]
|
||||
,s.[total_cpu_usage_ms]
|
||||
,s.[total_scheduler_delay_ms]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_os_schedulers AS s
|
||||
`
|
313
plugins/inputs/sqlserver/azurearcsqlmiqueries_test.go
Normal file
313
plugins/inputs/sqlserver/azurearcsqlmiqueries_test.go
Normal file
|
@ -0,0 +1,313 @@
|
|||
package sqlserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_DatabaseIO_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMIDatabaseIO"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_database_io"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "physical_filename"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "logical_filename"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "file_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "writes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_read_stall_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_write_stall_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_ServerProperties_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMIServerProperties"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_server_properties"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_instance"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "cpu_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "server_memory"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sku"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "engine_edition"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "hardware_type"))
|
||||
require.True(t, acc.HasField("sqlserver_server_properties", "uptime")) // Time field.
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_version"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_version_desc"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_online"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_restoring"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_recovering"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_recoveryPending"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_suspect"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "replica_updateability"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_OsWaitStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMIOsWaitstats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_waitstats"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "waiting_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "max_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "signal_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "resource_wait_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_category"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_MemoryClerks_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMIMemoryClerks"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_memory_clerks"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "clerk_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_memory_clerks", "size_kb"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_PerformanceCounters_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMIPerformanceCounters"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_performance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "object"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "instance"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_performance", "value"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter_type"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_Requests_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMIRequests"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_requests"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "database_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "session_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "request_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "status"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "cpu_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "total_elapsed_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "logical_reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "writes"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "command"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "wait_time_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "wait_type"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "wait_resource"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "blocking_session_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "program_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "host_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "nt_user_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "login_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "open_transaction"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "transaction_isolation_level"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "granted_query_memory_pages"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_requests", "percent_complete"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "statement_text"))
|
||||
require.True(t, acc.HasField("sqlserver_requests", "objectid")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_requests", "stmt_object_name")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_requests", "stmt_db_name")) // Can be null.
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "query_hash"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "query_plan_hash"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "session_db_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ArcManaged_Schedulers_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_ARCMI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureArcSQLMISchedulers"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureArcSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_schedulers"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "scheduler_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "cpu_id"))
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_online")) // Bool field.
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_idle")) // Bool field.
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "preemptive_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "context_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "runnable_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "active_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "work_queue_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "pending_disk_io_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "load_factor"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "yield_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_cpu_usage_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_scheduler_delay_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
702
plugins/inputs/sqlserver/azuresqldbqueries.go
Normal file
702
plugins/inputs/sqlserver/azuresqldbqueries.go
Normal file
|
@ -0,0 +1,702 @@
|
|||
//nolint:lll // conditionally long lines allowed
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
_ "github.com/microsoft/go-mssqldb" // go-mssqldb initialization
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------ Azure SQL Database ----------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Only executed if AzureDB flag is set
|
||||
const sqlAzureDBResourceStats string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT TOP(1)
|
||||
'sqlserver_azure_db_resource_stats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,cast([avg_cpu_percent] as float) as [avg_cpu_percent]
|
||||
,cast([avg_data_io_percent] as float) as [avg_data_io_percent]
|
||||
,cast([avg_log_write_percent] as float) as [avg_log_write_percent]
|
||||
,cast([avg_memory_usage_percent] as float) as [avg_memory_usage_percent]
|
||||
,cast([xtp_storage_percent] as float) as [xtp_storage_percent]
|
||||
,cast([max_worker_percent] as float) as [max_worker_percent]
|
||||
,cast([max_session_percent] as float) as [max_session_percent]
|
||||
,[dtu_limit]
|
||||
,cast([avg_login_rate_percent] as float) as [avg_login_rate_percent]
|
||||
,[end_time]
|
||||
,cast([avg_instance_memory_percent] as float) as [avg_instance_memory_percent]
|
||||
,cast([avg_instance_cpu_percent] as float) as [avg_instance_cpu_percent]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM
|
||||
sys.dm_db_resource_stats WITH (NOLOCK)
|
||||
ORDER BY
|
||||
[end_time] DESC;
|
||||
`
|
||||
|
||||
// Resource Governance is only relevant to Azure SQL DB into separate collector
|
||||
// This will only be collected for Azure SQL Database.
|
||||
const sqlAzureDBResourceGovernance string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_db_resource_governance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,[slo_name]
|
||||
,[dtu_limit]
|
||||
,[max_cpu]
|
||||
,[cap_cpu]
|
||||
,[instance_cap_cpu]
|
||||
,[max_db_memory]
|
||||
,[max_db_max_size_in_mb]
|
||||
,[db_file_growth_in_mb]
|
||||
,[log_size_in_mb]
|
||||
,[instance_max_worker_threads]
|
||||
,[primary_group_max_workers]
|
||||
,[instance_max_log_rate]
|
||||
,[primary_min_log_rate]
|
||||
,[primary_max_log_rate]
|
||||
,[primary_group_min_io]
|
||||
,[primary_group_max_io]
|
||||
,[primary_group_min_cpu]
|
||||
,[primary_group_max_cpu]
|
||||
,[primary_pool_max_workers]
|
||||
,[pool_max_io]
|
||||
,[checkpoint_rate_mbps]
|
||||
,[checkpoint_rate_io]
|
||||
,[volume_local_iops]
|
||||
,[volume_managed_xstore_iops]
|
||||
,[volume_external_xstore_iops]
|
||||
,[volume_type_local_iops]
|
||||
,[volume_type_managed_xstore_iops]
|
||||
,[volume_type_external_xstore_iops]
|
||||
,[volume_pfs_iops]
|
||||
,[volume_type_pfs_iops]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM
|
||||
sys.dm_user_db_resource_governance WITH (NOLOCK);
|
||||
`
|
||||
|
||||
// DB level wait stats that are only relevant to Azure SQL DB into separate collector
|
||||
// This will only be collected for Azure SQL Database.
|
||||
const sqlAzureDBWaitStats string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_azuredb_waitstats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,dbws.[wait_type]
|
||||
,dbws.[wait_time_ms]
|
||||
,dbws.[wait_time_ms] - [signal_wait_time_ms] AS [resource_wait_ms]
|
||||
,dbws.[signal_wait_time_ms]
|
||||
,dbws.[max_wait_time_ms]
|
||||
,dbws.[waiting_tasks_count]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM
|
||||
sys.dm_db_wait_stats AS dbws WITH (NOLOCK)
|
||||
WHERE
|
||||
dbws.[wait_type] NOT IN (
|
||||
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
|
||||
N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
|
||||
N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
|
||||
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
|
||||
N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
|
||||
N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
|
||||
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT',
|
||||
N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
|
||||
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE',
|
||||
N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
|
||||
N'PARALLEL_REDO_WORKER_WAIT_WORK',
|
||||
N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
|
||||
N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
|
||||
N'PREEMPTIVE_OS_PIPEOPS','PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
|
||||
N'PREEMPTIVE_OS_DEVICEOPS',
|
||||
N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
|
||||
N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
|
||||
N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
|
||||
N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
|
||||
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
|
||||
N'QDS_ASYNC_QUEUE',
|
||||
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
|
||||
N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
|
||||
N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
|
||||
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
|
||||
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
|
||||
N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
N'SQLTRACE_WAIT_ENTRIES',
|
||||
N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
|
||||
N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE',
|
||||
N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
|
||||
N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT',
|
||||
N'SOS_WORK_DISPATCHER','RESERVED_MEMORY_ALLOCATION_EXT')
|
||||
AND [waiting_tasks_count] > 0
|
||||
AND [wait_time_ms] > 100;
|
||||
`
|
||||
|
||||
const sqlAzureDBDatabaseIO = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_database_io' As [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,vfs.[database_id] /*needed as tempdb is different for each Azure SQL DB as grouping has to be by logical server + db_name + database_id*/
|
||||
,vfs.[file_id]
|
||||
,vfs.[io_stall_read_ms] AS [read_latency_ms]
|
||||
,vfs.[num_of_reads] AS [reads]
|
||||
,vfs.[num_of_bytes_read] AS [read_bytes]
|
||||
,vfs.[io_stall_write_ms] AS [write_latency_ms]
|
||||
,vfs.[num_of_writes] AS [writes]
|
||||
,vfs.[num_of_bytes_written] AS [write_bytes]
|
||||
,vfs.[io_stall_queued_read_ms] AS [rg_read_stall_ms]
|
||||
,vfs.[io_stall_queued_write_ms] AS [rg_write_stall_ms]
|
||||
,CASE
|
||||
WHEN (vfs.[database_id] = 0) THEN 'RBPEX'
|
||||
ELSE b.[logical_filename]
|
||||
END as [logical_filename]
|
||||
,CASE
|
||||
WHEN (vfs.[database_id] = 0) THEN 'RBPEX'
|
||||
ELSE b.[physical_filename]
|
||||
END as [physical_filename]
|
||||
,CASE
|
||||
WHEN vfs.[file_id] = 2 THEN 'LOG'
|
||||
ELSE 'DATA'
|
||||
END AS [file_type]
|
||||
,ISNULL([size],0)/128 AS [current_size_mb]
|
||||
,ISNULL(FILEPROPERTY(b.[logical_filename],'SpaceUsed')/128,0) as [space_used_mb]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM
|
||||
[sys].[dm_io_virtual_file_stats](NULL,NULL) AS vfs
|
||||
-- needed to get Tempdb file names on Azure SQL DB so you can join appropriately. Without this had a bug where join was only on file_id
|
||||
LEFT OUTER join (
|
||||
SELECT
|
||||
DB_ID() as [database_id]
|
||||
,[file_id]
|
||||
,[logical_filename]= [name] COLLATE SQL_Latin1_General_CP1_CI_AS
|
||||
,[physical_filename] = [physical_name] COLLATE SQL_Latin1_General_CP1_CI_AS
|
||||
,[size]
|
||||
FROM sys.database_files
|
||||
WHERE
|
||||
[type] <> 2
|
||||
UNION ALL
|
||||
SELECT
|
||||
2 as [database_id]
|
||||
,[file_id]
|
||||
,[logical_filename] = [name]
|
||||
,[physical_filename] = [physical_name]
|
||||
,[size]
|
||||
FROM tempdb.sys.database_files
|
||||
) b
|
||||
ON
|
||||
b.[database_id] = vfs.[database_id]
|
||||
AND b.[file_id] = vfs.[file_id]
|
||||
WHERE
|
||||
vfs.[database_id] IN (DB_ID(),0,2)
|
||||
`
|
||||
|
||||
const sqlAzureDBProperties = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_server_properties' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,(SELECT count(*) FROM sys.dm_os_schedulers WHERE status = 'VISIBLE ONLINE') AS [cpu_count]
|
||||
,(SELECT [process_memory_limit_mb] FROM sys.dm_os_job_object) AS [server_memory]
|
||||
,slo.[edition] as [sku]
|
||||
,SERVERPROPERTY('EngineEdition') AS [engine_edition]
|
||||
,slo.[service_objective] AS [hardware_type]
|
||||
,CASE
|
||||
WHEN slo.[edition] = 'Hyperscale' then NULL
|
||||
ELSE CAST(DATABASEPROPERTYEX(DB_NAME(),'MaxSizeInBytes') as bigint)/(1024*1024)
|
||||
END AS [total_storage_mb]
|
||||
,(SELECT SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT) / 128) FROM sys.database_files WHERE type_desc = 'ROWS') AS used_storage_mb
|
||||
,CASE
|
||||
WHEN slo.[edition] = 'Hyperscale' then NULL
|
||||
ELSE (
|
||||
SELECT (CAST(DATABASEPROPERTYEX(DB_NAME(), 'MaxSizeInBytes') AS BIGINT) / (1024 * 1024) - SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT) / 128))
|
||||
FROM sys.database_files
|
||||
WHERE type_desc = 'ROWS'
|
||||
)
|
||||
END AS [available_storage_mb]
|
||||
,(SELECT SUM(CAST (max_size as BIGINT)) * 8 / (1024 * 1024) FROM sys.database_files WHERE type_desc = 'LOG') AS max_log_mb
|
||||
,(select DATEDIFF(MINUTE,sqlserver_start_time,GETDATE()) from sys.dm_os_sys_info) as [uptime]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.[databases] AS d
|
||||
-- sys.databases.database_id may not match current DB_ID on Azure SQL DB
|
||||
CROSS JOIN sys.[database_service_objectives] AS slo
|
||||
WHERE
|
||||
d.[name] = DB_NAME()
|
||||
AND slo.[database_id] = DB_ID();
|
||||
`
|
||||
|
||||
const sqlAzureDBOsWaitStats = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_waitstats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,ws.[wait_type]
|
||||
,[wait_time_ms]
|
||||
,[wait_time_ms] - [signal_wait_time_ms] AS [resource_wait_ms]
|
||||
,[signal_wait_time_ms]
|
||||
,[max_wait_time_ms]
|
||||
,[waiting_tasks_count]
|
||||
,CASE
|
||||
WHEN ws.[wait_type] LIKE 'SOS_SCHEDULER_YIELD' then 'CPU'
|
||||
WHEN ws.[wait_type] = 'THREADPOOL' THEN 'Worker Thread'
|
||||
WHEN ws.[wait_type] LIKE 'LCK[_]%' THEN 'Lock'
|
||||
WHEN ws.[wait_type] LIKE 'LATCH[_]%' THEN 'Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGELATCH[_]%' THEN 'Buffer Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGEIOLATCH[_]%' THEN 'Buffer IO'
|
||||
WHEN ws.[wait_type] LIKE 'RESOURCE_SEMAPHORE_QUERY_COMPILE%' THEN 'Compilation'
|
||||
WHEN ws.[wait_type] LIKE 'CLR[_]%' or ws.[wait_type] like 'SQLCLR%' THEN 'SQL CLR'
|
||||
WHEN ws.[wait_type] LIKE 'DBMIRROR_%' THEN 'Mirroring'
|
||||
WHEN ws.[wait_type] LIKE 'DTC[_]%' or ws.[wait_type] LIKE 'DTCNEW%' or ws.[wait_type] LIKE 'TRAN_%'
|
||||
or ws.[wait_type] LIKE 'XACT%' or ws.[wait_type] like 'MSQL_XACT%' THEN 'Transaction'
|
||||
WHEN ws.[wait_type] LIKE 'SLEEP[_]%'
|
||||
or ws.[wait_type] IN (
|
||||
'LAZYWRITER_SLEEP', 'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
'SQLTRACE_WAIT_ENTRIES', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT',
|
||||
'REQUEST_FOR_DEADLOCK_SEARCH', 'LOGMGR_QUEUE', 'ONDEMAND_TASK_QUEUE',
|
||||
'CHECKPOINT_QUEUE', 'XE_TIMER_EVENT') THEN 'Idle'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_IO_COMPLETION','BACKUPIO','CHKPT','WRITE_COMPLETION',
|
||||
'IO_QUEUE_LIMIT', 'IO_RETRY') THEN 'Other Disk IO'
|
||||
WHEN ws.[wait_type] LIKE 'PREEMPTIVE_%' THEN 'Preemptive'
|
||||
WHEN ws.[wait_type] LIKE 'BROKER[_]%' THEN 'Service Broker'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'WRITELOG','LOGBUFFER','LOGMGR_RESERVE_APPEND',
|
||||
'LOGMGR_FLUSH', 'LOGMGR_PMM_LOG') THEN 'Tran Log IO'
|
||||
WHEN ws.[wait_type] LIKE 'LOG_RATE%' then 'Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'HADR_THROTTLE[_]%'
|
||||
or ws.[wait_type] = 'THROTTLE_LOG_RATE_LOG_STORAGE' THEN 'HADR Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO_RG%' or ws.[wait_type] like 'WAIT_RBIO_RG%' then 'VLDB Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO[_]%' or ws.[wait_type] like 'WAIT_RBIO[_]%' then 'VLDB RBIO'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_NETWORK_IO','EXTERNAL_SCRIPT_NETWORK_IOF',
|
||||
'NET_WAITFOR_PACKET','PROXY_NETWORK_IO') THEN 'Network IO'
|
||||
WHEN ws.[wait_type] IN ( 'CXPACKET', 'CXCONSUMER')
|
||||
or ws.[wait_type] like 'HT%' or ws.[wait_type] like 'BMP%'
|
||||
or ws.[wait_type] like 'BP%' THEN 'Parallelism'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'CMEMTHREAD','CMEMPARTITIONED','EE_PMOLOCK','EXCHANGE',
|
||||
'RESOURCE_SEMAPHORE','MEMORY_ALLOCATION_EXT',
|
||||
'RESERVED_MEMORY_ALLOCATION_EXT', 'MEMORY_GRANT_UPDATE') THEN 'Memory'
|
||||
WHEN ws.[wait_type] IN ('WAITFOR','WAIT_FOR_RESULTS') THEN 'User Wait'
|
||||
WHEN ws.[wait_type] LIKE 'HADR[_]%' or ws.[wait_type] LIKE 'PWAIT_HADR%'
|
||||
or ws.[wait_type] LIKE 'REPLICA[_]%' or ws.[wait_type] LIKE 'REPL_%'
|
||||
or ws.[wait_type] LIKE 'SE_REPL[_]%'
|
||||
or ws.[wait_type] LIKE 'FCB_REPLICA%' THEN 'Replication'
|
||||
WHEN ws.[wait_type] LIKE 'SQLTRACE[_]%'
|
||||
or ws.[wait_type] IN (
|
||||
'TRACEWRITE', 'SQLTRACE_LOCK', 'SQLTRACE_FILE_BUFFER', 'SQLTRACE_FILE_WRITE_IO_COMPLETION',
|
||||
'SQLTRACE_FILE_READ_IO_COMPLETION', 'SQLTRACE_PENDING_BUFFER_WRITERS', 'SQLTRACE_SHUTDOWN',
|
||||
'QUERY_TRACEOUT', 'TRACE_EVTNOTIF') THEN 'Tracing'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'FT_RESTART_CRAWL', 'FULLTEXT GATHERER', 'MSSEARCH', 'FT_METADATA_MUTEX',
|
||||
'FT_IFTSHC_MUTEX', 'FT_IFTSISM_MUTEX', 'FT_IFTS_RWLOCK', 'FT_COMPROWSET_RWLOCK',
|
||||
'FT_MASTER_MERGE', 'FT_PROPERTYLIST_CACHE', 'FT_MASTER_MERGE_COORDINATOR',
|
||||
'PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC') THEN 'Full Text Search'
|
||||
ELSE 'Other'
|
||||
END as [wait_category]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_os_wait_stats AS ws WITH (NOLOCK)
|
||||
WHERE
|
||||
ws.[wait_type] NOT IN (
|
||||
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
|
||||
N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
|
||||
N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
|
||||
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_QUEUE',
|
||||
N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
|
||||
N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
|
||||
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT',
|
||||
N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
|
||||
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE',
|
||||
N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
|
||||
N'PARALLEL_REDO_WORKER_WAIT_WORK',
|
||||
N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
|
||||
N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
|
||||
N'PREEMPTIVE_OS_PIPEOPS','PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
|
||||
N'PREEMPTIVE_OS_DEVICEOPS',
|
||||
N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
|
||||
N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
|
||||
N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
|
||||
N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
|
||||
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
|
||||
N'QDS_ASYNC_QUEUE',
|
||||
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
|
||||
N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
|
||||
N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
|
||||
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
|
||||
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
|
||||
N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
N'SQLTRACE_WAIT_ENTRIES',
|
||||
N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
|
||||
N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE',
|
||||
N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
|
||||
N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT',
|
||||
N'SOS_WORK_DISPATCHER','RESERVED_MEMORY_ALLOCATION_EXT','SQLTRACE_WAIT_ENTRIES',
|
||||
N'RBIO_COMM_RETRY')
|
||||
AND [waiting_tasks_count] > 10
|
||||
AND [wait_time_ms] > 100;
|
||||
`
|
||||
|
||||
const sqlAzureDBMemoryClerks = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_memory_clerks' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,DB_NAME() AS [database_name]
|
||||
,mc.[type] AS [clerk_type]
|
||||
,SUM(mc.[pages_kb]) AS [size_kb]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.[dm_os_memory_clerks] AS mc WITH (NOLOCK)
|
||||
GROUP BY
|
||||
mc.[type]
|
||||
HAVING
|
||||
SUM(mc.[pages_kb]) >= 1024
|
||||
OPTION(RECOMPILE);
|
||||
`
|
||||
|
||||
const sqlAzureDBPerformanceCounters = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
DECLARE @PCounters TABLE
|
||||
(
|
||||
[object_name] nvarchar(128),
|
||||
[counter_name] nvarchar(128),
|
||||
[instance_name] nvarchar(128),
|
||||
[cntr_value] bigint,
|
||||
[cntr_type] INT ,
|
||||
Primary Key([object_name],[counter_name],[instance_name])
|
||||
);
|
||||
|
||||
WITH PerfCounters AS (
|
||||
SELECT DISTINCT
|
||||
RTrim(spi.[object_name]) [object_name]
|
||||
,RTrim(spi.[counter_name]) [counter_name]
|
||||
,CASE WHEN (
|
||||
RTRIM(spi.[object_name]) LIKE '%:Databases'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Database Replica'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Catalog Metadata'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Query Store'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Columnstore'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Advanced Analytics')
|
||||
AND TRY_CONVERT([uniqueidentifier], spi.[instance_name]) IS NOT NULL -- for cloud only
|
||||
THEN ISNULL(d.[name],RTRIM(spi.instance_name)) -- Elastic Pools counters exist for all databases but sys.databases only has current DB value
|
||||
WHEN
|
||||
RTRIM([object_name]) LIKE '%:Availability Replica'
|
||||
AND TRY_CONVERT([uniqueidentifier], spi.[instance_name]) IS NOT NULL -- for cloud only
|
||||
THEN ISNULL(d.[name],RTRIM(spi.[instance_name])) + RTRIM(SUBSTRING(spi.[instance_name], 37, LEN(spi.[instance_name])))
|
||||
ELSE RTRIM(spi.instance_name)
|
||||
END AS [instance_name]
|
||||
,CAST(spi.[cntr_value] AS BIGINT) AS [cntr_value]
|
||||
,spi.[cntr_type]
|
||||
FROM sys.dm_os_performance_counters AS spi
|
||||
LEFT JOIN sys.databases AS d
|
||||
ON LEFT(spi.[instance_name], 36) -- some instance_name values have an additional identifier appended after the GUID
|
||||
= CASE
|
||||
/*in SQL DB standalone, physical_database_name for master is the GUID of the user database*/
|
||||
WHEN d.[name] = 'master' AND TRY_CONVERT([uniqueidentifier], d.[physical_database_name]) IS NOT NULL
|
||||
THEN d.[name]
|
||||
ELSE d.[physical_database_name]
|
||||
END
|
||||
WHERE
|
||||
/*filter out unnecessary SQL DB system database counters, other than master and tempdb*/
|
||||
NOT (spi.object_name LIKE 'MSSQL%:Databases%' AND spi.instance_name IN ('model','model_masterdb','model_userdb','msdb','mssqlsystemresource'))
|
||||
AND
|
||||
(
|
||||
counter_name IN (
|
||||
'SQL Compilations/sec'
|
||||
,'SQL Re-Compilations/sec'
|
||||
,'User Connections'
|
||||
,'Batch Requests/sec'
|
||||
,'Logouts/sec'
|
||||
,'Logins/sec'
|
||||
,'Processes blocked'
|
||||
,'Latch Waits/sec'
|
||||
,'Full Scans/sec'
|
||||
,'Index Searches/sec'
|
||||
,'Page Splits/sec'
|
||||
,'Page lookups/sec'
|
||||
,'Page reads/sec'
|
||||
,'Page writes/sec'
|
||||
,'Readahead pages/sec'
|
||||
,'Lazy writes/sec'
|
||||
,'Checkpoint pages/sec'
|
||||
,'Table Lock Escalations/sec'
|
||||
,'Page life expectancy'
|
||||
,'Log File(s) Size (KB)'
|
||||
,'Log File(s) Used Size (KB)'
|
||||
,'Data File(s) Size (KB)'
|
||||
,'Transactions/sec'
|
||||
,'Write Transactions/sec'
|
||||
,'Active Transactions'
|
||||
,'Log Growths'
|
||||
,'Active Temp Tables'
|
||||
,'Logical Connections'
|
||||
,'Temp Tables Creation Rate'
|
||||
,'Temp Tables For Destruction'
|
||||
,'Free Space in tempdb (KB)'
|
||||
,'Version Store Size (KB)'
|
||||
,'Memory Grants Pending'
|
||||
,'Memory Grants Outstanding'
|
||||
,'Free list stalls/sec'
|
||||
,'Buffer cache hit ratio'
|
||||
,'Buffer cache hit ratio base'
|
||||
,'Backup/Restore Throughput/sec'
|
||||
,'Total Server Memory (KB)'
|
||||
,'Target Server Memory (KB)'
|
||||
,'Log Flushes/sec'
|
||||
,'Log Flush Wait Time'
|
||||
,'Memory broker clerk size'
|
||||
,'Log Bytes Flushed/sec'
|
||||
,'Bytes Sent to Replica/sec'
|
||||
,'Log Send Queue'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Replica/sec'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Transport/sec'
|
||||
,'Bytes Received from Replica/sec'
|
||||
,'Receives from Replica/sec'
|
||||
,'Flow Control Time (ms/sec)'
|
||||
,'Flow Control/sec'
|
||||
,'Resent Messages/sec'
|
||||
,'Redone Bytes/sec'
|
||||
,'XTP Memory Used (KB)'
|
||||
,'Transaction Delay'
|
||||
,'Log Bytes Received/sec'
|
||||
,'Log Apply Pending Queue'
|
||||
,'Redone Bytes/sec'
|
||||
,'Recovery Queue'
|
||||
,'Log Apply Ready Queue'
|
||||
,'CPU usage %'
|
||||
,'CPU usage % base'
|
||||
,'Queued requests'
|
||||
,'Requests completed/sec'
|
||||
,'Blocked tasks'
|
||||
,'Active memory grant amount (KB)'
|
||||
,'Disk Read Bytes/sec'
|
||||
,'Disk Read IO Throttled/sec'
|
||||
,'Disk Read IO/sec'
|
||||
,'Disk Write Bytes/sec'
|
||||
,'Disk Write IO Throttled/sec'
|
||||
,'Disk Write IO/sec'
|
||||
,'Used memory (KB)'
|
||||
,'Forwarded Records/sec'
|
||||
,'Background Writer pages/sec'
|
||||
,'Percent Log Used'
|
||||
,'Log Send Queue KB'
|
||||
,'Redo Queue KB'
|
||||
,'Mirrored Write Transactions/sec'
|
||||
,'Group Commit Time'
|
||||
,'Group Commits/Sec'
|
||||
,'Workfiles Created/sec'
|
||||
,'Worktables Created/sec'
|
||||
,'Query Store CPU usage'
|
||||
) OR (
|
||||
spi.[object_name] LIKE '%User Settable%'
|
||||
OR spi.[object_name] LIKE '%SQL Errors%'
|
||||
OR spi.[object_name] LIKE '%Batch Resp Statistics%'
|
||||
) OR (
|
||||
spi.[instance_name] IN ('_Total')
|
||||
AND spi.[counter_name] IN (
|
||||
'Lock Timeouts/sec'
|
||||
,'Lock Timeouts (timeout > 0)/sec'
|
||||
,'Number of Deadlocks/sec'
|
||||
,'Lock Waits/sec'
|
||||
,'Latch Waits/sec'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
INSERT INTO @PCounters select * from PerfCounters
|
||||
|
||||
SELECT
|
||||
'sqlserver_performance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,pc.[object_name] AS [object]
|
||||
,pc.[counter_name] AS [counter]
|
||||
,CASE pc.[instance_name]
|
||||
WHEN '_Total' THEN 'Total'
|
||||
ELSE ISNULL(pc.[instance_name],'')
|
||||
END AS [instance]
|
||||
,CAST(CASE WHEN pc.[cntr_type] = 537003264 AND pc1.[cntr_value] > 0 THEN (pc.[cntr_value] * 1.0) / (pc1.[cntr_value] * 1.0) * 100 ELSE pc.[cntr_value] END AS float(10)) AS [value]
|
||||
,cast(pc.[cntr_type] as varchar(25)) as [counter_type]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
from @PCounters pc
|
||||
LEFT OUTER JOIN @PCounters AS pc1
|
||||
ON (
|
||||
pc.[counter_name] = REPLACE(pc1.[counter_name],' base','')
|
||||
OR pc.[counter_name] = REPLACE(pc1.[counter_name],' base',' (ms)')
|
||||
)
|
||||
AND pc.[object_name] = pc1.[object_name]
|
||||
AND pc.[instance_name] = pc1.[instance_name]
|
||||
AND pc1.[counter_name] LIKE '%base'
|
||||
WHERE
|
||||
pc.[counter_name] NOT LIKE '% base'
|
||||
OPTION (RECOMPILE);
|
||||
`
|
||||
|
||||
const sqlAzureDBRequests string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
|
||||
|
||||
SELECT
|
||||
[measurement],[sql_instance],[database_name],[session_id]
|
||||
,ISNULL([request_id],0) AS [request_id]
|
||||
,[blocking_session_id],[status],[cpu_time_ms]
|
||||
,[total_elapsed_time_ms],[logical_reads],[writes]
|
||||
,[command],[wait_time_ms],[wait_type]
|
||||
,[wait_resource],[program_name]
|
||||
,[host_name],[nt_user_name],[login_name]
|
||||
,[transaction_isolation_level],[granted_query_memory_pages],[percent_complete]
|
||||
,[statement_text],[objectid],[stmt_object_name]
|
||||
,[stmt_db_name],[query_hash],[query_plan_hash]
|
||||
,replica_updateability
|
||||
,[session_db_name],[open_transaction]
|
||||
FROM (
|
||||
SELECT
|
||||
'sqlserver_requests' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,s.[session_id]
|
||||
,r.[request_id]
|
||||
,DB_NAME(COALESCE(r.[database_id], s.[database_id])) AS [session_db_name]
|
||||
,COALESCE(r.[status], s.[status]) AS [status]
|
||||
,COALESCE(r.[cpu_time], s.[cpu_time]) AS [cpu_time_ms]
|
||||
,COALESCE(r.[total_elapsed_time], s.[total_elapsed_time]) AS [total_elapsed_time_ms]
|
||||
,COALESCE(r.[logical_reads], s.[logical_reads]) AS [logical_reads]
|
||||
,COALESCE(r.[writes], s.[writes]) AS [writes]
|
||||
,r.[command]
|
||||
,r.[wait_time] AS [wait_time_ms]
|
||||
,r.[wait_type]
|
||||
,r.[wait_resource]
|
||||
,NULLIF(r.[blocking_session_id],0) AS [blocking_session_id]
|
||||
,s.[program_name]
|
||||
,s.[host_name]
|
||||
,s.[nt_user_name]
|
||||
,s.[login_name]
|
||||
,COALESCE(r.[open_transaction_count], s.[open_transaction_count]) AS [open_transaction]
|
||||
,LEFT (CASE COALESCE(r.[transaction_isolation_level], s.[transaction_isolation_level])
|
||||
WHEN 0 THEN '0-Read Committed'
|
||||
WHEN 1 THEN '1-Read Uncommitted (NOLOCK)'
|
||||
WHEN 2 THEN '2-Read Committed'
|
||||
WHEN 3 THEN '3-Repeatable Read'
|
||||
WHEN 4 THEN '4-Serializable'
|
||||
WHEN 5 THEN '5-Snapshot'
|
||||
ELSE CONVERT (varchar(30), r.[transaction_isolation_level]) + '-UNKNOWN'
|
||||
END, 30) AS [transaction_isolation_level]
|
||||
,r.[granted_query_memory] AS [granted_query_memory_pages]
|
||||
,r.[percent_complete]
|
||||
,SUBSTRING(
|
||||
qt.[text],
|
||||
r.[statement_start_offset] / 2 + 1,
|
||||
(CASE WHEN r.[statement_end_offset] = -1
|
||||
THEN DATALENGTH(qt.[text])
|
||||
ELSE r.[statement_end_offset]
|
||||
END - r.[statement_start_offset]) / 2 + 1
|
||||
) AS [statement_text]
|
||||
,qt.[objectid]
|
||||
,QUOTENAME(OBJECT_SCHEMA_NAME(qt.[objectid], qt.[dbid])) + '.' + QUOTENAME(OBJECT_NAME(qt.[objectid], qt.[dbid])) as [stmt_object_name]
|
||||
,DB_NAME(qt.[dbid]) AS [stmt_db_name]
|
||||
,CONVERT(varchar(20),r.[query_hash],1) AS [query_hash]
|
||||
,CONVERT(varchar(20),r.[query_plan_hash],1) AS [query_plan_hash]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
,s.[is_user_process]
|
||||
,[blocking_or_blocked] = COUNT(*) OVER(PARTITION BY ISNULL(NULLIF(r.[blocking_session_id], 0),s.[session_id]))
|
||||
FROM sys.dm_exec_sessions AS s
|
||||
LEFT OUTER JOIN sys.dm_exec_requests AS r
|
||||
ON s.[session_id] = r.[session_id]
|
||||
OUTER APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
|
||||
) AS data
|
||||
WHERE
|
||||
[blocking_or_blocked] > 1 --Always include blocking or blocked sessions/requests
|
||||
OR [open_transaction] >= 1 --Always include sessions with open transactions
|
||||
OR (
|
||||
[request_id] IS NOT NULL --A request must exists
|
||||
AND ( --Always fetch user process (in any state), fetch system process only if active
|
||||
[is_user_process] = 1
|
||||
OR [status] COLLATE Latin1_General_BIN NOT IN ('background', 'sleeping')
|
||||
)
|
||||
AND [session_id] <> @@SPID
|
||||
)
|
||||
OPTION(MAXDOP 1);
|
||||
`
|
||||
|
||||
const sqlAzureDBSchedulers string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN /*not Azure SQL DB*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL DB. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_schedulers' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,CAST(s.[scheduler_id] AS VARCHAR(4)) AS [scheduler_id]
|
||||
,CAST(s.[cpu_id] AS VARCHAR(4)) AS [cpu_id]
|
||||
,s.[is_online]
|
||||
,s.[is_idle]
|
||||
,s.[preemptive_switches_count]
|
||||
,s.[context_switches_count]
|
||||
,s.[current_tasks_count]
|
||||
,s.[runnable_tasks_count]
|
||||
,s.[current_workers_count]
|
||||
,s.[active_workers_count]
|
||||
,s.[work_queue_count]
|
||||
,s.[pending_disk_io_count]
|
||||
,s.[load_factor]
|
||||
,s.[yield_count]
|
||||
,s.[total_cpu_usage_ms]
|
||||
,s.[total_scheduler_delay_ms]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_os_schedulers AS s
|
||||
`
|
462
plugins/inputs/sqlserver/azuresqldbqueries_test.go
Normal file
462
plugins/inputs/sqlserver/azuresqldbqueries_test.go
Normal file
|
@ -0,0 +1,462 @@
|
|||
package sqlserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestAzureSQLIntegration_Database_ResourceStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBResourceStats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_azure_db_resource_stats"))
|
||||
require.True(t, acc.HasTag("sqlserver_azure_db_resource_stats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_azure_db_resource_stats", "database_name"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_cpu_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_data_io_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_log_write_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_memory_usage_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "xtp_storage_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "max_worker_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "max_session_percent"))
|
||||
require.True(t, acc.HasField("sqlserver_azure_db_resource_stats", "dtu_limit")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_azure_db_resource_stats", "avg_login_rate_percent")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_azure_db_resource_stats", "end_time")) // Time field.
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_instance_memory_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_instance_cpu_percent"))
|
||||
require.True(t, acc.HasTag("sqlserver_azure_db_resource_stats", "replica_updateability"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_ResourceGovernance_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBResourceGovernance"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_db_resource_governance"))
|
||||
require.True(t, acc.HasTag("sqlserver_db_resource_governance", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_db_resource_governance", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_db_resource_governance", "slo_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "dtu_limit"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "max_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "cap_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "instance_cap_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "max_db_memory"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "max_db_max_size_in_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "db_file_growth_in_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "log_size_in_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "instance_max_worker_threads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "primary_group_max_workers"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "instance_max_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "primary_min_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "primary_max_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "primary_group_min_io"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "primary_group_max_io"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_db_resource_governance", "primary_group_min_cpu"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_db_resource_governance", "primary_group_max_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "primary_pool_max_workers"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "pool_max_io"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "checkpoint_rate_mbps"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "checkpoint_rate_io"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_local_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_managed_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_external_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_type_local_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_type_managed_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_type_external_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_pfs_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_db_resource_governance", "volume_type_pfs_iops"))
|
||||
require.True(t, acc.HasTag("sqlserver_db_resource_governance", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_WaitStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBWaitStats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_azuredb_waitstats"))
|
||||
require.True(t, acc.HasTag("sqlserver_azuredb_waitstats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_azuredb_waitstats", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_azuredb_waitstats", "wait_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_azuredb_waitstats", "wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_azuredb_waitstats", "resource_wait_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_azuredb_waitstats", "signal_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_azuredb_waitstats", "max_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_azuredb_waitstats", "waiting_tasks_count"))
|
||||
require.True(t, acc.HasTag("sqlserver_azuredb_waitstats", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_DatabaseIO_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBDatabaseIO"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_database_io"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "database_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "database_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "file_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "writes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_read_stall_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_write_stall_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "logical_filename"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "physical_filename"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "file_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "current_size_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "space_used_mb"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_ServerProperties_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBServerProperties"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_server_properties"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "database_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "cpu_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "server_memory"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sku"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "engine_edition"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "hardware_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "total_storage_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "available_storage_mb"))
|
||||
require.True(t, acc.HasField("sqlserver_server_properties", "uptime")) // Time field.
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "replica_updateability"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_OsWaitstats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBOsWaitstats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_waitstats"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "resource_wait_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "signal_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "max_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "waiting_tasks_count"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_category"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_MemoryClerks_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBMemoryClerks"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_memory_clerks"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "clerk_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_memory_clerks", "size_kb"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_PerformanceCounters_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBPerformanceCounters"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_performance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "object"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "instance"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_performance", "value"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter_type"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_Requests_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBRequests"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_requests"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "database_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "session_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "request_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "session_db_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "status"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "cpu_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "total_elapsed_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "logical_reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "writes"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "command"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "wait_time_ms"))
|
||||
require.True(t, acc.HasField("sqlserver_requests", "wait_type")) // Can be null.
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "wait_resource"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "blocking_session_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "program_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "host_name"))
|
||||
require.True(t, acc.HasField("sqlserver_requests", "nt_user_name")) // Can be null.
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "login_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "open_transaction"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "transaction_isolation_level"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "granted_query_memory_pages"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_requests", "percent_complete"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "statement_text"))
|
||||
require.True(t, acc.HasField("sqlserver_requests", "objectid")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_requests", "stmt_object_name")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_requests", "stmt_db_name")) // Can be null.
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "query_hash"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "query_plan_hash"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Database_Schedulers_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_DB_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_DB_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_DB_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLDBSchedulers"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLDB",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_schedulers"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "scheduler_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "cpu_id"))
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_online")) // Bool field.
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_idle")) // Bool field.
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "preemptive_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "context_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "runnable_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "active_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "work_queue_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "pending_disk_io_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "load_factor"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "yield_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_cpu_usage_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_scheduler_delay_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
572
plugins/inputs/sqlserver/azuresqlmanagedqueries.go
Normal file
572
plugins/inputs/sqlserver/azuresqlmanagedqueries.go
Normal file
|
@ -0,0 +1,572 @@
|
|||
//nolint:lll // conditionally long lines allowed
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
_ "github.com/microsoft/go-mssqldb" // go-mssqldb initialization
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------ Azure Managed Instance ------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
const sqlAzureMIProperties = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT TOP 1
|
||||
'sqlserver_server_properties' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,[virtual_core_count] AS [cpu_count]
|
||||
,(SELECT [process_memory_limit_mb] FROM sys.dm_os_job_object) AS [server_memory]
|
||||
,[sku]
|
||||
,SERVERPROPERTY('EngineEdition') AS [engine_edition]
|
||||
,[hardware_generation] AS [hardware_type]
|
||||
,cast([reserved_storage_mb] as bigint) AS [total_storage_mb]
|
||||
,cast(([reserved_storage_mb] - [storage_space_used_mb]) as bigint) AS [available_storage_mb]
|
||||
,(SELECT DATEDIFF(MINUTE,[sqlserver_start_time],GETDATE()) from sys.dm_os_sys_info) as [uptime]
|
||||
,SERVERPROPERTY('ProductVersion') AS [sql_version]
|
||||
,LEFT(@@VERSION,CHARINDEX(' - ',@@VERSION)) AS [sql_version_desc]
|
||||
,[db_online]
|
||||
,[db_restoring]
|
||||
,[db_recovering]
|
||||
,[db_recoveryPending]
|
||||
,[db_suspect]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.server_resource_stats
|
||||
CROSS APPLY (
|
||||
SELECT
|
||||
SUM( CASE WHEN [state] = 0 THEN 1 ELSE 0 END ) AS [db_online]
|
||||
,SUM( CASE WHEN [state] = 1 THEN 1 ELSE 0 END ) AS [db_restoring]
|
||||
,SUM( CASE WHEN [state] = 2 THEN 1 ELSE 0 END ) AS [db_recovering]
|
||||
,SUM( CASE WHEN [state] = 3 THEN 1 ELSE 0 END ) AS [db_recoveryPending]
|
||||
,SUM( CASE WHEN [state] = 4 THEN 1 ELSE 0 END ) AS [db_suspect]
|
||||
,SUM( CASE WHEN [state] IN (6,10) THEN 1 ELSE 0 END ) AS [db_offline]
|
||||
FROM sys.databases
|
||||
) AS dbs
|
||||
ORDER BY
|
||||
[start_time] DESC;
|
||||
`
|
||||
|
||||
const sqlAzureMIResourceStats = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT TOP(1)
|
||||
'sqlserver_azure_db_resource_stats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,cast([avg_cpu_percent] as float) as [avg_cpu_percent]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM
|
||||
sys.server_resource_stats
|
||||
ORDER BY
|
||||
[end_time] DESC;
|
||||
`
|
||||
|
||||
const sqlAzureMIResourceGovernance string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_instance_resource_governance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,[instance_cap_cpu]
|
||||
,[instance_max_log_rate]
|
||||
,[instance_max_worker_threads]
|
||||
,[tempdb_log_file_number]
|
||||
,[volume_local_iops]
|
||||
,[volume_external_xstore_iops]
|
||||
,[volume_managed_xstore_iops]
|
||||
,[volume_type_local_iops] as [voltype_local_iops]
|
||||
,[volume_type_managed_xstore_iops] as [voltype_man_xtore_iops]
|
||||
,[volume_type_external_xstore_iops] as [voltype_ext_xtore_iops]
|
||||
,[volume_external_xstore_iops] as [vol_ext_xtore_iops]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_instance_resource_governance;
|
||||
`
|
||||
|
||||
const sqlAzureMIDatabaseIO = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_database_io' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME(mf.[database_id]) AS [database_name]
|
||||
,COALESCE(mf.[physical_name],'RBPEX') AS [physical_filename] --RPBEX = Resilient Buffer Pool Extension
|
||||
,COALESCE(mf.[name],'RBPEX') AS [logical_filename] --RPBEX = Resilient Buffer Pool Extension
|
||||
,mf.[type_desc] AS [file_type]
|
||||
,vfs.[io_stall_read_ms] AS [read_latency_ms]
|
||||
,vfs.[num_of_reads] AS [reads]
|
||||
,vfs.[num_of_bytes_read] AS [read_bytes]
|
||||
,vfs.[io_stall_write_ms] AS [write_latency_ms]
|
||||
,vfs.[num_of_writes] AS [writes]
|
||||
,vfs.[num_of_bytes_written] AS [write_bytes]
|
||||
,vfs.io_stall_queued_read_ms AS [rg_read_stall_ms]
|
||||
,vfs.io_stall_queued_write_ms AS [rg_write_stall_ms]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
|
||||
LEFT OUTER JOIN sys.master_files AS mf WITH (NOLOCK)
|
||||
ON vfs.[database_id] = mf.[database_id]
|
||||
AND vfs.[file_id] = mf.[file_id]
|
||||
WHERE
|
||||
vfs.[database_id] < 32760
|
||||
`
|
||||
|
||||
const sqlAzureMIMemoryClerks = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_memory_clerks' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,mc.[type] AS [clerk_type]
|
||||
,SUM(mc.[pages_kb]) AS [size_kb]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.[dm_os_memory_clerks] AS mc WITH (NOLOCK)
|
||||
GROUP BY
|
||||
mc.[type]
|
||||
HAVING
|
||||
SUM(mc.[pages_kb]) >= 1024
|
||||
OPTION(RECOMPILE);
|
||||
`
|
||||
|
||||
const sqlAzureMIOsWaitStats = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_waitstats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,ws.[wait_type]
|
||||
,[wait_time_ms]
|
||||
,[wait_time_ms] - [signal_wait_time_ms] AS [resource_wait_ms]
|
||||
,[signal_wait_time_ms]
|
||||
,[max_wait_time_ms]
|
||||
,[waiting_tasks_count]
|
||||
,CASE
|
||||
WHEN ws.[wait_type] LIKE 'SOS_SCHEDULER_YIELD' then 'CPU'
|
||||
WHEN ws.[wait_type] = 'THREADPOOL' THEN 'Worker Thread'
|
||||
WHEN ws.[wait_type] LIKE 'LCK[_]%' THEN 'Lock'
|
||||
WHEN ws.[wait_type] LIKE 'LATCH[_]%' THEN 'Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGELATCH[_]%' THEN 'Buffer Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGEIOLATCH[_]%' THEN 'Buffer IO'
|
||||
WHEN ws.[wait_type] LIKE 'RESOURCE_SEMAPHORE_QUERY_COMPILE%' THEN 'Compilation'
|
||||
WHEN ws.[wait_type] LIKE 'CLR[_]%' or ws.[wait_type] like 'SQLCLR%' THEN 'SQL CLR'
|
||||
WHEN ws.[wait_type] LIKE 'DBMIRROR_%' THEN 'Mirroring'
|
||||
WHEN ws.[wait_type] LIKE 'DTC[_]%' or ws.[wait_type] LIKE 'DTCNEW%' or ws.[wait_type] LIKE 'TRAN_%'
|
||||
or ws.[wait_type] LIKE 'XACT%' or ws.[wait_type] like 'MSQL_XACT%' THEN 'Transaction'
|
||||
WHEN ws.[wait_type] LIKE 'SLEEP[_]%'
|
||||
or ws.[wait_type] IN (
|
||||
'LAZYWRITER_SLEEP', 'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
'SQLTRACE_WAIT_ENTRIES', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT',
|
||||
'REQUEST_FOR_DEADLOCK_SEARCH', 'LOGMGR_QUEUE', 'ONDEMAND_TASK_QUEUE',
|
||||
'CHECKPOINT_QUEUE', 'XE_TIMER_EVENT') THEN 'Idle'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_IO_COMPLETION','BACKUPIO','CHKPT','WRITE_COMPLETION',
|
||||
'IO_QUEUE_LIMIT', 'IO_RETRY') THEN 'Other Disk IO'
|
||||
WHEN ws.[wait_type] LIKE 'PREEMPTIVE_%' THEN 'Preemptive'
|
||||
WHEN ws.[wait_type] LIKE 'BROKER[_]%' THEN 'Service Broker'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'WRITELOG','LOGBUFFER','LOGMGR_RESERVE_APPEND',
|
||||
'LOGMGR_FLUSH', 'LOGMGR_PMM_LOG') THEN 'Tran Log IO'
|
||||
WHEN ws.[wait_type] LIKE 'LOG_RATE%' then 'Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'HADR_THROTTLE[_]%'
|
||||
or ws.[wait_type] = 'THROTTLE_LOG_RATE_LOG_STORAGE' THEN 'HADR Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO_RG%' or ws.[wait_type] like 'WAIT_RBIO_RG%' then 'VLDB Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO[_]%' or ws.[wait_type] like 'WAIT_RBIO[_]%' then 'VLDB RBIO'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_NETWORK_IO','EXTERNAL_SCRIPT_NETWORK_IOF',
|
||||
'NET_WAITFOR_PACKET','PROXY_NETWORK_IO') THEN 'Network IO'
|
||||
WHEN ws.[wait_type] IN ( 'CXPACKET', 'CXCONSUMER')
|
||||
or ws.[wait_type] like 'HT%' or ws.[wait_type] like 'BMP%'
|
||||
or ws.[wait_type] like 'BP%' THEN 'Parallelism'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'CMEMTHREAD','CMEMPARTITIONED','EE_PMOLOCK','EXCHANGE',
|
||||
'RESOURCE_SEMAPHORE','MEMORY_ALLOCATION_EXT',
|
||||
'RESERVED_MEMORY_ALLOCATION_EXT', 'MEMORY_GRANT_UPDATE') THEN 'Memory'
|
||||
WHEN ws.[wait_type] IN ('WAITFOR','WAIT_FOR_RESULTS') THEN 'User Wait'
|
||||
WHEN ws.[wait_type] LIKE 'HADR[_]%' or ws.[wait_type] LIKE 'PWAIT_HADR%'
|
||||
or ws.[wait_type] LIKE 'REPLICA[_]%' or ws.[wait_type] LIKE 'REPL_%'
|
||||
or ws.[wait_type] LIKE 'SE_REPL[_]%'
|
||||
or ws.[wait_type] LIKE 'FCB_REPLICA%' THEN 'Replication'
|
||||
WHEN ws.[wait_type] LIKE 'SQLTRACE[_]%'
|
||||
or ws.[wait_type] IN (
|
||||
'TRACEWRITE', 'SQLTRACE_LOCK', 'SQLTRACE_FILE_BUFFER', 'SQLTRACE_FILE_WRITE_IO_COMPLETION',
|
||||
'SQLTRACE_FILE_READ_IO_COMPLETION', 'SQLTRACE_PENDING_BUFFER_WRITERS', 'SQLTRACE_SHUTDOWN',
|
||||
'QUERY_TRACEOUT', 'TRACE_EVTNOTIF') THEN 'Tracing'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'FT_RESTART_CRAWL', 'FULLTEXT GATHERER', 'MSSEARCH', 'FT_METADATA_MUTEX',
|
||||
'FT_IFTSHC_MUTEX', 'FT_IFTSISM_MUTEX', 'FT_IFTS_RWLOCK', 'FT_COMPROWSET_RWLOCK',
|
||||
'FT_MASTER_MERGE', 'FT_PROPERTYLIST_CACHE', 'FT_MASTER_MERGE_COORDINATOR',
|
||||
'PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC') THEN 'Full Text Search'
|
||||
ELSE 'Other'
|
||||
END as [wait_category]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_os_wait_stats AS ws WITH (NOLOCK)
|
||||
WHERE
|
||||
ws.[wait_type] NOT IN (
|
||||
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
|
||||
N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
|
||||
N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
|
||||
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_QUEUE',
|
||||
N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
|
||||
N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
|
||||
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT',
|
||||
N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
|
||||
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE',
|
||||
N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
|
||||
N'PARALLEL_REDO_WORKER_WAIT_WORK',
|
||||
N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
|
||||
N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
|
||||
N'PREEMPTIVE_OS_PIPEOPS','PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
|
||||
N'PREEMPTIVE_OS_DEVICEOPS',
|
||||
N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
|
||||
N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
|
||||
N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
|
||||
N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
|
||||
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
|
||||
N'QDS_ASYNC_QUEUE',
|
||||
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
|
||||
N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
|
||||
N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
|
||||
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
|
||||
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
|
||||
N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
N'SQLTRACE_WAIT_ENTRIES',
|
||||
N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
|
||||
N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE',
|
||||
N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
|
||||
N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT',
|
||||
N'SOS_WORK_DISPATCHER','RESERVED_MEMORY_ALLOCATION_EXT','SQLTRACE_WAIT_ENTRIES',
|
||||
N'RBIO_COMM_RETRY')
|
||||
AND [waiting_tasks_count] > 10
|
||||
AND [wait_time_ms] > 100;
|
||||
`
|
||||
|
||||
const sqlAzureMIPerformanceCounters = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
DECLARE @PCounters TABLE
|
||||
(
|
||||
[object_name] nvarchar(128),
|
||||
[counter_name] nvarchar(128),
|
||||
[instance_name] nvarchar(128),
|
||||
[cntr_value] bigint,
|
||||
[cntr_type] INT ,
|
||||
Primary Key([object_name],[counter_name],[instance_name])
|
||||
);
|
||||
|
||||
WITH PerfCounters AS (
|
||||
SELECT DISTINCT
|
||||
RTrim(spi.[object_name]) [object_name]
|
||||
,RTrim(spi.[counter_name]) [counter_name]
|
||||
,CASE WHEN (
|
||||
RTRIM(spi.[object_name]) LIKE '%:Databases'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Database Replica'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Catalog Metadata'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Query Store'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Columnstore'
|
||||
OR RTRIM(spi.[object_name]) LIKE '%:Advanced Analytics')
|
||||
AND TRY_CONVERT([uniqueidentifier], spi.[instance_name]) IS NOT NULL -- for cloud only
|
||||
THEN ISNULL(d.[name],RTRIM(spi.instance_name)) -- Elastic Pools counters exist for all databases but sys.databases only has current DB value
|
||||
WHEN
|
||||
RTRIM([object_name]) LIKE '%:Availability Replica'
|
||||
AND TRY_CONVERT([uniqueidentifier], spi.[instance_name]) IS NOT NULL -- for cloud only
|
||||
THEN ISNULL(d.[name],RTRIM(spi.[instance_name])) + RTRIM(SUBSTRING(spi.[instance_name], 37, LEN(spi.[instance_name])))
|
||||
ELSE RTRIM(spi.instance_name)
|
||||
END AS [instance_name]
|
||||
,CAST(spi.[cntr_value] AS BIGINT) AS [cntr_value]
|
||||
,spi.[cntr_type]
|
||||
FROM sys.dm_os_performance_counters AS spi
|
||||
LEFT JOIN sys.databases AS d
|
||||
ON LEFT(spi.[instance_name], 36) -- some instance_name values have an additional identifier appended after the GUID
|
||||
= CASE
|
||||
/*in SQL DB standalone, physical_database_name for master is the GUID of the user database*/
|
||||
WHEN d.[name] = 'master' AND TRY_CONVERT([uniqueidentifier], d.[physical_database_name]) IS NOT NULL
|
||||
THEN d.[name]
|
||||
ELSE d.[physical_database_name]
|
||||
END
|
||||
WHERE
|
||||
counter_name IN (
|
||||
'SQL Compilations/sec'
|
||||
,'SQL Re-Compilations/sec'
|
||||
,'User Connections'
|
||||
,'Batch Requests/sec'
|
||||
,'Logouts/sec'
|
||||
,'Logins/sec'
|
||||
,'Processes blocked'
|
||||
,'Latch Waits/sec'
|
||||
,'Full Scans/sec'
|
||||
,'Index Searches/sec'
|
||||
,'Page Splits/sec'
|
||||
,'Page lookups/sec'
|
||||
,'Page reads/sec'
|
||||
,'Page writes/sec'
|
||||
,'Readahead pages/sec'
|
||||
,'Lazy writes/sec'
|
||||
,'Checkpoint pages/sec'
|
||||
,'Table Lock Escalations/sec'
|
||||
,'Page life expectancy'
|
||||
,'Log File(s) Size (KB)'
|
||||
,'Log File(s) Used Size (KB)'
|
||||
,'Data File(s) Size (KB)'
|
||||
,'Transactions/sec'
|
||||
,'Write Transactions/sec'
|
||||
,'Active Transactions'
|
||||
,'Log Growths'
|
||||
,'Active Temp Tables'
|
||||
,'Logical Connections'
|
||||
,'Temp Tables Creation Rate'
|
||||
,'Temp Tables For Destruction'
|
||||
,'Free Space in tempdb (KB)'
|
||||
,'Version Store Size (KB)'
|
||||
,'Memory Grants Pending'
|
||||
,'Memory Grants Outstanding'
|
||||
,'Free list stalls/sec'
|
||||
,'Buffer cache hit ratio'
|
||||
,'Buffer cache hit ratio base'
|
||||
,'Backup/Restore Throughput/sec'
|
||||
,'Total Server Memory (KB)'
|
||||
,'Target Server Memory (KB)'
|
||||
,'Log Flushes/sec'
|
||||
,'Log Flush Wait Time'
|
||||
,'Memory broker clerk size'
|
||||
,'Log Bytes Flushed/sec'
|
||||
,'Bytes Sent to Replica/sec'
|
||||
,'Log Send Queue'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Replica/sec'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Transport/sec'
|
||||
,'Bytes Received from Replica/sec'
|
||||
,'Receives from Replica/sec'
|
||||
,'Flow Control Time (ms/sec)'
|
||||
,'Flow Control/sec'
|
||||
,'Resent Messages/sec'
|
||||
,'Redone Bytes/sec'
|
||||
,'XTP Memory Used (KB)'
|
||||
,'Transaction Delay'
|
||||
,'Log Bytes Received/sec'
|
||||
,'Log Apply Pending Queue'
|
||||
,'Redone Bytes/sec'
|
||||
,'Recovery Queue'
|
||||
,'Log Apply Ready Queue'
|
||||
,'CPU usage %'
|
||||
,'CPU usage % base'
|
||||
,'Queued requests'
|
||||
,'Requests completed/sec'
|
||||
,'Blocked tasks'
|
||||
,'Active memory grant amount (KB)'
|
||||
,'Disk Read Bytes/sec'
|
||||
,'Disk Read IO Throttled/sec'
|
||||
,'Disk Read IO/sec'
|
||||
,'Disk Write Bytes/sec'
|
||||
,'Disk Write IO Throttled/sec'
|
||||
,'Disk Write IO/sec'
|
||||
,'Used memory (KB)'
|
||||
,'Forwarded Records/sec'
|
||||
,'Background Writer pages/sec'
|
||||
,'Percent Log Used'
|
||||
,'Log Send Queue KB'
|
||||
,'Redo Queue KB'
|
||||
,'Mirrored Write Transactions/sec'
|
||||
,'Group Commit Time'
|
||||
,'Group Commits/Sec'
|
||||
,'Workfiles Created/sec'
|
||||
,'Worktables Created/sec'
|
||||
,'Distributed Query'
|
||||
,'DTC calls'
|
||||
,'Query Store CPU usage'
|
||||
) OR (
|
||||
spi.[object_name] LIKE '%User Settable%'
|
||||
OR spi.[object_name] LIKE '%SQL Errors%'
|
||||
OR spi.[object_name] LIKE '%Batch Resp Statistics%'
|
||||
) OR (
|
||||
spi.[instance_name] IN ('_Total')
|
||||
AND spi.[counter_name] IN (
|
||||
'Lock Timeouts/sec'
|
||||
,'Lock Timeouts (timeout > 0)/sec'
|
||||
,'Number of Deadlocks/sec'
|
||||
,'Lock Waits/sec'
|
||||
,'Latch Waits/sec'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
INSERT INTO @PCounters select * from PerfCounters
|
||||
|
||||
SELECT
|
||||
'sqlserver_performance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,pc.[object_name] AS [object]
|
||||
,pc.[counter_name] AS [counter]
|
||||
,CASE pc.[instance_name]
|
||||
WHEN '_Total' THEN 'Total'
|
||||
ELSE ISNULL(pc.[instance_name],'')
|
||||
END AS [instance]
|
||||
,CAST(CASE WHEN pc.[cntr_type] = 537003264 AND pc1.[cntr_value] > 0 THEN (pc.[cntr_value] * 1.0) / (pc1.[cntr_value] * 1.0) * 100 ELSE pc.[cntr_value] END AS float(10)) AS [value]
|
||||
,cast(pc.[cntr_type] as varchar(25)) as [counter_type]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
from @PCounters pc
|
||||
LEFT OUTER JOIN @PCounters AS pc1
|
||||
ON (
|
||||
pc.[counter_name] = REPLACE(pc1.[counter_name],' base','')
|
||||
OR pc.[counter_name] = REPLACE(pc1.[counter_name],' base',' (ms)')
|
||||
)
|
||||
AND pc.[object_name] = pc1.[object_name]
|
||||
AND pc.[instance_name] = pc1.[instance_name]
|
||||
AND pc1.[counter_name] LIKE '%base'
|
||||
WHERE
|
||||
pc.[counter_name] NOT LIKE '% base'
|
||||
OPTION (RECOMPILE);
|
||||
`
|
||||
|
||||
const sqlAzureMIRequests string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
|
||||
|
||||
BEGIN TRY
|
||||
SELECT
|
||||
[measurement],[sql_instance],[database_name],[session_id]
|
||||
,ISNULL([request_id],0) AS [request_id]
|
||||
,[blocking_session_id],[status],[cpu_time_ms]
|
||||
,[total_elapsed_time_ms],[logical_reads],[writes]
|
||||
,[command],[wait_time_ms],[wait_type]
|
||||
,[wait_resource],[program_name]
|
||||
,[host_name],[nt_user_name],[login_name]
|
||||
,[transaction_isolation_level],[granted_query_memory_pages],[percent_complete]
|
||||
,[statement_text],[objectid],[stmt_object_name]
|
||||
,[stmt_db_name],[query_hash],[query_plan_hash]
|
||||
,replica_updateability
|
||||
,[session_db_name],[open_transaction]
|
||||
FROM (
|
||||
SELECT
|
||||
'sqlserver_requests' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,DB_NAME() as [database_name]
|
||||
,s.[session_id]
|
||||
,r.[request_id]
|
||||
,DB_NAME(COALESCE(r.[database_id], s.[database_id])) AS [session_db_name]
|
||||
,COALESCE(r.[status], s.[status]) AS [status]
|
||||
,COALESCE(r.[cpu_time], s.[cpu_time]) AS [cpu_time_ms]
|
||||
,COALESCE(r.[total_elapsed_time], s.[total_elapsed_time]) AS [total_elapsed_time_ms]
|
||||
,COALESCE(r.[logical_reads], s.[logical_reads]) AS [logical_reads]
|
||||
,COALESCE(r.[writes], s.[writes]) AS [writes]
|
||||
,r.[command]
|
||||
,r.[wait_time] AS [wait_time_ms]
|
||||
,r.[wait_type]
|
||||
,r.[wait_resource]
|
||||
,NULLIF(r.[blocking_session_id],0) AS [blocking_session_id]
|
||||
,s.[program_name]
|
||||
,s.[host_name]
|
||||
,s.[nt_user_name]
|
||||
,s.[login_name]
|
||||
,COALESCE(r.[open_transaction_count], s.[open_transaction_count]) AS [open_transaction]
|
||||
,LEFT (CASE COALESCE(r.[transaction_isolation_level], s.[transaction_isolation_level])
|
||||
WHEN 0 THEN '0-Read Committed'
|
||||
WHEN 1 THEN '1-Read Uncommitted (NOLOCK)'
|
||||
WHEN 2 THEN '2-Read Committed'
|
||||
WHEN 3 THEN '3-Repeatable Read'
|
||||
WHEN 4 THEN '4-Serializable'
|
||||
WHEN 5 THEN '5-Snapshot'
|
||||
ELSE CONVERT (varchar(30), r.[transaction_isolation_level]) + '-UNKNOWN'
|
||||
END, 30) AS [transaction_isolation_level]
|
||||
,r.[granted_query_memory] AS [granted_query_memory_pages]
|
||||
,r.[percent_complete]
|
||||
,SUBSTRING(
|
||||
qt.[text],
|
||||
r.[statement_start_offset] / 2 + 1,
|
||||
(CASE WHEN r.[statement_end_offset] = -1
|
||||
THEN DATALENGTH(qt.[text])
|
||||
ELSE r.[statement_end_offset]
|
||||
END - r.[statement_start_offset]) / 2 + 1
|
||||
) AS [statement_text]
|
||||
,qt.[objectid]
|
||||
,QUOTENAME(OBJECT_SCHEMA_NAME(qt.[objectid], qt.[dbid])) + '.' + QUOTENAME(OBJECT_NAME(qt.[objectid], qt.[dbid])) as [stmt_object_name]
|
||||
,DB_NAME(qt.[dbid]) AS [stmt_db_name]
|
||||
,CONVERT(varchar(20),r.[query_hash],1) AS [query_hash]
|
||||
,CONVERT(varchar(20),r.[query_plan_hash],1) AS [query_plan_hash]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
,s.[is_user_process]
|
||||
,[blocking_or_blocked] = COUNT(*) OVER(PARTITION BY ISNULL(NULLIF(r.[blocking_session_id], 0),s.[session_id]))
|
||||
FROM sys.dm_exec_sessions AS s
|
||||
LEFT OUTER JOIN sys.dm_exec_requests AS r
|
||||
ON s.[session_id] = r.[session_id]
|
||||
OUTER APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
|
||||
) AS data
|
||||
WHERE
|
||||
[blocking_or_blocked] > 1 --Always include blocking or blocked sessions/requests
|
||||
OR [open_transaction] >= 1 --Always include sessions with open transactions
|
||||
OR (
|
||||
[request_id] IS NOT NULL --A request must exists
|
||||
AND ( --Always fetch user process (in any state), fetch system process only if active
|
||||
[is_user_process] = 1
|
||||
OR [status] COLLATE Latin1_General_BIN NOT IN ('background', 'sleeping')
|
||||
)
|
||||
AND [session_id] <> @@SPID
|
||||
)
|
||||
OPTION(MAXDOP 1);
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF (ERROR_NUMBER() <> 976) --Avoid possible errors from secondary replica
|
||||
THROW;
|
||||
END CATCH
|
||||
`
|
||||
|
||||
const sqlAzureMISchedulers string = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 8 BEGIN /*not Azure Managed Instance*/
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure Managed Instance. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_schedulers' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,CAST(s.[scheduler_id] AS VARCHAR(4)) AS [scheduler_id]
|
||||
,CAST(s.[cpu_id] AS VARCHAR(4)) AS [cpu_id]
|
||||
,s.[is_online]
|
||||
,s.[is_idle]
|
||||
,s.[preemptive_switches_count]
|
||||
,s.[context_switches_count]
|
||||
,s.[current_tasks_count]
|
||||
,s.[runnable_tasks_count]
|
||||
,s.[current_workers_count]
|
||||
,s.[active_workers_count]
|
||||
,s.[work_queue_count]
|
||||
,s.[pending_disk_io_count]
|
||||
,s.[load_factor]
|
||||
,s.[yield_count]
|
||||
,s.[total_cpu_usage_ms]
|
||||
,s.[total_scheduler_delay_ms]
|
||||
,DATABASEPROPERTYEX(DB_NAME(), 'Updateability') as replica_updateability
|
||||
FROM sys.dm_os_schedulers AS s
|
||||
`
|
391
plugins/inputs/sqlserver/azuresqlmanagedqueries_test.go
Normal file
391
plugins/inputs/sqlserver/azuresqlmanagedqueries_test.go
Normal file
|
@ -0,0 +1,391 @@
|
|||
package sqlserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestAzureSQLIntegration_Managed_ResourceStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIResourceStats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_azure_db_resource_stats"))
|
||||
require.True(t, acc.HasTag("sqlserver_azure_db_resource_stats", "sql_instance"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_azure_db_resource_stats", "avg_cpu_percent"))
|
||||
require.True(t, acc.HasTag("sqlserver_azure_db_resource_stats", "replica_updateability"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_ResourceGovernance_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIResourceGovernance"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_instance_resource_governance"))
|
||||
require.True(t, acc.HasTag("sqlserver_instance_resource_governance", "sql_instance"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "instance_cap_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "instance_max_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "instance_max_worker_threads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "tempdb_log_file_number"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "volume_local_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "volume_external_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "volume_managed_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "voltype_local_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "voltype_man_xtore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "voltype_ext_xtore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_instance_resource_governance", "vol_ext_xtore_iops"))
|
||||
require.True(t, acc.HasTag("sqlserver_instance_resource_governance", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_DatabaseIO_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIDatabaseIO"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_database_io"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "database_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "physical_filename"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "logical_filename"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "file_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "writes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_read_stall_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_write_stall_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_ServerProperties_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIServerProperties"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_server_properties"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_instance"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "cpu_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "server_memory"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sku"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "engine_edition"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "hardware_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "total_storage_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "available_storage_mb"))
|
||||
require.True(t, acc.HasField("sqlserver_server_properties", "uptime")) // Time field.
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_version"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "sql_version_desc"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_online"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_restoring"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_recovering"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_recoveryPending"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_server_properties", "db_suspect"))
|
||||
require.True(t, acc.HasTag("sqlserver_server_properties", "replica_updateability"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_OsWaitStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIOsWaitstats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_waitstats"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "waiting_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "max_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "signal_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "resource_wait_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_category"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_MemoryClerks_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIMemoryClerks"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_memory_clerks"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "clerk_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_memory_clerks", "size_kb"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_PerformanceCounters_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIPerformanceCounters"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_performance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "object"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "instance"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_performance", "value"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter_type"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_Requests_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMIRequests"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_requests"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "database_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "session_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "request_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "status"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "cpu_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "total_elapsed_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "logical_reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "writes"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "command"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "wait_time_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "wait_type"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "wait_resource"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "blocking_session_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "program_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "host_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "nt_user_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "login_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "open_transaction"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "transaction_isolation_level"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_requests", "granted_query_memory_pages"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_requests", "percent_complete"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "statement_text"))
|
||||
require.True(t, acc.HasField("sqlserver_requests", "objectid")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_requests", "stmt_object_name")) // Can be null.
|
||||
require.True(t, acc.HasField("sqlserver_requests", "stmt_db_name")) // Can be null.
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "query_hash"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "query_plan_hash"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "session_db_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_requests", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_Managed_Schedulers_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_MI_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_MI_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_MI_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLMISchedulers"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLManagedInstance",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_schedulers"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "scheduler_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "cpu_id"))
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_online")) // Bool field.
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_idle")) // Bool field.
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "preemptive_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "context_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "runnable_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "active_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "work_queue_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "pending_disk_io_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "load_factor"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "yield_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_cpu_usage_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_scheduler_delay_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "replica_updateability"))
|
||||
|
||||
server.Stop()
|
||||
}
|
478
plugins/inputs/sqlserver/azuresqlpoolqueries.go
Normal file
478
plugins/inputs/sqlserver/azuresqlpoolqueries.go
Normal file
|
@ -0,0 +1,478 @@
|
|||
//nolint:lll // conditionally long lines allowed
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
_ "github.com/microsoft/go-mssqldb" // go-mssqldb initialization
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------ Azure Sql Elastic Pool ------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
const sqlAzurePoolResourceStats = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT TOP(1)
|
||||
'sqlserver_pool_resource_stats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,(SELECT [elastic_pool_name] FROM sys.database_service_objectives WHERE database_id = DB_ID()) AS [elastic_pool_name]
|
||||
,[snapshot_time]
|
||||
,cast([cap_vcores_used_percent] as float) AS [avg_cpu_percent]
|
||||
,cast([avg_data_io_percent] as float) AS [avg_data_io_percent]
|
||||
,cast([avg_log_write_percent] as float) AS [avg_log_write_percent]
|
||||
,cast([avg_storage_percent] as float) AS [avg_storage_percent]
|
||||
,cast([max_worker_percent] as float) AS [max_worker_percent]
|
||||
,cast([max_session_percent] as float) AS [max_session_percent]
|
||||
,cast([max_data_space_kb]/1024. as int) AS [storage_limit_mb]
|
||||
,cast([avg_instance_cpu_percent] as float) AS [avg_instance_cpu_percent]
|
||||
,cast([avg_allocated_storage_percent] as float) AS [avg_allocated_storage_percent]
|
||||
FROM
|
||||
sys.dm_resource_governor_resource_pools_history_ex
|
||||
WHERE
|
||||
[name] = 'SloSharedPool1'
|
||||
ORDER BY
|
||||
[snapshot_time] DESC;
|
||||
`
|
||||
|
||||
const sqlAzurePoolResourceGovernance = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_pool_resource_governance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,(SELECT [elastic_pool_name] FROM sys.database_service_objectives WHERE database_id = DB_ID()) AS [elastic_pool_name]
|
||||
,[slo_name]
|
||||
,[dtu_limit]
|
||||
,[cpu_limit]
|
||||
,[max_cpu]
|
||||
,[cap_cpu]
|
||||
,[max_db_memory]
|
||||
,[max_db_max_size_in_mb]
|
||||
,[db_file_growth_in_mb]
|
||||
,[log_size_in_mb]
|
||||
,[instance_cap_cpu]
|
||||
,[instance_max_log_rate]
|
||||
,[instance_max_worker_threads]
|
||||
,[checkpoint_rate_mbps]
|
||||
,[checkpoint_rate_io]
|
||||
,[primary_group_max_workers]
|
||||
,[primary_min_log_rate]
|
||||
,[primary_max_log_rate]
|
||||
,[primary_group_min_io]
|
||||
,[primary_group_max_io]
|
||||
,[primary_group_min_cpu]
|
||||
,[primary_group_max_cpu]
|
||||
,[primary_pool_max_workers]
|
||||
,[pool_max_io]
|
||||
,[volume_local_iops]
|
||||
,[volume_managed_xstore_iops]
|
||||
,[volume_external_xstore_iops]
|
||||
,[volume_type_local_iops]
|
||||
,[volume_type_managed_xstore_iops]
|
||||
,[volume_type_external_xstore_iops]
|
||||
,[volume_pfs_iops]
|
||||
,[volume_type_pfs_iops]
|
||||
FROM
|
||||
sys.dm_user_db_resource_governance
|
||||
WHERE database_id = DB_ID();
|
||||
`
|
||||
|
||||
const sqlAzurePoolDatabaseIO = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_database_io' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,(SELECT [elastic_pool_name] FROM sys.database_service_objectives WHERE database_id = DB_ID()) AS [elastic_pool_name]
|
||||
,CASE
|
||||
WHEN vfs.[database_id] = 1 THEN 'master'
|
||||
WHEN vfs.[database_id] = 2 THEN 'tempdb'
|
||||
WHEN vfs.[database_id] = 3 THEN 'model'
|
||||
WHEN vfs.[database_id] = 4 THEN 'msdb'
|
||||
ELSE gov.[database_name]
|
||||
END AS [database_name]
|
||||
,vfs.[database_id]
|
||||
,vfs.[file_id]
|
||||
,CASE
|
||||
WHEN vfs.[file_id] = 2 THEN 'LOG'
|
||||
ELSE 'ROWS'
|
||||
END AS [file_type]
|
||||
,vfs.[num_of_reads] AS [reads]
|
||||
,vfs.[num_of_bytes_read] AS [read_bytes]
|
||||
,vfs.[io_stall_read_ms] AS [read_latency_ms]
|
||||
,vfs.[io_stall_write_ms] AS [write_latency_ms]
|
||||
,vfs.[num_of_writes] AS [writes]
|
||||
,vfs.[num_of_bytes_written] AS [write_bytes]
|
||||
,vfs.[io_stall_queued_read_ms] AS [rg_read_stall_ms]
|
||||
,vfs.[io_stall_queued_write_ms] AS [rg_write_stall_ms]
|
||||
,[size_on_disk_bytes]
|
||||
,ISNULL([size_on_disk_bytes],0)/(1024*1024) AS [size_on_disk_mb]
|
||||
FROM
|
||||
sys.dm_io_virtual_file_stats(NULL,NULL) AS vfs
|
||||
LEFT OUTER JOIN
|
||||
sys.dm_user_db_resource_governance AS gov
|
||||
ON vfs.[database_id] = gov.[database_id];
|
||||
`
|
||||
|
||||
const sqlAzurePoolOsWaitStats = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_waitstats' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,(SELECT [elastic_pool_name] FROM sys.database_service_objectives WHERE database_id = DB_ID()) AS [elastic_pool_name]
|
||||
,[wait_type]
|
||||
,[waiting_tasks_count]
|
||||
,[wait_time_ms]
|
||||
,[max_wait_time_ms]
|
||||
,[signal_wait_time_ms]
|
||||
,[wait_time_ms]-[signal_wait_time_ms] AS [resource_wait_ms]
|
||||
,CASE
|
||||
WHEN ws.[wait_type] LIKE 'SOS_SCHEDULER_YIELD' THEN 'CPU'
|
||||
WHEN ws.[wait_type] = 'THREADPOOL' THEN 'Worker Thread'
|
||||
WHEN ws.[wait_type] LIKE 'LCK[_]%' THEN 'Lock'
|
||||
WHEN ws.[wait_type] LIKE 'LATCH[_]%' THEN 'Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGELATCH[_]%' THEN 'Buffer Latch'
|
||||
WHEN ws.[wait_type] LIKE 'PAGEIOLATCH[_]%' THEN 'Buffer IO'
|
||||
WHEN ws.[wait_type] LIKE 'RESOURCE_SEMAPHORE_QUERY_COMPILE%' THEN 'Compilation'
|
||||
WHEN ws.[wait_type] LIKE 'CLR[_]%' OR ws.[wait_type] LIKE 'SQLCLR%' THEN 'SQL CLR'
|
||||
WHEN ws.[wait_type] LIKE 'DBMIRROR_%' THEN 'Mirroring'
|
||||
WHEN ws.[wait_type] LIKE 'DTC[_]%' OR ws.[wait_type] LIKE 'DTCNEW%' OR ws.[wait_type] LIKE 'TRAN_%'
|
||||
OR ws.[wait_type] LIKE 'XACT%' OR ws.[wait_type] LIKE 'MSQL_XACT%' THEN 'Transaction'
|
||||
WHEN ws.[wait_type] LIKE 'SLEEP[_]%' OR ws.[wait_type] IN (
|
||||
'LAZYWRITER_SLEEP', 'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
'SQLTRACE_WAIT_ENTRIES', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT',
|
||||
'REQUEST_FOR_DEADLOCK_SEARCH', 'LOGMGR_QUEUE', 'ONDEMAND_TASK_QUEUE',
|
||||
'CHECKPOINT_QUEUE', 'XE_TIMER_EVENT') THEN 'Idle'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'ASYNC_IO_COMPLETION','BACKUPIO','CHKPT','WRITE_COMPLETION',
|
||||
'IO_QUEUE_LIMIT', 'IO_RETRY') THEN 'Other Disk IO'
|
||||
WHEN ws.[wait_type] LIKE 'PREEMPTIVE_%' THEN 'Preemptive'
|
||||
WHEN ws.[wait_type] LIKE 'BROKER[_]%' THEN 'Service Broker'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'WRITELOG','LOGBUFFER','LOGMGR_RESERVE_APPEND',
|
||||
'LOGMGR_FLUSH', 'LOGMGR_PMM_LOG') THEN 'Tran Log IO'
|
||||
WHEN ws.[wait_type] LIKE 'LOG_RATE%' then 'Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'HADR_THROTTLE[_]%'
|
||||
OR ws.[wait_type] = 'THROTTLE_LOG_RATE_LOG_STORAGE' THEN 'HADR Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO_RG%' OR ws.[wait_type] LIKE 'WAIT_RBIO_RG%' THEN 'VLDB Log Rate Governor'
|
||||
WHEN ws.[wait_type] LIKE 'RBIO[_]%' OR ws.[wait_type] LIKE 'WAIT_RBIO[_]%' THEN 'VLDB RBIO'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'ASYNC_NETWORK_IO','EXTERNAL_SCRIPT_NETWORK_IOF',
|
||||
'NET_WAITFOR_PACKET','PROXY_NETWORK_IO') THEN 'Network IO'
|
||||
WHEN ws.[wait_type] IN ( 'CXPACKET', 'CXCONSUMER')
|
||||
OR ws.[wait_type] LIKE 'HT%' or ws.[wait_type] LIKE 'BMP%'
|
||||
OR ws.[wait_type] LIKE 'BP%' THEN 'Parallelism'
|
||||
WHEN ws.[wait_type] IN(
|
||||
'CMEMTHREAD','CMEMPARTITIONED','EE_PMOLOCK','EXCHANGE',
|
||||
'RESOURCE_SEMAPHORE','MEMORY_ALLOCATION_EXT',
|
||||
'RESERVED_MEMORY_ALLOCATION_EXT', 'MEMORY_GRANT_UPDATE') THEN 'Memory'
|
||||
WHEN ws.[wait_type] IN ('WAITFOR','WAIT_FOR_RESULTS') THEN 'User Wait'
|
||||
WHEN ws.[wait_type] LIKE 'HADR[_]%' or ws.[wait_type] LIKE 'PWAIT_HADR%'
|
||||
OR ws.[wait_type] LIKE 'REPLICA[_]%' or ws.[wait_type] LIKE 'REPL_%'
|
||||
OR ws.[wait_type] LIKE 'SE_REPL[_]%'
|
||||
OR ws.[wait_type] LIKE 'FCB_REPLICA%' THEN 'Replication'
|
||||
WHEN ws.[wait_type] LIKE 'SQLTRACE[_]%'
|
||||
OR ws.[wait_type] IN (
|
||||
'TRACEWRITE', 'SQLTRACE_LOCK', 'SQLTRACE_FILE_BUFFER', 'SQLTRACE_FILE_WRITE_IO_COMPLETION',
|
||||
'SQLTRACE_FILE_READ_IO_COMPLETION', 'SQLTRACE_PENDING_BUFFER_WRITERS', 'SQLTRACE_SHUTDOWN',
|
||||
'QUERY_TRACEOUT', 'TRACE_EVTNOTIF') THEN 'Tracing'
|
||||
WHEN ws.[wait_type] IN (
|
||||
'FT_RESTART_CRAWL', 'FULLTEXT GATHERER', 'MSSEARCH', 'FT_METADATA_MUTEX',
|
||||
'FT_IFTSHC_MUTEX', 'FT_IFTSISM_MUTEX', 'FT_IFTS_RWLOCK', 'FT_COMPROWSET_RWLOCK',
|
||||
'FT_MASTER_MERGE', 'FT_PROPERTYLIST_CACHE', 'FT_MASTER_MERGE_COORDINATOR',
|
||||
'PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC') THEN 'Full Text Search'
|
||||
ELSE 'Other'
|
||||
END AS [wait_category]
|
||||
FROM sys.dm_os_wait_stats AS ws
|
||||
WHERE
|
||||
ws.[wait_type] NOT IN (
|
||||
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
|
||||
N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
|
||||
N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
|
||||
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_QUEUE',
|
||||
N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
|
||||
N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
|
||||
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT',
|
||||
N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
|
||||
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE',
|
||||
N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
|
||||
N'PARALLEL_REDO_WORKER_WAIT_WORK',
|
||||
N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
|
||||
N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
|
||||
N'PREEMPTIVE_OS_PIPEOPS','PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
|
||||
N'PREEMPTIVE_OS_DEVICEOPS',
|
||||
N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
|
||||
N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
|
||||
N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
|
||||
N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
|
||||
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_ASYNC_QUEUE',
|
||||
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
|
||||
N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
|
||||
N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
|
||||
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
|
||||
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
|
||||
N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
|
||||
N'SQLTRACE_WAIT_ENTRIES', N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN',
|
||||
N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE',
|
||||
N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
|
||||
N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT',
|
||||
N'SOS_WORK_DISPATCHER','RESERVED_MEMORY_ALLOCATION_EXT','SQLTRACE_WAIT_ENTRIES',
|
||||
N'RBIO_COMM_RETRY')
|
||||
AND [waiting_tasks_count] > 10
|
||||
AND [wait_time_ms] > 100;
|
||||
`
|
||||
|
||||
const sqlAzurePoolMemoryClerks = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_memory_clerks' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,(SELECT [elastic_pool_name] FROM sys.database_service_objectives WHERE database_id = DB_ID()) AS [elastic_pool_name]
|
||||
,mc.[type] AS [clerk_type]
|
||||
,SUM(mc.[pages_kb]) AS [size_kb]
|
||||
FROM
|
||||
sys.dm_os_memory_clerks AS mc
|
||||
GROUP BY
|
||||
mc.[type]
|
||||
HAVING
|
||||
SUM(mc.[pages_kb]) >= 1024
|
||||
OPTION(RECOMPILE);
|
||||
`
|
||||
|
||||
// Specific case on this query when cntr_type = 537003264 to return a percentage value between 0 and 100
|
||||
// cf. https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-os-performance-counters-transact-sql?view=azuresqldb-current
|
||||
// Performance counters where the cntr_type column value is 537003264 display the ratio of a subset to its set as a percentage.
|
||||
// For example, the Buffer Manager:Buffer cache hit ratio counter compares the total number of cache hits and the total number of cache lookups.
|
||||
// As such, to get a snapshot-like reading of the last second only, you must compare the delta between the current value and the base value (denominator)
|
||||
// between two collection points that are one second apart.
|
||||
// The corresponding base value is the performance counter Buffer Manager:Buffer cache hit ratio base where the cntr_type column value is 1073939712.
|
||||
const sqlAzurePoolPerformanceCounters = `
|
||||
SET DEADLOCK_PRIORITY -10;
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
DECLARE @PCounters TABLE
|
||||
(
|
||||
[object_name] nvarchar(128),
|
||||
[counter_name] nvarchar(128),
|
||||
[instance_name] nvarchar(128),
|
||||
[cntr_value] bigint,
|
||||
[cntr_type] int
|
||||
Primary Key([object_name],[counter_name],[instance_name])
|
||||
);
|
||||
|
||||
WITH PerfCounters AS (
|
||||
SELECT DISTINCT
|
||||
RTRIM(pc.[object_name]) AS [object_name]
|
||||
,RTRIM(pc.[counter_name]) AS [counter_name]
|
||||
,ISNULL(gov.[database_name], RTRIM(pc.instance_name)) AS [instance_name]
|
||||
,pc.[cntr_value] AS [cntr_value]
|
||||
,pc.[cntr_type] AS [cntr_type]
|
||||
FROM sys.dm_os_performance_counters AS pc
|
||||
LEFT JOIN sys.dm_user_db_resource_governance AS gov
|
||||
ON
|
||||
TRY_CONVERT([uniqueidentifier], pc.[instance_name]) = gov.[physical_database_guid]
|
||||
WHERE
|
||||
/*filter out unnecessary SQL DB system database counters, other than master and tempdb*/
|
||||
NOT (pc.[object_name] LIKE 'MSSQL%:Databases%' AND pc.[instance_name] IN ('model','model_masterdb','model_userdb','msdb','mssqlsystemresource'))
|
||||
AND
|
||||
(
|
||||
pc.[counter_name] IN (
|
||||
'SQL Compilations/sec'
|
||||
,'SQL Re-Compilations/sec'
|
||||
,'User Connections'
|
||||
,'Batch Requests/sec'
|
||||
,'Logouts/sec'
|
||||
,'Logins/sec'
|
||||
,'Processes blocked'
|
||||
,'Latch Waits/sec'
|
||||
,'Full Scans/sec'
|
||||
,'Index Searches/sec'
|
||||
,'Page Splits/sec'
|
||||
,'Page lookups/sec'
|
||||
,'Page reads/sec'
|
||||
,'Page writes/sec'
|
||||
,'Readahead pages/sec'
|
||||
,'Lazy writes/sec'
|
||||
,'Checkpoint pages/sec'
|
||||
,'Table Lock Escalations/sec'
|
||||
,'Page life expectancy'
|
||||
,'Log File(s) Size (KB)'
|
||||
,'Log File(s) Used Size (KB)'
|
||||
,'Data File(s) Size (KB)'
|
||||
,'Transactions/sec'
|
||||
,'Write Transactions/sec'
|
||||
,'Active Transactions'
|
||||
,'Log Growths'
|
||||
,'Active Temp Tables'
|
||||
,'Logical Connections'
|
||||
,'Temp Tables Creation Rate'
|
||||
,'Temp Tables For Destruction'
|
||||
,'Free Space in tempdb (KB)'
|
||||
,'Version Store Size (KB)'
|
||||
,'Memory Grants Pending'
|
||||
,'Memory Grants Outstanding'
|
||||
,'Free list stalls/sec'
|
||||
,'Buffer cache hit ratio'
|
||||
,'Buffer cache hit ratio base'
|
||||
,'Backup/Restore Throughput/sec'
|
||||
,'Total Server Memory (KB)'
|
||||
,'Target Server Memory (KB)'
|
||||
,'Log Flushes/sec'
|
||||
,'Log Flush Wait Time'
|
||||
,'Memory broker clerk size'
|
||||
,'Log Bytes Flushed/sec'
|
||||
,'Bytes Sent to Replica/sec'
|
||||
,'Log Send Queue'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Replica/sec'
|
||||
,'Bytes Sent to Transport/sec'
|
||||
,'Sends to Transport/sec'
|
||||
,'Bytes Received from Replica/sec'
|
||||
,'Receives from Replica/sec'
|
||||
,'Flow Control Time (ms/sec)'
|
||||
,'Flow Control/sec'
|
||||
,'Resent Messages/sec'
|
||||
,'Redone Bytes/sec'
|
||||
,'XTP Memory Used (KB)'
|
||||
,'Transaction Delay'
|
||||
,'Log Bytes Received/sec'
|
||||
,'Log Apply Pending Queue'
|
||||
,'Redone Bytes/sec'
|
||||
,'Recovery Queue'
|
||||
,'Log Apply Ready Queue'
|
||||
,'CPU usage %'
|
||||
,'CPU usage % base'
|
||||
,'Queued requests'
|
||||
,'Requests completed/sec'
|
||||
,'Blocked tasks'
|
||||
,'Active memory grant amount (KB)'
|
||||
,'Disk Read Bytes/sec'
|
||||
,'Disk Read IO Throttled/sec'
|
||||
,'Disk Read IO/sec'
|
||||
,'Disk Write Bytes/sec'
|
||||
,'Disk Write IO Throttled/sec'
|
||||
,'Disk Write IO/sec'
|
||||
,'Used memory (KB)'
|
||||
,'Forwarded Records/sec'
|
||||
,'Background Writer pages/sec'
|
||||
,'Percent Log Used'
|
||||
,'Log Send Queue KB'
|
||||
,'Redo Queue KB'
|
||||
,'Mirrored Write Transactions/sec'
|
||||
,'Group Commit Time'
|
||||
,'Group Commits/Sec'
|
||||
,'Workfiles Created/sec'
|
||||
,'Worktables Created/sec'
|
||||
,'Query Store CPU usage'
|
||||
) OR (
|
||||
pc.[object_name] LIKE '%User Settable%'
|
||||
OR pc.[object_name] LIKE '%SQL Errors%'
|
||||
OR pc.[object_name] LIKE '%Batch Resp Statistics%'
|
||||
) OR (
|
||||
pc.[instance_name] IN ('_Total')
|
||||
AND pc.[counter_name] IN (
|
||||
'Lock Timeouts/sec'
|
||||
,'Lock Timeouts (timeout > 0)/sec'
|
||||
,'Number of Deadlocks/sec'
|
||||
,'Lock Waits/sec'
|
||||
,'Latch Waits/sec'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
INSERT INTO @PCounters select * from PerfCounters
|
||||
|
||||
SELECT
|
||||
'sqlserver_performance' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME,'\',':') AS [sql_instance]
|
||||
,pc.[object_name] AS [object]
|
||||
,pc.[counter_name] AS [counter]
|
||||
,CASE pc.[instance_name] WHEN '_Total' THEN 'Total' ELSE ISNULL(pc.[instance_name],'') END AS [instance]
|
||||
,CAST(
|
||||
CASE WHEN pc.[cntr_type] = 537003264 AND base.[cntr_value] > 0
|
||||
THEN (pc.[cntr_value] * 1.0) / (base.[cntr_value] * 1.0) * 100
|
||||
ELSE pc.[cntr_value]
|
||||
END
|
||||
AS float) AS [value]
|
||||
,CAST(pc.[cntr_type] AS varchar(25)) AS [counter_type]
|
||||
FROM @PCounters AS pc
|
||||
LEFT OUTER JOIN @PCounters AS base
|
||||
ON
|
||||
pc.[counter_name] = REPLACE(base.[counter_name],' base','')
|
||||
AND pc.[object_name] = base.[object_name]
|
||||
AND pc.[instance_name] = base.[instance_name]
|
||||
AND base.[cntr_type] = 1073939712
|
||||
WHERE
|
||||
pc.[cntr_type] <> 1073939712
|
||||
OPTION(RECOMPILE)
|
||||
`
|
||||
|
||||
const sqlAzurePoolSchedulers = `
|
||||
IF SERVERPROPERTY('EngineEdition') <> 5
|
||||
OR NOT EXISTS (SELECT 1 FROM sys.database_service_objectives WHERE database_id = DB_ID() AND elastic_pool_name IS NOT NULL) BEGIN
|
||||
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@SERVERNAME + ',Database:' + DB_NAME() +' is not an Azure SQL database in an elastic pool. Check the database_type parameter in the telegraf configuration.';
|
||||
RAISERROR (@ErrorMessage,11,1)
|
||||
RETURN
|
||||
END
|
||||
|
||||
SELECT
|
||||
'sqlserver_schedulers' AS [measurement]
|
||||
,REPLACE(@@SERVERNAME, '\', ':') AS [sql_instance]
|
||||
,(SELECT [elastic_pool_name] FROM sys.database_service_objectives WHERE database_id = DB_ID()) AS [elastic_pool_name]
|
||||
,[scheduler_id]
|
||||
,[cpu_id]
|
||||
,[status]
|
||||
,[is_online]
|
||||
,[is_idle]
|
||||
,[preemptive_switches_count]
|
||||
,[context_switches_count]
|
||||
,[idle_switches_count]
|
||||
,[current_tasks_count]
|
||||
,[runnable_tasks_count]
|
||||
,[current_workers_count]
|
||||
,[active_workers_count]
|
||||
,[work_queue_count]
|
||||
,[pending_disk_io_count]
|
||||
,[load_factor]
|
||||
,[failed_to_create_worker]
|
||||
,[quantum_length_us]
|
||||
,[yield_count]
|
||||
,[total_cpu_usage_ms]
|
||||
,[total_cpu_idle_capped_ms]
|
||||
,[total_scheduler_delay_ms]
|
||||
,[ideal_workers_limit]
|
||||
FROM
|
||||
sys.dm_os_schedulers;
|
||||
`
|
322
plugins/inputs/sqlserver/azuresqlpoolqueries_test.go
Normal file
322
plugins/inputs/sqlserver/azuresqlpoolqueries_test.go
Normal file
|
@ -0,0 +1,322 @@
|
|||
package sqlserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_ResourceStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolResourceStats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_pool_resource_stats"))
|
||||
require.True(t, acc.HasTag("sqlserver_pool_resource_stats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_pool_resource_stats", "elastic_pool_name"))
|
||||
require.True(t, acc.HasField("sqlserver_pool_resource_stats", "snapshot_time"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "avg_cpu_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "avg_data_io_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "avg_log_write_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "avg_storage_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "max_worker_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "max_session_percent"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_stats", "storage_limit_mb"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "avg_instance_cpu_percent"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_stats", "avg_allocated_storage_percent"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_ResourceGovernance_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolResourceGovernance"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_pool_resource_governance"))
|
||||
require.True(t, acc.HasTag("sqlserver_pool_resource_governance", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_pool_resource_governance", "elastic_pool_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_pool_resource_governance", "slo_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "dtu_limit"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "cpu_limit"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "max_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "cap_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "max_db_memory"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "max_db_max_size_in_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "db_file_growth_in_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "log_size_in_mb"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "instance_cap_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "instance_max_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "instance_max_worker_threads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "checkpoint_rate_mbps"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "checkpoint_rate_io"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "primary_group_max_workers"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "primary_min_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "primary_max_log_rate"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "primary_group_min_io"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "primary_group_max_io"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_governance", "primary_group_min_cpu"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_pool_resource_governance", "primary_group_max_cpu"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "primary_pool_max_workers"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "pool_max_io"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_local_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_managed_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_external_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_type_local_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_type_managed_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_type_external_xstore_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_pfs_iops"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_pool_resource_governance", "volume_type_pfs_iops"))
|
||||
|
||||
// This query should only return one row
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_DatabaseIO_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolDatabaseIO"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_database_io"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "elastic_pool_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "database_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "database_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "file_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_database_io", "file_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "reads"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "read_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_latency_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "writes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "write_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_read_stall_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "rg_write_stall_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "size_on_disk_bytes"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_database_io", "size_on_disk_mb"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_OsWaitStats_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolOsWaitStats"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_waitstats"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "elastic_pool_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "waiting_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "max_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "signal_wait_time_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_waitstats", "resource_wait_ms"))
|
||||
require.True(t, acc.HasTag("sqlserver_waitstats", "wait_category"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_MemoryClerks_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolMemoryClerks"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_memory_clerks"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "elastic_pool_name"))
|
||||
require.True(t, acc.HasTag("sqlserver_memory_clerks", "clerk_type"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_memory_clerks", "size_kb"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_PerformanceCounters_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolPerformanceCounters"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_performance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "object"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "instance"))
|
||||
require.True(t, acc.HasFloatField("sqlserver_performance", "value"))
|
||||
require.True(t, acc.HasTag("sqlserver_performance", "counter_type"))
|
||||
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func TestAzureSQLIntegration_ElasticPool_Schedulers_Query(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
if os.Getenv("AZURESQL_POOL_CONNECTION_STRING") == "" {
|
||||
t.Skip("Missing environment variable AZURESQL_POOL_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
connectionString := os.Getenv("AZURESQL_POOL_CONNECTION_STRING")
|
||||
sl := config.NewSecret([]byte(connectionString))
|
||||
|
||||
server := &SQLServer{
|
||||
Servers: []*config.Secret{&sl},
|
||||
IncludeQuery: []string{"AzureSQLPoolSchedulers"},
|
||||
AuthMethod: "connection_string",
|
||||
DatabaseType: "AzureSQLPool",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, server.Start(&acc))
|
||||
require.NoError(t, server.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("sqlserver_schedulers"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "sql_instance"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "elastic_pool_name"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "scheduler_id"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "cpu_id"))
|
||||
require.True(t, acc.HasTag("sqlserver_schedulers", "status"))
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_online"))
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "is_idle"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "preemptive_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "context_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "idle_switches_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "runnable_tasks_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "current_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "active_workers_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "work_queue_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "pending_disk_io_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "load_factor"))
|
||||
require.True(t, acc.HasField("sqlserver_schedulers", "failed_to_create_worker"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "quantum_length_us"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "yield_count"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_cpu_usage_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_cpu_idle_capped_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "total_scheduler_delay_ms"))
|
||||
require.True(t, acc.HasInt64Field("sqlserver_schedulers", "ideal_workers_limit"))
|
||||
|
||||
server.Stop()
|
||||
}
|
17
plugins/inputs/sqlserver/azuretoken.go
Normal file
17
plugins/inputs/sqlserver/azuretoken.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package sqlserver
|
||||
|
||||
import "time"
|
||||
|
||||
// New token structure for Azure Identity SDK
|
||||
type azureToken struct {
|
||||
token string
|
||||
expiresOn time.Time
|
||||
}
|
||||
|
||||
// IsExpired helper method for Azure token expiry
|
||||
func (t *azureToken) IsExpired() bool {
|
||||
if t == nil {
|
||||
return true
|
||||
}
|
||||
return time.Now().After(t.expiresOn)
|
||||
}
|
98
plugins/inputs/sqlserver/connectionstring.go
Normal file
98
plugins/inputs/sqlserver/connectionstring.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package sqlserver
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
emptySQLInstance = "<empty-sql-instance>"
|
||||
emptyDatabaseName = "<empty-database-name>"
|
||||
)
|
||||
|
||||
// getConnectionIdentifiers returns the sqlInstance and databaseName from the given connection string.
|
||||
// The name of the SQL instance is returned as-is in the connection string
|
||||
// If the connection string could not be parsed or sqlInstance/databaseName were not present, a placeholder value is returned
|
||||
func getConnectionIdentifiers(connectionString string) (sqlInstance, databaseName string) {
|
||||
if len(connectionString) == 0 {
|
||||
return emptySQLInstance, emptyDatabaseName
|
||||
}
|
||||
|
||||
trimmedConnectionString := strings.TrimSpace(connectionString)
|
||||
|
||||
if strings.HasPrefix(trimmedConnectionString, "odbc:") {
|
||||
connectionStringWithoutOdbc := strings.TrimPrefix(trimmedConnectionString, "odbc:")
|
||||
return parseConnectionStringKeyValue(connectionStringWithoutOdbc)
|
||||
}
|
||||
if strings.HasPrefix(trimmedConnectionString, "sqlserver://") {
|
||||
return parseConnectionStringURL(trimmedConnectionString)
|
||||
}
|
||||
return parseConnectionStringKeyValue(trimmedConnectionString)
|
||||
}
|
||||
|
||||
// parseConnectionStringKeyValue parses a "key=value;" connection string and returns the SQL instance and database name
|
||||
func parseConnectionStringKeyValue(connectionString string) (sqlInstance, databaseName string) {
|
||||
sqlInstance = ""
|
||||
databaseName = ""
|
||||
|
||||
keyValuePairs := strings.Split(connectionString, ";")
|
||||
for _, keyValuePair := range keyValuePairs {
|
||||
if len(keyValuePair) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
keyAndValue := strings.SplitN(keyValuePair, "=", 2)
|
||||
key := strings.TrimSpace(strings.ToLower(keyAndValue[0]))
|
||||
if len(key) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
value := ""
|
||||
if len(keyAndValue) > 1 {
|
||||
value = strings.TrimSpace(keyAndValue[1])
|
||||
}
|
||||
if strings.EqualFold("server", key) {
|
||||
sqlInstance = value
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold("database", key) {
|
||||
databaseName = value
|
||||
}
|
||||
}
|
||||
|
||||
if sqlInstance == "" {
|
||||
sqlInstance = emptySQLInstance
|
||||
}
|
||||
if databaseName == "" {
|
||||
databaseName = emptyDatabaseName
|
||||
}
|
||||
|
||||
return sqlInstance, databaseName
|
||||
}
|
||||
|
||||
// parseConnectionStringURL parses a URL-formatted connection string and returns the SQL instance and database name
|
||||
func parseConnectionStringURL(connectionString string) (sqlInstance, databaseName string) {
|
||||
databaseName = emptyDatabaseName
|
||||
|
||||
u, err := url.Parse(connectionString)
|
||||
if err != nil {
|
||||
return emptySQLInstance, emptyDatabaseName
|
||||
}
|
||||
|
||||
sqlInstance = u.Hostname()
|
||||
if len(u.Path) > 1 {
|
||||
// There was a SQL instance name specified in addition to the host
|
||||
// E.g. "the.host.com:1234/InstanceName" or "the.host.com/InstanceName"
|
||||
sqlInstance = sqlInstance + "\\" + u.Path[1:]
|
||||
}
|
||||
|
||||
query := u.Query()
|
||||
for key, value := range query {
|
||||
if strings.EqualFold("database", key) {
|
||||
databaseName = value[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return sqlInstance, databaseName
|
||||
}
|
131
plugins/inputs/sqlserver/sample.conf
Normal file
131
plugins/inputs/sqlserver/sample.conf
Normal file
|
@ -0,0 +1,131 @@
|
|||
# Read metrics from Microsoft SQL Server
|
||||
[[inputs.sqlserver]]
|
||||
## Specify instances to monitor with a list of connection strings.
|
||||
## All connection parameters are optional.
|
||||
## By default, the host is localhost, listening on default port, TCP 1433.
|
||||
## for Windows, the user is the currently running AD user (SSO).
|
||||
## See https://github.com/microsoft/go-mssqldb for detailed connection
|
||||
## parameters, in particular, tls connections can be created like so:
|
||||
## "encrypt=true;certificate=<cert>;hostNameInCertificate=<SqlServer host fqdn>"
|
||||
servers = [
|
||||
"Server=192.168.1.10;Port=1433;User Id=<user>;Password=<pw>;app name=telegraf;log=1;",
|
||||
]
|
||||
|
||||
## Timeout for query execution operation
|
||||
## Note that the timeout for queries is per query not per gather.
|
||||
## 0 value means no timeout
|
||||
# query_timeout = "0s"
|
||||
|
||||
## Authentication method
|
||||
## valid methods: "connection_string", "AAD"
|
||||
# auth_method = "connection_string"
|
||||
|
||||
## ClientID is the is the client ID of the user assigned identity of the VM
|
||||
## that should be used to authenticate to the Azure SQL server.
|
||||
# client_id = ""
|
||||
|
||||
## "database_type" enables a specific set of queries depending on the database type. If specified, it replaces azuredb = true/false and query_version = 2
|
||||
## In the config file, the sql server plugin section should be repeated each with a set of servers for a specific database_type.
|
||||
## Possible values for database_type are - "SQLServer" or "AzureSQLDB" or "AzureSQLManagedInstance" or "AzureSQLPool"
|
||||
database_type = "SQLServer"
|
||||
|
||||
## A list of queries to include. If not specified, all the below listed queries are used.
|
||||
include_query = []
|
||||
|
||||
## A list of queries to explicitly ignore.
|
||||
exclude_query = ["SQLServerAvailabilityReplicaStates", "SQLServerDatabaseReplicaStates"]
|
||||
|
||||
## Force using the deprecated ADAL authentication method instead of the recommended
|
||||
## MSAL method. Setting this option is not recommended and only exists for backward
|
||||
## compatibility.
|
||||
# use_deprecated_adal_authentication = false
|
||||
|
||||
## Queries enabled by default for database_type = "SQLServer" are -
|
||||
## SQLServerPerformanceCounters, SQLServerWaitStatsCategorized, SQLServerDatabaseIO, SQLServerProperties, SQLServerMemoryClerks,
|
||||
## SQLServerSchedulers, SQLServerRequests, SQLServerVolumeSpace, SQLServerCpu, SQLServerAvailabilityReplicaStates, SQLServerDatabaseReplicaStates,
|
||||
## SQLServerRecentBackups
|
||||
|
||||
## Queries enabled by default for database_type = "AzureSQLDB" are -
|
||||
## AzureSQLDBResourceStats, AzureSQLDBResourceGovernance, AzureSQLDBWaitStats, AzureSQLDBDatabaseIO, AzureSQLDBServerProperties,
|
||||
## AzureSQLDBOsWaitstats, AzureSQLDBMemoryClerks, AzureSQLDBPerformanceCounters, AzureSQLDBRequests, AzureSQLDBSchedulers
|
||||
|
||||
## Queries enabled by default for database_type = "AzureSQLManagedInstance" are -
|
||||
## AzureSQLMIResourceStats, AzureSQLMIResourceGovernance, AzureSQLMIDatabaseIO, AzureSQLMIServerProperties, AzureSQLMIOsWaitstats,
|
||||
## AzureSQLMIMemoryClerks, AzureSQLMIPerformanceCounters, AzureSQLMIRequests, AzureSQLMISchedulers
|
||||
|
||||
## Queries enabled by default for database_type = "AzureSQLPool" are -
|
||||
## AzureSQLPoolResourceStats, AzureSQLPoolResourceGovernance, AzureSQLPoolDatabaseIO, AzureSQLPoolWaitStats,
|
||||
## AzureSQLPoolMemoryClerks, AzureSQLPoolPerformanceCounters, AzureSQLPoolSchedulers
|
||||
|
||||
## Queries enabled by default for database_type = "AzureArcSQLManagedInstance" are -
|
||||
## AzureSQLMIDatabaseIO, AzureSQLMIServerProperties, AzureSQLMIOsWaitstats,
|
||||
## AzureSQLMIMemoryClerks, AzureSQLMIPerformanceCounters, AzureSQLMIRequests, AzureSQLMISchedulers
|
||||
|
||||
## Following are old config settings
|
||||
## You may use them only if you are using the earlier flavor of queries, however it is recommended to use
|
||||
## the new mechanism of identifying the database_type there by use it's corresponding queries
|
||||
|
||||
## Optional parameter, setting this to 2 will use a new version
|
||||
## of the collection queries that break compatibility with the original
|
||||
## dashboards.
|
||||
## Version 2 - is compatible from SQL Server 2012 and later versions and also for SQL Azure DB
|
||||
# query_version = 2
|
||||
|
||||
## If you are using AzureDB, setting this to true will gather resource utilization metrics
|
||||
# azuredb = false
|
||||
|
||||
## Toggling this to true will emit an additional metric called "sqlserver_telegraf_health".
|
||||
## This metric tracks the count of attempted queries and successful queries for each SQL instance specified in "servers".
|
||||
## The purpose of this metric is to assist with identifying and diagnosing any connectivity or query issues.
|
||||
## This setting/metric is optional and is disabled by default.
|
||||
# health_metric = false
|
||||
|
||||
## Possible queries across different versions of the collectors
|
||||
## Queries enabled by default for specific Database Type
|
||||
|
||||
## database_type = AzureSQLDB by default collects the following queries
|
||||
## - AzureSQLDBWaitStats
|
||||
## - AzureSQLDBResourceStats
|
||||
## - AzureSQLDBResourceGovernance
|
||||
## - AzureSQLDBDatabaseIO
|
||||
## - AzureSQLDBServerProperties
|
||||
## - AzureSQLDBOsWaitstats
|
||||
## - AzureSQLDBMemoryClerks
|
||||
## - AzureSQLDBPerformanceCounters
|
||||
## - AzureSQLDBRequests
|
||||
## - AzureSQLDBSchedulers
|
||||
|
||||
## database_type = AzureSQLManagedInstance by default collects the following queries
|
||||
## - AzureSQLMIResourceStats
|
||||
## - AzureSQLMIResourceGovernance
|
||||
## - AzureSQLMIDatabaseIO
|
||||
## - AzureSQLMIServerProperties
|
||||
## - AzureSQLMIOsWaitstats
|
||||
## - AzureSQLMIMemoryClerks
|
||||
## - AzureSQLMIPerformanceCounters
|
||||
## - AzureSQLMIRequests
|
||||
## - AzureSQLMISchedulers
|
||||
|
||||
## database_type = AzureSQLPool by default collects the following queries
|
||||
## - AzureSQLPoolResourceStats
|
||||
## - AzureSQLPoolResourceGovernance
|
||||
## - AzureSQLPoolDatabaseIO
|
||||
## - AzureSQLPoolOsWaitStats,
|
||||
## - AzureSQLPoolMemoryClerks
|
||||
## - AzureSQLPoolPerformanceCounters
|
||||
## - AzureSQLPoolSchedulers
|
||||
|
||||
## database_type = SQLServer by default collects the following queries
|
||||
## - SQLServerPerformanceCounters
|
||||
## - SQLServerWaitStatsCategorized
|
||||
## - SQLServerDatabaseIO
|
||||
## - SQLServerProperties
|
||||
## - SQLServerMemoryClerks
|
||||
## - SQLServerSchedulers
|
||||
## - SQLServerRequests
|
||||
## - SQLServerVolumeSpace
|
||||
## - SQLServerCpu
|
||||
## - SQLServerRecentBackups
|
||||
## and following as optional (if mentioned in the include_query list)
|
||||
## - SQLServerAvailabilityReplicaStates
|
||||
## - SQLServerDatabaseReplicaStates
|
1409
plugins/inputs/sqlserver/sqlqueriesV1.go
Normal file
1409
plugins/inputs/sqlserver/sqlqueriesV1.go
Normal file
File diff suppressed because it is too large
Load diff
1427
plugins/inputs/sqlserver/sqlqueriesV2.go
Normal file
1427
plugins/inputs/sqlserver/sqlqueriesV2.go
Normal file
File diff suppressed because it is too large
Load diff
720
plugins/inputs/sqlserver/sqlserver.go
Normal file
720
plugins/inputs/sqlserver/sqlserver.go
Normal file
|
@ -0,0 +1,720 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
// Legacy ADAL package - kept for backward compatibility
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
mssql "github.com/microsoft/go-mssqldb"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/filter"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
const (
|
||||
defaultServer = "Server=.;app name=telegraf;log=1;"
|
||||
|
||||
typeAzureSQLDB = "AzureSQLDB"
|
||||
typeAzureSQLManagedInstance = "AzureSQLManagedInstance"
|
||||
typeAzureSQLPool = "AzureSQLPool"
|
||||
typeSQLServer = "SQLServer"
|
||||
typeAzureArcSQLManagedInstance = "AzureArcSQLManagedInstance"
|
||||
|
||||
healthMetricName = "sqlserver_telegraf_health"
|
||||
healthMetricInstanceTag = "sql_instance"
|
||||
healthMetricDatabaseTag = "database_name"
|
||||
healthMetricAttemptedQueries = "attempted_queries"
|
||||
healthMetricSuccessfulQueries = "successful_queries"
|
||||
healthMetricDatabaseType = "database_type"
|
||||
|
||||
sqlAzureResourceID = "https://database.windows.net/"
|
||||
)
|
||||
|
||||
type SQLServer struct {
|
||||
Servers []*config.Secret `toml:"servers"`
|
||||
QueryTimeout config.Duration `toml:"query_timeout"`
|
||||
AuthMethod string `toml:"auth_method"`
|
||||
ClientID string `toml:"client_id"`
|
||||
QueryVersion int `toml:"query_version" deprecated:"1.16.0;1.35.0;use 'database_type' instead"`
|
||||
AzureDB bool `toml:"azuredb" deprecated:"1.16.0;1.35.0;use 'database_type' instead"`
|
||||
DatabaseType string `toml:"database_type"`
|
||||
IncludeQuery []string `toml:"include_query"`
|
||||
ExcludeQuery []string `toml:"exclude_query"`
|
||||
HealthMetric bool `toml:"health_metric"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
pools []*sql.DB
|
||||
queries mapQuery
|
||||
|
||||
// Legacy token - kept for backward compatibility
|
||||
adalToken *adal.Token
|
||||
// New token using Azure Identity SDK
|
||||
azToken *azureToken
|
||||
// Config option to use legacy ADAL authentication instead of the newer Azure Identity SDK
|
||||
// When true, the deprecated ADAL library will be used
|
||||
// When false (default), the new Azure Identity SDK will be used
|
||||
UseAdalToken bool `toml:"use_deprecated_adal_authentication" deprecated:"1.40.0;migrate to MSAL authentication"`
|
||||
|
||||
muCacheLock sync.RWMutex
|
||||
}
|
||||
|
||||
type query struct {
|
||||
ScriptName string
|
||||
Script string
|
||||
ResultByRow bool
|
||||
OrderedColumns []string
|
||||
}
|
||||
|
||||
type mapQuery map[string]query
|
||||
|
||||
// healthMetric struct tracking the number of attempted vs. successful connections for each connection string
|
||||
type healthMetric struct {
|
||||
attemptedQueries int
|
||||
successfulQueries int
|
||||
}
|
||||
|
||||
type scanner interface {
|
||||
Scan(dest ...interface{}) error
|
||||
}
|
||||
|
||||
func (*SQLServer) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (s *SQLServer) Init() error {
|
||||
if len(s.Servers) == 0 {
|
||||
srv := config.NewSecret([]byte(defaultServer))
|
||||
s.Servers = append(s.Servers, &srv)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start initialize a list of connection pools
|
||||
func (s *SQLServer) Start(acc telegraf.Accumulator) error {
|
||||
if err := s.initQueries(); err != nil {
|
||||
acc.AddError(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// initialize mutual exclusion lock
|
||||
s.muCacheLock = sync.RWMutex{}
|
||||
|
||||
for _, serv := range s.Servers {
|
||||
var pool *sql.DB
|
||||
|
||||
switch strings.ToLower(s.AuthMethod) {
|
||||
case "connection_string":
|
||||
// Get the connection string potentially containing secrets
|
||||
dsn, err := serv.Get()
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Use the DSN (connection string) directly. In this case,
|
||||
// empty username/password causes use of Windows
|
||||
// integrated authentication.
|
||||
pool, err = sql.Open("mssql", dsn.String())
|
||||
dsn.Destroy()
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
}
|
||||
case "aad":
|
||||
// AAD Auth with system-assigned managed identity (MSI)
|
||||
// AAD Auth is only supported for Azure SQL Database or Azure SQL Managed Instance
|
||||
if s.DatabaseType == "SQLServer" {
|
||||
err := errors.New("database connection failed : AAD auth is not supported for SQL VM i.e. DatabaseType=SQLServer")
|
||||
acc.AddError(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// get token from in-memory cache variable or from Azure Active Directory
|
||||
tokenProvider, err := s.getTokenProvider()
|
||||
if err != nil {
|
||||
acc.AddError(fmt.Errorf("error creating AAD token provider for system assigned Azure managed identity: %w", err))
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the connection string potentially containing secrets
|
||||
dsn, err := serv.Get()
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
}
|
||||
connector, err := mssql.NewAccessTokenConnector(dsn.String(), tokenProvider)
|
||||
dsn.Destroy()
|
||||
if err != nil {
|
||||
acc.AddError(fmt.Errorf("error creating the SQL connector: %w", err))
|
||||
continue
|
||||
}
|
||||
|
||||
pool = sql.OpenDB(connector)
|
||||
default:
|
||||
return fmt.Errorf("unknown auth method: %v", s.AuthMethod)
|
||||
}
|
||||
|
||||
s.pools = append(s.pools, pool)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLServer) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
var mutex sync.Mutex
|
||||
var healthMetrics = make(map[string]*healthMetric)
|
||||
|
||||
for i, pool := range s.pools {
|
||||
dnsSecret, err := s.Servers[i].Get()
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
}
|
||||
dsn := dnsSecret.String()
|
||||
dnsSecret.Destroy()
|
||||
|
||||
for _, q := range s.queries {
|
||||
wg.Add(1)
|
||||
go func(pool *sql.DB, q query, dsn string) {
|
||||
defer wg.Done()
|
||||
queryError := s.gatherServer(pool, q, acc, dsn)
|
||||
|
||||
if s.HealthMetric {
|
||||
mutex.Lock()
|
||||
gatherHealth(healthMetrics, dsn, queryError)
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
acc.AddError(queryError)
|
||||
}(pool, q, dsn)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if s.HealthMetric {
|
||||
s.accHealth(healthMetrics, acc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop cleanup server connection pools
|
||||
func (s *SQLServer) Stop() {
|
||||
for _, pool := range s.pools {
|
||||
_ = pool.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLServer) initQueries() error {
|
||||
s.queries = make(mapQuery)
|
||||
queries := s.queries
|
||||
s.Log.Infof("Config: database_type: %s , query_version:%d , azuredb: %t", s.DatabaseType, s.QueryVersion, s.AzureDB)
|
||||
|
||||
// To prevent query definition conflicts
|
||||
// Constant definitions for type "AzureSQLDB" start with sqlAzureDB
|
||||
// Constant definitions for type "AzureSQLManagedInstance" start with sqlAzureMI
|
||||
// Constant definitions for type "AzureSQLPool" start with sqlAzurePool
|
||||
// Constant definitions for type "AzureArcSQLManagedInstance" start with sqlAzureArcMI
|
||||
// Constant definitions for type "SQLServer" start with sqlServer
|
||||
if s.DatabaseType == typeAzureSQLDB {
|
||||
queries["AzureSQLDBResourceStats"] = query{ScriptName: "AzureSQLDBResourceStats", Script: sqlAzureDBResourceStats, ResultByRow: false}
|
||||
queries["AzureSQLDBResourceGovernance"] = query{ScriptName: "AzureSQLDBResourceGovernance", Script: sqlAzureDBResourceGovernance, ResultByRow: false}
|
||||
queries["AzureSQLDBWaitStats"] = query{ScriptName: "AzureSQLDBWaitStats", Script: sqlAzureDBWaitStats, ResultByRow: false}
|
||||
queries["AzureSQLDBDatabaseIO"] = query{ScriptName: "AzureSQLDBDatabaseIO", Script: sqlAzureDBDatabaseIO, ResultByRow: false}
|
||||
queries["AzureSQLDBServerProperties"] = query{ScriptName: "AzureSQLDBServerProperties", Script: sqlAzureDBProperties, ResultByRow: false}
|
||||
queries["AzureSQLDBOsWaitstats"] = query{ScriptName: "AzureSQLOsWaitstats", Script: sqlAzureDBOsWaitStats, ResultByRow: false}
|
||||
queries["AzureSQLDBMemoryClerks"] = query{ScriptName: "AzureSQLDBMemoryClerks", Script: sqlAzureDBMemoryClerks, ResultByRow: false}
|
||||
queries["AzureSQLDBPerformanceCounters"] = query{ScriptName: "AzureSQLDBPerformanceCounters", Script: sqlAzureDBPerformanceCounters, ResultByRow: false}
|
||||
queries["AzureSQLDBRequests"] = query{ScriptName: "AzureSQLDBRequests", Script: sqlAzureDBRequests, ResultByRow: false}
|
||||
queries["AzureSQLDBSchedulers"] = query{ScriptName: "AzureSQLDBSchedulers", Script: sqlAzureDBSchedulers, ResultByRow: false}
|
||||
} else if s.DatabaseType == typeAzureSQLManagedInstance {
|
||||
queries["AzureSQLMIResourceStats"] = query{ScriptName: "AzureSQLMIResourceStats", Script: sqlAzureMIResourceStats, ResultByRow: false}
|
||||
queries["AzureSQLMIResourceGovernance"] = query{ScriptName: "AzureSQLMIResourceGovernance", Script: sqlAzureMIResourceGovernance, ResultByRow: false}
|
||||
queries["AzureSQLMIDatabaseIO"] = query{ScriptName: "AzureSQLMIDatabaseIO", Script: sqlAzureMIDatabaseIO, ResultByRow: false}
|
||||
queries["AzureSQLMIServerProperties"] = query{ScriptName: "AzureSQLMIServerProperties", Script: sqlAzureMIProperties, ResultByRow: false}
|
||||
queries["AzureSQLMIOsWaitstats"] = query{ScriptName: "AzureSQLMIOsWaitstats", Script: sqlAzureMIOsWaitStats, ResultByRow: false}
|
||||
queries["AzureSQLMIMemoryClerks"] = query{ScriptName: "AzureSQLMIMemoryClerks", Script: sqlAzureMIMemoryClerks, ResultByRow: false}
|
||||
queries["AzureSQLMIPerformanceCounters"] = query{ScriptName: "AzureSQLMIPerformanceCounters", Script: sqlAzureMIPerformanceCounters, ResultByRow: false}
|
||||
queries["AzureSQLMIRequests"] = query{ScriptName: "AzureSQLMIRequests", Script: sqlAzureMIRequests, ResultByRow: false}
|
||||
queries["AzureSQLMISchedulers"] = query{ScriptName: "AzureSQLMISchedulers", Script: sqlAzureMISchedulers, ResultByRow: false}
|
||||
} else if s.DatabaseType == typeAzureSQLPool {
|
||||
queries["AzureSQLPoolResourceStats"] = query{ScriptName: "AzureSQLPoolResourceStats", Script: sqlAzurePoolResourceStats, ResultByRow: false}
|
||||
queries["AzureSQLPoolResourceGovernance"] =
|
||||
query{ScriptName: "AzureSQLPoolResourceGovernance", Script: sqlAzurePoolResourceGovernance, ResultByRow: false}
|
||||
queries["AzureSQLPoolDatabaseIO"] = query{ScriptName: "AzureSQLPoolDatabaseIO", Script: sqlAzurePoolDatabaseIO, ResultByRow: false}
|
||||
queries["AzureSQLPoolOsWaitStats"] = query{ScriptName: "AzureSQLPoolOsWaitStats", Script: sqlAzurePoolOsWaitStats, ResultByRow: false}
|
||||
queries["AzureSQLPoolMemoryClerks"] = query{ScriptName: "AzureSQLPoolMemoryClerks", Script: sqlAzurePoolMemoryClerks, ResultByRow: false}
|
||||
queries["AzureSQLPoolPerformanceCounters"] =
|
||||
query{ScriptName: "AzureSQLPoolPerformanceCounters", Script: sqlAzurePoolPerformanceCounters, ResultByRow: false}
|
||||
queries["AzureSQLPoolSchedulers"] = query{ScriptName: "AzureSQLPoolSchedulers", Script: sqlAzurePoolSchedulers, ResultByRow: false}
|
||||
} else if s.DatabaseType == typeAzureArcSQLManagedInstance {
|
||||
queries["AzureArcSQLMIDatabaseIO"] = query{ScriptName: "AzureArcSQLMIDatabaseIO", Script: sqlAzureArcMIDatabaseIO, ResultByRow: false}
|
||||
queries["AzureArcSQLMIServerProperties"] = query{ScriptName: "AzureArcSQLMIServerProperties", Script: sqlAzureArcMIProperties, ResultByRow: false}
|
||||
queries["AzureArcSQLMIOsWaitstats"] = query{ScriptName: "AzureArcSQLMIOsWaitstats", Script: sqlAzureArcMIOsWaitStats, ResultByRow: false}
|
||||
queries["AzureArcSQLMIMemoryClerks"] = query{ScriptName: "AzureArcSQLMIMemoryClerks", Script: sqlAzureArcMIMemoryClerks, ResultByRow: false}
|
||||
queries["AzureArcSQLMIPerformanceCounters"] =
|
||||
query{ScriptName: "AzureArcSQLMIPerformanceCounters", Script: sqlAzureArcMIPerformanceCounters, ResultByRow: false}
|
||||
queries["AzureArcSQLMIRequests"] = query{ScriptName: "AzureArcSQLMIRequests", Script: sqlAzureArcMIRequests, ResultByRow: false}
|
||||
queries["AzureArcSQLMISchedulers"] = query{ScriptName: "AzureArcSQLMISchedulers", Script: sqlAzureArcMISchedulers, ResultByRow: false}
|
||||
} else if s.DatabaseType == typeSQLServer { // These are still V2 queries and have not been refactored yet.
|
||||
queries["SQLServerPerformanceCounters"] = query{ScriptName: "SQLServerPerformanceCounters", Script: sqlServerPerformanceCounters, ResultByRow: false}
|
||||
queries["SQLServerWaitStatsCategorized"] = query{ScriptName: "SQLServerWaitStatsCategorized", Script: sqlServerWaitStatsCategorized, ResultByRow: false}
|
||||
queries["SQLServerDatabaseIO"] = query{ScriptName: "SQLServerDatabaseIO", Script: sqlServerDatabaseIO, ResultByRow: false}
|
||||
queries["SQLServerProperties"] = query{ScriptName: "SQLServerProperties", Script: sqlServerProperties, ResultByRow: false}
|
||||
queries["SQLServerMemoryClerks"] = query{ScriptName: "SQLServerMemoryClerks", Script: sqlServerMemoryClerks, ResultByRow: false}
|
||||
queries["SQLServerSchedulers"] = query{ScriptName: "SQLServerSchedulers", Script: sqlServerSchedulers, ResultByRow: false}
|
||||
queries["SQLServerRequests"] = query{ScriptName: "SQLServerRequests", Script: sqlServerRequests, ResultByRow: false}
|
||||
queries["SQLServerVolumeSpace"] = query{ScriptName: "SQLServerVolumeSpace", Script: sqlServerVolumeSpace, ResultByRow: false}
|
||||
queries["SQLServerCpu"] = query{ScriptName: "SQLServerCpu", Script: sqlServerRingBufferCPU, ResultByRow: false}
|
||||
queries["SQLServerAvailabilityReplicaStates"] =
|
||||
query{ScriptName: "SQLServerAvailabilityReplicaStates", Script: sqlServerAvailabilityReplicaStates, ResultByRow: false}
|
||||
queries["SQLServerDatabaseReplicaStates"] =
|
||||
query{ScriptName: "SQLServerDatabaseReplicaStates", Script: sqlServerDatabaseReplicaStates, ResultByRow: false}
|
||||
queries["SQLServerRecentBackups"] = query{ScriptName: "SQLServerRecentBackups", Script: sqlServerRecentBackups, ResultByRow: false}
|
||||
queries["SQLServerPersistentVersionStore"] =
|
||||
query{ScriptName: "SQLServerPersistentVersionStore", Script: sqlServerPersistentVersionStore, ResultByRow: false}
|
||||
} else {
|
||||
// If this is an AzureDB instance, grab some extra metrics
|
||||
if s.AzureDB {
|
||||
queries["AzureDBResourceStats"] = query{ScriptName: "AzureDBPerformanceCounters", Script: sqlAzureDBResourceStats, ResultByRow: false}
|
||||
queries["AzureDBResourceGovernance"] = query{ScriptName: "AzureDBPerformanceCounters", Script: sqlAzureDBResourceGovernance, ResultByRow: false}
|
||||
}
|
||||
// Decide if we want to run version 1 or version 2 queries
|
||||
if s.QueryVersion == 2 {
|
||||
queries["PerformanceCounters"] = query{ScriptName: "PerformanceCounters", Script: sqlPerformanceCountersV2, ResultByRow: true}
|
||||
queries["WaitStatsCategorized"] = query{ScriptName: "WaitStatsCategorized", Script: sqlWaitStatsCategorizedV2, ResultByRow: false}
|
||||
queries["DatabaseIO"] = query{ScriptName: "DatabaseIO", Script: sqlDatabaseIOV2, ResultByRow: false}
|
||||
queries["ServerProperties"] = query{ScriptName: "ServerProperties", Script: sqlServerPropertiesV2, ResultByRow: false}
|
||||
queries["MemoryClerk"] = query{ScriptName: "MemoryClerk", Script: sqlMemoryClerkV2, ResultByRow: false}
|
||||
queries["Schedulers"] = query{ScriptName: "Schedulers", Script: sqlServerSchedulersV2, ResultByRow: false}
|
||||
queries["SqlRequests"] = query{ScriptName: "SqlRequests", Script: sqlServerRequestsV2, ResultByRow: false}
|
||||
queries["VolumeSpace"] = query{ScriptName: "VolumeSpace", Script: sqlServerVolumeSpaceV2, ResultByRow: false}
|
||||
queries["Cpu"] = query{ScriptName: "Cpu", Script: sqlServerCPUV2, ResultByRow: false}
|
||||
} else {
|
||||
queries["PerformanceCounters"] = query{ScriptName: "PerformanceCounters", Script: sqlPerformanceCounters, ResultByRow: true}
|
||||
queries["WaitStatsCategorized"] = query{ScriptName: "WaitStatsCategorized", Script: sqlWaitStatsCategorized, ResultByRow: false}
|
||||
queries["CPUHistory"] = query{ScriptName: "CPUHistory", Script: sqlCPUHistory, ResultByRow: false}
|
||||
queries["DatabaseIO"] = query{ScriptName: "DatabaseIO", Script: sqlDatabaseIO, ResultByRow: false}
|
||||
queries["DatabaseSize"] = query{ScriptName: "DatabaseSize", Script: sqlDatabaseSize, ResultByRow: false}
|
||||
queries["DatabaseStats"] = query{ScriptName: "DatabaseStats", Script: sqlDatabaseStats, ResultByRow: false}
|
||||
queries["DatabaseProperties"] = query{ScriptName: "DatabaseProperties", Script: sqlDatabaseProperties, ResultByRow: false}
|
||||
queries["MemoryClerk"] = query{ScriptName: "MemoryClerk", Script: sqlMemoryClerk, ResultByRow: false}
|
||||
queries["VolumeSpace"] = query{ScriptName: "VolumeSpace", Script: sqlVolumeSpace, ResultByRow: false}
|
||||
queries["PerformanceMetrics"] = query{ScriptName: "PerformanceMetrics", Script: sqlPerformanceMetrics, ResultByRow: false}
|
||||
}
|
||||
}
|
||||
|
||||
filterQueries, err := filter.NewIncludeExcludeFilter(s.IncludeQuery, s.ExcludeQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for query := range queries {
|
||||
if !filterQueries.Match(query) {
|
||||
delete(queries, query)
|
||||
}
|
||||
}
|
||||
|
||||
queryList := make([]string, 0, len(queries))
|
||||
for query := range queries {
|
||||
queryList = append(queryList, query)
|
||||
}
|
||||
s.Log.Infof("Config: Effective Queries: %#v\n", queryList)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLServer) gatherServer(pool *sql.DB, query query, acc telegraf.Accumulator, connectionString string) error {
|
||||
// execute query
|
||||
ctx := context.Background()
|
||||
// Use the query timeout if any
|
||||
if s.QueryTimeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(s.QueryTimeout))
|
||||
defer cancel()
|
||||
}
|
||||
rows, err := pool.QueryContext(ctx, query.Script)
|
||||
if err != nil {
|
||||
serverName, databaseName := getConnectionIdentifiers(connectionString)
|
||||
|
||||
// Error msg based on the format in SSMS. SQLErrorClass() is another term for severity/level: http://msdn.microsoft.com/en-us/library/dd304156.aspx
|
||||
var sqlErr mssql.Error
|
||||
if errors.As(err, &sqlErr) {
|
||||
return fmt.Errorf("query %s failed for server: %s and database: %s with Msg %d, Level %d, State %d:, Line %d, Error: %w", query.ScriptName,
|
||||
serverName, databaseName, sqlErr.SQLErrorNumber(), sqlErr.SQLErrorClass(), sqlErr.SQLErrorState(), sqlErr.SQLErrorLineNo(), err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("query %s failed for server: %s and database: %s with Error: %w", query.ScriptName, serverName, databaseName, err)
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
// grab the column information from the result
|
||||
query.OrderedColumns, err = rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
err = s.accRow(query, acc, rows)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (s *SQLServer) accRow(query query, acc telegraf.Accumulator, row scanner) error {
|
||||
var fields = make(map[string]interface{})
|
||||
|
||||
// store the column name with its *interface{}
|
||||
columnMap := make(map[string]*interface{})
|
||||
for _, column := range query.OrderedColumns {
|
||||
columnMap[column] = new(interface{})
|
||||
}
|
||||
|
||||
columnVars := make([]interface{}, 0, len(columnMap))
|
||||
// populate the array of interface{} with the pointers in the right order
|
||||
for i := 0; i < len(columnMap); i++ {
|
||||
columnVars = append(columnVars, columnMap[query.OrderedColumns[i]])
|
||||
}
|
||||
// deconstruct array of variables and send to Scan
|
||||
err := row.Scan(columnVars...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// measurement: identified by the header
|
||||
// tags: all other fields of type string
|
||||
tags := make(map[string]string, len(columnMap)+1)
|
||||
var measurement string
|
||||
for header, val := range columnMap {
|
||||
if str, ok := (*val).(string); ok {
|
||||
if header == "measurement" {
|
||||
measurement = str
|
||||
} else {
|
||||
tags[header] = str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.DatabaseType != "" {
|
||||
tags["measurement_db_type"] = s.DatabaseType
|
||||
}
|
||||
|
||||
if query.ResultByRow {
|
||||
// add measurement to Accumulator
|
||||
acc.AddFields(measurement,
|
||||
map[string]interface{}{"value": *columnMap["value"]},
|
||||
tags, time.Now())
|
||||
} else {
|
||||
// values
|
||||
for header, val := range columnMap {
|
||||
if _, ok := (*val).(string); !ok {
|
||||
fields[header] = *val
|
||||
}
|
||||
}
|
||||
// add fields to Accumulator
|
||||
acc.AddFields(measurement, fields, tags, time.Now())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// gatherHealth stores info about any query errors in the healthMetrics map
|
||||
func gatherHealth(healthMetrics map[string]*healthMetric, serv string, queryError error) {
|
||||
if healthMetrics[serv] == nil {
|
||||
healthMetrics[serv] = &healthMetric{}
|
||||
}
|
||||
|
||||
healthMetrics[serv].attemptedQueries++
|
||||
if queryError == nil {
|
||||
healthMetrics[serv].successfulQueries++
|
||||
}
|
||||
}
|
||||
|
||||
// accHealth accumulates the query health data contained within the healthMetrics map
|
||||
func (s *SQLServer) accHealth(healthMetrics map[string]*healthMetric, acc telegraf.Accumulator) {
|
||||
for connectionString, connectionStats := range healthMetrics {
|
||||
sqlInstance, databaseName := getConnectionIdentifiers(connectionString)
|
||||
tags := map[string]string{healthMetricInstanceTag: sqlInstance, healthMetricDatabaseTag: databaseName}
|
||||
fields := map[string]interface{}{
|
||||
healthMetricAttemptedQueries: connectionStats.attemptedQueries,
|
||||
healthMetricSuccessfulQueries: connectionStats.successfulQueries,
|
||||
healthMetricDatabaseType: s.getDatabaseTypeToLog(),
|
||||
}
|
||||
|
||||
acc.AddFields(healthMetricName, fields, tags, time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// getDatabaseTypeToLog returns the type of database monitored by this plugin instance
|
||||
func (s *SQLServer) getDatabaseTypeToLog() string {
|
||||
if s.DatabaseType == typeAzureSQLDB || s.DatabaseType == typeAzureSQLManagedInstance || s.DatabaseType == typeSQLServer {
|
||||
return s.DatabaseType
|
||||
}
|
||||
|
||||
logname := fmt.Sprintf("QueryVersion-%d", s.QueryVersion)
|
||||
if s.AzureDB {
|
||||
logname += "-AzureDB"
|
||||
}
|
||||
return logname
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Token Provider Implementation
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// getTokenProvider returns a function that provides authentication tokens for SQL Server.
|
||||
//
|
||||
// DEPRECATION NOTICE:
|
||||
// The ADAL authentication library is deprecated and will be removed in a future version.
|
||||
// It is strongly recommended to migrate to the Azure Identity SDK.
|
||||
// See the migration documentation at: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-migration
|
||||
//
|
||||
// This implementation supports both authentication methods:
|
||||
// 1. Azure Identity SDK (default, recommended)
|
||||
// 2. Legacy ADAL library (deprecated, maintained for backward compatibility)
|
||||
//
|
||||
// To control which authentication library is used, set the use_deprecated_adal_authentication config option:
|
||||
// - use_deprecated_adal_authentication = true : Use legacy ADAL authentication (deprecated)
|
||||
// - use_deprecated_adal_authentication = false : Use Azure Identity SDK (recommended)
|
||||
// - Not set : Use Azure Identity SDK (recommended)
|
||||
func (s *SQLServer) getTokenProvider() (func() (string, error), error) {
|
||||
// Check if use_deprecated_adal_authentication config option is set to determine which auth method to use
|
||||
// Default to using Azure Identity SDK if the config is not set
|
||||
useAzureIdentity := !s.UseAdalToken
|
||||
if useAzureIdentity {
|
||||
s.Log.Debugf("Using Azure Identity SDK for authentication (recommended)")
|
||||
} else {
|
||||
s.Log.Debugf("Using legacy ADAL for authentication (deprecated, will be removed in 1.40.0)")
|
||||
}
|
||||
|
||||
var tokenString string
|
||||
|
||||
if useAzureIdentity {
|
||||
// Use Azure Identity SDK
|
||||
s.muCacheLock.RLock()
|
||||
token, err := s.loadAzureToken()
|
||||
s.muCacheLock.RUnlock()
|
||||
|
||||
// If the token is nil, expired, or there was an error loading it, refresh the token
|
||||
if err != nil || token == nil || token.IsExpired() {
|
||||
// Refresh token within a write-lock
|
||||
s.muCacheLock.Lock()
|
||||
defer s.muCacheLock.Unlock()
|
||||
|
||||
// Load token again, in case it's been refreshed by another thread
|
||||
token, err = s.loadAzureToken()
|
||||
|
||||
// Check loaded token's error/validity, then refresh/save token
|
||||
if err != nil || token == nil || token.IsExpired() {
|
||||
// Get new token
|
||||
newToken, err := s.refreshAzureToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the refreshed token
|
||||
tokenString = newToken.token
|
||||
} else {
|
||||
// Use locally cached token
|
||||
tokenString = token.token
|
||||
}
|
||||
} else {
|
||||
// Use locally cached token
|
||||
tokenString = token.token
|
||||
}
|
||||
} else {
|
||||
// Use legacy ADAL approach for backward compatibility
|
||||
s.muCacheLock.RLock()
|
||||
token, err := s.loadToken()
|
||||
s.muCacheLock.RUnlock()
|
||||
|
||||
// If there's an error while loading token or found an expired token, refresh token and save it
|
||||
if err != nil || token.IsExpired() {
|
||||
// Refresh token within a write-lock
|
||||
s.muCacheLock.Lock()
|
||||
defer s.muCacheLock.Unlock()
|
||||
|
||||
// Load token again, in case it's been refreshed by another thread
|
||||
token, err = s.loadToken()
|
||||
|
||||
// Check loaded token's error/validity, then refresh/save token
|
||||
if err != nil || token.IsExpired() {
|
||||
// Get new token
|
||||
spt, err := s.refreshToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the refreshed token
|
||||
tokenString = spt.OAuthToken()
|
||||
} else {
|
||||
// Use locally cached token
|
||||
tokenString = token.OAuthToken()
|
||||
}
|
||||
} else {
|
||||
// Use locally cached token
|
||||
tokenString = token.OAuthToken()
|
||||
}
|
||||
}
|
||||
|
||||
// Return acquired token
|
||||
//nolint:unparam // token provider function always returns nil error in this scenario
|
||||
return func() (string, error) {
|
||||
return tokenString, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Legacy ADAL Token Methods - Kept for backward compatibility
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// loadToken loads a token from in-memory cache using the legacy ADAL method.
|
||||
//
|
||||
// Deprecated: This method uses the deprecated ADAL library and will be removed in a future version.
|
||||
// Use the Azure Identity SDK instead of setting use_deprecated_adal_authentication = false or omitting it.
|
||||
// See migration documentation: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-migration
|
||||
func (s *SQLServer) loadToken() (*adal.Token, error) {
|
||||
// This method currently does a simplistic task of reading from a variable (in-mem cache);
|
||||
// however, it's been structured here to allow extending the cache mechanism to a different approach in future
|
||||
|
||||
if s.adalToken == nil {
|
||||
return nil, errors.New("token is nil or failed to load existing token")
|
||||
}
|
||||
|
||||
return s.adalToken, nil
|
||||
}
|
||||
|
||||
// refreshToken refreshes the token using the legacy ADAL method.
|
||||
//
|
||||
// Deprecated: This method uses the deprecated ADAL library and will be removed in a future version.
|
||||
// Use the Azure Identity SDK instead of setting use_deprecated_adal_authentication = false or omitting it.
|
||||
// See migration documentation: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-migration
|
||||
func (s *SQLServer) refreshToken() (*adal.Token, error) {
|
||||
// get MSI endpoint to get a token
|
||||
msiEndpoint, err := adal.GetMSIVMEndpoint()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get MSI endpoint: %w", err)
|
||||
}
|
||||
|
||||
// get a new token for the resource id
|
||||
var spt *adal.ServicePrincipalToken
|
||||
if s.ClientID == "" {
|
||||
// Using system-assigned managed identity
|
||||
s.Log.Debugf("Using system-assigned managed identity with ADAL")
|
||||
spt, err = adal.NewServicePrincipalTokenFromMSI(msiEndpoint, sqlAzureResourceID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create service principal token from MSI: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Using user-assigned managed identity
|
||||
s.Log.Debugf("Using user-assigned managed identity with ClientID: %s with ADAL", s.ClientID)
|
||||
spt, err = adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, sqlAzureResourceID, s.ClientID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create service principal token from MSI with user-assigned ID: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the token is fresh
|
||||
if err := spt.EnsureFresh(); err != nil {
|
||||
return nil, fmt.Errorf("failed to ensure token freshness: %w", err)
|
||||
}
|
||||
|
||||
// save token to local in-mem cache
|
||||
s.adalToken = &adal.Token{
|
||||
AccessToken: spt.Token().AccessToken,
|
||||
RefreshToken: spt.Token().RefreshToken,
|
||||
ExpiresIn: spt.Token().ExpiresIn,
|
||||
ExpiresOn: spt.Token().ExpiresOn,
|
||||
NotBefore: spt.Token().NotBefore,
|
||||
Resource: spt.Token().Resource,
|
||||
Type: spt.Token().Type,
|
||||
}
|
||||
|
||||
return s.adalToken, nil
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// New Azure Identity SDK Token Methods
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// loadAzureToken loads a token from in-memory cache using the Azure Identity SDK.
|
||||
//
|
||||
// This is the recommended authentication method for Azure SQL resources.
|
||||
func (s *SQLServer) loadAzureToken() (*azureToken, error) {
|
||||
// This method reads from variable (in-mem cache) but can be extended
|
||||
// for different cache mechanisms in the future
|
||||
|
||||
if s.azToken == nil {
|
||||
return nil, errors.New("token is nil or failed to load existing token")
|
||||
}
|
||||
|
||||
return s.azToken, nil
|
||||
}
|
||||
|
||||
// refreshAzureToken refreshes the token using the Azure Identity SDK.
|
||||
//
|
||||
// This is the recommended authentication method for Azure SQL resources.
|
||||
func (s *SQLServer) refreshAzureToken() (*azureToken, error) {
|
||||
var options *azidentity.ManagedIdentityCredentialOptions
|
||||
|
||||
if s.ClientID != "" {
|
||||
options = &azidentity.ManagedIdentityCredentialOptions{
|
||||
ID: azidentity.ResourceID(s.ClientID),
|
||||
}
|
||||
}
|
||||
cred, err := azidentity.NewManagedIdentityCredential(options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create managed identity credential: %w", err)
|
||||
}
|
||||
|
||||
// Get token from Azure AD
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
accessToken, err := cred.GetToken(ctx, policy.TokenRequestOptions{
|
||||
Scopes: []string{sqlAzureResourceID + "/.default"},
|
||||
})
|
||||
if err != nil {
|
||||
credType := "system-assigned"
|
||||
if s.ClientID != "" {
|
||||
credType = fmt.Sprintf("user-assigned (ClientID: %s)", s.ClientID)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get token using %s managed identity: %w", credType, err)
|
||||
}
|
||||
|
||||
// Save token to cache
|
||||
s.azToken = &azureToken{
|
||||
token: accessToken.Token,
|
||||
expiresOn: accessToken.ExpiresOn,
|
||||
}
|
||||
|
||||
return s.azToken, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("sqlserver", func() telegraf.Input {
|
||||
return &SQLServer{
|
||||
AuthMethod: "connection_string",
|
||||
}
|
||||
})
|
||||
}
|
1609
plugins/inputs/sqlserver/sqlserver_test.go
Normal file
1609
plugins/inputs/sqlserver/sqlserver_test.go
Normal file
File diff suppressed because it is too large
Load diff
1482
plugins/inputs/sqlserver/sqlserverqueries.go
Normal file
1482
plugins/inputs/sqlserver/sqlserverqueries.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue