github.com/netdata/go.d.plugin@v0.58.1/modules/nginxplus/collect.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package nginxplus 4 5 import ( 6 "errors" 7 "fmt" 8 "time" 9 ) 10 11 func (n *NginxPlus) collect() (map[string]int64, error) { 12 if n.apiVersion == 0 { 13 v, err := n.queryAPIVersion() 14 if err != nil { 15 return nil, err 16 } 17 n.apiVersion = v 18 } 19 20 now := time.Now() 21 if now.Sub(n.queryEndpointsTime) > n.queryEndpointsEvery { 22 n.queryEndpointsTime = now 23 if err := n.queryAvailableEndpoints(); err != nil { 24 return nil, err 25 } 26 } 27 28 ms := n.queryMetrics() 29 if ms.empty() { 30 return nil, errors.New("no metrics collected") 31 } 32 33 mx := make(map[string]int64) 34 n.cache.resetUpdated() 35 n.collectInfo(mx, ms) 36 n.collectConnections(mx, ms) 37 n.collectSSL(mx, ms) 38 n.collectHTTPRequests(mx, ms) 39 n.collectHTTPCache(mx, ms) 40 n.collectHTTPServerZones(mx, ms) 41 n.collectHTTPLocationZones(mx, ms) 42 n.collectHTTPUpstreams(mx, ms) 43 n.collectStreamServerZones(mx, ms) 44 n.collectStreamUpstreams(mx, ms) 45 n.collectResolvers(mx, ms) 46 n.updateCharts() 47 48 return mx, nil 49 } 50 51 func (n *NginxPlus) collectInfo(mx map[string]int64, ms *nginxMetrics) { 52 if ms.info == nil { 53 return 54 } 55 mx["uptime"] = int64(ms.info.Timestamp.Sub(ms.info.LoadTimestamp).Seconds()) 56 } 57 58 func (n *NginxPlus) collectConnections(mx map[string]int64, ms *nginxMetrics) { 59 if ms.connections == nil { 60 return 61 } 62 mx["connections_accepted"] = ms.connections.Accepted 63 mx["connections_dropped"] = ms.connections.Dropped 64 mx["connections_active"] = ms.connections.Active 65 mx["connections_idle"] = ms.connections.Idle 66 } 67 68 func (n *NginxPlus) collectSSL(mx map[string]int64, ms *nginxMetrics) { 69 if ms.ssl == nil { 70 return 71 } 72 mx["ssl_handshakes"] = ms.ssl.Handshakes 73 mx["ssl_handshakes_failed"] = ms.ssl.HandshakesFailed 74 mx["ssl_session_reuses"] = ms.ssl.SessionReuses 75 mx["ssl_no_common_protocol"] = ms.ssl.NoCommonProtocol 76 mx["ssl_no_common_cipher"] = ms.ssl.NoCommonCipher 77 mx["ssl_handshake_timeout"] = ms.ssl.HandshakeTimeout 78 mx["ssl_peer_rejected_cert"] = ms.ssl.PeerRejectedCert 79 mx["ssl_verify_failures_no_cert"] = ms.ssl.VerifyFailures.NoCert 80 mx["ssl_verify_failures_expired_cert"] = ms.ssl.VerifyFailures.ExpiredCert 81 mx["ssl_verify_failures_revoked_cert"] = ms.ssl.VerifyFailures.RevokedCert 82 mx["ssl_verify_failures_hostname_mismatch"] = ms.ssl.VerifyFailures.HostnameMismatch 83 mx["ssl_verify_failures_other"] = ms.ssl.VerifyFailures.Other 84 } 85 86 func (n *NginxPlus) collectHTTPRequests(mx map[string]int64, ms *nginxMetrics) { 87 if ms.httpRequests == nil { 88 return 89 } 90 mx["http_requests_total"] = ms.httpRequests.Total 91 mx["http_requests_current"] = ms.httpRequests.Current 92 } 93 94 func (n *NginxPlus) collectHTTPCache(mx map[string]int64, ms *nginxMetrics) { 95 if ms.httpCaches == nil { 96 return 97 } 98 for name, cache := range *ms.httpCaches { 99 n.cache.putHTTPCache(name) 100 px := fmt.Sprintf("http_cache_%s_", name) 101 mx[px+"state_cold"] = boolToInt(cache.Cold) 102 mx[px+"state_warm"] = boolToInt(!cache.Cold) 103 mx[px+"size"] = cache.Size 104 mx[px+"served_responses"] = cache.Hit.Responses + cache.Stale.Responses + cache.Updating.Responses + cache.Revalidated.Responses 105 mx[px+"written_responses"] = cache.Miss.ResponsesWritten + cache.Expired.ResponsesWritten + cache.Bypass.ResponsesWritten 106 mx[px+"bypassed_responses"] = cache.Miss.Responses + cache.Expired.Responses + cache.Bypass.Responses 107 mx[px+"served_bytes"] = cache.Hit.Bytes + cache.Stale.Bytes + cache.Updating.Bytes + cache.Revalidated.Bytes 108 mx[px+"written_bytes"] = cache.Miss.BytesWritten + cache.Expired.BytesWritten + cache.Bypass.BytesWritten 109 mx[px+"bypassed_bytes"] = cache.Miss.Bytes + cache.Expired.Bytes + cache.Bypass.Bytes 110 } 111 } 112 113 func (n *NginxPlus) collectHTTPServerZones(mx map[string]int64, ms *nginxMetrics) { 114 if ms.httpServerZones == nil { 115 return 116 } 117 for name, zone := range *ms.httpServerZones { 118 n.cache.putHTTPServerZone(name) 119 120 px := fmt.Sprintf("http_server_zone_%s_", name) 121 mx[px+"requests_processing"] = zone.Processing 122 mx[px+"requests"] = zone.Requests 123 mx[px+"requests_discarded"] = zone.Discarded 124 mx[px+"bytes_received"] = zone.Received 125 mx[px+"bytes_sent"] = zone.Sent 126 mx[px+"responses"] = zone.Responses.Total 127 mx[px+"responses_1xx"] = zone.Responses.Class1xx 128 mx[px+"responses_2xx"] = zone.Responses.Class2xx 129 mx[px+"responses_3xx"] = zone.Responses.Class3xx 130 mx[px+"responses_4xx"] = zone.Responses.Class4xx 131 mx[px+"responses_5xx"] = zone.Responses.Class5xx 132 } 133 } 134 135 func (n *NginxPlus) collectHTTPLocationZones(mx map[string]int64, ms *nginxMetrics) { 136 if ms.httpLocationZones == nil { 137 return 138 } 139 for name, zone := range *ms.httpLocationZones { 140 n.cache.putHTTPLocationZone(name) 141 142 px := fmt.Sprintf("http_location_zone_%s_", name) 143 mx[px+"requests"] = zone.Requests 144 mx[px+"requests_discarded"] = zone.Discarded 145 mx[px+"bytes_received"] = zone.Received 146 mx[px+"bytes_sent"] = zone.Sent 147 mx[px+"responses"] = zone.Responses.Total 148 mx[px+"responses_1xx"] = zone.Responses.Class1xx 149 mx[px+"responses_2xx"] = zone.Responses.Class2xx 150 mx[px+"responses_3xx"] = zone.Responses.Class3xx 151 mx[px+"responses_4xx"] = zone.Responses.Class4xx 152 mx[px+"responses_5xx"] = zone.Responses.Class5xx 153 } 154 } 155 156 func (n *NginxPlus) collectHTTPUpstreams(mx map[string]int64, ms *nginxMetrics) { 157 if ms.httpUpstreams == nil { 158 return 159 } 160 for name, upstream := range *ms.httpUpstreams { 161 n.cache.putHTTPUpstream(name, upstream.Zone) 162 163 px := fmt.Sprintf("http_upstream_%s_zone_%s_", name, upstream.Zone) 164 mx[px+"zombies"] = upstream.Zombies 165 mx[px+"keepalive"] = upstream.Keepalive 166 mx[px+"peers"] = int64(len(upstream.Peers)) 167 168 for _, peer := range upstream.Peers { 169 n.cache.putHTTPUpstreamServer(name, peer.Server, peer.Name, upstream.Zone) 170 171 px = fmt.Sprintf("http_upstream_%s_server_%s_zone_%s_", name, peer.Server, upstream.Zone) 172 mx[px+"active"] = peer.Active 173 mx[px+"state_up"] = boolToInt(peer.State == "up") 174 mx[px+"state_down"] = boolToInt(peer.State == "down") 175 mx[px+"state_draining"] = boolToInt(peer.State == "draining") 176 mx[px+"state_unavail"] = boolToInt(peer.State == "unavail") 177 mx[px+"state_checking"] = boolToInt(peer.State == "checking") 178 mx[px+"state_unhealthy"] = boolToInt(peer.State == "unhealthy") 179 mx[px+"bytes_received"] = peer.Received 180 mx[px+"bytes_sent"] = peer.Sent 181 mx[px+"requests"] = peer.Requests 182 mx[px+"responses"] = peer.Responses.Total 183 mx[px+"responses_1xx"] = peer.Responses.Class1xx 184 mx[px+"responses_2xx"] = peer.Responses.Class2xx 185 mx[px+"responses_3xx"] = peer.Responses.Class3xx 186 mx[px+"responses_4xx"] = peer.Responses.Class4xx 187 mx[px+"responses_5xx"] = peer.Responses.Class5xx 188 mx[px+"response_time"] = peer.ResponseTime 189 mx[px+"header_time"] = peer.HeaderTime 190 mx[px+"downtime"] = peer.Downtime / 1000 191 } 192 } 193 } 194 195 func (n *NginxPlus) collectStreamServerZones(mx map[string]int64, ms *nginxMetrics) { 196 if ms.streamServerZones == nil { 197 return 198 } 199 for name, zone := range *ms.streamServerZones { 200 n.cache.putStreamServerZone(name) 201 202 px := fmt.Sprintf("stream_server_zone_%s_", name) 203 mx[px+"connections"] = zone.Connections 204 mx[px+"connections_processing"] = zone.Processing 205 mx[px+"connections_discarded"] = zone.Discarded 206 mx[px+"bytes_received"] = zone.Received 207 mx[px+"bytes_sent"] = zone.Sent 208 mx[px+"sessions"] = zone.Sessions.Total 209 mx[px+"sessions_2xx"] = zone.Sessions.Class2xx 210 mx[px+"sessions_4xx"] = zone.Sessions.Class4xx 211 mx[px+"sessions_5xx"] = zone.Sessions.Class5xx 212 } 213 } 214 215 func (n *NginxPlus) collectStreamUpstreams(mx map[string]int64, ms *nginxMetrics) { 216 if ms.streamUpstreams == nil { 217 return 218 } 219 for name, upstream := range *ms.streamUpstreams { 220 n.cache.putStreamUpstream(name, upstream.Zone) 221 222 px := fmt.Sprintf("stream_upstream_%s_zone_%s_", name, upstream.Zone) 223 mx[px+"zombies"] = upstream.Zombies 224 mx[px+"peers"] = int64(len(upstream.Peers)) 225 226 for _, peer := range upstream.Peers { 227 n.cache.putStreamUpstreamServer(name, peer.Server, peer.Name, upstream.Zone) 228 229 px = fmt.Sprintf("stream_upstream_%s_server_%s_zone_%s_", name, peer.Server, upstream.Zone) 230 mx[px+"active"] = peer.Active 231 mx[px+"connections"] = peer.Connections 232 mx[px+"state_up"] = boolToInt(peer.State == "up") 233 mx[px+"state_down"] = boolToInt(peer.State == "down") 234 mx[px+"state_unavail"] = boolToInt(peer.State == "unavail") 235 mx[px+"state_checking"] = boolToInt(peer.State == "checking") 236 mx[px+"state_unhealthy"] = boolToInt(peer.State == "unhealthy") 237 mx[px+"bytes_received"] = peer.Received 238 mx[px+"bytes_sent"] = peer.Sent 239 mx[px+"downtime"] = peer.Downtime / 1000 240 } 241 } 242 } 243 244 func (n *NginxPlus) collectResolvers(mx map[string]int64, ms *nginxMetrics) { 245 if ms.resolvers == nil { 246 return 247 } 248 for name, zone := range *ms.resolvers { 249 n.cache.putResolver(name) 250 251 px := fmt.Sprintf("resolver_zone_%s_", name) 252 mx[px+"requests_name"] = zone.Requests.Name 253 mx[px+"requests_srv"] = zone.Requests.Srv 254 mx[px+"requests_addr"] = zone.Requests.Addr 255 mx[px+"responses_noerror"] = zone.Responses.NoError 256 mx[px+"responses_formerr"] = zone.Responses.Formerr 257 mx[px+"responses_servfail"] = zone.Responses.Servfail 258 mx[px+"responses_nxdomain"] = zone.Responses.Nxdomain 259 mx[px+"responses_notimp"] = zone.Responses.Notimp 260 mx[px+"responses_refused"] = zone.Responses.Refused 261 mx[px+"responses_timedout"] = zone.Responses.TimedOut 262 mx[px+"responses_unknown"] = zone.Responses.Unknown 263 } 264 } 265 266 func (n *NginxPlus) updateCharts() { 267 const notSeenLimit = 3 268 269 for key, v := range n.cache.httpCaches { 270 if v.updated && !v.hasCharts { 271 v.hasCharts = true 272 n.addHTTPCacheCharts(v.name) 273 continue 274 } 275 if !v.updated { 276 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 277 delete(n.cache.httpCaches, key) 278 n.removeHTTPCacheCharts(v.name) 279 } 280 } 281 } 282 for key, v := range n.cache.httpServerZones { 283 if v.updated && !v.hasCharts { 284 v.hasCharts = true 285 n.addHTTPServerZoneCharts(v.zone) 286 continue 287 } 288 if !v.updated { 289 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 290 delete(n.cache.httpServerZones, key) 291 n.removeHTTPServerZoneCharts(v.zone) 292 } 293 } 294 } 295 for key, v := range n.cache.httpLocationZones { 296 if v.updated && !v.hasCharts { 297 v.hasCharts = true 298 n.addHTTPLocationZoneCharts(v.zone) 299 continue 300 } 301 if !v.updated { 302 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 303 delete(n.cache.httpLocationZones, key) 304 n.removeHTTPLocationZoneCharts(v.zone) 305 } 306 } 307 } 308 for key, v := range n.cache.httpUpstreams { 309 if v.updated && !v.hasCharts { 310 v.hasCharts = true 311 n.addHTTPUpstreamCharts(v.name, v.zone) 312 continue 313 } 314 if !v.updated { 315 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 316 delete(n.cache.httpUpstreams, key) 317 n.removeHTTPUpstreamCharts(v.name, v.zone) 318 } 319 } 320 } 321 for key, v := range n.cache.httpUpstreamServers { 322 if v.updated && !v.hasCharts { 323 v.hasCharts = true 324 n.addHTTPUpstreamServerCharts(v.name, v.serverAddr, v.serverName, v.zone) 325 continue 326 } 327 if !v.updated { 328 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 329 delete(n.cache.httpUpstreamServers, key) 330 n.removeHTTPUpstreamServerCharts(v.name, v.serverAddr, v.zone) 331 } 332 } 333 } 334 for key, v := range n.cache.streamServerZones { 335 if v.updated && !v.hasCharts { 336 v.hasCharts = true 337 n.addStreamServerZoneCharts(v.zone) 338 continue 339 } 340 if !v.updated { 341 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 342 delete(n.cache.streamServerZones, key) 343 n.removeStreamServerZoneCharts(v.zone) 344 } 345 } 346 } 347 for key, v := range n.cache.streamUpstreams { 348 if v.updated && !v.hasCharts { 349 v.hasCharts = true 350 n.addStreamUpstreamCharts(v.name, v.zone) 351 continue 352 } 353 if !v.updated { 354 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 355 delete(n.cache.streamUpstreams, key) 356 n.removeStreamUpstreamCharts(v.name, v.zone) 357 } 358 } 359 } 360 for key, v := range n.cache.streamUpstreamServers { 361 if v.updated && !v.hasCharts { 362 v.hasCharts = true 363 n.addStreamUpstreamServerCharts(v.name, v.serverAddr, v.serverName, v.zone) 364 continue 365 } 366 if !v.updated { 367 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 368 delete(n.cache.streamUpstreamServers, key) 369 n.removeStreamUpstreamServerCharts(v.name, v.serverAddr, v.zone) 370 } 371 } 372 } 373 for key, v := range n.cache.resolvers { 374 if v.updated && !v.hasCharts { 375 v.hasCharts = true 376 n.addResolverZoneCharts(v.zone) 377 continue 378 } 379 if !v.updated { 380 if v.notSeenTimes++; v.notSeenTimes >= notSeenLimit { 381 delete(n.cache.resolvers, key) 382 n.removeResolverZoneCharts(v.zone) 383 } 384 } 385 } 386 } 387 388 func boolToInt(v bool) int64 { 389 if v { 390 return 1 391 } 392 return 0 393 }