diff --git a/csp-mixin/config.libsonnet b/csp-mixin/config.libsonnet index 330aedbaf..8a8481641 100644 --- a/csp-mixin/config.libsonnet +++ b/csp-mixin/config.libsonnet @@ -8,6 +8,7 @@ { blobstore: (import './signals/blobstore.libsonnet')(this), azureelasticpool: (import './signals/azureelasticpool.libsonnet')(this), + azuresqldb: (import './signals/azuresqldb.libsonnet')(this), }, blobStorage: { enableAvailability: false, diff --git a/csp-mixin/dashboards.libsonnet b/csp-mixin/dashboards.libsonnet index ffda49fbd..dc90d5ec3 100644 --- a/csp-mixin/dashboards.libsonnet +++ b/csp-mixin/dashboards.libsonnet @@ -44,5 +44,21 @@ local commonlib = import 'common-lib/common/main.libsonnet'; csplib.grafana.rows.aep_resources ) ), + + [csplib.config.uid + '-sqldb.json']: + local variables = csplib.signals.azuresqldb.getVariablesMultiChoice(); + g.dashboard.new(csplib.config.dashboardNamePrefix + 'SQL database') + + g.dashboard.withUid(csplib.config.uid + '-sqldb') + + g.dashboard.withTags(csplib.config.dashboardTags) + + g.dashboard.withTimezone(csplib.config.dashboardTimezone) + + g.dashboard.withRefresh(csplib.config.dashboardRefresh) + + g.dashboard.timepicker.withTimeOptions(csplib.config.dashboardPeriod) + + g.dashboard.withVariables(variables) + + g.dashboard.withPanels( + g.util.grid.wrapPanels( + csplib.grafana.rows.asql_connections + + csplib.grafana.rows.asql_resources + ) + ), } else {}, } diff --git a/csp-mixin/main.libsonnet b/csp-mixin/main.libsonnet index d7eb6fc46..9ffd7aece 100644 --- a/csp-mixin/main.libsonnet +++ b/csp-mixin/main.libsonnet @@ -8,6 +8,7 @@ local commonlib = import 'common-lib/common/main.libsonnet'; { blobstore: commonlib.signals.unmarshallJsonMulti(this.config.signals.blobstore, type=this.config.metricsSource), azureelasticpool: commonlib.signals.unmarshallJsonMulti(this.config.signals.azureelasticpool, type=this.config.metricsSource), + azuresqldb: commonlib.signals.unmarshallJsonMulti(this.config.signals.azuresqldb, type=this.config.metricsSource), }, grafana: { panels: (import './panels.libsonnet').new(this), diff --git a/csp-mixin/panels.libsonnet b/csp-mixin/panels.libsonnet index 4f5d4b87b..553ac40a0 100644 --- a/csp-mixin/panels.libsonnet +++ b/csp-mixin/panels.libsonnet @@ -268,5 +268,120 @@ local commonlib = import 'common-lib/common/main.libsonnet'; this.signals.azureelasticpool.session.asTimeSeries() + commonlib.panels.generic.timeSeries.base.stylize() + g.panel.timeSeries.fieldConfig.defaults.custom.stacking.withMode('normal'), + + // Azure SQL Database + _asql_tableCommon():: + g.panel.table.queryOptions.withTransformations([ + g.panel.table.queryOptions.transformation.withId('filterFieldsByName') + + g.panel.table.queryOptions.transformation.withOptions({ + include: { + pattern: 'database.*|Value.*', + }, + }), + g.panel.table.queryOptions.transformation.withId('joinByField') + + g.panel.table.queryOptions.transformation.withOptions({ + byField: 'database', + mode: 'outer', + }), + ]) + + g.panel.table.standardOptions.withOverrides([ + { + matcher: { + id: 'byName', + options: 'database', + }, + properties: [ + { + id: 'displayName', + value: 'Database', + }, + ], + }, + { + matcher: { + id: 'byName', + options: 'Value #Used', + }, + properties: [ + { + id: 'displayName', + value: 'Used', + }, + ], + }, + { + matcher: { + id: 'byName', + options: 'Value #Percent of limit', + }, + properties: [ + { + id: 'displayName', + value: 'Percent of limit', + }, + ], + }, + { + matcher: { + id: 'byName', + options: 'Value #Limit', + }, + properties: [ + { + id: 'displayName', + value: 'Limit', + }, + ], + }, + ]), + asql_conns: + this.signals.azuresqldb.successfulConns.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize(), + + asql_deadlocks: + this.signals.azuresqldb.deadlocks.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize(), + + asql_sessions: + this.signals.azuresqldb.sessions.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize(), + + asql_cpu: + this.signals.azuresqldb.cpuPercent.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize() + + this.signals.azuresqldb.vCoreCpuPercent.asPanelMixin(), + + asql_storagebytes: + this.signals.azuresqldb.storageBytes.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize() + + g.panel.timeSeries.fieldConfig.defaults.custom.stacking.withMode('normal'), + + asql_storagepercent: + this.signals.azuresqldb.storagePercent.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize(), + + asql_dtuts: + this.signals.azuresqldb.dtuUsed.asTimeSeries() + + commonlib.panels.generic.timeSeries.base.stylize(), + + asql_dtutbl: + this.signals.azuresqldb.dtuUsed.common + + commonlib.panels.generic.table.base.new( + 'DTU utilization and limits by database', + [ + this.signals.azuresqldb.dtuUsed.asTarget() + + g.query.prometheus.withFormat('table') + + g.query.prometheus.withInstant(true), + + this.signals.azuresqldb.dtuPercent.asTarget() + + g.query.prometheus.withFormat('table') + + g.query.prometheus.withInstant(true), + + this.signals.azuresqldb.dtuLimit.asTarget() + + g.query.prometheus.withFormat('table') + + g.query.prometheus.withInstant(true), + ], + 'DTU utilization and limits by database' + ) + self._asql_tableCommon(), }, } diff --git a/csp-mixin/rows.libsonnet b/csp-mixin/rows.libsonnet index df1996c9a..8738c3911 100644 --- a/csp-mixin/rows.libsonnet +++ b/csp-mixin/rows.libsonnet @@ -77,5 +77,44 @@ local g = import './g.libsonnet'; + g.panel.timeSeries.gridPos.withW(12) + g.panel.timeSeries.gridPos.withH(6), ], + + // Azure SQL Database + asql_connections: [ + g.panel.row.new('Connections'), + this.grafana.panels.asql_conns + + g.panel.timeSeries.gridPos.withW(8) + + g.panel.timeSeries.gridPos.withH(8), + + this.grafana.panels.asql_deadlocks + + g.panel.timeSeries.gridPos.withW(8) + + g.panel.timeSeries.gridPos.withH(8), + + this.grafana.panels.asql_sessions + + g.panel.timeSeries.gridPos.withW(8) + + g.panel.timeSeries.gridPos.withH(8), + ], + + asql_resources: [ + g.panel.row.new('Resources'), + this.grafana.panels.asql_cpu + + g.panel.timeSeries.gridPos.withW(24) + + g.panel.timeSeries.gridPos.withH(8), + + this.grafana.panels.asql_storagebytes + + g.panel.timeSeries.gridPos.withW(12) + + g.panel.timeSeries.gridPos.withH(8), + + this.grafana.panels.asql_storagepercent + + g.panel.timeSeries.gridPos.withW(12) + + g.panel.timeSeries.gridPos.withH(8), + + this.grafana.panels.asql_dtuts + + g.panel.timeSeries.gridPos.withW(12) + + g.panel.timeSeries.gridPos.withH(8), + + this.grafana.panels.asql_dtutbl + + g.panel.timeSeries.gridPos.withW(12) + + g.panel.timeSeries.gridPos.withH(8), + ], }, } diff --git a/csp-mixin/signals/azuresqldb.libsonnet b/csp-mixin/signals/azuresqldb.libsonnet new file mode 100644 index 000000000..9effbbb6d --- /dev/null +++ b/csp-mixin/signals/azuresqldb.libsonnet @@ -0,0 +1,140 @@ +local commonlib = import 'common-lib/common/main.libsonnet'; +function(this) + { + local s = self, + filteringSelector: this.filteringSelector, + groupLabels: this.groupLabels, + instanceLabels: this.instanceLabels, + aggLevel: 'none', + discoveryMetric: { + azuremonitor: 'azure_microsoft_storage_storageaccounts_blobservices_blobcount_average_count', + }, + + signals: { + successfulConns: { + name: 'Successful connections by database', + description: 'Count of successful connections by database', + type: 'gauge', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_connection_successful_total_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + deadlocks: { + name: 'Deadlocks by database', + description: 'Count of deadlocks by database', + type: 'gauge', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_deadlock_total_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + sessions: { + name: 'Average sessions by database', + description: 'Average number of sessions by database', + type: 'gauge', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_sessions_count_average_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + cpuPercent: { + name: 'CPU utilization by database', + description: 'Percent of CPU utilization by database. If database uses vCores, the vCore percent utilization is shown', + type: 'gauge', + unit: 'percent', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_cpu_percent_average_percent{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + vCoreCpuPercent: { + name: 'vCore utilization by database', + description: 'Percent of CPU utilization by database. If database uses vCores, the vCore percent utilization is shown', + type: 'gauge', + unit: 'percent', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_cpu_used_average_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)") / label_replace(azure_microsoft_sql_servers_databases_cpu_limit_average_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + exprWrappers: [['100 * (', ')']], + legendCustomTemplate: '{{database}} vCore', + }, + }, + }, + + storageBytes: { + name: 'Storage utilization by database', + description: 'Bytes of storage by database', + type: 'gauge', + unit: 'bytes', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_storage_maximum_bytes{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + storagePercent: { + name: 'Storage % of limit by database', + description: 'Percent used of storage limitby database', + type: 'gauge', + unit: 'percent', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_storage_percent_maximum_percent{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + dtuUsed: { + name: 'Used', + description: 'Average number of DTUs utilized by database', + type: 'gauge', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_dtu_used_average_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + dtuPercent: { + name: 'Percent of limit', + description: 'Average percent of DTU limit utilized by database', + type: 'gauge', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_dtu_consumption_percent_average_percent{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + + dtuLimit: { + name: 'Limit', + description: 'DTU limit by database', + type: 'gauge', + sources: { + azuremonitor: { + expr: 'label_replace(azure_microsoft_sql_servers_databases_dtu_limit_average_count{%(queriesSelector)s}, "database", "$1", "resourceID", ".+/(.*)")', + legendCustomTemplate: '{{database}}', + }, + }, + }, + }, + + }