master
go 1,402 lines 40.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package mssql
4
5 import (
6 "fmt"
7 "strings"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
10 )
11
12 const (
13 prioUserConnections = collectorapi.Priority + iota
14 prioSessionConnections
15 prioBlockedProcesses
16
17 prioBatchRequests
18 prioCompilations
19 prioRecompilations
20 prioAutoParamAttempts
21 prioSQLErrors
22
23 prioBufferCacheHitRatio
24 prioBufferPageIOPS
25 prioBufferCheckpointPages
26 prioBufferPageLifeExpectancy
27 prioBufferLazyWrites
28 prioBufferPageLookups
29
30 prioPageSplits
31
32 prioMemoryTotal
33 prioMemoryConnection
34 prioMemoryPendingGrants
35 prioMemoryExternalBenefit
36
37 prioProcessMemoryResident
38 prioProcessMemoryVirtual
39 prioProcessMemoryUtilization
40 prioProcessPageFaults
41
42 prioOSMemory
43 prioOSPageFile
44
45 prioDatabaseActiveTransactions
46 prioDatabaseTransactions
47 prioDatabaseWriteTransactions
48 prioDatabaseLogFlushes
49 prioDatabaseLogFlushed
50 prioDatabaseLogGrowths
51 prioDatabaseLogFileSize
52 prioDatabaseLogPercentUsed
53 prioDatabaseLogTruncationsShrinks
54 prioDatabaseIOStall
55 prioDatabaseDeadlocks
56 prioDatabaseLockWaits
57 prioDatabaseLockTimeouts
58 prioDatabaseLockRequests
59 prioDatabaseDataFileSize
60 prioDatabaseState
61 prioDatabaseReadOnly
62
63 prioLocksByResource
64
65 prioWaitTotalTime
66 prioWaitResourceTime
67 prioWaitSignalTime
68 prioWaitMaxTime
69 prioWaitCount
70
71 prioJobStatus
72
73 prioReplicationStatus
74 prioReplicationLatency
75
76 prioAGSyncHealth
77 prioAGRecoveryHealth
78
79 prioAGReplicaRole
80 prioAGReplicaConnectedState
81 prioAGReplicaSyncHealth
82
83 prioAGDBSyncState
84 prioAGDBLogSendQueue
85 prioAGDBLogSendRate
86 prioAGDBRedoQueue
87 prioAGDBRedoRate
88 prioAGDBFilestreamSendRate
89 prioAGDBSecondaryLag
90 prioAGDBSuspended
91 prioAGDBFailoverReadiness
92 prioAGDBJoined
93
94 prioAGClusterQuorumState
95 prioAGClusterMemberState
96 prioAGClusterMemberQuorumVotes
97
98 prioAGThreads
99
100 prioAGPageRepair
101 )
102
103 // instanceCharts are charts for the SQL Server instance level metrics
104 var instanceCharts = collectorapi.Charts{
105 userConnectionsChart.Copy(),
106 sessionConnectionsChart.Copy(),
107 blockedProcessesChart.Copy(),
108
109 batchRequestsChart.Copy(),
110 compilationsChart.Copy(),
111 recompilationsChart.Copy(),
112 autoParamAttemptsChart.Copy(),
113 sqlErrorsChart.Copy(),
114
115 bufferCacheHitRatioChart.Copy(),
116 bufferPageIOPSChart.Copy(),
117 bufferCheckpointPagesChart.Copy(),
118 bufferPageLifeExpectancyChart.Copy(),
119 bufferLazyWritesChart.Copy(),
120 bufferPageLookupsChart.Copy(),
121
122 pageSplitsChart.Copy(),
123
124 memoryTotalChart.Copy(),
125 memoryConnectionChart.Copy(),
126 memoryPendingGrantsChart.Copy(),
127 memoryExternalBenefitChart.Copy(),
128
129 processMemoryResidentChart.Copy(),
130 processMemoryVirtualChart.Copy(),
131 processMemoryUtilizationChart.Copy(),
132 processPageFaultsChart.Copy(),
133
134 osMemoryChart.Copy(),
135 osPageFileChart.Copy(),
136 }
137
138 var (
139 userConnectionsChart = collectorapi.Chart{
140 ID: "user_connections",
141 Title: "User connections",
142 Units: "connections",
143 Fam: "connections",
144 Ctx: "mssql.user_connections",
145 Priority: prioUserConnections,
146 Dims: collectorapi.Dims{
147 {ID: "user_connections", Name: "user"},
148 },
149 }
150 blockedProcessesChart = collectorapi.Chart{
151 ID: "blocked_processes",
152 Title: "Blocked processes",
153 Units: "processes",
154 Fam: "processes",
155 Ctx: "mssql.blocked_processes",
156 Priority: prioBlockedProcesses,
157 Dims: collectorapi.Dims{
158 {ID: "blocked_processes", Name: "blocked"},
159 },
160 }
161 sessionConnectionsChart = collectorapi.Chart{
162 ID: "session_connections",
163 Title: "Session connections",
164 Units: "connections",
165 Fam: "connections",
166 Ctx: "mssql.session_connections",
167 Priority: prioSessionConnections,
168 Dims: collectorapi.Dims{
169 {ID: "session_connections_user", Name: "user"},
170 {ID: "session_connections_internal", Name: "internal"},
171 },
172 }
173
174 batchRequestsChart = collectorapi.Chart{
175 ID: "batch_requests",
176 Title: "Batch requests",
177 Units: "requests/s",
178 Fam: "queries",
179 Ctx: "mssql.batch_requests",
180 Priority: prioBatchRequests,
181 Dims: collectorapi.Dims{
182 {ID: "batch_requests", Name: "batch", Algo: collectorapi.Incremental},
183 },
184 }
185 compilationsChart = collectorapi.Chart{
186 ID: "compilations",
187 Title: "SQL compilations",
188 Units: "compilations/s",
189 Fam: "queries",
190 Ctx: "mssql.compilations",
191 Priority: prioCompilations,
192 Dims: collectorapi.Dims{
193 {ID: "sql_compilations", Name: "compilations", Algo: collectorapi.Incremental},
194 },
195 }
196 recompilationsChart = collectorapi.Chart{
197 ID: "recompilations",
198 Title: "SQL re-compilations",
199 Units: "recompilations/s",
200 Fam: "queries",
201 Ctx: "mssql.recompilations",
202 Priority: prioRecompilations,
203 Dims: collectorapi.Dims{
204 {ID: "sql_recompilations", Name: "recompilations", Algo: collectorapi.Incremental},
205 },
206 }
207 autoParamAttemptsChart = collectorapi.Chart{
208 ID: "auto_param_attempts",
209 Title: "Auto-parameterization attempts",
210 Units: "attempts/s",
211 Fam: "queries",
212 Ctx: "mssql.auto_param_attempts",
213 Priority: prioAutoParamAttempts,
214 Dims: collectorapi.Dims{
215 {ID: "auto_param_attempts", Name: "total", Algo: collectorapi.Incremental},
216 {ID: "auto_param_safe", Name: "safe", Algo: collectorapi.Incremental},
217 {ID: "auto_param_failed", Name: "failed", Algo: collectorapi.Incremental, Mul: -1},
218 },
219 }
220 sqlErrorsChart = collectorapi.Chart{
221 ID: "sql_errors",
222 Title: "SQL errors",
223 Units: "errors/s",
224 Fam: "errors",
225 Ctx: "mssql.sql_errors",
226 Priority: prioSQLErrors,
227 Dims: collectorapi.Dims{
228 {ID: "sql_errors_total", Name: "errors", Algo: collectorapi.Incremental},
229 },
230 }
231
232 bufferCacheHitRatioChart = collectorapi.Chart{
233 ID: "buffer_cache_hit_ratio",
234 Title: "Buffer cache hit ratio",
235 Units: "percentage",
236 Fam: "buffer",
237 Ctx: "mssql.buffer_cache_hit_ratio",
238 Priority: prioBufferCacheHitRatio,
239 Dims: collectorapi.Dims{
240 {ID: "buffer_cache_hit_ratio", Name: "hit_ratio"},
241 },
242 }
243 bufferPageIOPSChart = collectorapi.Chart{
244 ID: "buffer_page_iops",
245 Title: "Buffer page I/O",
246 Units: "pages/s",
247 Fam: "buffer",
248 Ctx: "mssql.buffer_page_iops",
249 Priority: prioBufferPageIOPS,
250 Dims: collectorapi.Dims{
251 {ID: "buffer_page_reads", Name: "read", Algo: collectorapi.Incremental},
252 {ID: "buffer_page_writes", Name: "written", Algo: collectorapi.Incremental, Mul: -1},
253 },
254 }
255 bufferCheckpointPagesChart = collectorapi.Chart{
256 ID: "buffer_checkpoint_pages",
257 Title: "Buffer checkpoint pages flushed",
258 Units: "pages/s",
259 Fam: "buffer",
260 Ctx: "mssql.buffer_checkpoint_pages",
261 Priority: prioBufferCheckpointPages,
262 Dims: collectorapi.Dims{
263 {ID: "buffer_checkpoint_pages", Name: "flushed", Algo: collectorapi.Incremental},
264 },
265 }
266 bufferPageLifeExpectancyChart = collectorapi.Chart{
267 ID: "buffer_page_life_expectancy",
268 Title: "Buffer page life expectancy",
269 Units: "seconds",
270 Fam: "buffer",
271 Ctx: "mssql.buffer_page_life_expectancy",
272 Priority: prioBufferPageLifeExpectancy,
273 Dims: collectorapi.Dims{
274 {ID: "buffer_page_life_expectancy", Name: "life_expectancy"},
275 },
276 }
277 bufferLazyWritesChart = collectorapi.Chart{
278 ID: "buffer_lazy_writes",
279 Title: "Buffer lazy writes",
280 Units: "writes/s",
281 Fam: "buffer",
282 Ctx: "mssql.buffer_lazy_writes",
283 Priority: prioBufferLazyWrites,
284 Dims: collectorapi.Dims{
285 {ID: "buffer_lazy_writes", Name: "lazy_writes", Algo: collectorapi.Incremental},
286 },
287 }
288 bufferPageLookupsChart = collectorapi.Chart{
289 ID: "buffer_page_lookups",
290 Title: "Buffer page lookups",
291 Units: "lookups/s",
292 Fam: "buffer",
293 Ctx: "mssql.buffer_page_lookups",
294 Priority: prioBufferPageLookups,
295 Dims: collectorapi.Dims{
296 {ID: "buffer_page_lookups", Name: "lookups", Algo: collectorapi.Incremental},
297 },
298 }
299
300 pageSplitsChart = collectorapi.Chart{
301 ID: "page_splits",
302 Title: "Page splits",
303 Units: "splits/s",
304 Fam: "access",
305 Ctx: "mssql.page_splits",
306 Priority: prioPageSplits,
307 Dims: collectorapi.Dims{
308 {ID: "page_splits", Name: "page", Algo: collectorapi.Incremental},
309 },
310 }
311
312 memoryTotalChart = collectorapi.Chart{
313 ID: "memory_total",
314 Title: "Total server memory",
315 Units: "bytes",
316 Fam: "memory",
317 Ctx: "mssql.memory_total",
318 Priority: prioMemoryTotal,
319 Dims: collectorapi.Dims{
320 {ID: "memory_total", Name: "memory"},
321 },
322 }
323 memoryConnectionChart = collectorapi.Chart{
324 ID: "memory_connection",
325 Title: "Connection memory",
326 Units: "bytes",
327 Fam: "memory",
328 Ctx: "mssql.memory_connection",
329 Priority: prioMemoryConnection,
330 Dims: collectorapi.Dims{
331 {ID: "memory_connection", Name: "memory"},
332 },
333 }
334 memoryPendingGrantsChart = collectorapi.Chart{
335 ID: "memory_pending_grants",
336 Title: "Pending memory grants",
337 Units: "processes",
338 Fam: "memory",
339 Ctx: "mssql.memory_pending_grants",
340 Priority: prioMemoryPendingGrants,
341 Dims: collectorapi.Dims{
342 {ID: "memory_pending_grants", Name: "pending"},
343 },
344 }
345 memoryExternalBenefitChart = collectorapi.Chart{
346 ID: "memory_external_benefit",
347 Title: "External benefit of memory",
348 Units: "benefit",
349 Fam: "memory",
350 Ctx: "mssql.memory_external_benefit",
351 Priority: prioMemoryExternalBenefit,
352 Dims: collectorapi.Dims{
353 {ID: "memory_external_benefit", Name: "benefit"},
354 },
355 }
356
357 processMemoryResidentChart = collectorapi.Chart{
358 ID: "process_memory_resident",
359 Title: "Process resident memory (working set)",
360 Units: "bytes",
361 Fam: "process memory",
362 Ctx: "mssql.process_memory_resident",
363 Priority: prioProcessMemoryResident,
364 Dims: collectorapi.Dims{
365 {ID: "process_memory_resident", Name: "resident"},
366 },
367 }
368 processMemoryVirtualChart = collectorapi.Chart{
369 ID: "process_memory_virtual",
370 Title: "Process virtual memory committed",
371 Units: "bytes",
372 Fam: "process memory",
373 Ctx: "mssql.process_memory_virtual",
374 Priority: prioProcessMemoryVirtual,
375 Dims: collectorapi.Dims{
376 {ID: "process_memory_virtual", Name: "virtual"},
377 },
378 }
379 processMemoryUtilizationChart = collectorapi.Chart{
380 ID: "process_memory_utilization",
381 Title: "Process memory utilization",
382 Units: "percentage",
383 Fam: "process memory",
384 Ctx: "mssql.process_memory_utilization",
385 Priority: prioProcessMemoryUtilization,
386 Dims: collectorapi.Dims{
387 {ID: "process_memory_utilization", Name: "utilization"},
388 },
389 }
390 processPageFaultsChart = collectorapi.Chart{
391 ID: "process_page_faults",
392 Title: "Process page faults",
393 Units: "faults",
394 Fam: "process memory",
395 Ctx: "mssql.process_page_faults",
396 Priority: prioProcessPageFaults,
397 Dims: collectorapi.Dims{
398 {ID: "process_page_faults", Name: "page_faults", Algo: collectorapi.Incremental},
399 },
400 }
401
402 osMemoryChart = collectorapi.Chart{
403 ID: "os_memory",
404 Title: "OS physical memory",
405 Units: "bytes",
406 Fam: "os memory",
407 Ctx: "mssql.os_memory",
408 Priority: prioOSMemory,
409 Dims: collectorapi.Dims{
410 {ID: "os_memory_used", Name: "used"},
411 {ID: "os_memory_available", Name: "available"},
412 },
413 }
414 osPageFileChart = collectorapi.Chart{
415 ID: "os_pagefile",
416 Title: "OS page file",
417 Units: "bytes",
418 Fam: "os memory",
419 Ctx: "mssql.os_pagefile",
420 Priority: prioOSPageFile,
421 Dims: collectorapi.Dims{
422 {ID: "os_pagefile_used", Name: "used"},
423 {ID: "os_pagefile_available", Name: "available"},
424 },
425 }
426 )
427
428 // Database chart templates
429 var (
430 databaseActiveTransactionsChartTmpl = collectorapi.Chart{
431 ID: "database_%s_active_transactions",
432 Title: "Active transactions",
433 Units: "transactions",
434 Fam: "db transactions",
435 Ctx: "mssql.database_active_transactions",
436 Priority: prioDatabaseActiveTransactions,
437 Dims: collectorapi.Dims{
438 {ID: "database_%s_active_transactions", Name: "active"},
439 },
440 }
441 databaseTransactionsChartTmpl = collectorapi.Chart{
442 ID: "database_%s_transactions",
443 Title: "Transactions",
444 Units: "transactions/s",
445 Fam: "db transactions",
446 Ctx: "mssql.database_transactions",
447 Priority: prioDatabaseTransactions,
448 Dims: collectorapi.Dims{
449 {ID: "database_%s_transactions", Name: "transactions", Algo: collectorapi.Incremental},
450 },
451 }
452 databaseWriteTransactionsChartTmpl = collectorapi.Chart{
453 ID: "database_%s_write_transactions",
454 Title: "Write transactions",
455 Units: "transactions/s",
456 Fam: "db transactions",
457 Ctx: "mssql.database_write_transactions",
458 Priority: prioDatabaseWriteTransactions,
459 Dims: collectorapi.Dims{
460 {ID: "database_%s_write_transactions", Name: "write", Algo: collectorapi.Incremental},
461 },
462 }
463 databaseLogFlushesChartTmpl = collectorapi.Chart{
464 ID: "database_%s_log_flushes",
465 Title: "Log flushes",
466 Units: "flushes/s",
467 Fam: "db log",
468 Ctx: "mssql.database_log_flushes",
469 Priority: prioDatabaseLogFlushes,
470 Dims: collectorapi.Dims{
471 {ID: "database_%s_log_flushes", Name: "flushes", Algo: collectorapi.Incremental},
472 },
473 }
474 databaseLogFlushedChartTmpl = collectorapi.Chart{
475 ID: "database_%s_log_flushed",
476 Title: "Log bytes flushed",
477 Units: "bytes/s",
478 Fam: "db log",
479 Ctx: "mssql.database_log_flushed",
480 Priority: prioDatabaseLogFlushed,
481 Dims: collectorapi.Dims{
482 {ID: "database_%s_log_flushed", Name: "flushed", Algo: collectorapi.Incremental},
483 },
484 }
485 databaseDataFileSizeChartTmpl = collectorapi.Chart{
486 ID: "database_%s_data_file_size",
487 Title: "Data file size",
488 Units: "bytes",
489 Fam: "db size",
490 Ctx: "mssql.database_data_file_size",
491 Priority: prioDatabaseDataFileSize,
492 Dims: collectorapi.Dims{
493 {ID: "database_%s_data_file_size", Name: "size"},
494 },
495 }
496 databaseBackupRestoreThroughputChartTmpl = collectorapi.Chart{
497 ID: "database_%s_backup_restore_throughput",
498 Title: "Backup/Restore throughput",
499 Units: "bytes/s",
500 Fam: "db backup",
501 Ctx: "mssql.database_backup_restore_throughput",
502 Priority: prioDatabaseDataFileSize + 1,
503 Dims: collectorapi.Dims{
504 {ID: "database_%s_backup_restore_throughput", Name: "throughput", Algo: collectorapi.Incremental},
505 },
506 }
507 databaseStateChartTmpl = collectorapi.Chart{
508 ID: "database_%s_state",
509 Title: "Database state",
510 Units: "state",
511 Fam: "db status",
512 Ctx: "mssql.database_state",
513 Priority: prioDatabaseState,
514 Dims: collectorapi.Dims{
515 {ID: "database_%s_state_online", Name: "online"},
516 {ID: "database_%s_state_restoring", Name: "restoring"},
517 {ID: "database_%s_state_recovering", Name: "recovering"},
518 {ID: "database_%s_state_pending", Name: "pending"},
519 {ID: "database_%s_state_suspect", Name: "suspect"},
520 {ID: "database_%s_state_emergency", Name: "emergency"},
521 {ID: "database_%s_state_offline", Name: "offline"},
522 },
523 }
524 databaseReadOnlyChartTmpl = collectorapi.Chart{
525 ID: "database_%s_read_only",
526 Title: "Database read-only status",
527 Units: "status",
528 Fam: "db status",
529 Ctx: "mssql.database_read_only",
530 Priority: prioDatabaseReadOnly,
531 Dims: collectorapi.Dims{
532 {ID: "database_%s_read_only", Name: "read_only"},
533 {ID: "database_%s_read_write", Name: "read_write"},
534 },
535 }
536 databaseLogGrowthsChartTmpl = collectorapi.Chart{
537 ID: "database_%s_log_growths",
538 Title: "Database log growths",
539 Units: "growths",
540 Fam: "db log",
541 Ctx: "mssql.database_log_growths",
542 Priority: prioDatabaseLogGrowths,
543 Dims: collectorapi.Dims{
544 {ID: "database_%s_log_growths", Name: "growths", Algo: collectorapi.Incremental},
545 },
546 }
547 databaseLogFileSizeChartTmpl = collectorapi.Chart{
548 ID: "database_%s_log_file_size",
549 Title: "Transaction log file size",
550 Units: "bytes",
551 Fam: "db log",
552 Ctx: "mssql.database_log_file_size",
553 Type: collectorapi.Stacked,
554 Priority: prioDatabaseLogFileSize,
555 Dims: collectorapi.Dims{
556 {ID: "database_%s_log_size_used", Name: "used"},
557 {ID: "database_%s_log_size_free", Name: "free"},
558 },
559 }
560 databaseLogPercentUsedChartTmpl = collectorapi.Chart{
561 ID: "database_%s_log_percent_used",
562 Title: "Transaction log space utilization",
563 Units: "percentage",
564 Fam: "db log",
565 Ctx: "mssql.database_log_percent_used",
566 Type: collectorapi.Line,
567 Priority: prioDatabaseLogPercentUsed,
568 Dims: collectorapi.Dims{
569 {ID: "database_%s_log_percent_used", Name: "used", Div: 100},
570 },
571 }
572 databaseLogTruncationsShrinksChartTmpl = collectorapi.Chart{
573 ID: "database_%s_log_truncations_shrinks",
574 Title: "Transaction log truncations and shrinks",
575 Units: "events/s",
576 Fam: "db log",
577 Ctx: "mssql.database_log_truncations_shrinks",
578 Type: collectorapi.Line,
579 Priority: prioDatabaseLogTruncationsShrinks,
580 Dims: collectorapi.Dims{
581 {ID: "database_%s_log_truncations", Name: "truncations", Algo: collectorapi.Incremental},
582 {ID: "database_%s_log_shrinks", Name: "shrinks", Algo: collectorapi.Incremental},
583 },
584 }
585 databaseIOStallChartTmpl = collectorapi.Chart{
586 ID: "database_%s_io_stall",
587 Title: "Database I/O stall time",
588 Units: "ms",
589 Fam: "db io",
590 Ctx: "mssql.database_io_stall",
591 Priority: prioDatabaseIOStall,
592 Dims: collectorapi.Dims{
593 {ID: "database_%s_io_stall_read", Name: "read", Algo: collectorapi.Incremental},
594 {ID: "database_%s_io_stall_write", Name: "write", Algo: collectorapi.Incremental},
595 },
596 }
597 )
598
599 // Lock stats chart templates (per lock resource type, from performance counters)
600 var (
601 lockStatsDeadlocksChartTmpl = collectorapi.Chart{
602 ID: "lock_stats_%s_deadlocks",
603 Title: "Deadlocks by lock resource type",
604 Units: "deadlocks/s",
605 Fam: "lock stats",
606 Ctx: "mssql.lock_stats_deadlocks",
607 Priority: prioDatabaseDeadlocks,
608 Dims: collectorapi.Dims{
609 {ID: "lock_stats_%s_deadlocks", Name: "deadlocks", Algo: collectorapi.Incremental},
610 },
611 }
612 lockStatsWaitsChartTmpl = collectorapi.Chart{
613 ID: "lock_stats_%s_waits",
614 Title: "Lock waits by lock resource type",
615 Units: "waits/s",
616 Fam: "lock stats",
617 Ctx: "mssql.lock_stats_waits",
618 Priority: prioDatabaseLockWaits,
619 Dims: collectorapi.Dims{
620 {ID: "lock_stats_%s_waits", Name: "waits", Algo: collectorapi.Incremental},
621 },
622 }
623 lockStatsTimeoutsChartTmpl = collectorapi.Chart{
624 ID: "lock_stats_%s_timeouts",
625 Title: "Lock timeouts by lock resource type",
626 Units: "timeouts/s",
627 Fam: "lock stats",
628 Ctx: "mssql.lock_stats_timeouts",
629 Priority: prioDatabaseLockTimeouts,
630 Dims: collectorapi.Dims{
631 {ID: "lock_stats_%s_timeouts", Name: "timeouts", Algo: collectorapi.Incremental},
632 },
633 }
634 lockStatsRequestsChartTmpl = collectorapi.Chart{
635 ID: "lock_stats_%s_requests",
636 Title: "Lock requests by lock resource type",
637 Units: "requests/s",
638 Fam: "lock stats",
639 Ctx: "mssql.lock_stats_requests",
640 Priority: prioDatabaseLockRequests,
641 Dims: collectorapi.Dims{
642 {ID: "lock_stats_%s_requests", Name: "requests", Algo: collectorapi.Incremental},
643 },
644 }
645 )
646
647 // Lock resource chart template
648 var locksByResourceChartTmpl = collectorapi.Chart{
649 ID: "locks_by_resource_%s",
650 Title: "Locks by resource type",
651 Units: "locks",
652 Fam: "locks",
653 Ctx: "mssql.locks_by_resource",
654 Priority: prioLocksByResource,
655 Dims: collectorapi.Dims{
656 {ID: "locks_%s_count", Name: "locks"},
657 },
658 }
659
660 // Wait type chart templates
661 var (
662 waitTotalTimeChartTmpl = collectorapi.Chart{
663 ID: "wait_%s_total_time",
664 Title: "Total wait time",
665 Units: "ms",
666 Fam: "waits",
667 Ctx: "mssql.wait_total_time",
668 Priority: prioWaitTotalTime,
669 Dims: collectorapi.Dims{
670 {ID: "wait_%s_total_ms", Name: "duration", Algo: collectorapi.Incremental},
671 },
672 }
673 waitResourceTimeChartTmpl = collectorapi.Chart{
674 ID: "wait_%s_resource_time",
675 Title: "Resource wait time",
676 Units: "ms",
677 Fam: "waits",
678 Ctx: "mssql.wait_resource_time",
679 Priority: prioWaitResourceTime,
680 Dims: collectorapi.Dims{
681 {ID: "wait_%s_resource_ms", Name: "duration", Algo: collectorapi.Incremental},
682 },
683 }
684 waitSignalTimeChartTmpl = collectorapi.Chart{
685 ID: "wait_%s_signal_time",
686 Title: "Signal wait time",
687 Units: "ms",
688 Fam: "waits",
689 Ctx: "mssql.wait_signal_time",
690 Priority: prioWaitSignalTime,
691 Dims: collectorapi.Dims{
692 {ID: "wait_%s_signal_ms", Name: "duration", Algo: collectorapi.Incremental},
693 },
694 }
695 waitCountChartTmpl = collectorapi.Chart{
696 ID: "wait_%s_count",
697 Title: "Wait count",
698 Units: "waits/s",
699 Fam: "waits",
700 Ctx: "mssql.wait_count",
701 Priority: prioWaitCount,
702 Dims: collectorapi.Dims{
703 {ID: "wait_%s_tasks", Name: "waits", Algo: collectorapi.Incremental},
704 },
705 }
706 waitMaxTimeChartTmpl = collectorapi.Chart{
707 ID: "wait_%s_max_time",
708 Title: "Maximum wait time",
709 Units: "ms",
710 Fam: "waits",
711 Ctx: "mssql.wait_max_time",
712 Priority: prioWaitMaxTime,
713 Dims: collectorapi.Dims{
714 {ID: "wait_%s_max_ms", Name: "max_time"},
715 },
716 }
717 )
718
719 // Job status chart template
720 var jobStatusChartTmpl = collectorapi.Chart{
721 ID: "job_%s_status",
722 Title: "Job status",
723 Units: "status",
724 Fam: "jobs",
725 Ctx: "mssql.job_status",
726 Priority: prioJobStatus,
727 Dims: collectorapi.Dims{
728 {ID: "job_%s_enabled", Name: "enabled"},
729 {ID: "job_%s_disabled", Name: "disabled"},
730 },
731 }
732
733 // Replication chart templates
734 var (
735 replicationStatusChartTmpl = collectorapi.Chart{
736 ID: "replication_%s_status",
737 Title: "Replication status",
738 Units: "status",
739 Fam: "replication",
740 Ctx: "mssql.replication_status",
741 Priority: prioReplicationStatus,
742 Dims: collectorapi.Dims{
743 {ID: "replication_%s_status_started", Name: "started"},
744 {ID: "replication_%s_status_succeeded", Name: "succeeded"},
745 {ID: "replication_%s_status_in_progress", Name: "in_progress"},
746 {ID: "replication_%s_status_idle", Name: "idle"},
747 {ID: "replication_%s_status_retrying", Name: "retrying"},
748 {ID: "replication_%s_status_failed", Name: "failed"},
749 },
750 }
751 replicationWarningChartTmpl = collectorapi.Chart{
752 ID: "replication_%s_warning",
753 Title: "Replication warnings",
754 Units: "flags",
755 Fam: "replication",
756 Ctx: "mssql.replication_warning",
757 Priority: prioReplicationStatus + 1,
758 Dims: collectorapi.Dims{
759 {ID: "replication_%s_warning_expiration", Name: "expiration"},
760 {ID: "replication_%s_warning_latency", Name: "latency"},
761 {ID: "replication_%s_warning_mergeexpiration", Name: "merge_expiration"},
762 {ID: "replication_%s_warning_mergeslowrunduration", Name: "merge_slow_duration"},
763 {ID: "replication_%s_warning_mergefastrunduration", Name: "merge_fast_duration"},
764 {ID: "replication_%s_warning_mergefastrunspeed", Name: "merge_fast_speed"},
765 {ID: "replication_%s_warning_mergeslowrunspeed", Name: "merge_slow_speed"},
766 },
767 }
768 replicationLatencyChartTmpl = collectorapi.Chart{
769 ID: "replication_%s_latency",
770 Title: "Replication latency",
771 Units: "seconds",
772 Fam: "replication",
773 Ctx: "mssql.replication_latency",
774 Priority: prioReplicationLatency,
775 Dims: collectorapi.Dims{
776 {ID: "replication_%s_latency_avg", Name: "average"},
777 {ID: "replication_%s_latency_best", Name: "best"},
778 {ID: "replication_%s_latency_worst", Name: "worst"},
779 },
780 }
781 replicationSubscriptionsChartTmpl = collectorapi.Chart{
782 ID: "replication_%s_subscriptions",
783 Title: "Replication subscriptions",
784 Units: "subscriptions",
785 Fam: "replication",
786 Ctx: "mssql.replication_subscriptions",
787 Priority: prioReplicationStatus + 1,
788 Dims: collectorapi.Dims{
789 {ID: "replication_%s_subscriptions", Name: "total"},
790 {ID: "replication_%s_agents_running", Name: "agents_running"},
791 },
792 }
793 )
794
795 func (c *Collector) addDatabaseCharts(dbName string) {
796 // internal databases used by SQL Server's engine
797 // Their metrics don't represent actual workload or user activity
798 if strings.HasPrefix(dbName, "model_") {
799 return
800 }
801
802 charts := &collectorapi.Charts{
803 databaseActiveTransactionsChartTmpl.Copy(),
804 databaseTransactionsChartTmpl.Copy(),
805 databaseWriteTransactionsChartTmpl.Copy(),
806 databaseLogFlushesChartTmpl.Copy(),
807 databaseLogFlushedChartTmpl.Copy(),
808 databaseLogGrowthsChartTmpl.Copy(),
809 databaseIOStallChartTmpl.Copy(),
810 databaseDataFileSizeChartTmpl.Copy(),
811 databaseBackupRestoreThroughputChartTmpl.Copy(),
812 databaseStateChartTmpl.Copy(),
813 databaseReadOnlyChartTmpl.Copy(),
814 }
815
816 dbID := cleanDatabaseName(dbName)
817
818 for _, chart := range *charts {
819 chart.ID = fmt.Sprintf(chart.ID, dbID)
820 chart.Labels = []collectorapi.Label{
821 {Key: "database", Value: dbName},
822 }
823 for _, dim := range chart.Dims {
824 dim.ID = fmt.Sprintf(dim.ID, dbID)
825 }
826 }
827
828 if err := c.Charts().Add(*charts...); err != nil {
829 c.Warning(err)
830 }
831 }
832
833 func (c *Collector) addDatabaseLogCharts(dbName string) {
834 charts := &collectorapi.Charts{
835 databaseLogFileSizeChartTmpl.Copy(),
836 databaseLogPercentUsedChartTmpl.Copy(),
837 databaseLogTruncationsShrinksChartTmpl.Copy(),
838 }
839
840 dbID := cleanDatabaseName(dbName)
841
842 for _, chart := range *charts {
843 chart.ID = fmt.Sprintf(chart.ID, dbID)
844 chart.Labels = []collectorapi.Label{
845 {Key: "database", Value: dbName},
846 }
847 for _, dim := range chart.Dims {
848 dim.ID = fmt.Sprintf(dim.ID, dbID)
849 }
850 }
851
852 if err := c.Charts().Add(*charts...); err != nil {
853 c.Warning(err)
854 }
855 }
856
857 func (c *Collector) addWaitTypeCharts(waitType string, waitCategory string) {
858 charts := &collectorapi.Charts{
859 waitTotalTimeChartTmpl.Copy(),
860 waitResourceTimeChartTmpl.Copy(),
861 waitSignalTimeChartTmpl.Copy(),
862 waitMaxTimeChartTmpl.Copy(),
863 waitCountChartTmpl.Copy(),
864 }
865
866 waitID := cleanWaitTypeName(waitType)
867
868 for _, chart := range *charts {
869 chart.ID = fmt.Sprintf(chart.ID, waitID)
870 chart.Labels = []collectorapi.Label{
871 {Key: "wait_type", Value: waitType},
872 {Key: "wait_category", Value: waitCategory},
873 }
874 for _, dim := range chart.Dims {
875 dim.ID = fmt.Sprintf(dim.ID, waitID)
876 }
877 }
878
879 if err := c.Charts().Add(*charts...); err != nil {
880 c.Warning(err)
881 }
882 }
883
884 func (c *Collector) addLockResourceCharts(resourceType string) {
885 chart := locksByResourceChartTmpl.Copy()
886
887 resID := cleanResourceTypeName(resourceType)
888
889 chart.ID = fmt.Sprintf(chart.ID, resID)
890 chart.Labels = []collectorapi.Label{
891 {Key: "resource", Value: resourceType},
892 }
893 for _, dim := range chart.Dims {
894 dim.ID = fmt.Sprintf(dim.ID, resID)
895 }
896
897 if err := c.Charts().Add(chart); err != nil {
898 c.Warning(err)
899 }
900 }
901
902 func (c *Collector) addLockStatsCharts(resourceType string) {
903 charts := &collectorapi.Charts{
904 lockStatsDeadlocksChartTmpl.Copy(),
905 lockStatsWaitsChartTmpl.Copy(),
906 lockStatsTimeoutsChartTmpl.Copy(),
907 lockStatsRequestsChartTmpl.Copy(),
908 }
909
910 resID := cleanResourceTypeName(resourceType)
911
912 for _, chart := range *charts {
913 chart.ID = fmt.Sprintf(chart.ID, resID)
914 chart.Labels = []collectorapi.Label{
915 {Key: "resource", Value: resourceType},
916 }
917 for _, dim := range chart.Dims {
918 dim.ID = fmt.Sprintf(dim.ID, resID)
919 }
920 }
921
922 if err := c.Charts().Add(*charts...); err != nil {
923 c.Warning(err)
924 }
925 }
926
927 func (c *Collector) addJobCharts(jobName string) {
928 chart := jobStatusChartTmpl.Copy()
929
930 jobID := cleanJobName(jobName)
931
932 chart.ID = fmt.Sprintf(chart.ID, jobID)
933 chart.Labels = []collectorapi.Label{
934 {Key: "job_name", Value: jobName},
935 }
936 for _, dim := range chart.Dims {
937 dim.ID = fmt.Sprintf(dim.ID, jobID)
938 }
939
940 if err := c.Charts().Add(chart); err != nil {
941 c.Warning(err)
942 }
943 }
944
945 func (c *Collector) addReplicationCharts(pubDB, publication string) {
946 charts := &collectorapi.Charts{
947 replicationStatusChartTmpl.Copy(),
948 replicationWarningChartTmpl.Copy(),
949 replicationLatencyChartTmpl.Copy(),
950 replicationSubscriptionsChartTmpl.Copy(),
951 }
952
953 pubID := cleanPublicationName(pubDB, publication)
954
955 for _, chart := range *charts {
956 chart.ID = fmt.Sprintf(chart.ID, pubID)
957 chart.Labels = []collectorapi.Label{
958 {Key: "publisher_db", Value: pubDB},
959 {Key: "publication", Value: publication},
960 }
961 for _, dim := range chart.Dims {
962 dim.ID = fmt.Sprintf(dim.ID, pubID)
963 }
964 }
965
966 if err := c.Charts().Add(*charts...); err != nil {
967 c.Warning(err)
968 }
969 }
970
971 func cleanDatabaseName(name string) string {
972 return strings.ReplaceAll(strings.ToLower(name), " ", "_")
973 }
974
975 func cleanWaitTypeName(name string) string {
976 return strings.ToLower(name)
977 }
978
979 func cleanResourceTypeName(name string) string {
980 return strings.ToLower(name)
981 }
982
983 func cleanJobName(name string) string {
984 r := strings.NewReplacer(" ", "_", ".", "_", "-", "_")
985 return strings.ToLower(r.Replace(name))
986 }
987
988 func cleanPublicationName(pubDB, publication string) string {
989 r := strings.NewReplacer(" ", "_", ".", "_", "-", "_")
990 return strings.ToLower(r.Replace(pubDB + "_" + publication))
991 }
992
993 func cleanAGName(name string) string {
994 r := strings.NewReplacer(" ", "_", ".", "_", "-", "_", "\\", "_")
995 return strings.ToLower(r.Replace(name))
996 }
997
998 func cleanAGReplicaName(agName, replicaServer string) string {
999 return cleanAGName(agName) + "_" + cleanAGName(replicaServer)
1000 }
1001
1002 func cleanAGDatabaseReplicaName(agName, replicaServer, dbName string) string {
1003 return cleanAGName(agName) + "_" + cleanAGName(replicaServer) + "_" + cleanDatabaseName(dbName)
1004 }
1005
1006 // AG-level chart templates
1007 var (
1008 agSyncHealthChartTmpl = collectorapi.Chart{
1009 ID: "ag_%s_sync_health",
1010 Title: "Availability group synchronization health",
1011 Units: "state",
1012 Fam: "ag health",
1013 Ctx: "mssql.ag_sync_health",
1014 Priority: prioAGSyncHealth,
1015 Dims: collectorapi.Dims{
1016 {ID: "ag_%s_sync_health_not_healthy", Name: "not_healthy"},
1017 {ID: "ag_%s_sync_health_partially_healthy", Name: "partially_healthy"},
1018 {ID: "ag_%s_sync_health_healthy", Name: "healthy"},
1019 },
1020 }
1021 agRecoveryHealthChartTmpl = collectorapi.Chart{
1022 ID: "ag_%s_recovery_health",
1023 Title: "Availability group recovery health",
1024 Units: "state",
1025 Fam: "ag health",
1026 Ctx: "mssql.ag_recovery_health",
1027 Priority: prioAGRecoveryHealth,
1028 Dims: collectorapi.Dims{
1029 {ID: "ag_%s_primary_recovery_online", Name: "primary_online"},
1030 {ID: "ag_%s_primary_recovery_in_progress", Name: "primary_in_progress"},
1031 {ID: "ag_%s_secondary_recovery_online", Name: "secondary_online"},
1032 {ID: "ag_%s_secondary_recovery_in_progress", Name: "secondary_in_progress"},
1033 },
1034 }
1035 )
1036
1037 // Replica-level chart templates
1038 var (
1039 agReplicaRoleChartTmpl = collectorapi.Chart{
1040 ID: "ag_replica_%s_role",
1041 Title: "Availability group replica role",
1042 Units: "state",
1043 Fam: "ag replicas",
1044 Ctx: "mssql.ag_replica_role",
1045 Priority: prioAGReplicaRole,
1046 Dims: collectorapi.Dims{
1047 {ID: "ag_replica_%s_role_primary", Name: "primary"},
1048 {ID: "ag_replica_%s_role_secondary", Name: "secondary"},
1049 {ID: "ag_replica_%s_role_resolving", Name: "resolving"},
1050 {ID: "ag_replica_%s_role_unknown", Name: "unknown"},
1051 },
1052 }
1053 agReplicaConnectedStateChartTmpl = collectorapi.Chart{
1054 ID: "ag_replica_%s_connected_state",
1055 Title: "Availability group replica connected state",
1056 Units: "state",
1057 Fam: "ag replicas",
1058 Ctx: "mssql.ag_replica_connected_state",
1059 Priority: prioAGReplicaConnectedState,
1060 Dims: collectorapi.Dims{
1061 {ID: "ag_replica_%s_connected", Name: "connected"},
1062 {ID: "ag_replica_%s_disconnected", Name: "disconnected"},
1063 {ID: "ag_replica_%s_conn_unknown", Name: "unknown"},
1064 },
1065 }
1066 agReplicaSyncHealthChartTmpl = collectorapi.Chart{
1067 ID: "ag_replica_%s_sync_health",
1068 Title: "Availability group replica synchronization health",
1069 Units: "state",
1070 Fam: "ag replicas",
1071 Ctx: "mssql.ag_replica_sync_health",
1072 Priority: prioAGReplicaSyncHealth,
1073 Dims: collectorapi.Dims{
1074 {ID: "ag_replica_%s_sync_health_not_healthy", Name: "not_healthy"},
1075 {ID: "ag_replica_%s_sync_health_partially_healthy", Name: "partially_healthy"},
1076 {ID: "ag_replica_%s_sync_health_healthy", Name: "healthy"},
1077 },
1078 }
1079 )
1080
1081 // Database-replica-level chart templates
1082 var (
1083 agDBSyncStateChartTmpl = collectorapi.Chart{
1084 ID: "ag_db_%s_sync_state",
1085 Title: "AG database synchronization state",
1086 Units: "state",
1087 Fam: "ag databases",
1088 Ctx: "mssql.ag_db_sync_state",
1089 Priority: prioAGDBSyncState,
1090 Dims: collectorapi.Dims{
1091 {ID: "ag_db_%s_sync_state_not_synchronizing", Name: "not_synchronizing"},
1092 {ID: "ag_db_%s_sync_state_synchronizing", Name: "synchronizing"},
1093 {ID: "ag_db_%s_sync_state_synchronized", Name: "synchronized"},
1094 {ID: "ag_db_%s_sync_state_reverting", Name: "reverting"},
1095 {ID: "ag_db_%s_sync_state_initializing", Name: "initializing"},
1096 },
1097 }
1098 agDBLogSendQueueChartTmpl = collectorapi.Chart{
1099 ID: "ag_db_%s_log_send_queue",
1100 Title: "AG database log send queue size",
1101 Units: "bytes",
1102 Fam: "ag databases",
1103 Ctx: "mssql.ag_db_log_send_queue",
1104 Priority: prioAGDBLogSendQueue,
1105 Dims: collectorapi.Dims{
1106 {ID: "ag_db_%s_log_send_queue_size", Name: "queue_size"},
1107 },
1108 }
1109 agDBLogSendRateChartTmpl = collectorapi.Chart{
1110 ID: "ag_db_%s_log_send_rate",
1111 Title: "AG database log send rate",
1112 Units: "bytes/s",
1113 Fam: "ag databases",
1114 Ctx: "mssql.ag_db_log_send_rate",
1115 Priority: prioAGDBLogSendRate,
1116 Dims: collectorapi.Dims{
1117 {ID: "ag_db_%s_log_send_rate", Name: "send_rate"},
1118 },
1119 }
1120 agDBRedoQueueChartTmpl = collectorapi.Chart{
1121 ID: "ag_db_%s_redo_queue",
1122 Title: "AG database redo queue size",
1123 Units: "bytes",
1124 Fam: "ag databases",
1125 Ctx: "mssql.ag_db_redo_queue",
1126 Priority: prioAGDBRedoQueue,
1127 Dims: collectorapi.Dims{
1128 {ID: "ag_db_%s_redo_queue_size", Name: "queue_size"},
1129 },
1130 }
1131 agDBRedoRateChartTmpl = collectorapi.Chart{
1132 ID: "ag_db_%s_redo_rate",
1133 Title: "AG database redo rate",
1134 Units: "bytes/s",
1135 Fam: "ag databases",
1136 Ctx: "mssql.ag_db_redo_rate",
1137 Priority: prioAGDBRedoRate,
1138 Dims: collectorapi.Dims{
1139 {ID: "ag_db_%s_redo_rate", Name: "redo_rate"},
1140 },
1141 }
1142 agDBFilestreamSendRateChartTmpl = collectorapi.Chart{
1143 ID: "ag_db_%s_filestream_send_rate",
1144 Title: "AG database filestream send rate",
1145 Units: "bytes/s",
1146 Fam: "ag databases",
1147 Ctx: "mssql.ag_db_filestream_send_rate",
1148 Priority: prioAGDBFilestreamSendRate,
1149 Dims: collectorapi.Dims{
1150 {ID: "ag_db_%s_filestream_send_rate", Name: "send_rate"},
1151 },
1152 }
1153 agDBSecondaryLagChartTmpl = collectorapi.Chart{
1154 ID: "ag_db_%s_secondary_lag",
1155 Title: "AG database secondary lag",
1156 Units: "seconds",
1157 Fam: "ag databases",
1158 Ctx: "mssql.ag_db_secondary_lag",
1159 Priority: prioAGDBSecondaryLag,
1160 Dims: collectorapi.Dims{
1161 {ID: "ag_db_%s_secondary_lag_seconds", Name: "lag"},
1162 },
1163 }
1164 agDBSuspendedChartTmpl = collectorapi.Chart{
1165 ID: "ag_db_%s_suspended",
1166 Title: "AG database data movement suspended state",
1167 Units: "state",
1168 Fam: "ag databases",
1169 Ctx: "mssql.ag_db_suspended",
1170 Priority: prioAGDBSuspended,
1171 Dims: collectorapi.Dims{
1172 {ID: "ag_db_%s_not_suspended", Name: "active"},
1173 {ID: "ag_db_%s_suspended", Name: "suspended"},
1174 },
1175 }
1176 agDBFailoverReadinessChartTmpl = collectorapi.Chart{
1177 ID: "ag_db_%s_failover_readiness",
1178 Title: "AG database failover readiness",
1179 Units: "state",
1180 Fam: "ag databases",
1181 Ctx: "mssql.ag_db_failover_readiness",
1182 Priority: prioAGDBFailoverReadiness,
1183 Dims: collectorapi.Dims{
1184 {ID: "ag_db_%s_failover_ready", Name: "ready"},
1185 {ID: "ag_db_%s_failover_not_ready", Name: "not_ready"},
1186 },
1187 }
1188 agDBJoinedChartTmpl = collectorapi.Chart{
1189 ID: "ag_db_%s_joined_state",
1190 Title: "AG database joined state",
1191 Units: "state",
1192 Fam: "ag databases",
1193 Ctx: "mssql.ag_db_joined_state",
1194 Priority: prioAGDBJoined,
1195 Dims: collectorapi.Dims{
1196 {ID: "ag_db_%s_joined", Name: "joined"},
1197 {ID: "ag_db_%s_not_joined", Name: "not_joined"},
1198 },
1199 }
1200 )
1201
1202 // Cluster-level chart
1203 var agClusterQuorumStateChart = collectorapi.Chart{
1204 ID: "ag_cluster_quorum_state",
1205 Title: "WSFC cluster quorum state",
1206 Units: "state",
1207 Fam: "ag cluster",
1208 Ctx: "mssql.ag_cluster_quorum_state",
1209 Priority: prioAGClusterQuorumState,
1210 Dims: collectorapi.Dims{
1211 {ID: "ag_cluster_quorum_state_normal", Name: "normal"},
1212 {ID: "ag_cluster_quorum_state_forced", Name: "forced"},
1213 {ID: "ag_cluster_quorum_state_unknown", Name: "unknown"},
1214 },
1215 }
1216
1217 // Cluster member chart templates
1218 var (
1219 agClusterMemberStateChartTmpl = collectorapi.Chart{
1220 ID: "ag_cluster_member_%s_state",
1221 Title: "WSFC cluster member state",
1222 Units: "state",
1223 Fam: "ag cluster",
1224 Ctx: "mssql.ag_cluster_member_state",
1225 Priority: prioAGClusterMemberState,
1226 Dims: collectorapi.Dims{
1227 {ID: "ag_cluster_member_%s_up", Name: "up"},
1228 {ID: "ag_cluster_member_%s_down", Name: "down"},
1229 },
1230 }
1231 agClusterMemberQuorumVotesChartTmpl = collectorapi.Chart{
1232 ID: "ag_cluster_member_%s_quorum_votes",
1233 Title: "WSFC cluster member quorum votes",
1234 Units: "votes",
1235 Fam: "ag cluster",
1236 Ctx: "mssql.ag_cluster_member_quorum_votes",
1237 Priority: prioAGClusterMemberQuorumVotes,
1238 Dims: collectorapi.Dims{
1239 {ID: "ag_cluster_member_%s_quorum_votes", Name: "votes"},
1240 },
1241 }
1242 )
1243
1244 // Page repair chart template (keyed by database name)
1245 var agPageRepairChartTmpl = collectorapi.Chart{
1246 ID: "ag_page_repair_%s",
1247 Title: "AG automatic page repair events",
1248 Units: "repairs",
1249 Fam: "ag page repair",
1250 Ctx: "mssql.ag_page_repair",
1251 Priority: prioAGPageRepair,
1252 Dims: collectorapi.Dims{
1253 {ID: "ag_page_repair_%s_successful", Name: "successful"},
1254 {ID: "ag_page_repair_%s_failed", Name: "failed"},
1255 },
1256 }
1257
1258 // Thread chart template
1259 var agThreadsChartTmpl = collectorapi.Chart{
1260 ID: "ag_%s_threads",
1261 Title: "Availability group threads",
1262 Units: "threads",
1263 Fam: "ag threads",
1264 Ctx: "mssql.ag_threads",
1265 Priority: prioAGThreads,
1266 Dims: collectorapi.Dims{
1267 {ID: "ag_%s_capture_threads", Name: "capture"},
1268 {ID: "ag_%s_redo_threads", Name: "redo"},
1269 {ID: "ag_%s_parallel_redo_threads", Name: "parallel_redo"},
1270 },
1271 }
1272
1273 func (c *Collector) addAGCharts(agName string) {
1274 charts := &collectorapi.Charts{
1275 agSyncHealthChartTmpl.Copy(),
1276 agRecoveryHealthChartTmpl.Copy(),
1277 }
1278
1279 if c.majorVersion >= 15 { // SQL Server 2019+
1280 *charts = append(*charts, agThreadsChartTmpl.Copy())
1281 }
1282
1283 agID := cleanAGName(agName)
1284
1285 for _, chart := range *charts {
1286 chart.ID = fmt.Sprintf(chart.ID, agID)
1287 chart.Labels = []collectorapi.Label{
1288 {Key: "ag_name", Value: agName},
1289 }
1290 for _, dim := range chart.Dims {
1291 dim.ID = fmt.Sprintf(dim.ID, agID)
1292 }
1293 }
1294
1295 if err := c.Charts().Add(*charts...); err != nil {
1296 c.Warning(err)
1297 }
1298 }
1299
1300 func (c *Collector) addAGReplicaCharts(agName, replicaServer, availMode, failoverMode string) {
1301 charts := &collectorapi.Charts{
1302 agReplicaRoleChartTmpl.Copy(),
1303 agReplicaConnectedStateChartTmpl.Copy(),
1304 agReplicaSyncHealthChartTmpl.Copy(),
1305 }
1306
1307 rID := cleanAGReplicaName(agName, replicaServer)
1308
1309 for _, chart := range *charts {
1310 chart.ID = fmt.Sprintf(chart.ID, rID)
1311 chart.Labels = []collectorapi.Label{
1312 {Key: "ag_name", Value: agName},
1313 {Key: "replica_server", Value: replicaServer},
1314 {Key: "availability_mode", Value: availMode},
1315 {Key: "failover_mode", Value: failoverMode},
1316 }
1317 for _, dim := range chart.Dims {
1318 dim.ID = fmt.Sprintf(dim.ID, rID)
1319 }
1320 }
1321
1322 if err := c.Charts().Add(*charts...); err != nil {
1323 c.Warning(err)
1324 }
1325 }
1326
1327 func (c *Collector) addAGDatabaseReplicaCharts(agName, replicaServer, dbName string) {
1328 charts := &collectorapi.Charts{
1329 agDBSyncStateChartTmpl.Copy(),
1330 agDBLogSendQueueChartTmpl.Copy(),
1331 agDBLogSendRateChartTmpl.Copy(),
1332 agDBRedoQueueChartTmpl.Copy(),
1333 agDBRedoRateChartTmpl.Copy(),
1334 agDBFilestreamSendRateChartTmpl.Copy(),
1335 agDBSuspendedChartTmpl.Copy(),
1336 agDBFailoverReadinessChartTmpl.Copy(),
1337 agDBJoinedChartTmpl.Copy(),
1338 }
1339
1340 if c.majorVersion >= 13 { // SQL Server 2016+
1341 *charts = append(*charts, agDBSecondaryLagChartTmpl.Copy())
1342 }
1343
1344 drID := cleanAGDatabaseReplicaName(agName, replicaServer, dbName)
1345
1346 for _, chart := range *charts {
1347 chart.ID = fmt.Sprintf(chart.ID, drID)
1348 chart.Labels = []collectorapi.Label{
1349 {Key: "ag_name", Value: agName},
1350 {Key: "replica_server", Value: replicaServer},
1351 {Key: "database", Value: dbName},
1352 }
1353 for _, dim := range chart.Dims {
1354 dim.ID = fmt.Sprintf(dim.ID, drID)
1355 }
1356 }
1357
1358 if err := c.Charts().Add(*charts...); err != nil {
1359 c.Warning(err)
1360 }
1361 }
1362
1363 func (c *Collector) addAGClusterMemberCharts(memberName string) {
1364 charts := &collectorapi.Charts{
1365 agClusterMemberStateChartTmpl.Copy(),
1366 agClusterMemberQuorumVotesChartTmpl.Copy(),
1367 }
1368
1369 mID := cleanAGName(memberName)
1370
1371 for _, chart := range *charts {
1372 chart.ID = fmt.Sprintf(chart.ID, mID)
1373 chart.Labels = []collectorapi.Label{
1374 {Key: "cluster_member", Value: memberName},
1375 }
1376 for _, dim := range chart.Dims {
1377 dim.ID = fmt.Sprintf(dim.ID, mID)
1378 }
1379 }
1380
1381 if err := c.Charts().Add(*charts...); err != nil {
1382 c.Warning(err)
1383 }
1384 }
1385
1386 func (c *Collector) addAGPageRepairCharts(dbName string) {
1387 chart := agPageRepairChartTmpl.Copy()
1388
1389 dbID := cleanDatabaseName(dbName)
1390
1391 chart.ID = fmt.Sprintf(chart.ID, dbID)
1392 chart.Labels = []collectorapi.Label{
1393 {Key: "database", Value: dbName},
1394 }
1395 for _, dim := range chart.Dims {
1396 dim.ID = fmt.Sprintf(dim.ID, dbID)
1397 }
1398
1399 if err := c.Charts().Add(chart); err != nil {
1400 c.Warning(err)
1401 }
1402 }