ZendDiagnostics
Simple component for performing diagnostic tests in real-world PHP applications., (*1)
, (*2)
It currently ships with the following Diagnostic Checks:, (*3)
-
ApcFragmentation - check if APC memory fragmentation is below given threshold,
-
ApcMemory - check available APC memory,
-
Callback - call a user-defined diagnostic function,
-
ClassExists - make sure class exists in current environment,
-
CouchDBCheck - check if connection is possible,
-
CpuPerformance - check server CPU performance is above baseline,
-
DirReadable - make sure given path is readable,
-
DirWritable - make sure given path is writable,
-
DiskFree - check there's enough free space on given path,
-
DiskUsage - check if the disk usage is below warning/critical percent thresholds,
-
DoctrineMigration - make sure all migrations are applied.
-
ExtensionLoaded - make sure extension is loaded,
-
GuzzleHttpService - check if given http host is responding using Guzzle,
-
HttpService - check if given http host is responding,
-
Memcache - check if memcache extension is loaded and given server is reachable,
-
OpCacheMemory - check if the OpCache memory usage is below warning/critical thresholds,
-
PDOCheck - check if connection is possible,
-
PhpVersion - make sure that PHP version matches constraint,
-
PhpFlag - make sure that given PHP flag (feature) is turned on or off.
-
ProcessRunning - check if a process with given name or ID is currently running,
-
RabbitMQ - Validate that a RabbitMQ service is running,
-
Redis - Validate that a Redis service is running,
-
SecurityAdvisory - check installed composer dependencies against SensioLabs SA database,
-
StreamWrapperExists - make sure given stream wrapper is available.
File validation checks:, (*4)
-
IniFile - check if given INI file is available and valid,
-
JsonFile - check if given JSON file is available and valid,
-
XmlFile - check if given XML file is available and valid,
-
YamlFile - check if given YAML file is available and valid
Using diagnostics with Zend Framework 2
- Install the ZFTool module.
- Enable diagnostic tests in your application config.php.
- In your console type
php public/index.php diag
to run diagnostics.
Using diagnostics with Symfony 2
- Install the LiipMonitorBundle.
- Enable diagnostic tests in your application configuration.
- In your console type
./app/console monitor:health
to run diagnostics.
Using diagnostics with PSR-7 middleware
Install the rstgroup/diagnostics-middleware., (*5)
Using diagnostics in plain PHP
- Add ZendDiagnostics component to your application
- If you are not using Composer, use
include "autoload_register.php";
- Create an instance of
ZendDiagnostics\Runner
- Add tests using
Runner::addTest()
- Optionally add a reporter to display progress using
Runner::addReporter()
- Run diagnostics
Runner::run()
For example:, (*6)
// run_diagnostics.php
use ZendDiagnostics\Check;
use ZendDiagnostics\Runner\Runner;
use ZendDiagnostics\Runner\Reporter\BasicConsole;
use ZendDiagnostics\Check\DiskFree;
include 'autoload_register.php';
// Create Runner instance
$runner = new Runner();
// Add checks
$runner->addCheck(new Check\DirWritable('/tmp'));
$runner->addCheck(new Check\DiskFree('/tmp', 100000000));
// Add console reporter
$runner->addReporter(new BasicConsole(80, true));
// Run all checks
$runner->run();
You can now run the file in your console (command line):, (*7)
> php run_diagnostics.php
Starting diagnostics:
..
OK (2 diagnostic tests)
Using Result Collection
The Runner will always return a Result\Collection (even without any
attached Reporter). This collection contains results for all tests and failure counters., (*8)
Simple example:, (*9)
$runner = new Runner();
$checkSpace = new Check\DiskFree('/tmp', 100000000);
$checkTemp = new Check\DirWritable('/tmp');
$runner->addCheck($checkSpace);
$runner->addCheck($checkTemp);
// Run all checks
$results = $runner->run();
echo "Number of successful tests: " . $results->getSuccessCount() . "\n";
echo "Number of failed tests: " . $results->getFailureCount() . "\n";
if ($results[$checkSpace] instanceof \ZendDiagnostics\Result\FailureInterface) {
echo "Oooops! We're running out of space on temp.\n";
}
if ($results[$checkTemp] instanceof \ZendDiagnostics\Result\FailureInterface) {
echo "It seems that /tmp is not writable - this is a serious problem!\n";
}
Architecture
A single diagnostic Check performs one particular
test on the application or environment., (*10)
It must return a Result
which implements one of the following result interfaces:, (*11)
-
Success - in case the check ran through without any issue.
-
Warning - in case there might be something wrong.
-
Failure - when the test failed and an intervention is required.
Each test Result can additionally return:, (*12)
-
result message via
getMessage()
. It can be used to describe the context of the result.
-
result data via
getData()
. This can be used for providing detailed information on the cause of particular
result, which might be useful for debugging problems.
One can define additional result interfaces, i.e. denoting
severity levels (i.e. critical, alert, notice) or appropriate actions (i.e. missing, incomplete). However, it
is recommended to extend the primary set of Success, Warning, Failure interfaces for compatibility with other
applications and libraries., (*13)
Writing custom Checks
A Check class has to implement Check and provide the following methods:, (*14)
interface CheckInterface
{
/**
* @return ResultInterface
*/
public function check();
/**
* Return a label describing this test instance.
*
* @return string
*/
public function getLabel();
}
The main check()
method is responsible for doing the actual check and is expected to return a
Result. It is recommended to use the built-in result classes for
compatibility with Runner and other checks., (*15)
Here is an example trivial class, that will check if PHP default timezone is set to UTC., (*16)
namespace MyApp\Diagnostics\Check;
use ZendDiagnostics\Check\CheckInterface;
use ZendDiagnostics\Result\Success;
use ZendDiagnostics\Result\Failure;
class TimezoneSetToUTC implements CheckInterface
{
public function check()
{
$tz = date_default_timezone_get();
if ($tz == 'UTC') {
return new Success('Default timezone is UTC');
} else {
return new Failure('Default timezone is not UTC! It is actually ' . $tz);
}
}
public function getLabel()
{
return 'Check if PHP default timezone is set to UTC';
}
}
Writing custom Reporters
A Reporter is a class implementing ReporterInterface., (*17)
interface ReporterInterface
{
public function onStart(ArrayObject $checks, $runnerConfig);
public function onBeforeRun(Check $check);
public function onAfterRun(Check $check, Result $result);
public function onStop(ResultsCollection $results);
public function onFinish(ResultsCollection $results);
}
A Runner invokes above methods while running diagnostics in the following order:, (*18)
-
onStart
- right after calling Runner::run()
-
onBeforeRun
- before each individual Check.
-
onAfterRun
- after each individual check has finished running.
-
onFinish
- after Runner has finished its job.
-
onStop
- in case Runner has been interrupted:
- when the Reporter has returned
false
from onAfterRun
method
- or when runner is configured with
setBreakOnFailure(true)
and one of the Checks fails.
Some Reporter methods can be used to interrupt the operation of a Runner:, (*19)
-
onBeforeRun(Check $check)
- in case this method returns false
, that particular Check will be omitted.
-
onAfterRun(Check $check, Result($result))
- in case this method returns false
, the Runner will abort checking.
All other return values are ignored., (*20)
ZendDiagnostics ships with a simple Console reporter - it
can serve as a good example on how to write your own Reporters., (*21)
Built-in diagnostics checks
ZendDiagnostics provides several "just add water" checks you can use straight away., (*22)
The following built-in tests are currently available:, (*23)
ApcFragmentation
Make sure that APC memory fragmentation level is below given threshold:, (*24)
<?php
use ZendDiagnostics\Check\ApcFragmentation;
// Display a warning with fragmentation > 50% and failure when above 90%
$fragmentation = new ApcFragmentation(50, 90);
ApcMemory
Check APC memory usage percent and make sure it's below given threshold., (*25)
<?php
use ZendDiagnostics\Check\ApcMemory;
// Display a warning with memory usage is above 70% and a failure above 90%
$checkFreeMemory = new ApcMemory(70, 90);
Callback
Run a function (callback) and use return value as the result:, (*26)
<?php
use ZendDiagnostics\Check\Callback;
use ZendDiagnostics\Result\Success;
use ZendDiagnostics\Result\Failure;
$checkDbFile = new Callback(function(){
$path = __DIR__ . '/data/db.sqlite';
if(is_file($path) && is_readable($path) && filesize($path)) {
return new Success('Db file is ok');
} else {
return new Failure('There is something wrong with the db file');
}
});
Note: The callback must return either a boolean
(true for success, false for failure) or a valid instance of
ResultInterface. All other objects will result in an exception
and scalars (i.e. a string) will be interpreted as warnings., (*27)
ClassExists
Check if a class (or an array of classes) exist. For example:, (*28)
<?php
use ZendDiagnostics\Check\ClassExists;
$checkLuaClass = new ClassExists('Lua');
$checkRbacClasses = new ClassExists(array(
'ZfcRbac\Module',
'ZfcRbac\Controller\Plugin\IsGranted'
));
Benchmark CPU performance and return failure if it is below the given ratio. The baseline for performance calculation
is the speed of Amazon EC2 Micro Instance (Q1 2013). You can specify the expected performance for the test, where a
ratio of 1.0
(one) means at least the speed of EC2 Micro Instance. A ratio of 2
would mean "at least double the
performance of EC2 Micro Instance" and a fraction of 0.5
means "at least half the performance of Micro Instance"., (*29)
The following check will test if current server has at least half the CPU power of EC2 Micro Instance:, (*30)
<?php
use ZendDiagnostics\Check\CpuPerformance;
$checkMinCPUSpeed = new CpuPerformance(0.5); // at least 50% of EC2 micro instance
DirReadable
Check if a given path (or array of paths) points to a directory and it is readable., (*31)
<?php
use ZendDiagnostics\Check\DirReadable;
$checkPublic = new DirReadable('public/');
$checkAssets = new DirReadable(array(
__DIR__ . '/assets/img',
__DIR__ . '/assets/js'
));
DirWritable
Check if a given path (or array of paths) points to a directory and if it can be written to., (*32)
<?php
use ZendDiagnostics\Check\DirWritable;
$checkTemporary = new DirWritable('/tmp');
$checkAssets = new DirWritable(array(
__DIR__ . '/assets/customImages',
__DIR__ . '/assets/customJs',
__DIR__ . '/assets/uploads',
));
DiskFree
Check if there is enough remaining free disk space., (*33)
The first parameter is the minimum disk space, which can be supplied as integer (in bytes, i.e. 1024
)
or as a string with a multiplier (IEC, SI or Jedec, i.e. "150MB"
). The second parameter is the path to check -
on *NIX systems it is an ordinary path (i.e. /home
), on Windows systems it is a drive letter (i.e. "C:"
)., (*34)
<?php
use ZendDiagnostics\Check\DiskFree;
$tempHasAtLeast100Megs = new DiskFree('100MB', '/tmp');
$homeHasAtLeast1TB = new DiskFree('1TiB', '/home');
$dataHasAtLeast900Bytes = new DiskFree(900, __DIR__ . '/data/');
ExtensionLoaded
Check if a PHP extension (or an array of extensions) is currently loaded., (*35)
<?php
use ZendDiagnostics\Check\ExtensionLoaded;
$checkMbstring = new ExtensionLoaded('mbstring');
$checkCompression = new ExtensionLoaded(array(
'rar',
'bzip2',
'zip'
));
HttpService
Attempt connection to given HTTP host or IP address and try to load a web page. The check also supports
checking response codes and page contents., (*36)
<?php
use ZendDiagnostics\Check\HttpService;
// Try to connect to google.com
$checkGoogle = new HttpService('www.google.com');
// Check port 8080 on localhost
$checkLocal = new HttpService('127.0.0.1', 8080);
// Check that the page exists (response code must equal 200)
$checkPage = new HttpService('www.example.com', 80, '/some/page.html', 200);
// Check page content
$checkPageContent = new HttpService(
'www.example.com',
80,
'/some/page.html',
200,
'<title>Hello World</title>'
);
GuzzleHttpService
Attempt connection to given HTTP host or IP address and try to load a web page using
Guzzle. The check also supports checking response
codes and page contents., (*37)
<?php
use ZendDiagnostics\Check\GuzzleHttpService;
// Try to connect to google.com
$checkGoogle = new GuzzleHttpService('www.google.com');
// Check port 8080 on localhost
$checkLocal = new GuzzleHttpService('127.0.0.1:8080');
// Check that the page exists (response code must equal 200)
$checkPage = new GuzzleHttpService('www.example.com/some/page.html');
// Check page content
$checkPageContent = new GuzzleHttpService(
'www.example.com/some/page.html',
array(),
array(),
200,
'<title>Hello World</title>'
);
// Check that the post request returns the content
$checkPageContent = new GuzzleHttpService(
'www.example.com/user/update',
array(),
array(),
200,
'{"status":"success"}',
'POST',
array("post_field" => "post_value")
);
Memcache
Attempt to connect to given Memcache server., (*38)
<?php
use ZendDiagnostics\Check\Memcache;
$checkLocal = new Memcache('127.0.0.1'); // default port
$checkBackup = new Memcache('10.0.30.40', 11212);
PhpVersion
Check if current PHP version matches the given requirement. The test accepts 2 parameters - baseline version and
optional comparison operator., (*39)
<?php
use ZendDiagnostics\Check\PhpVersion;
$require545orNewer = new PhpVersion('5.4.5');
$rejectBetaVersions = new PhpVersion('5.5.0', '<');
PhpFlag
Make sure that given PHP flag(s) is enabled or disabled (i.e. as defined in php.ini). You can use this test to
alert the user about unsafe or behavior-changing PHP settings., (*40)
<?php
use ZendDiagnostics\Check\PhpFlag;
// This check will fail if use_only_cookies is not enabled
$sessionOnlyUsesCookies = new PhpFlag('session.use_only_cookies', true);
// This check will fail if safe_mode has been enabled
$noSafeMode = new PhpFlag('safe_mode', false);
// The following will fail if any of the flags is enabled
$check = new PhpFlag(array(
'expose_php',
'ignore_user_abort',
'html_errors'
), false);
ProcessRunning
Check if a given unix process is running. This check supports PIDs and process names., (*41)
<?php
use ZendDiagnostics\Check\ProcessRunning;
$checkApache = new ProcessRunning('httpd');
$checkProcess1000 = new ProcessRunning(1000);
RabbitMQ
Validate that a RabbitMQ service is running., (*42)
<?php
use ZendDiagnostics\Check\RabbitMQ;
$rabbitMQCheck = new RabbitMQ('localhost', 5672, 'guest', 'guest', '/');
Redis
Validate that a Redis service is running., (*43)
<?php
use ZendDiagnostics\Check\Redis;
$redisCheck = new Redis('localhost', 6379, 'secret');
SecurityAdvisory
Run a security check of libraries locally installed by Composer against
SensioLabs Security Advisory database and warn about potential
security vulnerabilities., (*44)
<?php
use ZendDiagnostics\Check\SecurityAdvisory;
// Warn about any packages that might have security vulnerabilities and require updating
$security = new SecurityAdvisory();
// Check another composer.lock
$security = new SecurityAdvisory('/var/www/project/composer.lock');
StreamWrapperExists
Check if a given stream wrapper (or an array of wrappers) is available. For example:, (*45)
<?php
use ZendDiagnostics\Check\StreamWrapperExists;
$checkOGGStream = new StreamWrapperExists('ogg');
$checkCompression = new StreamWrapperExists(array(
'zlib',
'bzip2',
'zip'
));
DoctrineMigration
Make sure all migrations are applied:, (*46)
<?php
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\ORM\EntityManager;
use ZendDiagnostics\Check\DoctrineMigration;
$em = EntityManager::create(/** config */);
$migrationConfig = new Configuration($em);
$check = new DoctrineMigration($migrationConfig);
IniFile
Read an INI-file from the given path and try to parse it., (*47)
<?php
use ZendDiagnostics\Check\IniFile;
$checkIniFile = new IniFile('/my/path/to/file.ini');
$checkIniFile = new IniFile(['file1.ini', 'file2.ini', '...']);
JsonFile
Read a JSON-file from the given path and try to decode it., (*48)
<?php
use ZendDiagnostics\Check\JsonFile;
$checkJsonFile = new JsonFile('/my/path/to/file.json');
$checkJsonFile = new JsonFile(['file1.json', 'file2.json', '...']);
XmlFile
Read an XML-file from the given path, try to parse it and validate it agaist its DTD schema if possible., (*49)
<?php
use ZendDiagnostics\Check\XmlFile;
$checkXmlFile = new XmlFile('/my/path/to/file.xml');
$checkXmlFile = new XmlFile(['file1.xml', 'file2.xml', '...']);
YamlFile
Read a YAML-file from the given path and try to parse it., (*50)
<?php
use ZendDiagnostics\Check\YamlFile;
$checkYamlFile = new YamlFile('/my/path/to/file.yml');
$checkYamlFile = new YamlFile(['file1.yml', 'file2.yml', '...']);