bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/dotnet_windows.go (about) 1 package collectors 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "bosun.org/slog" 10 11 "bosun.org/cmd/scollector/conf" 12 "bosun.org/metadata" 13 "bosun.org/opentsdb" 14 "bosun.org/util" 15 "github.com/StackExchange/wmi" 16 ) 17 18 var regexesDotNet = []*regexp.Regexp{} 19 20 func init() { 21 AddProcessDotNetConfig = func(params conf.ProcessDotNet) error { 22 if params.Name == "" { 23 return fmt.Errorf("empty dotnet process name") 24 } 25 reg, err := regexp.Compile(params.Name) 26 if err != nil { 27 return err 28 } 29 regexesDotNet = append(regexesDotNet, reg) 30 return nil 31 } 32 WatchProcessesDotNet = func() { 33 if len(regexesDotNet) == 0 { 34 // If no process_dotnet settings configured in config file, use this set instead. 35 regexesDotNet = append(regexesDotNet, regexp.MustCompile("^w3wp")) 36 } 37 38 c := &IntervalCollector{ 39 F: c_dotnet_loading, 40 } 41 c.init = wmiInit(c, func() interface{} { 42 return &[]Win32_PerfRawData_NETFramework_NETCLRLoading{} 43 }, "", &dotnetLoadingQuery) 44 collectors = append(collectors, c) 45 46 c = &IntervalCollector{ 47 F: c_dotnet_memory, 48 } 49 c.init = wmiInit(c, func() interface{} { 50 return &[]Win32_PerfRawData_NETFramework_NETCLRMemory{} 51 }, `WHERE ProcessID <> 0`, &dotnetMemoryQuery) 52 collectors = append(collectors, c) 53 54 c = &IntervalCollector{ 55 F: c_dotnet_sql, 56 } 57 c.init = wmiInit(c, func() interface{} { 58 return &[]Win32_PerfRawData_NETDataProviderforSqlServer_NETDataProviderforSqlServer{} 59 }, "", &dotnetSQLQuery) 60 collectors = append(collectors, c) 61 } 62 } 63 64 var ( 65 dotnetLoadingQuery string 66 dotnetMemoryQuery string 67 dotnetSQLQuery string 68 ) 69 70 func c_dotnet_loading() (opentsdb.MultiDataPoint, error) { 71 var dst []Win32_PerfRawData_NETFramework_NETCLRLoading 72 err := queryWmi(dotnetLoadingQuery, &dst) 73 if err != nil { 74 return nil, err 75 } 76 var md opentsdb.MultiDataPoint 77 for _, v := range dst { 78 if !util.NameMatches(v.Name, regexesDotNet) { 79 continue 80 } 81 id := "0" 82 raw_name := strings.Split(v.Name, "#") 83 name := raw_name[0] 84 if len(raw_name) == 2 { 85 id = raw_name[1] 86 } 87 // If you have a hash sign in your process name you don't deserve monitoring ;-) 88 if len(raw_name) > 2 { 89 continue 90 } 91 tags := opentsdb.TagSet{"name": name, "id": id} 92 Add(&md, "dotnet.current.appdomains", v.Currentappdomains, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingCurrentappdomains) 93 Add(&md, "dotnet.current.assemblies", v.CurrentAssemblies, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingCurrentAssemblies) 94 Add(&md, "dotnet.current.classes", v.CurrentClassesLoaded, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingCurrentClassesLoaded) 95 Add(&md, "dotnet.total.appdomains", v.TotalAppdomains, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingTotalAppdomains) 96 Add(&md, "dotnet.total.appdomains_unloaded", v.Totalappdomainsunloaded, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingTotalappdomainsunloaded) 97 Add(&md, "dotnet.total.assemblies", v.TotalAssemblies, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingTotalAssemblies) 98 Add(&md, "dotnet.total.classes", v.TotalClassesLoaded, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingTotalClassesLoaded) 99 Add(&md, "dotnet.total.load_failures", v.TotalNumberofLoadFailures, tags, metadata.Gauge, metadata.Count, descWinDotNetLoadingTotalNumberofLoadFailures) 100 } 101 return md, nil 102 } 103 104 const ( 105 descWinDotNetLoadingCurrentappdomains = "This counter displays the current number of AppDomains loaded in this application. AppDomains (application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications running in the same process." 106 descWinDotNetLoadingCurrentAssemblies = "This counter displays the current number of Assemblies loaded across all AppDomains in this application. If the Assembly is loaded as domain-neutral from multiple AppDomains then this counter is incremented once only. Assemblies can be loaded as domain-neutral when their code can be shared by all AppDomains or they can be loaded as domain-specific when their code is private to the AppDomain." 107 descWinDotNetLoadingCurrentClassesLoaded = "This counter displays the current number of classes loaded in all Assemblies." 108 descWinDotNetLoadingTotalAppdomains = "This counter displays the peak number of AppDomains loaded since the start of this application." 109 descWinDotNetLoadingTotalappdomainsunloaded = "This counter displays the total number of AppDomains unloaded since the start of the application. If an AppDomain is loaded and unloaded multiple times this counter would count each of those unloads as separate." 110 descWinDotNetLoadingTotalAssemblies = "This counter displays the total number of Assemblies loaded since the start of this application. If the Assembly is loaded as domain-neutral from multiple AppDomains then this counter is incremented once only." 111 descWinDotNetLoadingTotalClassesLoaded = "This counter displays the cumulative number of classes loaded in all Assemblies since the start of this application." 112 descWinDotNetLoadingTotalNumberofLoadFailures = "This counter displays the peak number of classes that have failed to load since the start of the application. These load failures could be due to many reasons like inadequate security or illegal format." 113 ) 114 115 type Win32_PerfRawData_NETFramework_NETCLRLoading struct { 116 Currentappdomains uint32 117 CurrentAssemblies uint32 118 CurrentClassesLoaded uint32 119 Name string 120 TotalAppdomains uint32 121 Totalappdomainsunloaded uint32 122 TotalAssemblies uint32 123 TotalClassesLoaded uint32 124 TotalNumberofLoadFailures uint32 125 } 126 127 func c_dotnet_memory() (opentsdb.MultiDataPoint, error) { 128 var dst []Win32_PerfRawData_NETFramework_NETCLRMemory 129 err := queryWmi(dotnetMemoryQuery, &dst) 130 if err != nil { 131 return nil, err 132 } 133 var svc_dst []Win32_Service 134 var svc_q = wmi.CreateQuery(&svc_dst, `WHERE Started=true`) 135 err = queryWmi(svc_q, &svc_dst) 136 if err != nil { 137 return nil, err 138 } 139 var iis_dst []WorkerProcess 140 iis_q := wmi.CreateQuery(&iis_dst, "") 141 err = queryWmiNamespace(iis_q, &iis_dst, "root\\WebAdministration") 142 if err != nil { 143 iis_dst = nil 144 } 145 var md opentsdb.MultiDataPoint 146 for _, v := range dst { 147 var name string 148 service_match := false 149 iis_match := false 150 process_match := util.NameMatches(v.Name, regexesDotNet) 151 id := "0" 152 if process_match { 153 raw_name := strings.Split(v.Name, "#") 154 name = raw_name[0] 155 if len(raw_name) == 2 { 156 id = raw_name[1] 157 } 158 // If you have a hash sign in your process name you don't deserve monitoring ;-) 159 if len(raw_name) > 2 { 160 continue 161 } 162 } 163 // A Service match could "overwrite" a process match, but that is probably what we would want. 164 for _, svc := range svc_dst { 165 if util.NameMatches(svc.Name, regexesDotNet) { 166 // It is possible the pid has gone and been reused, but I think this unlikely 167 // and I'm not aware of an atomic join we could do anyways. 168 if svc.ProcessId != 0 && svc.ProcessId == v.ProcessID { 169 id = "0" 170 service_match = true 171 name = svc.Name 172 break 173 } 174 } 175 } 176 for _, a_pool := range iis_dst { 177 if a_pool.ProcessId == v.ProcessID { 178 id = "0" 179 iis_match = true 180 name = strings.Join([]string{"iis", a_pool.AppPoolName}, "_") 181 break 182 } 183 } 184 if !(service_match || process_match || iis_match) { 185 continue 186 } 187 tags := opentsdb.TagSet{"name": name, "id": id} 188 Add(&md, "dotnet.memory.finalization_survivors", v.FinalizationSurvivors, tags, metadata.Gauge, metadata.Count, descWinDotNetMemoryFinalizationSurvivors) 189 Add(&md, "dotnet.memory.gen0_promoted", v.Gen0PromotedBytesPerSec, tags, metadata.Counter, metadata.BytesPerSecond, descWinDotNetMemoryGen0PromotedBytesPerSec) 190 Add(&md, "dotnet.memory.gen0_promoted_finalized", v.PromotedFinalizationMemoryfromGen0, tags, metadata.Gauge, metadata.PerSecond, descWinDotNetMemoryPromotedFinalizationMemoryfromGen0) 191 Add(&md, "dotnet.memory.gen1_promoted", v.Gen1PromotedBytesPerSec, tags, metadata.Counter, metadata.BytesPerSecond, descWinDotNetMemoryGen1PromotedBytesPerSec) 192 Add(&md, "dotnet.memory.heap_allocations", v.AllocatedBytesPersec, tags, metadata.Counter, metadata.BytesPerSecond, descWinDotNetMemoryAllocatedBytesPersec) 193 Add(&md, "dotnet.memory.heap_size_gen0_max", v.Gen0heapsize, tags, metadata.Gauge, metadata.Bytes, descWinDotNetMemoryGen0heapsize) 194 Add(&md, "dotnet.memory.heap_size", v.Gen1heapsize, opentsdb.TagSet{"type": "gen1"}.Merge(tags), metadata.Gauge, metadata.Bytes, descWinDotNetMemoryGen1heapsize) 195 Add(&md, "dotnet.memory.heap_size", v.Gen2heapsize, opentsdb.TagSet{"type": "gen2"}.Merge(tags), metadata.Gauge, metadata.Bytes, descWinDotNetMemoryGen2heapsize) 196 Add(&md, "dotnet.memory.heap_size", v.LargeObjectHeapsize, opentsdb.TagSet{"type": "large_object"}.Merge(tags), metadata.Gauge, metadata.Bytes, descWinDotNetMemoryLargeObjectHeapsize) 197 Add(&md, "dotnet.memory.heap_size", v.NumberBytesinallHeaps, opentsdb.TagSet{"type": "total"}.Merge(tags), metadata.Gauge, metadata.Bytes, descWinDotNetMemoryNumberBytesinallHeaps) 198 Add(&md, "dotnet.memory.gc_handles", v.NumberGCHandles, tags, metadata.Gauge, metadata.Count, descWinDotNetMemoryNumberGCHandles) 199 Add(&md, "dotnet.memory.gc_collections", v.NumberGen0Collections, opentsdb.TagSet{"type": "gen0"}.Merge(tags), metadata.Counter, metadata.Count, descWinDotNetMemoryNumberGen0Collections) 200 Add(&md, "dotnet.memory.gc_collections", v.NumberGen1Collections, opentsdb.TagSet{"type": "gen1"}.Merge(tags), metadata.Counter, metadata.Count, descWinDotNetMemoryNumberGen1Collections) 201 Add(&md, "dotnet.memory.gc_collections", v.NumberGen2Collections, opentsdb.TagSet{"type": "gen2"}.Merge(tags), metadata.Counter, metadata.Count, descWinDotNetMemoryNumberGen2Collections) 202 Add(&md, "dotnet.memory.gc_collections", v.NumberInducedGC, opentsdb.TagSet{"type": "induced"}.Merge(tags), metadata.Counter, metadata.Count, descWinDotNetMemoryNumberInducedGC) 203 Add(&md, "dotnet.memory.pinned_objects", v.NumberofPinnedObjects, tags, metadata.Gauge, metadata.Count, descWinDotNetMemoryNumberofPinnedObjects) 204 Add(&md, "dotnet.memory.sink_blocks", v.NumberofSinkBlocksinuse, tags, metadata.Gauge, metadata.Count, descWinDotNetMemoryNumberofSinkBlocksinuse) 205 Add(&md, "dotnet.memory.virtual_committed", v.NumberTotalcommittedBytes, tags, metadata.Gauge, metadata.Bytes, descWinDotNetMemoryNumberTotalcommittedBytes) 206 Add(&md, "dotnet.memory.virtual_reserved", v.NumberTotalreservedBytes, tags, metadata.Gauge, metadata.Bytes, descWinDotNetMemoryNumberTotalreservedBytes) 207 if v.PercentTimeinGC_Base != 0 { 208 Add(&md, "dotnet.memory.gc_time", float64(v.PercentTimeinGC)/float64(v.PercentTimeinGC_Base)*100, tags, metadata.Gauge, metadata.Pct, descWinDotNetMemoryPercentTimeinGC) 209 } 210 } 211 return md, nil 212 } 213 214 const ( 215 descWinDotNetMemoryAllocatedBytesPersec = "This counter displays the rate of bytes per second allocated on the GC Heap. This counter is updated at the end of every GC; not at each allocation." 216 descWinDotNetMemoryFinalizationSurvivors = "This counter displays the number of garbage collected objects that survive a collection because they are waiting to be finalized. If these objects hold references to other objects then those objects also survive but are not counted by this counter; This counter is not a cumulative counter; its updated at the end of every GC with count of the survivors during that particular GC only. This counter was designed to indicate the extra overhead that the application might incur because of finalization." 217 descWinDotNetMemoryGen0heapsize = "This counter displays the maximum bytes that can be allocated in generation 0 (Gen 0); its does not indicate the current number of bytes allocated in Gen 0. A Gen 0 GC is triggered when the allocations since the last GC exceed this size. The Gen 0 size is tuned by the Garbage Collector and can change during the execution of the application. At the end of a Gen 0 collection the size of the Gen 0 heap is infact 0 bytes; this counter displays the size (in bytes) of allocations that would trigger the next Gen 0 GC. This counter is updated at the end of a GC; its not updated on every allocation." 218 descWinDotNetMemoryGen0PromotedBytesPerSec = "This counter displays the bytes per second that are promoted from generation 0 (youngest) to generation 1; objects that are promoted just because they are waiting to be finalized are not included in this counter. Memory is promoted when it survives a garbage collection. This counter was designed as an indicator of relatively long-lived objects being created per sec." 219 descWinDotNetMemoryGen1heapsize = "This counter displays the current number of bytes in generation 1 (Gen 1); this counter does not display the maximum size of Gen 1. Objects are not directly allocated in this generation; they are promoted from previous Gen 0 GCs. This counter is updated at the end of a GC; its not updated on every allocation." 220 descWinDotNetMemoryGen1PromotedBytesPerSec = "This counter displays the bytes per second that are promoted from generation 1 to generation 2 (oldest); objects that are promoted just because they are waiting to be finalized are not included in this counter. Memory is promoted when it survives a garbage collection. Nothing is promoted from generation 2 since it is the oldest." 221 descWinDotNetMemoryGen2heapsize = "This counter displays the current number of bytes in generation 2 (Gen 2)." 222 descWinDotNetMemoryLargeObjectHeapsize = "This counter displays the current size of the Large Object Heap in bytes. Objects greater than a threshold are treated as large objects by the Garbage Collector and are directly allocated in a special heap; they are not promoted through the generations. In CLR v1.1 and above this threshold is equal to 85000 bytes." 223 descWinDotNetMemoryNumberBytesinallHeaps = "This counter is the sum of four other counters; Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size. This counter indicates the current memory allocated in bytes on the GC Heaps." 224 descWinDotNetMemoryNumberGCHandles = "This counter displays the current number of GC Handles in use. GCHandles are handles to resources external to the CLR and the managed environment. Handles occupy small amounts of memory in the GCHeap but potentially expensive unmanaged resources." 225 descWinDotNetMemoryNumberGen0Collections = "This counter displays the number of times the generation 0 objects (youngest; most recently allocated) are garbage collected (Gen 0 GC) since the start of the application. Gen 0 GC occurs when the available memory in generation 0 is not sufficient to satisfy an allocation request. This counter is incremented at the end of a Gen 0 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 1 or Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored." 226 descWinDotNetMemoryNumberGen1Collections = "This counter displays the number of times the generation 1 objects are garbage collected since the start of the application. The counter is incremented at the end of a Gen 1 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored." 227 descWinDotNetMemoryNumberGen2Collections = "This counter displays the number of times the generation 2 objects (older) are garbage collected since the start of the application. The counter is incremented at the end of a Gen 2 GC (also called full GC). _Global_ counter value is not accurate and should be ignored." 228 descWinDotNetMemoryNumberInducedGC = "This counter displays the peak number of times a garbage collection was performed because of an explicit call to GC.Collect. Its a good practice to let the GC tune the frequency of its collections." 229 descWinDotNetMemoryNumberofPinnedObjects = "This counter displays the number of pinned objects encountered in the last GC. This counter tracks the pinned objects only in the heaps that were garbage collected e.g. a Gen 0 GC would cause enumeration of pinned objects in the generation 0 heap only. A pinned object is one that the Garbage Collector cannot move in memory." 230 descWinDotNetMemoryNumberofSinkBlocksinuse = "This counter displays the current number of sync blocks in use. Sync blocks are per-object data structures allocated for storing synchronization information. Sync blocks hold weak references to managed objects and need to be scanned by the Garbage Collector. Sync blocks are not limited to storing synchronization information and can also store COM interop metadata. This counter was designed to indicate performance problems with heavy use of synchronization primitives." 231 descWinDotNetMemoryNumberTotalcommittedBytes = "This counter displays the amount of virtual memory (in bytes) currently committed by the Garbage Collector. Committed memory is the physical memory for which space has been reserved on the disk paging file." 232 descWinDotNetMemoryNumberTotalreservedBytes = "This counter displays the amount of virtual memory (in bytes) currently reserved by the Garbage Collector. Reserved memory is the virtual memory space reserved for the application but no disk or main memory pages have been used." 233 descWinDotNetMemoryPercentTimeinGC = "Percent Time in GC is the percentage of elapsed time that was spent in performing a garbage collection (GC) since the last GC cycle. This counter is usually an indicator of the work done by the Garbage Collector on behalf of the application to collect and compact memory. This counter is updated only at the end of every GC and the counter value reflects the last observed value; its not an average." 234 descWinDotNetMemoryPromotedFinalizationMemoryfromGen0 = "This counter displays the bytes of memory that are promoted from generation 0 to generation 1 just because they are waiting to be finalized. This counter displays the value observed at the end of the last GC; its not a cumulative counter." 235 ) 236 237 type Win32_PerfRawData_NETFramework_NETCLRMemory struct { 238 AllocatedBytesPersec uint32 239 FinalizationSurvivors uint32 240 Gen0heapsize uint32 241 Gen0PromotedBytesPerSec uint32 242 Gen1heapsize uint32 243 Gen1PromotedBytesPerSec uint32 244 Gen2heapsize uint32 245 LargeObjectHeapsize uint32 246 Name string 247 NumberBytesinallHeaps uint32 248 NumberGCHandles uint32 249 NumberGen0Collections uint32 250 NumberGen1Collections uint32 251 NumberGen2Collections uint32 252 NumberInducedGC uint32 253 NumberofPinnedObjects uint32 254 NumberofSinkBlocksinuse uint32 255 NumberTotalcommittedBytes uint32 256 NumberTotalreservedBytes uint32 257 PercentTimeinGC uint32 258 PercentTimeinGC_Base uint32 259 ProcessID uint32 260 PromotedFinalizationMemoryfromGen0 uint32 261 } 262 263 // The PID must be extracted from the Name field, and we get the name from the Pid 264 var dotNetSQLPIDRegex = regexp.MustCompile(`.*\[(\d+)\]$`) 265 266 func c_dotnet_sql() (opentsdb.MultiDataPoint, error) { 267 var dst []Win32_PerfRawData_NETDataProviderforSqlServer_NETDataProviderforSqlServer 268 err := queryWmi(dotnetSQLQuery, &dst) 269 if err != nil { 270 return nil, err 271 } 272 var svc_dst []Win32_Service 273 var svc_q = wmi.CreateQuery(&svc_dst, `WHERE Started=true`) 274 err = queryWmi(svc_q, &svc_dst) 275 if err != nil { 276 return nil, err 277 } 278 var iis_dst []WorkerProcess 279 iis_q := wmi.CreateQuery(&iis_dst, "") 280 err = queryWmiNamespace(iis_q, &iis_dst, "root\\WebAdministration") 281 if err != nil { 282 iis_dst = nil 283 } 284 var md opentsdb.MultiDataPoint 285 286 // We add the values of multiple pools that share a PID, this could cause some counter edge cases 287 // pidToRows is a map of pid to all the row indexes that share that IP 288 pidToRows := make(map[string][]int) 289 for i, v := range dst { 290 m := dotNetSQLPIDRegex.FindStringSubmatch(v.Name) 291 if len(m) != 2 { 292 slog.Errorf("unable to extract pid from dontnet SQL Provider for '%s'", v.Name) 293 continue 294 } 295 pidToRows[m[1]] = append(pidToRows[m[1]], i) 296 } 297 // skipIndex is used to skip entries in the metric loop that had their values added to an earlier entry 298 skipIndex := make(map[int]bool) 299 for _, rows := range pidToRows { 300 if len(rows) == 1 { 301 continue // Only one entry for this PID 302 } 303 // All fields that share a PID are summed to the first entry, other entries are marked 304 // for skipping in main metric loop 305 firstRow := &dst[rows[0]] 306 for _, idx := range rows[1:] { 307 skipIndex[idx] = true 308 nextRow := &dst[idx] 309 firstRow.HardDisconnectsPerSecond += nextRow.HardDisconnectsPerSecond 310 firstRow.NumberOfActiveConnectionPoolGroups += nextRow.NumberOfActiveConnectionPoolGroups 311 firstRow.NumberOfActiveConnectionPools += nextRow.NumberOfActiveConnectionPools 312 firstRow.NumberOfActiveConnections += nextRow.NumberOfActiveConnections 313 firstRow.NumberOfFreeConnections += nextRow.NumberOfFreeConnections 314 firstRow.NumberOfInactiveConnectionPoolGroups += nextRow.NumberOfInactiveConnectionPoolGroups 315 firstRow.NumberOfInactiveConnectionPools += nextRow.NumberOfInactiveConnectionPools 316 firstRow.NumberOfNonPooledConnections += nextRow.NumberOfNonPooledConnections 317 firstRow.NumberOfPooledConnections += nextRow.NumberOfPooledConnections 318 firstRow.NumberOfReclaimedConnections += nextRow.NumberOfReclaimedConnections 319 firstRow.NumberOfStasisConnections += nextRow.NumberOfStasisConnections 320 firstRow.SoftConnectsPerSecond += nextRow.SoftConnectsPerSecond 321 firstRow.SoftDisconnectsPerSecond += nextRow.SoftDisconnectsPerSecond 322 } 323 } 324 325 for i, v := range dst { 326 if skipIndex[i] { 327 // Skip entries that were summed into the first entry 328 continue 329 } 330 var name string 331 // Extract PID from the Name field, which is odd in this class 332 m := dotNetSQLPIDRegex.FindStringSubmatch(v.Name) 333 if len(m) != 2 { 334 // Error captured above 335 continue 336 } 337 pid, err := strconv.ParseUint(m[1], 10, 32) 338 if err != nil { 339 slog.Errorf("unable to parse extracted pid '%v' from '%v' into a unit32 in dotnet sql provider", m[1], v.Name) 340 continue 341 } 342 // We only look for Service and IIS matches in this collector 343 service_match := false 344 iis_match := false 345 // A Service match could "overwrite" a process match, but that is probably what we would want. 346 for _, svc := range svc_dst { 347 if util.NameMatches(svc.Name, regexesDotNet) { 348 // It is possible the pid has gone and been reused, but I think this unlikely 349 // and I'm not aware of an atomic join we could do anyways. 350 if svc.ProcessId != 0 && svc.ProcessId == uint32(pid) { 351 service_match = true 352 name = svc.Name 353 break 354 } 355 } 356 } 357 for _, a_pool := range iis_dst { 358 if a_pool.ProcessId == uint32(pid) { 359 iis_match = true 360 name = strings.Join([]string{"iis", a_pool.AppPoolName}, "_") 361 break 362 } 363 } 364 if !(service_match || iis_match) { 365 continue 366 } 367 tags := opentsdb.TagSet{"name": name, "id": "0"} 368 // Not a 100% on counter / gauge here, may see some wrong after collecting more data. PerSecond being a counter is expected 369 // however since this is a PerfRawData table 370 Add(&md, "dotnet.sql.hard_connects", v.HardConnectsPerSecond, tags, metadata.Counter, metadata.Connection, descWinDotNetSQLHardConnectsPerSecond) 371 Add(&md, "dotnet.sql.hard_disconnects", v.HardDisconnectsPerSecond, tags, metadata.Counter, metadata.Connection, descWinDotNetSQLHardDisconnectsPerSecond) 372 373 Add(&md, "dotnet.sql.soft_connects", v.SoftConnectsPerSecond, tags, metadata.Counter, metadata.Connection, descWinDotNetSQLSoftConnectsPerSecond) 374 Add(&md, "dotnet.sql.soft_disconnects", v.SoftDisconnectsPerSecond, tags, metadata.Counter, metadata.Connection, descWinDotNetSQLSoftDisconnectsPerSecond) 375 376 Add(&md, "dotnet.sql.active_conn_pool_groups", v.NumberOfActiveConnectionPoolGroups, tags, metadata.Gauge, metadata.Group, descWinDotNetSQLNumberOfActiveConnectionPoolGroups) 377 Add(&md, "dotnet.sql.inactive_conn_pool_groups", v.NumberOfInactiveConnectionPoolGroups, tags, metadata.Gauge, metadata.Group, descWinDotNetSQLNumberOfInactiveConnectionPoolGroups) 378 379 Add(&md, "dotnet.sql.active_conn_pools", v.NumberOfActiveConnectionPools, tags, metadata.Gauge, metadata.Pool, descWinDotNetSQLNumberOfActiveConnectionPools) 380 Add(&md, "dotnet.sql.inactive_conn_pools", v.NumberOfInactiveConnectionPools, tags, metadata.Gauge, metadata.Pool, descWinDotNetSQLNumberOfInactiveConnectionPools) 381 382 Add(&md, "dotnet.sql.active_connections", v.NumberOfActiveConnections, tags, metadata.Gauge, metadata.Connection, descWinDotNetSQLNumberOfActiveConnections) 383 Add(&md, "dotnet.sql.free_connections", v.NumberOfFreeConnections, tags, metadata.Gauge, metadata.Connection, descWinDotNetSQLNumberOfFreeConnections) 384 Add(&md, "dotnet.sql.non_pooled_connections", v.NumberOfNonPooledConnections, tags, metadata.Gauge, metadata.Connection, descWinDotNetSQLNumberOfNonPooledConnections) 385 Add(&md, "dotnet.sql.pooled_connections", v.NumberOfPooledConnections, tags, metadata.Gauge, metadata.Connection, descWinDotNetSQLNumberOfPooledConnections) 386 Add(&md, "dotnet.sql.reclaimed_connections", v.NumberOfReclaimedConnections, tags, metadata.Gauge, metadata.Connection, descWinDotNetSQLNumberOfReclaimedConnections) 387 Add(&md, "dotnet.sql.statis_connections", v.NumberOfStasisConnections, tags, metadata.Gauge, metadata.Connection, descWinDotNetSQLNumberOfStasisConnections) 388 } 389 return md, nil 390 } 391 392 const ( 393 descWinDotNetSQLHardConnectsPerSecond = "The number of actual connections per second that are being made to servers." 394 descWinDotNetSQLHardDisconnectsPerSecond = "The number of actual disconnects per second that are being made to servers." 395 descWinDotNetSQLNumberOfActiveConnectionPoolGroups = "The number of unique connection pool groups." 396 descWinDotNetSQLNumberOfActiveConnectionPools = "The number of active connection pools." 397 descWinDotNetSQLNumberOfActiveConnections = "The number of connections currently in-use." 398 descWinDotNetSQLNumberOfFreeConnections = "The number of connections currently available for use." 399 descWinDotNetSQLNumberOfInactiveConnectionPoolGroups = "The number of unique pool groups waiting for pruning." 400 descWinDotNetSQLNumberOfInactiveConnectionPools = "The number of inactive connection pools." 401 descWinDotNetSQLNumberOfNonPooledConnections = "The number of connections that are not using connection pooling." 402 descWinDotNetSQLNumberOfPooledConnections = "The number of connections that are managed by the connection pooler." 403 descWinDotNetSQLNumberOfReclaimedConnections = "The number of connections reclaimed from GCed external connections." 404 descWinDotNetSQLNumberOfStasisConnections = "The number of connections currently waiting to be made ready for use." 405 descWinDotNetSQLSoftConnectsPerSecond = "The number of connections opened from the pool per second." 406 descWinDotNetSQLSoftDisconnectsPerSecond = "The number of connections closed and returned to the pool per second." 407 ) 408 409 // Win32_PerfRawData_NETDataProviderforSqlServer_NETDataProviderforSqlServer is actually a CIM_StatisticalInformation type 410 type Win32_PerfRawData_NETDataProviderforSqlServer_NETDataProviderforSqlServer struct { 411 HardConnectsPerSecond uint32 412 HardDisconnectsPerSecond uint32 413 Name string 414 NumberOfActiveConnectionPoolGroups uint32 415 NumberOfActiveConnectionPools uint32 416 NumberOfActiveConnections uint32 417 NumberOfFreeConnections uint32 418 NumberOfInactiveConnectionPoolGroups uint32 419 NumberOfInactiveConnectionPools uint32 420 NumberOfNonPooledConnections uint32 421 NumberOfPooledConnections uint32 422 NumberOfReclaimedConnections uint32 423 NumberOfStasisConnections uint32 424 SoftConnectsPerSecond uint32 425 SoftDisconnectsPerSecond uint32 426 }