1// Copyright © 2018 Enrico Stahn <enrico.stahn@gmail.com> 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14// Package phpfpm provides convenient access to PHP-FPM pool data 15package phpfpm 16 17import ( 18 "fmt" 19 "sync" 20 21 "github.com/prometheus/client_golang/prometheus" 22) 23 24const ( 25 namespace = "phpfpm" 26) 27 28// Exporter configures and exposes PHP-FPM metrics to Prometheus. 29type Exporter struct { 30 mutex sync.Mutex 31 PoolManager PoolManager 32 33 CountProcessState bool 34 35 up *prometheus.Desc 36 scrapeFailues *prometheus.Desc 37 startSince *prometheus.Desc 38 acceptedConnections *prometheus.Desc 39 listenQueue *prometheus.Desc 40 maxListenQueue *prometheus.Desc 41 listenQueueLength *prometheus.Desc 42 idleProcesses *prometheus.Desc 43 activeProcesses *prometheus.Desc 44 totalProcesses *prometheus.Desc 45 maxActiveProcesses *prometheus.Desc 46 maxChildrenReached *prometheus.Desc 47 slowRequests *prometheus.Desc 48 processRequests *prometheus.Desc 49 processLastRequestMemory *prometheus.Desc 50 processLastRequestCPU *prometheus.Desc 51 processRequestDuration *prometheus.Desc 52 processState *prometheus.Desc 53} 54 55// NewExporter creates a new Exporter for a PoolManager and configures the necessary metrics. 56func NewExporter(pm PoolManager) *Exporter { 57 return &Exporter{ 58 PoolManager: pm, 59 60 CountProcessState: false, 61 62 up: prometheus.NewDesc( 63 prometheus.BuildFQName(namespace, "", "up"), 64 "Could PHP-FPM be reached?", 65 []string{"pool", "scrape_uri"}, 66 nil), 67 68 scrapeFailues: prometheus.NewDesc( 69 prometheus.BuildFQName(namespace, "", "scrape_failures"), 70 "The number of failures scraping from PHP-FPM.", 71 []string{"pool", "scrape_uri"}, 72 nil), 73 74 startSince: prometheus.NewDesc( 75 prometheus.BuildFQName(namespace, "", "start_since"), 76 "The number of seconds since FPM has started.", 77 []string{"pool", "scrape_uri"}, 78 nil), 79 80 acceptedConnections: prometheus.NewDesc( 81 prometheus.BuildFQName(namespace, "", "accepted_connections"), 82 "The number of requests accepted by the pool.", 83 []string{"pool", "scrape_uri"}, 84 nil), 85 86 listenQueue: prometheus.NewDesc( 87 prometheus.BuildFQName(namespace, "", "listen_queue"), 88 "The number of requests in the queue of pending connections.", 89 []string{"pool", "scrape_uri"}, 90 nil), 91 92 maxListenQueue: prometheus.NewDesc( 93 prometheus.BuildFQName(namespace, "", "max_listen_queue"), 94 "The maximum number of requests in the queue of pending connections since FPM has started.", 95 []string{"pool", "scrape_uri"}, 96 nil), 97 98 listenQueueLength: prometheus.NewDesc( 99 prometheus.BuildFQName(namespace, "", "listen_queue_length"), 100 "The size of the socket queue of pending connections.", 101 []string{"pool", "scrape_uri"}, 102 nil), 103 104 idleProcesses: prometheus.NewDesc( 105 prometheus.BuildFQName(namespace, "", "idle_processes"), 106 "The number of idle processes.", 107 []string{"pool", "scrape_uri"}, 108 nil), 109 110 activeProcesses: prometheus.NewDesc( 111 prometheus.BuildFQName(namespace, "", "active_processes"), 112 "The number of active processes.", 113 []string{"pool", "scrape_uri"}, 114 nil), 115 116 totalProcesses: prometheus.NewDesc( 117 prometheus.BuildFQName(namespace, "", "total_processes"), 118 "The number of idle + active processes.", 119 []string{"pool", "scrape_uri"}, 120 nil), 121 122 maxActiveProcesses: prometheus.NewDesc( 123 prometheus.BuildFQName(namespace, "", "max_active_processes"), 124 "The maximum number of active processes since FPM has started.", 125 []string{"pool", "scrape_uri"}, 126 nil), 127 128 maxChildrenReached: prometheus.NewDesc( 129 prometheus.BuildFQName(namespace, "", "max_children_reached"), 130 "The number of times, the process limit has been reached, when pm tries to start more children (works only for pm 'dynamic' and 'ondemand').", 131 []string{"pool", "scrape_uri"}, 132 nil), 133 134 slowRequests: prometheus.NewDesc( 135 prometheus.BuildFQName(namespace, "", "slow_requests"), 136 "The number of requests that exceeded your 'request_slowlog_timeout' value.", 137 []string{"pool", "scrape_uri"}, 138 nil), 139 140 processRequests: prometheus.NewDesc( 141 prometheus.BuildFQName(namespace, "", "process_requests"), 142 "The number of requests the process has served.", 143 []string{"pool", "child", "scrape_uri"}, 144 nil), 145 146 processLastRequestMemory: prometheus.NewDesc( 147 prometheus.BuildFQName(namespace, "", "process_last_request_memory"), 148 "The max amount of memory the last request consumed.", 149 []string{"pool", "child", "scrape_uri"}, 150 nil), 151 152 processLastRequestCPU: prometheus.NewDesc( 153 prometheus.BuildFQName(namespace, "", "process_last_request_cpu"), 154 "The %cpu the last request consumed.", 155 []string{"pool", "child", "scrape_uri"}, 156 nil), 157 158 processRequestDuration: prometheus.NewDesc( 159 prometheus.BuildFQName(namespace, "", "process_request_duration"), 160 "The duration in microseconds of the requests.", 161 []string{"pool", "child", "scrape_uri"}, 162 nil), 163 164 processState: prometheus.NewDesc( 165 prometheus.BuildFQName(namespace, "", "process_state"), 166 "The state of the process (Idle, Running, ...).", 167 []string{"pool", "child", "state", "scrape_uri"}, 168 nil), 169 } 170} 171 172// Collect updates the Pools and sends the collected metrics to Prometheus 173func (e *Exporter) Collect(ch chan<- prometheus.Metric) { 174 e.mutex.Lock() 175 defer e.mutex.Unlock() 176 177 if err := e.PoolManager.Update(); err != nil { 178 log.Error(err) 179 } 180 181 for _, pool := range e.PoolManager.Pools { 182 ch <- prometheus.MustNewConstMetric(e.scrapeFailues, prometheus.CounterValue, float64(pool.ScrapeFailures), pool.Name, pool.Address) 183 184 if pool.ScrapeError != nil { 185 ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 0, pool.Name, pool.Address) 186 log.Errorf("Error scraping PHP-FPM: %v", pool.ScrapeError) 187 continue 188 } 189 190 active, idle, total := CountProcessState(pool.Processes) 191 if !e.CountProcessState && (active != pool.ActiveProcesses || idle != pool.IdleProcesses) { 192 log.Error("Inconsistent active and idle processes reported. Set `--fix-process-count` to have this calculated by php-fpm_exporter instead.") 193 } 194 195 if !e.CountProcessState { 196 active = pool.ActiveProcesses 197 idle = pool.IdleProcesses 198 total = pool.TotalProcesses 199 } 200 201 ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 1, pool.Name, pool.Address) 202 ch <- prometheus.MustNewConstMetric(e.startSince, prometheus.CounterValue, float64(pool.StartSince), pool.Name, pool.Address) 203 ch <- prometheus.MustNewConstMetric(e.acceptedConnections, prometheus.CounterValue, float64(pool.AcceptedConnections), pool.Name, pool.Address) 204 ch <- prometheus.MustNewConstMetric(e.listenQueue, prometheus.GaugeValue, float64(pool.ListenQueue), pool.Name, pool.Address) 205 ch <- prometheus.MustNewConstMetric(e.maxListenQueue, prometheus.CounterValue, float64(pool.MaxListenQueue), pool.Name, pool.Address) 206 ch <- prometheus.MustNewConstMetric(e.listenQueueLength, prometheus.GaugeValue, float64(pool.ListenQueueLength), pool.Name, pool.Address) 207 ch <- prometheus.MustNewConstMetric(e.idleProcesses, prometheus.GaugeValue, float64(idle), pool.Name, pool.Address) 208 ch <- prometheus.MustNewConstMetric(e.activeProcesses, prometheus.GaugeValue, float64(active), pool.Name, pool.Address) 209 ch <- prometheus.MustNewConstMetric(e.totalProcesses, prometheus.GaugeValue, float64(total), pool.Name, pool.Address) 210 ch <- prometheus.MustNewConstMetric(e.maxActiveProcesses, prometheus.CounterValue, float64(pool.MaxActiveProcesses), pool.Name, pool.Address) 211 ch <- prometheus.MustNewConstMetric(e.maxChildrenReached, prometheus.CounterValue, float64(pool.MaxChildrenReached), pool.Name, pool.Address) 212 ch <- prometheus.MustNewConstMetric(e.slowRequests, prometheus.CounterValue, float64(pool.SlowRequests), pool.Name, pool.Address) 213 214 for childNumber, process := range pool.Processes { 215 childName := fmt.Sprintf("%d", childNumber) 216 217 states := map[string]int{ 218 PoolProcessRequestIdle: 0, 219 PoolProcessRequestRunning: 0, 220 PoolProcessRequestFinishing: 0, 221 PoolProcessRequestReadingHeaders: 0, 222 PoolProcessRequestInfo: 0, 223 PoolProcessRequestEnding: 0, 224 } 225 states[process.State]++ 226 227 for stateName, inState := range states { 228 ch <- prometheus.MustNewConstMetric(e.processState, prometheus.GaugeValue, float64(inState), pool.Name, childName, stateName, pool.Address) 229 } 230 ch <- prometheus.MustNewConstMetric(e.processRequests, prometheus.CounterValue, float64(process.Requests), pool.Name, childName, pool.Address) 231 ch <- prometheus.MustNewConstMetric(e.processLastRequestMemory, prometheus.GaugeValue, float64(process.LastRequestMemory), pool.Name, childName, pool.Address) 232 ch <- prometheus.MustNewConstMetric(e.processLastRequestCPU, prometheus.GaugeValue, process.LastRequestCPU, pool.Name, childName, pool.Address) 233 ch <- prometheus.MustNewConstMetric(e.processRequestDuration, prometheus.GaugeValue, float64(process.RequestDuration), pool.Name, childName, pool.Address) 234 } 235 } 236} 237 238// Describe exposes the metric description to Prometheus 239func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { 240 ch <- e.up 241 ch <- e.startSince 242 ch <- e.acceptedConnections 243 ch <- e.listenQueue 244 ch <- e.maxListenQueue 245 ch <- e.listenQueueLength 246 ch <- e.idleProcesses 247 ch <- e.activeProcesses 248 ch <- e.totalProcesses 249 ch <- e.maxActiveProcesses 250 ch <- e.maxChildrenReached 251 ch <- e.slowRequests 252 ch <- e.processState 253 ch <- e.processRequests 254 ch <- e.processLastRequestMemory 255 ch <- e.processLastRequestCPU 256 ch <- e.processRequestDuration 257} 258