Skip to content

Commit

Permalink
[php-fpm] Revamp the PHP FPM integration
Browse files Browse the repository at this point in the history
* Consolidated in one check ping + status queries
* Removed a lot of unused vars, simplified functions, make it look
  prettier
* Set up integration testing on Travis by:
   * installing a fresh nginx as a fast CGI passthru
   * installing a PHP version from source which ships the php-fpm
     binary
   * making them play together accepting connections to the
     /status and /ping paths
  • Loading branch information
LeoCavaille committed Mar 18, 2015
1 parent 9fc51fa commit f42db79
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 201 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ env:
- TRAVIS_FLAVOR=rabbitmq
- TRAVIS_FLAVOR=etcd
- TRAVIS_FLAVOR=pgbouncer
- TRAVIS_FLAVOR=phpfpm

# Override travis defaults with empty jobs
before_install: echo "OVERRIDING TRAVIS STEPS"
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require './ci/mongo'
require './ci/mysql'
require './ci/nginx'
require './ci/pgbouncer'
require './ci/phpfpm'
require './ci/postgres'
require './ci/rabbitmq'
require './ci/redis'
Expand Down
123 changes: 0 additions & 123 deletions checks.d/php.py

This file was deleted.

125 changes: 125 additions & 0 deletions checks.d/php_fpm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# 3p
import requests

# project
from checks import AgentCheck
from util import headers


class PHPFPMCheck(AgentCheck):
"""
Tracks basic php-fpm metrics via the status module
Requires php-fpm pools to have the status option.
See http://www.php.net/manual/de/install.fpm.configuration.php#pm.status-path for more details
"""

SERVICE_CHECK_NAME = 'php_fpm.can_ping'

GAUGES = {
'listen queue': 'php_fpm.listen_queue.size',
'idle processes': 'php_fpm.processes.idle',
'active processes': 'php_fpm.processes.active',
'total processes': 'php_fpm.processes.total',
}

RATES = {
'max children reached': 'php_fpm.processes.max_reached'
}

COUNTERS = {
'accepted conn': 'php_fpm.requests.accepted',
'slow requests': 'php_fpm.requests.slow'
}

def check(self, instance):
status_url = instance.get('status_url')
ping_url = instance.get('ping_url')

auth = None
user = instance.get('user')
password = instance.get('password')

tags = instance.get('tags', [])

if user and password:
auth = (user, password)

if status_url is None and ping_url is None:
raise Exception("No status_url or ping_url specified for this instance")

pool = None
status_exception = None
if status_url is not None:
try:
pool = self._process_status(status_url, auth, tags)
except Exception as e:
status_exception = e
pass

if ping_url is not None:
self._process_ping(ping_url, auth, tags, pool)

if status_exception is not None:
raise status_exception

def _process_status(self, status_url, auth, tags):
data = {}
try:
# TODO: adding the 'full' parameter gets you per-process detailed
# informations, which could be nice to parse and output as metrics
resp = requests.get(status_url, auth=auth,
headers=headers(self.agentConfig),
params={'json': True})
resp.raise_for_status()

data = resp.json()
except Exception as e:
self.log.error("Failed to get metrics from {0}.\nError {1}".format(status_url, e))
raise

pool_name = data.get('pool', 'default')
metric_tags = tags + ["pool:{0}".format(pool_name)]

for key, mname in self.GAUGES.iteritems():
if key not in data:
self.log.warn("Gauge metric {0} is missing from FPM status".format(key))
continue
self.gauge(mname, int(data[key]), tags=metric_tags)

for key, mname in self.RATES.iteritems():
if key not in data:
self.log.warn("Rate metric {0} is missing from FPM status".format(key))
continue
self.rate(mname, int(data[key]), tags=metric_tags)

for key, mname in self.COUNTERS.iteritems():
if key not in data:
self.log.warn("Counter metric {0} is missing from FPM status".format(key))
continue
self.increment(mname, int(data[key]), tags=metric_tags)

# return pool, to tag the service check with it if we have one
return pool_name

def _process_ping(self, ping_url, auth, tags, pool_name):
sc_tags = tags[:]
if pool_name is not None:
sc_tags.append("pool:{0}".format(pool_name))

try:
# TODO: adding the 'full' parameter gets you per-process detailed
# informations, which could be nice to parse and output as metrics
resp = requests.get(ping_url, auth=auth,
headers=headers(self.agentConfig))
resp.raise_for_status()

if 'pong' not in resp.text:
raise Exception("Received unexpected reply to ping {0}".format(resp.text))

except Exception as e:
self.log.error("Failed to ping FPM pool {0} on URL {1}."
"\nError {2}".format(pool_name, ping_url, e))
self.service_check(self.SERVICE_CHECK_NAME,
AgentCheck.CRITICAL, tags=sc_tags, message=str(e))
else:
self.service_check(self.SERVICE_CHECK_NAME, AgentCheck.OK, tags=sc_tags)
70 changes: 0 additions & 70 deletions checks.d/php_ping.py

This file was deleted.

Loading

0 comments on commit f42db79

Please sign in to comment.