├── .gitignore ├── LICENSE ├── README.md ├── bin └── health.php ├── composer.json ├── composer.lock ├── examples ├── code │ ├── deviceSpaceUsed.php │ ├── deviceUptime.php │ ├── fileCreatedAfter.php │ ├── fileNumberOfLines.php │ ├── mysqlRunning.php │ ├── mysqlSlaveVarsSet.php │ ├── redisListLength.php │ └── resourceHttpStatus.php └── config │ ├── fileNumberOfLines.php │ └── yaml │ ├── TimeFilter.yml │ ├── dailyFilter.yml │ ├── dockerContainerRunning.yml │ ├── fileNumberOfLines.yml │ └── nonStrictFileNumberOfLines.yml └── src ├── Check ├── Basic │ ├── Fixed │ │ └── FailureCheck.php │ ├── Number │ │ └── LessThanCheck.php │ └── StaticCheck.php ├── BasicCheck.php ├── CacheAwareCheck.php ├── Check.php ├── Database │ ├── Mysql │ │ ├── MysqlRunningCheck.php │ │ └── Slave │ │ │ └── SlaveStatusFieldCheck.php │ └── Redis │ │ └── ListLengthCheck.php ├── Device │ └── SpaceUsedCheck.php ├── Docker │ └── Container │ │ └── ContainerIsRunningCheck.php ├── Files │ ├── Content │ │ └── NumberOfLinesCheck.php │ ├── FileCreatedAfterCheck.php │ └── FileExistsCheck.php ├── Frameworks │ └── Symfony │ │ └── DatabaseConnected.php ├── HttpClientAwareCheck.php ├── MetricAwareResult.php ├── Resource │ └── Http │ │ └── StatusCodeCheck.php ├── Result.php └── System │ ├── NumberProcessesCheck.php │ ├── Process │ └── LineCountCheck.php │ └── UptimeCheck.php ├── Cli ├── Application.php └── Command │ └── RunCommand.php ├── Config ├── FormatFactory.php └── HealthFoundationFactory.php ├── Extenstion └── Cache │ └── Cache.php ├── Filter ├── Basic │ └── NonStrictFilter.php ├── BasicFilter.php ├── Filter.php └── Time │ └── DailyFilter.php ├── HealthFoundation.php ├── Result └── Format │ ├── Format.php │ ├── Ietf │ └── IetfFormat.php │ └── Koality │ └── KoalityFormat.php ├── RunResult.php └── test ├── Cases ├── MemoryTest.php └── NonStrictTest.php └── Check ├── StaticStatusCheck.php └── ToggleStatusCheck.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Leankoala UG (haftungsbeschränkt) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HealthFoundation 2 | 3 | The HealthCheckFoundation is an open source library that should make it easy to provide continuous health statuses for all important components in web projects. 4 | It was designed to be very extensible. 5 | 6 | 7 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Leankoala/HealthFoundation/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Leankoala/HealthFoundation/?branch=master) 8 | 9 | ## Using HealthFoundation 10 | 11 | HealthFoundation was designed to be run standalone or within any project. 12 | 13 | ### Example 14 | 15 | #### Config file 16 | 17 | This example checks if the disc space is used is less than 95 percent. 18 | 19 | ```bash 20 | $ php bin/health.php run health.yml 21 | ``` 22 | 23 | The config file ```health.yml``` could look like this 24 | 25 | ```yml 26 | format: 27 | class: Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat 28 | parameters: 29 | passedMessage: "Storage server is up and running." 30 | failureMessage: "Some problems occurred on storage server." 31 | 32 | checks: 33 | spaceUsed: 34 | check: Leankoala\HealthFoundation\Check\Device\SpaceUsedCheck 35 | identifier: space_used_check 36 | description: 'Space used on storage server' 37 | parameters: 38 | maxUsageInPercent: 95 39 | ``` 40 | 41 | For more information on how to use this have a look at the `RunCommand`. 42 | 43 | #### Code 44 | 45 | The same check as code 46 | 47 | ```php 48 | # health.php 49 | 50 | include_once __DIR__ . '/../vendor/autoload.php'; 51 | 52 | $foundation = new \Leankoala\HealthFoundation\HealthFoundation(); 53 | 54 | // max disc usage 95% 55 | $spaceUsedCheck = new \Leankoala\HealthFoundation\Check\Device\SpaceUsedCheck(); 56 | $spaceUsedCheck->init(95); 57 | 58 | $foundation->registerCheck( 59 | $spaceUsedCheck, 60 | 'space_used_check', 61 | 'Space used on storage server'); 62 | 63 | $runResult = $foundation->runHealthCheck(); 64 | 65 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 66 | 'Storage server is up and running.', 67 | 'Some problems occurred on storage server.' 68 | ); 69 | 70 | $formatter->handle( 71 | $runResult 72 | ); 73 | ``` 74 | ### Checks 75 | 76 | ### Formatter 77 | 78 | It is possible to produce any kind of health check format. At the moment the IETF standard ([Health Check Response Format for HTTP APIs](https://datatracker.ietf.org/doc/draft-inadarei-api-health-check/?include_text=1)) is supported 79 | but there is an simple interface that can be implemented to create new formats. 80 | 81 | ## Status 82 | 83 | ### Implemented health checks 84 | 85 | As this is an open source project we want everybody to submit their own checks, that is why we provide the main author of every check in this list. 86 | 87 | - Basic 88 | - Number 89 | - **LessThan** (nils.langner@leankoala.com) 90 | - Database 91 | - MySQL 92 | - Slave 93 | - **SlaveStatusField** (nils.langner@leankoala.com) 94 | - **MysqlRunning** (nils.langner@leankoala.com) 95 | - Redis 96 | - **ListLength** (nils.langner@leankoala.com) 97 | - Device 98 | - **SpaceUsed** (nils.langner@leankoala.com) 99 | - **Uptime** (nils.langner@leankoala.com) 100 | - Docker 101 | - Container 102 | - **ContainerIsRunningCheck** (nils.langner@leankoala.com) 103 | - Files 104 | - Content 105 | - **NumberOfLines** (nils.langner@leankoala.com) 106 | - **FileCreatedAfter** (nils.langner@leankoala.com) 107 | - **FileExists** (nils.langner@leankoala.com) 108 | - Resource 109 | - HTTP 110 | - **StatusCode** (galenski@online-verlag-freiburg.de) 111 | - System 112 | - **Uptime** (nils.langner@leankoala.com) 113 | - **NumberProcesses** (nils.langner@leankoala.com) 114 | 115 | ### Ideas for health checks 116 | 117 | - Database 118 | - MySQL 119 | - **NumberOfReturnedElements** 120 | - Redis 121 | - **isRunning** 122 | - Files 123 | - **isWritable** 124 | - **FileEditedAfter** 125 | - Tool 126 | - Wordpress 127 | - Plugins 128 | - **NumberOfOutdatedPlugins** 129 | - **isOutdated** 130 | - **isInsecure** 131 | 132 | 133 | ## Outlook / Ideas 134 | 135 | - **Suggestions** - the tool should find on its own what can be tested 136 | - **Plugins** - It would be great if there where plugins/bundles for WordPress, Shopware, Symfony etc. 137 | - **History** - Remember the last health status 138 | - **Action** - Do something after a health check fails 139 | -------------------------------------------------------------------------------- /bin/health.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leankoala/healthfoundation", 3 | "description": "A expendable health check framework for PHP", 4 | "authors": [ 5 | { 6 | "name": "Nils Langner", 7 | "email": "nils.langner@leankoala.com" 8 | } 9 | ], 10 | "require": { 11 | "php": ">=7.0.0", 12 | "phmlabs/init": "dev-master", 13 | "guzzlehttp/guzzle": "^7.3", 14 | "uptime/uptime": "0.2.0", 15 | "symfony/console": ">=3.1 <6.0", 16 | "symfony/yaml": ">=3.1 <6.0" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "Leankoala\\HealthFoundation\\": "src/" 21 | } 22 | }, 23 | "suggest": { 24 | "ext-mysqli": "Needed if database checks are used", 25 | "ext-json": "Used for most output formats" 26 | }, 27 | "license": "MIT" 28 | } 29 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "9bfd2b7787ddac78dddad4933f84b343", 8 | "packages": [ 9 | { 10 | "name": "guzzlehttp/guzzle", 11 | "version": "6.3.3", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/guzzle/guzzle.git", 15 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", 20 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "guzzlehttp/promises": "^1.0", 25 | "guzzlehttp/psr7": "^1.4", 26 | "php": ">=5.5" 27 | }, 28 | "require-dev": { 29 | "ext-curl": "*", 30 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", 31 | "psr/log": "^1.0" 32 | }, 33 | "suggest": { 34 | "psr/log": "Required for using the Log middleware" 35 | }, 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "6.3-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "files": [ 44 | "src/functions_include.php" 45 | ], 46 | "psr-4": { 47 | "GuzzleHttp\\": "src/" 48 | } 49 | }, 50 | "notification-url": "https://packagist.org/downloads/", 51 | "license": [ 52 | "MIT" 53 | ], 54 | "authors": [ 55 | { 56 | "name": "Michael Dowling", 57 | "email": "mtdowling@gmail.com", 58 | "homepage": "https://github.com/mtdowling" 59 | } 60 | ], 61 | "description": "Guzzle is a PHP HTTP client library", 62 | "homepage": "http://guzzlephp.org/", 63 | "keywords": [ 64 | "client", 65 | "curl", 66 | "framework", 67 | "http", 68 | "http client", 69 | "rest", 70 | "web service" 71 | ], 72 | "time": "2018-04-22T15:46:56+00:00" 73 | }, 74 | { 75 | "name": "guzzlehttp/promises", 76 | "version": "v1.3.1", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/guzzle/promises.git", 80 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 85 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "php": ">=5.5.0" 90 | }, 91 | "require-dev": { 92 | "phpunit/phpunit": "^4.0" 93 | }, 94 | "type": "library", 95 | "extra": { 96 | "branch-alias": { 97 | "dev-master": "1.4-dev" 98 | } 99 | }, 100 | "autoload": { 101 | "psr-4": { 102 | "GuzzleHttp\\Promise\\": "src/" 103 | }, 104 | "files": [ 105 | "src/functions_include.php" 106 | ] 107 | }, 108 | "notification-url": "https://packagist.org/downloads/", 109 | "license": [ 110 | "MIT" 111 | ], 112 | "authors": [ 113 | { 114 | "name": "Michael Dowling", 115 | "email": "mtdowling@gmail.com", 116 | "homepage": "https://github.com/mtdowling" 117 | } 118 | ], 119 | "description": "Guzzle promises library", 120 | "keywords": [ 121 | "promise" 122 | ], 123 | "time": "2016-12-20T10:07:11+00:00" 124 | }, 125 | { 126 | "name": "guzzlehttp/psr7", 127 | "version": "1.5.2", 128 | "source": { 129 | "type": "git", 130 | "url": "https://github.com/guzzle/psr7.git", 131 | "reference": "9f83dded91781a01c63574e387eaa769be769115" 132 | }, 133 | "dist": { 134 | "type": "zip", 135 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", 136 | "reference": "9f83dded91781a01c63574e387eaa769be769115", 137 | "shasum": "" 138 | }, 139 | "require": { 140 | "php": ">=5.4.0", 141 | "psr/http-message": "~1.0", 142 | "ralouphie/getallheaders": "^2.0.5" 143 | }, 144 | "provide": { 145 | "psr/http-message-implementation": "1.0" 146 | }, 147 | "require-dev": { 148 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "1.5-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "psr-4": { 158 | "GuzzleHttp\\Psr7\\": "src/" 159 | }, 160 | "files": [ 161 | "src/functions_include.php" 162 | ] 163 | }, 164 | "notification-url": "https://packagist.org/downloads/", 165 | "license": [ 166 | "MIT" 167 | ], 168 | "authors": [ 169 | { 170 | "name": "Michael Dowling", 171 | "email": "mtdowling@gmail.com", 172 | "homepage": "https://github.com/mtdowling" 173 | }, 174 | { 175 | "name": "Tobias Schultze", 176 | "homepage": "https://github.com/Tobion" 177 | } 178 | ], 179 | "description": "PSR-7 message implementation that also provides common utility methods", 180 | "keywords": [ 181 | "http", 182 | "message", 183 | "psr-7", 184 | "request", 185 | "response", 186 | "stream", 187 | "uri", 188 | "url" 189 | ], 190 | "time": "2018-12-04T20:46:45+00:00" 191 | }, 192 | { 193 | "name": "phmlabs/init", 194 | "version": "dev-master", 195 | "source": { 196 | "type": "git", 197 | "url": "https://github.com/phmLabs/Init.git", 198 | "reference": "e0919ad9f43c6a2d6416f4d681dd31ac545de167" 199 | }, 200 | "dist": { 201 | "type": "zip", 202 | "url": "https://api.github.com/repos/phmLabs/Init/zipball/e0919ad9f43c6a2d6416f4d681dd31ac545de167", 203 | "reference": "e0919ad9f43c6a2d6416f4d681dd31ac545de167", 204 | "shasum": "" 205 | }, 206 | "require": { 207 | "phmlabs/namedparameters": "~1.0.0" 208 | }, 209 | "type": "library", 210 | "autoload": { 211 | "psr-4": { 212 | "PhmLabs\\Components\\Init\\": "" 213 | } 214 | }, 215 | "notification-url": "https://packagist.org/downloads/", 216 | "license": [ 217 | "MIT" 218 | ], 219 | "description": "phmLabs component for initializing class using a yaml file / array", 220 | "homepage": "https://www.thewebhatesme.com", 221 | "time": "2016-01-14T15:23:20+00:00" 222 | }, 223 | { 224 | "name": "phmlabs/namedparameters", 225 | "version": "1.0.2", 226 | "source": { 227 | "type": "git", 228 | "url": "https://github.com/phmLabs/NamedParameters.git", 229 | "reference": "d3717ccc6fb32d7a297ad648aaf8efedeb056fa2" 230 | }, 231 | "dist": { 232 | "type": "zip", 233 | "url": "https://api.github.com/repos/phmLabs/NamedParameters/zipball/d3717ccc6fb32d7a297ad648aaf8efedeb056fa2", 234 | "reference": "d3717ccc6fb32d7a297ad648aaf8efedeb056fa2", 235 | "shasum": "" 236 | }, 237 | "type": "library", 238 | "autoload": { 239 | "psr-4": { 240 | "PhmLabs\\Components\\NamedParameters\\": "" 241 | } 242 | }, 243 | "notification-url": "https://packagist.org/downloads/", 244 | "license": [ 245 | "MIT" 246 | ], 247 | "description": "phmLabs library for named parameters", 248 | "homepage": "https://www.thewebhatesme.com", 249 | "time": "2016-01-14T15:03:25+00:00" 250 | }, 251 | { 252 | "name": "psr/http-message", 253 | "version": "1.0.1", 254 | "source": { 255 | "type": "git", 256 | "url": "https://github.com/php-fig/http-message.git", 257 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 258 | }, 259 | "dist": { 260 | "type": "zip", 261 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 262 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 263 | "shasum": "" 264 | }, 265 | "require": { 266 | "php": ">=5.3.0" 267 | }, 268 | "type": "library", 269 | "extra": { 270 | "branch-alias": { 271 | "dev-master": "1.0.x-dev" 272 | } 273 | }, 274 | "autoload": { 275 | "psr-4": { 276 | "Psr\\Http\\Message\\": "src/" 277 | } 278 | }, 279 | "notification-url": "https://packagist.org/downloads/", 280 | "license": [ 281 | "MIT" 282 | ], 283 | "authors": [ 284 | { 285 | "name": "PHP-FIG", 286 | "homepage": "http://www.php-fig.org/" 287 | } 288 | ], 289 | "description": "Common interface for HTTP messages", 290 | "homepage": "https://github.com/php-fig/http-message", 291 | "keywords": [ 292 | "http", 293 | "http-message", 294 | "psr", 295 | "psr-7", 296 | "request", 297 | "response" 298 | ], 299 | "time": "2016-08-06T14:39:51+00:00" 300 | }, 301 | { 302 | "name": "psr/log", 303 | "version": "1.1.0", 304 | "source": { 305 | "type": "git", 306 | "url": "https://github.com/php-fig/log.git", 307 | "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" 308 | }, 309 | "dist": { 310 | "type": "zip", 311 | "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", 312 | "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", 313 | "shasum": "" 314 | }, 315 | "require": { 316 | "php": ">=5.3.0" 317 | }, 318 | "type": "library", 319 | "extra": { 320 | "branch-alias": { 321 | "dev-master": "1.0.x-dev" 322 | } 323 | }, 324 | "autoload": { 325 | "psr-4": { 326 | "Psr\\Log\\": "Psr/Log/" 327 | } 328 | }, 329 | "notification-url": "https://packagist.org/downloads/", 330 | "license": [ 331 | "MIT" 332 | ], 333 | "authors": [ 334 | { 335 | "name": "PHP-FIG", 336 | "homepage": "http://www.php-fig.org/" 337 | } 338 | ], 339 | "description": "Common interface for logging libraries", 340 | "homepage": "https://github.com/php-fig/log", 341 | "keywords": [ 342 | "log", 343 | "psr", 344 | "psr-3" 345 | ], 346 | "time": "2018-11-20T15:27:04+00:00" 347 | }, 348 | { 349 | "name": "ralouphie/getallheaders", 350 | "version": "2.0.5", 351 | "source": { 352 | "type": "git", 353 | "url": "https://github.com/ralouphie/getallheaders.git", 354 | "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" 355 | }, 356 | "dist": { 357 | "type": "zip", 358 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", 359 | "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", 360 | "shasum": "" 361 | }, 362 | "require": { 363 | "php": ">=5.3" 364 | }, 365 | "require-dev": { 366 | "phpunit/phpunit": "~3.7.0", 367 | "satooshi/php-coveralls": ">=1.0" 368 | }, 369 | "type": "library", 370 | "autoload": { 371 | "files": [ 372 | "src/getallheaders.php" 373 | ] 374 | }, 375 | "notification-url": "https://packagist.org/downloads/", 376 | "license": [ 377 | "MIT" 378 | ], 379 | "authors": [ 380 | { 381 | "name": "Ralph Khattar", 382 | "email": "ralph.khattar@gmail.com" 383 | } 384 | ], 385 | "description": "A polyfill for getallheaders.", 386 | "time": "2016-02-11T07:05:27+00:00" 387 | }, 388 | { 389 | "name": "symfony/console", 390 | "version": "v3.4.22", 391 | "source": { 392 | "type": "git", 393 | "url": "https://github.com/symfony/console.git", 394 | "reference": "069bf3f0e8f871a2169a06e43d9f3f03f355e9be" 395 | }, 396 | "dist": { 397 | "type": "zip", 398 | "url": "https://api.github.com/repos/symfony/console/zipball/069bf3f0e8f871a2169a06e43d9f3f03f355e9be", 399 | "reference": "069bf3f0e8f871a2169a06e43d9f3f03f355e9be", 400 | "shasum": "" 401 | }, 402 | "require": { 403 | "php": "^5.5.9|>=7.0.8", 404 | "symfony/debug": "~2.8|~3.0|~4.0", 405 | "symfony/polyfill-mbstring": "~1.0" 406 | }, 407 | "conflict": { 408 | "symfony/dependency-injection": "<3.4", 409 | "symfony/process": "<3.3" 410 | }, 411 | "provide": { 412 | "psr/log-implementation": "1.0" 413 | }, 414 | "require-dev": { 415 | "psr/log": "~1.0", 416 | "symfony/config": "~3.3|~4.0", 417 | "symfony/dependency-injection": "~3.4|~4.0", 418 | "symfony/event-dispatcher": "~2.8|~3.0|~4.0", 419 | "symfony/lock": "~3.4|~4.0", 420 | "symfony/process": "~3.3|~4.0" 421 | }, 422 | "suggest": { 423 | "psr/log": "For using the console logger", 424 | "symfony/event-dispatcher": "", 425 | "symfony/lock": "", 426 | "symfony/process": "" 427 | }, 428 | "type": "library", 429 | "extra": { 430 | "branch-alias": { 431 | "dev-master": "3.4-dev" 432 | } 433 | }, 434 | "autoload": { 435 | "psr-4": { 436 | "Symfony\\Component\\Console\\": "" 437 | }, 438 | "exclude-from-classmap": [ 439 | "/Tests/" 440 | ] 441 | }, 442 | "notification-url": "https://packagist.org/downloads/", 443 | "license": [ 444 | "MIT" 445 | ], 446 | "authors": [ 447 | { 448 | "name": "Fabien Potencier", 449 | "email": "fabien@symfony.com" 450 | }, 451 | { 452 | "name": "Symfony Community", 453 | "homepage": "https://symfony.com/contributors" 454 | } 455 | ], 456 | "description": "Symfony Console Component", 457 | "homepage": "https://symfony.com", 458 | "time": "2019-01-25T10:42:12+00:00" 459 | }, 460 | { 461 | "name": "symfony/debug", 462 | "version": "v4.2.3", 463 | "source": { 464 | "type": "git", 465 | "url": "https://github.com/symfony/debug.git", 466 | "reference": "cf9b2e33f757deb884ce474e06d2647c1c769b65" 467 | }, 468 | "dist": { 469 | "type": "zip", 470 | "url": "https://api.github.com/repos/symfony/debug/zipball/cf9b2e33f757deb884ce474e06d2647c1c769b65", 471 | "reference": "cf9b2e33f757deb884ce474e06d2647c1c769b65", 472 | "shasum": "" 473 | }, 474 | "require": { 475 | "php": "^7.1.3", 476 | "psr/log": "~1.0" 477 | }, 478 | "conflict": { 479 | "symfony/http-kernel": "<3.4" 480 | }, 481 | "require-dev": { 482 | "symfony/http-kernel": "~3.4|~4.0" 483 | }, 484 | "type": "library", 485 | "extra": { 486 | "branch-alias": { 487 | "dev-master": "4.2-dev" 488 | } 489 | }, 490 | "autoload": { 491 | "psr-4": { 492 | "Symfony\\Component\\Debug\\": "" 493 | }, 494 | "exclude-from-classmap": [ 495 | "/Tests/" 496 | ] 497 | }, 498 | "notification-url": "https://packagist.org/downloads/", 499 | "license": [ 500 | "MIT" 501 | ], 502 | "authors": [ 503 | { 504 | "name": "Fabien Potencier", 505 | "email": "fabien@symfony.com" 506 | }, 507 | { 508 | "name": "Symfony Community", 509 | "homepage": "https://symfony.com/contributors" 510 | } 511 | ], 512 | "description": "Symfony Debug Component", 513 | "homepage": "https://symfony.com", 514 | "time": "2019-01-25T14:35:16+00:00" 515 | }, 516 | { 517 | "name": "symfony/polyfill-ctype", 518 | "version": "v1.10.0", 519 | "source": { 520 | "type": "git", 521 | "url": "https://github.com/symfony/polyfill-ctype.git", 522 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" 523 | }, 524 | "dist": { 525 | "type": "zip", 526 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", 527 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", 528 | "shasum": "" 529 | }, 530 | "require": { 531 | "php": ">=5.3.3" 532 | }, 533 | "suggest": { 534 | "ext-ctype": "For best performance" 535 | }, 536 | "type": "library", 537 | "extra": { 538 | "branch-alias": { 539 | "dev-master": "1.9-dev" 540 | } 541 | }, 542 | "autoload": { 543 | "psr-4": { 544 | "Symfony\\Polyfill\\Ctype\\": "" 545 | }, 546 | "files": [ 547 | "bootstrap.php" 548 | ] 549 | }, 550 | "notification-url": "https://packagist.org/downloads/", 551 | "license": [ 552 | "MIT" 553 | ], 554 | "authors": [ 555 | { 556 | "name": "Symfony Community", 557 | "homepage": "https://symfony.com/contributors" 558 | }, 559 | { 560 | "name": "Gert de Pagter", 561 | "email": "BackEndTea@gmail.com" 562 | } 563 | ], 564 | "description": "Symfony polyfill for ctype functions", 565 | "homepage": "https://symfony.com", 566 | "keywords": [ 567 | "compatibility", 568 | "ctype", 569 | "polyfill", 570 | "portable" 571 | ], 572 | "time": "2018-08-06T14:22:27+00:00" 573 | }, 574 | { 575 | "name": "symfony/polyfill-mbstring", 576 | "version": "v1.10.0", 577 | "source": { 578 | "type": "git", 579 | "url": "https://github.com/symfony/polyfill-mbstring.git", 580 | "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" 581 | }, 582 | "dist": { 583 | "type": "zip", 584 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", 585 | "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", 586 | "shasum": "" 587 | }, 588 | "require": { 589 | "php": ">=5.3.3" 590 | }, 591 | "suggest": { 592 | "ext-mbstring": "For best performance" 593 | }, 594 | "type": "library", 595 | "extra": { 596 | "branch-alias": { 597 | "dev-master": "1.9-dev" 598 | } 599 | }, 600 | "autoload": { 601 | "psr-4": { 602 | "Symfony\\Polyfill\\Mbstring\\": "" 603 | }, 604 | "files": [ 605 | "bootstrap.php" 606 | ] 607 | }, 608 | "notification-url": "https://packagist.org/downloads/", 609 | "license": [ 610 | "MIT" 611 | ], 612 | "authors": [ 613 | { 614 | "name": "Nicolas Grekas", 615 | "email": "p@tchwork.com" 616 | }, 617 | { 618 | "name": "Symfony Community", 619 | "homepage": "https://symfony.com/contributors" 620 | } 621 | ], 622 | "description": "Symfony polyfill for the Mbstring extension", 623 | "homepage": "https://symfony.com", 624 | "keywords": [ 625 | "compatibility", 626 | "mbstring", 627 | "polyfill", 628 | "portable", 629 | "shim" 630 | ], 631 | "time": "2018-09-21T13:07:52+00:00" 632 | }, 633 | { 634 | "name": "symfony/yaml", 635 | "version": "v3.4.22", 636 | "source": { 637 | "type": "git", 638 | "url": "https://github.com/symfony/yaml.git", 639 | "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d" 640 | }, 641 | "dist": { 642 | "type": "zip", 643 | "url": "https://api.github.com/repos/symfony/yaml/zipball/ba11776e9e6c15ad5759a07bffb15899bac75c2d", 644 | "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d", 645 | "shasum": "" 646 | }, 647 | "require": { 648 | "php": "^5.5.9|>=7.0.8", 649 | "symfony/polyfill-ctype": "~1.8" 650 | }, 651 | "conflict": { 652 | "symfony/console": "<3.4" 653 | }, 654 | "require-dev": { 655 | "symfony/console": "~3.4|~4.0" 656 | }, 657 | "suggest": { 658 | "symfony/console": "For validating YAML files using the lint command" 659 | }, 660 | "type": "library", 661 | "extra": { 662 | "branch-alias": { 663 | "dev-master": "3.4-dev" 664 | } 665 | }, 666 | "autoload": { 667 | "psr-4": { 668 | "Symfony\\Component\\Yaml\\": "" 669 | }, 670 | "exclude-from-classmap": [ 671 | "/Tests/" 672 | ] 673 | }, 674 | "notification-url": "https://packagist.org/downloads/", 675 | "license": [ 676 | "MIT" 677 | ], 678 | "authors": [ 679 | { 680 | "name": "Fabien Potencier", 681 | "email": "fabien@symfony.com" 682 | }, 683 | { 684 | "name": "Symfony Community", 685 | "homepage": "https://symfony.com/contributors" 686 | } 687 | ], 688 | "description": "Symfony Yaml Component", 689 | "homepage": "https://symfony.com", 690 | "time": "2019-01-16T10:59:17+00:00" 691 | }, 692 | { 693 | "name": "uptime/uptime", 694 | "version": "0.2.0", 695 | "source": { 696 | "type": "git", 697 | "url": "https://github.com/marcioAlmada/uptime.git", 698 | "reference": "1f5e5b63850c2342e42366c184fc74445fa2d929" 699 | }, 700 | "dist": { 701 | "type": "zip", 702 | "url": "https://api.github.com/repos/marcioAlmada/uptime/zipball/1f5e5b63850c2342e42366c184fc74445fa2d929", 703 | "reference": "1f5e5b63850c2342e42366c184fc74445fa2d929", 704 | "shasum": "" 705 | }, 706 | "require": { 707 | "php": ">=5.4.0" 708 | }, 709 | "require-dev": { 710 | "mockery/mockery": "~0.9", 711 | "phpunit/phpunit": "~4.0" 712 | }, 713 | "type": "library", 714 | "autoload": { 715 | "psr-4": { 716 | "Uptime\\": "src/" 717 | }, 718 | "files": [ 719 | "__functions.php" 720 | ] 721 | }, 722 | "notification-url": "https://packagist.org/downloads/", 723 | "license": [ 724 | "MIT" 725 | ], 726 | "authors": [ 727 | { 728 | "name": "Márcio Almada", 729 | "email": "marcio3w@gmail.com", 730 | "homepage": "https://github.com/marcioAlmada" 731 | } 732 | ], 733 | "description": "Missing php uptime package inspired by python module uptime", 734 | "keywords": [ 735 | "boottime", 736 | "system", 737 | "uptime" 738 | ], 739 | "time": "2016-06-29T20:50:18+00:00" 740 | } 741 | ], 742 | "packages-dev": [], 743 | "aliases": [], 744 | "minimum-stability": "stable", 745 | "stability-flags": { 746 | "phmlabs/init": 20 747 | }, 748 | "prefer-stable": false, 749 | "prefer-lowest": false, 750 | "platform": { 751 | "php": ">=7.0.0" 752 | }, 753 | "platform-dev": [] 754 | } 755 | -------------------------------------------------------------------------------- /examples/code/deviceSpaceUsed.php: -------------------------------------------------------------------------------- 1 | init(95); 10 | 11 | $foundation->registerCheck($spaceUsedCheck, 'space_used_check', 'Space used on storage server'); 12 | 13 | $runResult = $foundation->runHealthCheck(); 14 | 15 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 16 | 'Storage server is up and running.', 17 | 'Some problems occurred on storage server.' 18 | ); 19 | 20 | $formatter->handle($runResult); 21 | -------------------------------------------------------------------------------- /examples/code/deviceUptime.php: -------------------------------------------------------------------------------- 1 | init('1 day'); 10 | 11 | $foundation->registerCheck($uptimeCheck); 12 | 13 | $runResult = $foundation->runHealthCheck(); 14 | 15 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 16 | 'Http resource status as expected', 17 | 'Http resource status is different or got errors'); 18 | 19 | $formatter->handle($runResult); 20 | -------------------------------------------------------------------------------- /examples/code/fileCreatedAfter.php: -------------------------------------------------------------------------------- 1 | init('/leankoala/storage/file/03/72', new DateTime("-1 year"), '*.json'); 10 | 11 | $foundation->registerCheck($fileCreatedAfterCheck); 12 | 13 | $runResult = $foundation->runHealthCheck(); 14 | 15 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 16 | 'Backup file was created successfully.', 17 | 'Seems like the backup script does not create new archives.' 18 | ); 19 | 20 | $formatter->handle($runResult); 21 | -------------------------------------------------------------------------------- /examples/code/fileNumberOfLines.php: -------------------------------------------------------------------------------- 1 | init('composer.lock', 15, \Leankoala\HealthFoundation\Check\Files\Content\NumberOfLinesCheck::RELATION_MIN, ['test', '234']); 9 | 10 | $foundation->registerCheck($numberOfLinesCheck, 'test', 'I am the description.'); 11 | 12 | $runResult = $foundation->runHealthCheck(); 13 | 14 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 15 | 'Backup file was created successfully.', 16 | 'Seems like the backup script does not create new archives.' 17 | ); 18 | 19 | $formatter->handle($runResult); -------------------------------------------------------------------------------- /examples/code/mysqlRunning.php: -------------------------------------------------------------------------------- 1 | init('root', '122'); 10 | 11 | $foundation->registerCheck($mysqlRunningCheck); 12 | 13 | $runResult = $foundation->runHealthCheck(); 14 | 15 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 16 | 'Mysql slave server is up and running.', 17 | 'Some problems occurred for mysql slave server.' 18 | ); 19 | 20 | $formatter->handle($runResult); 21 | -------------------------------------------------------------------------------- /examples/code/mysqlSlaveVarsSet.php: -------------------------------------------------------------------------------- 1 | init('Slave_IO_Running', 'Yes'); 10 | 11 | $foundation->registerCheck($slaveCheckRunning); 12 | 13 | // slave status check 14 | $slaveChecSqlkRunning = new \Leankoala\HealthFoundation\Check\Database\Mysql\Slave\SlaveStatusFieldCheck(); 15 | $slaveChecSqlkRunning->init('Slave_SQL_Running', 'Yes'); 16 | 17 | $foundation->registerCheck($slaveChecSqlkRunning); 18 | 19 | $runResult = $foundation->runHealthCheck(); 20 | 21 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 22 | 'Mysql slave server is up and running.', 23 | 'Some problems occurred for mysql slave server.' 24 | ); 25 | 26 | $formatter->handle($runResult); 27 | -------------------------------------------------------------------------------- /examples/code/redisListLength.php: -------------------------------------------------------------------------------- 1 | init('koalamon', 1000); 10 | 11 | $foundation->registerCheck($redisListLengthCheck); 12 | 13 | $runResult = $foundation->runHealthCheck(); 14 | 15 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 16 | 'Redis server is up and running.', 17 | 'Some problems occurred for redis server.' 18 | ); 19 | 20 | $formatter->handle($runResult); 21 | -------------------------------------------------------------------------------- /examples/code/resourceHttpStatus.php: -------------------------------------------------------------------------------- 1 | init('https://about.google/intl/com/', 200); // PASS 10 | // $httpStatusCheck->init('https://about.google/intl/com/',404); // WARN 11 | // $httpStatusCheck->init('https://www.there-is-no-domain-connected-'.time().'.com/index.html',200); // FAIL 12 | $foundation->registerCheck($httpStatusCheck); 13 | 14 | $runResult = $foundation->runHealthCheck(); 15 | 16 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 17 | 'Http resource status as expected', 18 | 'Http resource status is different or got errors' 19 | ); 20 | 21 | $formatter->handle($runResult); 22 | -------------------------------------------------------------------------------- /examples/config/fileNumberOfLines.php: -------------------------------------------------------------------------------- 1 | runHealthCheck(); 13 | 14 | $format->handle($runResult); -------------------------------------------------------------------------------- /examples/config/yaml/TimeFilter.yml: -------------------------------------------------------------------------------- 1 | format: 2 | class: Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat 3 | parameters: 4 | passedMessage: "Composer.lock has enough numbers of lines" 5 | failureMessage: "Composer.lock does not have enough numbers of lines" 6 | 7 | checks: 8 | fileNumberOfLines: 9 | check: Leankoala\HealthFoundation\Check\Files\Content\NumberOfLinesCheck 10 | filter: 11 | nonStrict: 12 | decorator: Leankoala\HealthFoundation\Filter\Basic\NonStrictFilter 13 | parameters: 14 | maxErrorsInARow: 20 15 | identifier: composer_length 16 | description: 'Number of lines in the composer.lock file' 17 | parameters: 18 | file: composer.lock 19 | limit: 10 20 | relation: min 21 | pattern: 2019 -------------------------------------------------------------------------------- /examples/config/yaml/dailyFilter.yml: -------------------------------------------------------------------------------- 1 | format: 2 | class: Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat 3 | parameters: 4 | passedMessage: "Composer.lock has enough numbers of lines" 5 | failureMessage: "Composer.lock does not have enough numbers of lines" 6 | 7 | checks: 8 | dockerContainerRunning: 9 | check: Leankoala\HealthFoundation\Check\Basic\Fixed\FailureCheck 10 | description: 'Always fail. But should be filtered' 11 | parameters: 12 | containerName: graphitedocker_graphite_1 13 | filter: 14 | nonStrict: 15 | filter: Leankoala\HealthFoundation\Filter\Time\DailyFilter 16 | parameters: 17 | hour: 6 18 | -------------------------------------------------------------------------------- /examples/config/yaml/dockerContainerRunning.yml: -------------------------------------------------------------------------------- 1 | format: 2 | class: Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat 3 | parameters: 4 | passedMessage: "Composer.lock has enough numbers of lines" 5 | failureMessage: "Composer.lock does not have enough numbers of lines" 6 | 7 | checks: 8 | dockerContainerRunning: 9 | check: Leankoala\HealthFoundation\Check\Docker\Container\ContainerIsRunningCheck 10 | description: 'Is the Graphite Docker container running' 11 | parameters: 12 | containerName: graphitedocker_graphite_1 13 | 14 | dockerContainerNotRunning: 15 | check: Leankoala\HealthFoundation\Check\Docker\Container\ContainerIsRunningCheck 16 | description: 'Is the Graphite Docker container running' 17 | parameters: 18 | containerName: graphitedocker_graphite_1w -------------------------------------------------------------------------------- /examples/config/yaml/fileNumberOfLines.yml: -------------------------------------------------------------------------------- 1 | format: 2 | class: Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat 3 | parameters: 4 | passedMessage: "Composer.lock has enough numbers of lines" 5 | failureMessage: "Composer.lock does not have enough numbers of lines" 6 | 7 | checks: 8 | fileNumberOfLines: 9 | check: Leankoala\HealthFoundation\Check\Files\Content\NumberOfLinesCheck 10 | identifier: composer_length 11 | description: 'Number of lines in the composer.lock file' 12 | parameters: 13 | file: composer.lock 14 | limit: 10 15 | relation: min 16 | pattern: 2019 -------------------------------------------------------------------------------- /examples/config/yaml/nonStrictFileNumberOfLines.yml: -------------------------------------------------------------------------------- 1 | format: 2 | class: Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat 3 | parameters: 4 | passedMessage: "Composer.lock has enough numbers of lines" 5 | failureMessage: "Composer.lock does not have enough numbers of lines" 6 | 7 | checks: 8 | fileNumberOfLines: 9 | check: Leankoala\HealthFoundation\Check\Files\Content\NumberOfLinesCheck 10 | filter: 11 | nonStrict: 12 | decorator: Leankoala\HealthFoundation\Filter\Basic\NonStrictFilter 13 | parameters: 14 | maxErrorsInARow: 20 15 | identifier: composer_length 16 | description: 'Number of lines in the composer.lock file' 17 | parameters: 18 | file: composer.lock 19 | limit: 10 20 | relation: min 21 | pattern: 2019 -------------------------------------------------------------------------------- /src/Check/Basic/Fixed/FailureCheck.php: -------------------------------------------------------------------------------- 1 | maxValue = $maxValue; 26 | $this->currentValue = $currentValue; 27 | $this->valueName = $valueName; 28 | } 29 | 30 | public function run() 31 | { 32 | if ($this->currentValue > $this->maxValue) { 33 | return new Result(Result::STATUS_FAIL, 'The given value ("' . $this->valueName . '") is ' . $this->currentValue . ' and too big, it must be less than ' . $this->maxValue . '.'); 34 | } else { 35 | return new Result(Result::STATUS_PASS, 'The value ("' . $this->valueName . '") was ' . $this->currentValue . '. Maximum was < ' . $this->maxValue . '.'); 36 | } 37 | } 38 | 39 | public function getIdentifier() 40 | { 41 | return self::IDENTIFIER . ':' . $this->valueName; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Check/Basic/StaticCheck.php: -------------------------------------------------------------------------------- 1 | 13 | * created 2021-08-05 14 | */ 15 | class StaticCheck implements Check 16 | { 17 | /** 18 | * @var string 19 | */ 20 | private $identifier; 21 | 22 | /** 23 | * @var string 24 | */ 25 | private $status; 26 | 27 | public function __construct($identifier, Result $result) 28 | { 29 | $this->identifier = $identifier; 30 | $this->result = $result; 31 | } 32 | 33 | public function run() 34 | { 35 | return $this->result; 36 | } 37 | 38 | public function getIdentifier() 39 | { 40 | return $this->identifier; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Check/BasicCheck.php: -------------------------------------------------------------------------------- 1 | customIdentfier = $customIdentfier; 17 | } 18 | 19 | protected function getCheckIdentifier() 20 | { 21 | return $this->identifier; 22 | } 23 | 24 | final public function getIdentifier() 25 | { 26 | if ($this->customIdentfier) { 27 | return $this->customIdentfier; 28 | } else { 29 | return $this->getCheckIdentifier(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Check/CacheAwareCheck.php: -------------------------------------------------------------------------------- 1 | username = $username; 31 | $this->password = $password; 32 | $this->host = $host; 33 | } 34 | 35 | public function run() 36 | { 37 | $mysqli = @(new \mysqli($this->host, $this->username, $this->password)); 38 | 39 | if ($mysqli->connect_errno) { 40 | $message = 'Database is not running; ' . $mysqli->connect_error; 41 | return new Result(Result::STATUS_FAIL, $message); 42 | } else { 43 | return new Result(Result::STATUS_PASS, 'Mysql server is up and running.'); 44 | } 45 | } 46 | 47 | public function getIdentifier() 48 | { 49 | return self::IDENTIFIER; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Check/Database/Mysql/Slave/SlaveStatusFieldCheck.php: -------------------------------------------------------------------------------- 1 | host = $host; 22 | $this->userName = $userName; 23 | $this->password = $password; 24 | $this->field = $field; 25 | $this->value = $value; 26 | } 27 | 28 | public function run() 29 | { 30 | $mysqli = new \mysqli($this->host, $this->userName, $this->password); 31 | 32 | if ($mysqli->connect_errno) { 33 | return new Result(Result::STATUS_FAIL, sprintf("Connect failed: %s\n", $mysqli->connect_error)); 34 | } 35 | 36 | $result = $mysqli->query("SHOW SLAVE STATUS"); 37 | 38 | $fields = $result->fetch_assoc(); 39 | 40 | if (is_array($fields)) { 41 | if (array_key_exists($this->field, $fields)) { 42 | if ($fields[$this->field] == $this->value) { 43 | return new Result(Result::STATUS_PASS, 'Field "' . $this->field . '" has value "' . $this->value . '" which was expected.'); 44 | } else { 45 | return new Result(Result::STATUS_FAIL, 'Field "' . $this->field . '" has value ' . $fields[$this->field] . '. Expected was "' . $this->value . '"'); 46 | } 47 | } 48 | } 49 | 50 | return new Result(Result::STATUS_FAIL, 'Field "' . $this->field . '" was not found in database slave status.'); 51 | } 52 | 53 | public function getIdentifier() 54 | { 55 | return self::IDENTFIER . ':' . $this->field; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Check/Database/Redis/ListLengthCheck.php: -------------------------------------------------------------------------------- 1 | listName = $listName; 29 | $this->maxLength = $maxLength; 30 | 31 | $this->auth = $auth; 32 | $this->host = $host; 33 | $this->port = $port; 34 | } 35 | 36 | public function run() 37 | { 38 | $redis = new \Redis(); 39 | $redis->connect($this->host); 40 | 41 | if ($this->auth) { 42 | $redis->auth($this->auth); 43 | } 44 | 45 | $length = $redis->lLen($this->listName); 46 | 47 | if ($length > $this->maxLength) { 48 | return new Result(Result::STATUS_FAIL, 'Too many elements in list ("' . $length . '"). Maximum was ' . $this->maxLength); 49 | } else { 50 | return new Result(Result::STATUS_FAIL, $length . ' elements in list. Maximum was ' . $this->maxLength); 51 | } 52 | } 53 | 54 | public function getIdentifier() 55 | { 56 | return self::IDENTFIER; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Check/Device/SpaceUsedCheck.php: -------------------------------------------------------------------------------- 1 | maxUsageInPercent = $maxUsageInPercent; 20 | $this->directory = $directory; 21 | } 22 | 23 | /** 24 | * Checks if the space left on device is sufficient 25 | * 26 | * @return Result 27 | */ 28 | public function run() 29 | { 30 | $free = disk_free_space($this->directory); 31 | $total = disk_total_space($this->directory); 32 | 33 | $usage = 100 - round(($free / $total) * 100); 34 | 35 | if ($usage > $this->maxUsageInPercent) { 36 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'No space left on device. ' . $usage . '% used (' . $this->directory . ').'); 37 | } else { 38 | $result = new MetricAwareResult(Result::STATUS_PASS, 'Enough space left on device. ' . $usage . '% used (' . $this->directory . ').'); 39 | } 40 | 41 | $result->setMetric($usage / 100, 'percent', MetricAwareResult::METRIC_TYPE_PERCENT); 42 | $result->setLimit($this->maxUsageInPercent / 100); 43 | $result->setLimitType(MetricAwareResult::LIMIT_TYPE_MAX); 44 | $result->setObservedValuePrecision(2); 45 | 46 | return $result; 47 | } 48 | 49 | public function getIdentifier() 50 | { 51 | return self::IDENTIFIER; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Check/Docker/Container/ContainerIsRunningCheck.php: -------------------------------------------------------------------------------- 1 | containerName = $containerName; 18 | } 19 | 20 | public function run() 21 | { 22 | $command = "docker inspect -f '{{.State.Running}}' " . $this->containerName . " 2>/dev/null "; 23 | 24 | exec($command, $output, $returnValue); 25 | 26 | $isRunning = ((bool)$output[0]); 27 | 28 | if ($isRunning) { 29 | return new Result(Result::STATUS_PASS, 'The docker container ' . $this->containerName . ' is running.'); 30 | } else { 31 | return new Result(Result::STATUS_FAIL, 'The docker container ' . $this->containerName . ' is not running.'); 32 | } 33 | } 34 | 35 | public function getIdentifier() 36 | { 37 | return self::IDENTIFIER . '.' . $this->containerName; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Check/Files/Content/NumberOfLinesCheck.php: -------------------------------------------------------------------------------- 1 | files as $file) { 30 | if (!file_exists($file)) { 31 | return new MetricAwareResult(Result::STATUS_FAIL, 'Unable to get document length because file "' . $file . '" does not exist.'); 32 | } 33 | } 34 | 35 | $numberLines = $this->getNumberOfLines(); 36 | 37 | return $this->processData($numberLines); 38 | } 39 | 40 | /** 41 | * @return int 42 | */ 43 | private function getNumberOfLines() 44 | { 45 | $numberOfLines = 0; 46 | 47 | foreach ($this->files as $file) { 48 | $grep = ''; 49 | if ($this->pattern) { 50 | foreach ($this->pattern as $pattern) { 51 | $grep .= ' | grep -a "' . $pattern . '"'; 52 | } 53 | } 54 | 55 | $command = 'cat ' . $file . $grep . ' | wc -l'; 56 | $output = []; 57 | exec($command, $output, $return); 58 | $numberOfLines += (int)$output[0]; 59 | } 60 | return $numberOfLines; 61 | } 62 | 63 | /** 64 | * @param $numberLines 65 | * @return Result 66 | */ 67 | private function processData($numberLines) 68 | { 69 | if ($this->relation === self::RELATION_MAX) { 70 | if ($numberLines > $this->limit) { 71 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'The document contains too many lines (' . $numberLines . '). Expected where ' . $this->limit . ' at the most.'); 72 | } else { 73 | $result = new MetricAwareResult(Result::STATUS_PASS, 'The document contains ' . $numberLines . ' lines. Expected where ' . $this->limit . ' at the most.'); 74 | } 75 | } else { 76 | if ($numberLines < $this->limit) { 77 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'The document contains too few lines (' . $numberLines . '). Expected where ' . $this->limit . ' at least.'); 78 | } else { 79 | $result = new MetricAwareResult(Result::STATUS_PASS, 'The document contains ' . $numberLines . ' lines. Expected where ' . $this->limit . ' at least.'); 80 | } 81 | } 82 | 83 | $result->setMetric($numberLines, 'lines'); 84 | 85 | $result->addAttribute('files', $this->files); 86 | $result->addAttribute('pattern', $this->pattern); 87 | 88 | return $result; 89 | } 90 | 91 | public function init($files, $limit, $relation = self::RELATION_MAX, $pattern = []) 92 | { 93 | if (is_string($files)) { 94 | $files = [$file]; 95 | } 96 | 97 | $this->files = $files; 98 | $this->pattern = (array)$pattern; 99 | $this->relation = $relation; 100 | $this->limit = $limit; 101 | } 102 | 103 | protected function getCheckIdentifier() 104 | { 105 | return $this->identifier . '.' . md5(json_encode($this->files) . serialize($this->pattern)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Check/Files/FileCreatedAfterCheck.php: -------------------------------------------------------------------------------- 1 | directory, '/') . DIRECTORY_SEPARATOR . $this->pattern); 34 | 35 | foreach ($files as $file) { 36 | if (is_file($file)) { 37 | if (filemtime($file) >= $this->date->getTimestamp()) { 38 | return new Result(Result::STATUS_PASS, 'At least one file (name: ' . basename($file) . ', created: ' . date('Y-d-m H:i', filemtime($file)) . ') was created after ' . $this->date->format('Y-m-d H:m') . '.'); 39 | } 40 | } 41 | } 42 | 43 | return new Result(Result::STATUS_FAIL, 'No file was found newer than ' . $this->date->format('Y-m-d H:m:s') . '.'); 44 | } 45 | 46 | /** 47 | * @param string $directory the directory where to look for the file 48 | * @param \DateTime $date the date the file must be newer than 49 | * @param string $pattern if not all files should be analyzed there can be set a filter 50 | */ 51 | public function init($directory, \DateTime $date, $pattern = '*') 52 | { 53 | $this->directory = $directory; 54 | $this->date = $date; 55 | $this->pattern = $pattern; 56 | } 57 | 58 | public function getIdentifier() 59 | { 60 | return self::IDENTIFIER; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Check/Files/FileExistsCheck.php: -------------------------------------------------------------------------------- 1 | filename)) { 25 | return new Result(Result::STATUS_PASS, 'The file "' . $this->filename . '" exists.'); 26 | }else{ 27 | return new Result(Result::STATUS_FAIL, 'The file "' . $this->filename . '" does not exist.'); 28 | } 29 | } 30 | 31 | /** 32 | * @param $filename 33 | */ 34 | public function init($filename) 35 | { 36 | $this->filename = $filename; 37 | } 38 | 39 | public function getIdentifier() 40 | { 41 | return self::IDENTIFIER; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Check/Frameworks/Symfony/DatabaseConnected.php: -------------------------------------------------------------------------------- 1 | 16 | * created 2021-03-02 17 | */ 18 | class DatabaseConnected implements Check 19 | { 20 | const IDENTIFIER = 'base:frameworks:symfony:databaseConnected'; 21 | 22 | /** 23 | * @var Registry 24 | */ 25 | private $doctrine; 26 | 27 | public function __construct(Registry $doctrine) 28 | { 29 | $this->doctrine = $doctrine; 30 | } 31 | 32 | public function run() 33 | { 34 | /** @var Connection $connection */ 35 | $connection = $this->doctrine->getConnection(); 36 | 37 | if ($connection->isConnected()) { 38 | $result = new Result(Result::STATUS_PASS, 'Doctrine is connected to the database.'); 39 | } else { 40 | $result = new Result(Result::STATUS_FAIL, 'Doctrine is not connected to the database.'); 41 | } 42 | 43 | return $result; 44 | } 45 | 46 | public function getIdentifier() 47 | { 48 | return self::IDENTIFIER; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Check/HttpClientAwareCheck.php: -------------------------------------------------------------------------------- 1 | 11 | * created 2021-02-26 12 | */ 13 | class MetricAwareResult extends Result 14 | { 15 | const METRIC_TYPE_NUMERIC = 'time_series_numeric'; 16 | const METRIC_TYPE_PERCENT = 'time_series_percent'; 17 | 18 | /** 19 | * @var integer 20 | */ 21 | private $metricValue; 22 | 23 | private $metricUnit; 24 | 25 | private $metricType = self::METRIC_TYPE_NUMERIC; 26 | 27 | /** 28 | * @var int 29 | */ 30 | private $limit; 31 | 32 | /** 33 | * @var float|int 34 | */ 35 | private $observedValue; 36 | 37 | /** 38 | * @var int 39 | */ 40 | private $observedValuePrecision; 41 | 42 | /** 43 | * @var string 44 | */ 45 | private $observedValueUnit; 46 | 47 | /** 48 | * @var string 49 | */ 50 | private $limitType; 51 | 52 | /** 53 | * @var string 54 | */ 55 | private $type; 56 | 57 | public function setMetric($value, $unit, $metricType = self::METRIC_TYPE_NUMERIC) 58 | { 59 | $this->metricValue = $value; 60 | $this->metricUnit = $unit; 61 | $this->metricType = $metricType; 62 | } 63 | 64 | /** 65 | * @return integer 66 | */ 67 | public function getMetricValue() 68 | { 69 | return $this->metricValue; 70 | } 71 | 72 | /** 73 | * @return string 74 | */ 75 | public function getMetricUnit() 76 | { 77 | return $this->metricUnit; 78 | } 79 | 80 | public function getMetricType() 81 | { 82 | return $this->metricType; 83 | } 84 | 85 | /** 86 | * Get the limit of the metric that was checked. 87 | * 88 | * This field is optional. 89 | * 90 | * @return int|null 91 | */ 92 | public function getLimit() 93 | { 94 | return $this->limit; 95 | } 96 | 97 | /** 98 | * Set the limit of the metric that was checked. 99 | * 100 | * @param int $limit 101 | */ 102 | public function setLimit($limit) 103 | { 104 | $this->limit = $limit; 105 | } 106 | 107 | /** 108 | * @return string 109 | */ 110 | public function getLimitType() 111 | { 112 | return $this->limitType; 113 | } 114 | 115 | /** 116 | * @param string $limitType 117 | */ 118 | public function setLimitType(string $limitType) 119 | { 120 | $this->limitType = $limitType; 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getType() 127 | { 128 | return $this->type; 129 | } 130 | 131 | /** 132 | * @param string $type 133 | */ 134 | public function setType(string $type) 135 | { 136 | $this->type = $type; 137 | } 138 | 139 | /** 140 | * @return int 141 | */ 142 | public function getObservedValuePrecision() 143 | { 144 | return $this->observedValuePrecision; 145 | } 146 | 147 | /** 148 | * @param int $observedValuePrecision 149 | */ 150 | public function setObservedValuePrecision($observedValuePrecision) 151 | { 152 | $this->observedValuePrecision = $observedValuePrecision; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/Check/Resource/Http/StatusCodeCheck.php: -------------------------------------------------------------------------------- 1 | destination = $destination; 26 | $this->expectedHttpStatus = (int)$expectedHttpStatus; 27 | } 28 | 29 | public function setHttpClient(Client $client) 30 | { 31 | $this->httpClient = $client; 32 | } 33 | 34 | /** 35 | * Checks the response status 36 | * 37 | * @return Result 38 | */ 39 | public function run() 40 | { 41 | try { 42 | $response = $this->httpClient->request('HEAD', $this->destination); 43 | $statusCode = $response->getStatusCode(); 44 | } catch (ClientException $e) { 45 | $statusCode = $e->getResponse()->getStatusCode(); 46 | } catch (\Exception $e) { 47 | $statusCode = 0; 48 | $errorMessage = $e->getMessage(); 49 | } 50 | 51 | if ($statusCode === $this->expectedHttpStatus) { 52 | return new Result(Result::STATUS_PASS, 'HTTP status code for ' . $this->destination . ' was ' . $statusCode . ' as expected.'); 53 | } else { 54 | if ($statusCode === 0) { 55 | return new Result(Result::STATUS_FAIL, 'The HTTP request could not be sent. Message: ' . $errorMessage); 56 | } else { 57 | return new Result(Result::STATUS_FAIL, 'HTTP status for ' . $this->destination . ' is different [' . $this->expectedHttpStatus . ' was expected but ' . $statusCode . ' was delivered]'); 58 | } 59 | } 60 | } 61 | 62 | public function getIdentifier() 63 | { 64 | return self::IDENTIFIER; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Check/Result.php: -------------------------------------------------------------------------------- 1 | 0, 24 | self::STATUS_WARN => 50, 25 | self::STATUS_FAIL => 100 26 | ]; 27 | 28 | const LIMIT_TYPE_MIN = 'min'; 29 | const LIMIT_TYPE_MAX = 'max'; 30 | 31 | const KOALITY_IDENTIFIER_ORDERS_TOO_FEW = 'orders.too_few'; 32 | const KOALITY_IDENTIFIER_PLUGINS_UPDATABLE = 'plugins.updatable'; 33 | const KOALITY_IDENTIFIER_PRODUCTS_COUNT = 'products.active'; 34 | 35 | const KOALITY_IDENTIFIER_SYSTEM_INSECURE = 'system.insecure'; 36 | const KOALITY_IDENTIFIER_SECURITY_USERS_ADMIN_COUNT = 'security.user.admin.count'; 37 | 38 | const KOALITY_IDENTIFIER_SERVER_DICS_SPACE_USED = 'server.space.used'; 39 | 40 | /** 41 | * @var string 42 | */ 43 | private $status; 44 | 45 | /** 46 | * @var string 47 | */ 48 | private $message; 49 | 50 | /** 51 | * @var array 52 | */ 53 | private $attributes = []; 54 | 55 | /** 56 | * Result constructor. 57 | * 58 | * @param $status 59 | * @param $message 60 | */ 61 | public function __construct($status, $message = "") 62 | { 63 | $this->status = $status; 64 | $this->message = $message; 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getStatus() 71 | { 72 | return $this->status; 73 | } 74 | 75 | /** 76 | * @return string 77 | */ 78 | public function getMessage() 79 | { 80 | return $this->message; 81 | } 82 | 83 | /** 84 | * Add a new attribute to the result. 85 | * 86 | * @param string $key 87 | * @param mixed $value 88 | */ 89 | public function addAttribute($key, $value) 90 | { 91 | $this->attributes[$key] = $value; 92 | } 93 | 94 | /** 95 | * Return a list of attribute 96 | * 97 | * @return array 98 | */ 99 | public function getAttributes() 100 | { 101 | return $this->attributes; 102 | } 103 | 104 | /** 105 | * Returns the higher weighted status. 106 | * 107 | * @param $status1 108 | * @param $status2 109 | * 110 | * @return string 111 | */ 112 | public static function getHigherWeightedStatus($status1, $status2) 113 | { 114 | if (self::$statuses[$status1] > self::$statuses[$status2]) { 115 | return $status1; 116 | } else { 117 | return $status2; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Check/System/NumberProcessesCheck.php: -------------------------------------------------------------------------------- 1 | processName = $processName; 20 | $this->maxNumber = $maxNumber; 21 | $this->minNumber = $minNumber; 22 | } 23 | 24 | public function run() 25 | { 26 | $command = 'ps ax | grep -a "' . $this->processName . '" | wc -l'; 27 | 28 | exec($command, $output); 29 | 30 | $count = (int)$output[0] - 2; 31 | 32 | if ($count > $this->maxNumber) { 33 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'Too many processes found "' . $this->processName . '" (current: ' . $count . ', expected < ' . $this->maxNumber . ').'); 34 | } elseif ($count < $this->minNumber) { 35 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'Too few processes found "' . $this->processName . '" (current: ' . $count . ' , expected > ' . $this->maxNumber . ').'); 36 | } else { 37 | $result = new MetricAwareResult(Result::STATUS_PASS, 'Number of processes "' . $this->processName . '" was within limits. Current number is ' . $count . '.'); 38 | } 39 | 40 | $result->setMetric($count, 'process'); 41 | $result->setLimit($this->maxNumber); 42 | $result->setLimitType(MetricAwareResult::LIMIT_TYPE_MAX); 43 | $result->setObservedValuePrecision(0); 44 | 45 | return $result; 46 | } 47 | 48 | public function getIdentifier() 49 | { 50 | return self::IDENTIFIER . ':' . md5($this->processName); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Check/System/Process/LineCountCheck.php: -------------------------------------------------------------------------------- 1 | 15 | * created 2021-12-01 16 | */ 17 | class LineCountCheck implements Check 18 | { 19 | private $command; 20 | private $maxNumber; 21 | private $minNumber; 22 | private $filters; 23 | 24 | public function init($command, $maxNumber = null, $minNumber = null, $filters = []) 25 | { 26 | $this->command = $command; 27 | $this->maxNumber = $maxNumber; 28 | $this->minNumber = $minNumber; 29 | $this->filters = $filters; 30 | } 31 | 32 | public function run() 33 | { 34 | $filterString = ''; 35 | 36 | foreach ($this->filters as $filter) { 37 | $filterString .= '| grep -a "' . $filter . '"'; 38 | } 39 | 40 | $command = $this->command . ' ' . $filterString . ' | wc -l'; 41 | 42 | exec($command, $output); 43 | 44 | $count = (int)$output[0]; 45 | 46 | if ($this->maxNumber && $count > $this->maxNumber) { 47 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'Too many lines found "' . $this->command . '" (current: ' . $count . ', expected < ' . $this->maxNumber . ').'); 48 | } elseif ($this->minNumber && $count < $this->minNumber) { 49 | $result = new MetricAwareResult(Result::STATUS_FAIL, 'Too few processes found "' . $this->command . '" (current: ' . $count . ' , expected >= ' . $this->minNumber . ').'); 50 | } else { 51 | $result = new MetricAwareResult(Result::STATUS_PASS, 'Number of processes "' . $this->command . '" was within limits. Current number is ' . $count . '.'); 52 | } 53 | 54 | $result->setMetric($count, 'process'); 55 | $result->setLimit($this->maxNumber); 56 | $result->setLimitType(MetricAwareResult::LIMIT_TYPE_MAX); 57 | $result->setObservedValuePrecision(0); 58 | 59 | $result->addAttribute('command', $command); 60 | 61 | return $result; 62 | } 63 | 64 | public function getIdentifier() 65 | { 66 | return 'base:system:process:lineCount'; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/Check/System/UptimeCheck.php: -------------------------------------------------------------------------------- 1 | dateInterval = \DateInterval::createFromDateString($maxUptime); 20 | } 21 | 22 | /** 23 | * Checks if the server uptime is ok 24 | * 25 | * @return Result 26 | */ 27 | public function run() 28 | { 29 | try { 30 | $uptime = $this->getUptime(); 31 | if ($this->dateIntervalToSeconds($uptime) > $this->dateIntervalToSeconds($this->dateInterval)) { 32 | return new Result(Result::STATUS_FAIL, 'Servers uptime is too high (' . $this->dateIntervalToString($uptime) . ')'); 33 | } else { 34 | return new Result(Result::STATUS_PASS, 'Servers uptime is ok (' . $this->dateIntervalToString($uptime) . ')'); 35 | } 36 | } catch (\Exception $e) { 37 | return new Result(Result::STATUS_FAIL, 'Error: ' . $e->getMessage()); 38 | } 39 | } 40 | 41 | /** 42 | * @return \DateInterval 43 | * @throws \Exception 44 | */ 45 | private function getUptime() 46 | { 47 | $uptimeTimestamp = \uptime(); 48 | 49 | $systemStartDate = new \DateTime(date('Y-m-d H:i:s', (int)$uptimeTimestamp)); 50 | $now = new \DateTime(); 51 | 52 | $uptime = $systemStartDate->diff($now); 53 | 54 | if ($uptime === false) { 55 | throw new \RuntimeException('Uptime cannot be calculated.'); 56 | } 57 | 58 | return $uptime; 59 | } 60 | 61 | /** 62 | * @param \DateInterval $dateInterval 63 | * @return int 64 | * @throws \Exception 65 | */ 66 | private function dateIntervalToSeconds(\DateInterval $dateInterval) 67 | { 68 | $reference = new \DateTimeImmutable; 69 | $endTime = $reference->add($dateInterval); 70 | 71 | return $endTime->getTimestamp() - $reference->getTimestamp(); 72 | } 73 | 74 | private function dateIntervalToString(\DateInterval $dateInterval) 75 | { 76 | $string = ''; 77 | 78 | if ($dateInterval->y > 0) { 79 | $string .= $dateInterval->y . ' years '; 80 | } 81 | 82 | if ($dateInterval->d > 0) { 83 | $string .= $dateInterval->d . ' days '; 84 | } 85 | 86 | if ($dateInterval->h > 0) { 87 | $string .= $dateInterval->h . ' hours '; 88 | } 89 | 90 | if ($dateInterval->i > 0) { 91 | $string .= $dateInterval->i . ' minutes '; 92 | } 93 | 94 | return trim($string); 95 | } 96 | 97 | public function getIdentifier() 98 | { 99 | return self::IDENTIFIER; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Cli/Application.php: -------------------------------------------------------------------------------- 1 | registerCommands(); 31 | return parent::doRun($input, $output); 32 | } 33 | 34 | /** 35 | * Initializes all the commands. 36 | */ 37 | private function registerCommands() 38 | { 39 | $this->add(new RunCommand()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Cli/Command/RunCommand.php: -------------------------------------------------------------------------------- 1 | setDefinition([ 20 | new InputArgument('config', InputArgument::REQUIRED, 'the configuration file') 21 | ]) 22 | ->setDescription('Run health checks.') 23 | ->setHelp('The run command for the health checker.') 24 | ->setName('run'); 25 | } 26 | 27 | protected function execute(InputInterface $input, OutputInterface $output) 28 | { 29 | $configFile = (string)$input->getArgument('config'); 30 | 31 | if (!file_exists($configFile)) { 32 | throw new \RuntimeException('Unable to find config file.'); 33 | } 34 | 35 | $configArray = Yaml::parse(file_get_contents($configFile)); 36 | 37 | $healthFoundation = HealthFoundationFactory::from($configArray); 38 | $format = FormatFactory::from($configArray); 39 | 40 | $runResult = $healthFoundation->runHealthCheck(); 41 | 42 | $format->handle($runResult); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Config/FormatFactory.php: -------------------------------------------------------------------------------- 1 | $checkArray) { 37 | 38 | /** @var Check $check */ 39 | $check = Init::initialize($checkArray, 'check'); 40 | 41 | if (array_key_exists('description', $checkArray)) { 42 | $description = $checkArray['description']; 43 | } else { 44 | $description = ""; 45 | } 46 | 47 | if (array_key_exists('identifier', $checkArray)) { 48 | $identifier = $checkArray['identifier']; 49 | } else { 50 | $identifier = $key; 51 | } 52 | 53 | if (array_key_exists('filter', $checkArray)) { 54 | foreach ($checkArray['filter'] as $decorator) { 55 | $decorator = Init::initialize($decorator, 'filter'); 56 | 57 | if ($decorator instanceof Filter) { 58 | $decorator->setCheck($check); 59 | $check = $decorator; 60 | } else { 61 | throw new \RuntimeException('The given decorator must implement the decorator interface.'); 62 | } 63 | } 64 | } 65 | 66 | $healthFoundation->registerCheck($check, $identifier, $description); 67 | } 68 | 69 | return $healthFoundation; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Extenstion/Cache/Cache.php: -------------------------------------------------------------------------------- 1 | cacheDir)) { 14 | mkdir($this->cacheDir, 0777, true); 15 | } 16 | $this->checkIdentifier = $checkIdentifier; 17 | } 18 | 19 | public function set($key, $value) 20 | { 21 | file_put_contents($this->getFilename($key), $value); 22 | } 23 | 24 | public function get($key) 25 | { 26 | $file = $this->getFilename($key); 27 | 28 | if (file_exists($file)) { 29 | return file_get_contents($file); 30 | } else { 31 | return null; 32 | } 33 | } 34 | 35 | private function getGlobalKey($key) 36 | { 37 | return $this->checkIdentifier . '_' . $key; 38 | } 39 | 40 | private function getFilename($key) 41 | { 42 | $globalKey = $this->getGlobalKey($key); 43 | return $this->cacheDir . md5($globalKey) . '.cache'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Filter/Basic/NonStrictFilter.php: -------------------------------------------------------------------------------- 1 | maxErrorsInARow = $maxErrorsInARow; 23 | } 24 | 25 | public function run() 26 | { 27 | $result = $this->getCheck()->run(); 28 | 29 | // @todo handle metric aware checks 30 | 31 | if ($result->getStatus() == Result::STATUS_FAIL) { 32 | $currentErrorsInARow = (int)$this->cache->get('errorsInARow'); 33 | $currentErrorsInARow++; 34 | 35 | $this->cache->set('errorsInARow', $currentErrorsInARow); 36 | 37 | if ($currentErrorsInARow >= $this->maxErrorsInARow) { 38 | return $result; 39 | } else { 40 | if ($result instanceof MetricAwareResult) { 41 | $metricResult = new MetricAwareResult(Result::STATUS_PASS, 'Passed because non-strict mode is activated. Failed with message: ' . $result->getMessage()); 42 | $metricResult->setMetric($result->getMetricValue(), $result->getMetricUnit()); 43 | return $metricResult; 44 | } else { 45 | return new Result(Result::STATUS_PASS, 'Passed because non-strict mode is activated. Failed with message: ' . $result->getMessage()); 46 | } 47 | } 48 | } else { 49 | $this->cache->set('errorsInARow', 0); 50 | return $result; 51 | } 52 | } 53 | 54 | public function setCache(Cache $cache) 55 | { 56 | $this->cache = $cache; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Filter/BasicFilter.php: -------------------------------------------------------------------------------- 1 | check->getIdentifier(); 17 | } 18 | 19 | public function setCheck(Check $check) 20 | { 21 | $this->check = $check; 22 | } 23 | 24 | protected function getCheck() 25 | { 26 | return $this->check; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Filter/Filter.php: -------------------------------------------------------------------------------- 1 | hour = $hour; 17 | $this->reason = $reason; 18 | } 19 | 20 | public function run() 21 | { 22 | $result = $this->getCheck()->run(); 23 | 24 | $currentHour = (int)date('H'); 25 | 26 | if ($result->getStatus() == Result::STATUS_FAIL) { 27 | if ($currentHour == $this->hour) { 28 | if ($this->reason) { 29 | $message = $this->reason . ' (fail reason was: ' . $result->getMessage() . ')'; 30 | } else { 31 | $message = 'Passed due daily filter (fail reason was: ' . $result->getMessage() . ')'; 32 | } 33 | 34 | if ($result instanceof MetricAwareResult) { 35 | $newResult = new MetricAwareResult(Result::STATUS_PASS, $message); 36 | $newResult->setMetric($result->getMetricValue(), $result->getMetricUnit()); 37 | } else { 38 | $newResult = new Result(Result::STATUS_PASS, $message); 39 | } 40 | 41 | return $newResult; 42 | } else { 43 | return $result; 44 | } 45 | } else { 46 | return $result; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/HealthFoundation.php: -------------------------------------------------------------------------------- 1 | httpClient = $httpClient; 29 | } 30 | 31 | /** 32 | * Return the current httpClient if set otherwise create one. 33 | * 34 | * @return Client 35 | */ 36 | private function getHttpClient() 37 | { 38 | if (!$this->httpClient) { 39 | $this->httpClient = new Client(); 40 | } 41 | 42 | return $this->httpClient; 43 | } 44 | 45 | /** 46 | * Return the current cache abstraction 47 | * 48 | * @return Cache 49 | */ 50 | private function getCache(CacheAwareCheck $check) 51 | { 52 | return new Cache($check->getIdentifier()); 53 | } 54 | 55 | public function registerCheck(Check $check, $identifier = false, $description = "", $group = "") 56 | { 57 | if ($check instanceof HttpClientAwareCheck) { 58 | $check->setHttpClient($this->getHttpClient()); 59 | } 60 | 61 | if ($check instanceof CacheAwareCheck) { 62 | $check->setCache($this->getCache($check)); 63 | } 64 | 65 | if ($identifier) { 66 | $this->registeredChecks[$identifier] = ['check' => $check, 'description' => $description, 'group' => $group]; 67 | } else { 68 | $this->registeredChecks[] = ['check' => $check, 'description' => $description, 'group' => $group]; 69 | } 70 | } 71 | 72 | public function runHealthCheck() 73 | { 74 | $runResult = new RunResult(); 75 | 76 | foreach ($this->registeredChecks as $identifier => $checkInfos) { 77 | /** @var Check $check */ 78 | $check = $checkInfos['check']; 79 | $group = $checkInfos['group']; 80 | 81 | $checkResult = $check->run(); 82 | $runResult->addResult($check, $checkResult, $identifier, $checkInfos['description'], $group); 83 | } 84 | 85 | return $runResult; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/Result/Format/Format.php: -------------------------------------------------------------------------------- 1 | passMessage = $passMessage; 24 | } 25 | 26 | if ($failMessage) { 27 | $this->failMessage = $failMessage; 28 | } 29 | } 30 | 31 | public function handle(RunResult $runResult, $echoValue = true) 32 | { 33 | header('Content-Type: application/json'); 34 | 35 | $output = $this->getOutput($runResult, $this->passMessage, $this->failMessage); 36 | 37 | $details = []; 38 | 39 | foreach ($runResult->getResults() as $resultArray) { 40 | /** @var Result $result */ 41 | $result = $resultArray['result']; 42 | 43 | /** @var Check $check */ 44 | $check = $resultArray['check']; 45 | 46 | 47 | if (is_string($resultArray['identifier'])) { 48 | $identifier = $resultArray['identifier']; 49 | } else { 50 | $identifier = $check->getIdentifier(); 51 | } 52 | 53 | $details[$identifier] = [ 54 | 'status' => $result->getStatus(), 55 | 'output' => $result->getMessage() 56 | ]; 57 | 58 | $description = $resultArray['description']; 59 | if ($description) { 60 | $details[$identifier]['description'] = $description; 61 | } 62 | 63 | if ($result instanceof MetricAwareResult) { 64 | $details[$identifier]["observedValue"] = $result->getMetricValue(); 65 | $details[$identifier]["observedUnit"] = $result->getMetricUnit(); 66 | } 67 | } 68 | 69 | $resultArray = [ 70 | 'status' => $runResult->getStatus(), 71 | 'output' => $output, 72 | 'details' => $details 73 | ]; 74 | 75 | echo json_encode($resultArray, JSON_PRETTY_PRINT); 76 | } 77 | 78 | private function getOutput(RunResult $runResult, $passMessage = null, $failMessage = null) 79 | { 80 | if ($runResult->getStatus() == Result::STATUS_PASS) { 81 | if (is_null($passMessage)) { 82 | $output = self::DEFAULT_OUTPUT_PASS; 83 | } else { 84 | $output = $passMessage; 85 | } 86 | } else { 87 | if (is_null($failMessage)) { 88 | $output = self::DEFAULT_OUTPUT_FAIL; 89 | } else { 90 | $output = $failMessage; 91 | } 92 | } 93 | 94 | return $output; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Result/Format/Koality/KoalityFormat.php: -------------------------------------------------------------------------------- 1 | passMessage = $passMessage; 26 | } 27 | 28 | if ($failMessage) { 29 | $this->failMessage = $failMessage; 30 | } 31 | 32 | $this->dataProctection = $dataProctection; 33 | } 34 | 35 | private function comvertToDataProtectedResult(Result $result) 36 | { 37 | if ($result instanceof MetricAwareResult) { 38 | $result->setLimitType(Result::LIMIT_TYPE_MIN); 39 | $result->setLimit(1); 40 | $result->setObservedValuePrecision(2); 41 | $result->setMetric(1, 'percent', MetricAwareResult::METRIC_TYPE_PERCENT); 42 | } 43 | 44 | return $result; 45 | } 46 | 47 | public function handle(RunResult $runResult, $echoValue = true) 48 | { 49 | header('Content-Type: application/json'); 50 | 51 | $output = $this->getOutput($runResult, $this->passMessage, $this->failMessage); 52 | 53 | $details = []; 54 | 55 | foreach ($runResult->getResults() as $resultArray) { 56 | 57 | /** @var Result $result */ 58 | $result = $resultArray['result']; 59 | 60 | if ($this->dataProctection) { 61 | $result = $this->comvertToDataProtectedResult($result); 62 | } 63 | 64 | /** @var Check $check */ 65 | $check = $resultArray['check']; 66 | 67 | if (is_string($resultArray['identifier'])) { 68 | $identifier = $resultArray['identifier']; 69 | } else { 70 | $identifier = $check->getIdentifier(); 71 | } 72 | 73 | $details[$identifier] = [ 74 | 'status' => $result->getStatus(), 75 | 'output' => $result->getMessage() 76 | ]; 77 | 78 | /** @var string $group */ 79 | $group = $resultArray['group']; 80 | 81 | if($group) { 82 | $details[$identifier]['group'] = $group; 83 | } 84 | 85 | $description = $resultArray['description']; 86 | if ($description) { 87 | $details[$identifier]['description'] = $description; 88 | } 89 | 90 | if ($result instanceof MetricAwareResult) { 91 | $details[$identifier]["observedValue"] = $result->getMetricValue(); 92 | $details[$identifier]["observedUnit"] = $result->getMetricUnit(); 93 | 94 | if ($result->getMetricType()) { 95 | $details[$identifier]['metricType'] = $result->getMetricType(); 96 | } 97 | 98 | if (!is_null($result->getObservedValuePrecision())) { 99 | $details[$identifier]['observedValuePrecision'] = $result->getObservedValuePrecision(); 100 | } 101 | 102 | if (is_numeric($result->getLimit())) { 103 | $details[$identifier]['limit'] = $result->getLimit(); 104 | } 105 | 106 | if (!is_null($result->getLimitType())) { 107 | $details[$identifier]['limitType'] = $result->getLimitType(); 108 | } 109 | } else { 110 | if ($result->getStatus() == Result::STATUS_PASS) { 111 | $details[$identifier]["observedValue"] = 1; 112 | } else { 113 | $details[$identifier]["observedValue"] = 0; 114 | } 115 | $details[$identifier]["metricType"] = MetricAwareResult::METRIC_TYPE_PERCENT; 116 | $details[$identifier]["observedUnit"] = 'percent'; 117 | } 118 | 119 | $attributes = $result->getAttributes(); 120 | if (count($attributes) > 0) { 121 | $details[$identifier]['attributes'] = $attributes; 122 | } 123 | } 124 | 125 | $resultArray = [ 126 | 'status' => $runResult->getStatus(), 127 | 'output' => $output, 128 | 'checks' => $details 129 | ]; 130 | 131 | $resultJson = json_encode($resultArray, JSON_PRETTY_PRINT); 132 | 133 | if ($echoValue) { 134 | echo $resultJson; 135 | } 136 | 137 | return $resultArray; 138 | } 139 | 140 | private function getOutput(RunResult $runResult, $passMessage = null, $failMessage = null) 141 | { 142 | if ($runResult->getStatus() == Result::STATUS_PASS) { 143 | if (is_null($passMessage)) { 144 | $output = self::DEFAULT_OUTPUT_PASS; 145 | } else { 146 | $output = $passMessage; 147 | } 148 | } else { 149 | if (is_null($failMessage)) { 150 | $output = self::DEFAULT_OUTPUT_FAIL; 151 | } else { 152 | $output = $failMessage; 153 | } 154 | } 155 | 156 | return $output; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/RunResult.php: -------------------------------------------------------------------------------- 1 | singleResults[] = [ 27 | 'check' => $check, 28 | 'result' => $result, 29 | 'identifier' => $identifier, 30 | 'description' => $description, 31 | 'group' => $group 32 | ]; 33 | 34 | $this->globalStatus = Result::getHigherWeightedStatus($this->globalStatus, $result->getStatus()); 35 | } 36 | 37 | /** 38 | * @return array 39 | */ 40 | public function getResults(): array 41 | { 42 | return $this->singleResults; 43 | } 44 | 45 | public function getStatus() 46 | { 47 | return $this->globalStatus; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/Cases/MemoryTest.php: -------------------------------------------------------------------------------- 1 | registerCheck($toggleCheck, null, 'Test memory by toggling the check pass/fail.'); 10 | 11 | $runResult = $foundation->runHealthCheck(); 12 | 13 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 14 | 'pass', 15 | 'fail' 16 | ); 17 | 18 | $formatter->handle($runResult); 19 | -------------------------------------------------------------------------------- /src/test/Cases/NonStrictTest.php: -------------------------------------------------------------------------------- 1 | setCheck($staticCheck); 11 | 12 | $foundation->registerCheck($nonStrictStaticCheck, null, 'Test non-strict mode for static check.'); 13 | 14 | $runResult = $foundation->runHealthCheck(); 15 | 16 | $formatter = new \Leankoala\HealthFoundation\Result\Format\Ietf\IetfFormat( 17 | 'pass', 18 | 'fail' 19 | ); 20 | 21 | $formatter->handle($runResult); 22 | -------------------------------------------------------------------------------- /src/test/Check/StaticStatusCheck.php: -------------------------------------------------------------------------------- 1 | status = $status; 16 | $this->message = $message; 17 | } 18 | 19 | public function run() 20 | { 21 | return new Result($this->status, $this->message); 22 | } 23 | 24 | public function getIdentifier() 25 | { 26 | return 'test.check.staticStatus'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/Check/ToggleStatusCheck.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 20 | } 21 | 22 | public function run() 23 | { 24 | $lastStatus = $this->cache->get('status'); 25 | 26 | if ($lastStatus === Result::STATUS_FAIL) { 27 | $this->cache->set('status', Result::STATUS_PASS); 28 | return new Result(Result::STATUS_PASS, 'Toggle to pass'); 29 | } else { 30 | $this->cache->set('status', Result::STATUS_FAIL); 31 | return new Result(Result::STATUS_FAIL, 'Toggle to fail'); 32 | } 33 | } 34 | 35 | public function getIdentifier() 36 | { 37 | return 'test.check.toggleNumber'; 38 | } 39 | } 40 | --------------------------------------------------------------------------------