A solution to improve PHP application logging when running under php-fpm in a containerized environment. It also provides Prometheus monitoring and a graceful shutdown mechanism tailored for Kubernetes deployments.
This project wraps around PHP-FPM, intercepting its logs and exposing Prometheus metrics. It is especially useful for containerized PHP applications running in Kubernetes, providing:
- Enhanced Logging: Routes php-fpm logs through a customizable logging proxy.
- Slowlog Proxy: Parse and proxy PHP-FPM slowlog output.
- Logging Proxy: Configure Unix pipe or socket for app log forwarding.
- Prometheus Monitoring: Exposes application metrics to help monitor application performance.
- Graceful Shutdown: Implements a delay mechanism to ensure smooth shutdowns in Kubernetes environments.
- Session Cleanup: Removes expired PHP session files, as the
sessioncleancron job of Debian does.
The following options can be configured via command-line flags or environment variables.
The name of the environment variable is the name of the flag in upper case, with _ instead of -:
for example, FPM_CONFIG for --fpm-config. A flag takes precedence over its environment variable.
Run docker-fpm-wrapper --help to see all options.
The wrapper passes the arguments after -- to php-fpm:
docker-fpm-wrapper -f /usr/local/sbin/php-fpm -- -d memory_limit=256M-
--log-level
- Description: Sets the internal log level: a name (
debug,info,warn,error) or a number from-1(debug) to5(fatal). - Default:
-1
- Description: Sets the internal log level: a name (
-
--log-encoder
- Description: Chooses the internal logging encoder:
auto,console, orjson.autoisconsoleon a terminal andjsonotherwise. - Default:
auto
- Description: Chooses the internal logging encoder:
-
--fpm, -f
- Description: Path to the PHP-FPM executable.
- Default: (Required)
-
--fpm-config, -c
- Description: Path to the PHP-FPM configuration file.
- Default:
/etc/php/php-fpm.conf
-
--run-dir
- Description: Directory for the generated php-fpm config and the log FIFOs. The user of the wrapper must have write access to it.
- Default:
/tmp/docker-fpm-wrapper
-
--fpm-no-errlog
- Description: Disable php-fpm error log parsing and proxy. php-fpm then uses the
error_logof its config. - Default:
false
- Description: Disable php-fpm error log parsing and proxy. php-fpm then uses the
-
--fpm-no-slowlog
- Description: Disable php-fpm slow log parsing and proxy. php-fpm then uses the
slowlogof each pool. - Default:
false
- Description: Disable php-fpm slow log parsing and proxy. php-fpm then uses the
-
--wrapper-pipe, -p
- Description: Path to the logging pipe. Set to an empty string (
"") to disable. - Default:
/tmp/fpm-wrapper-pipe
- Description: Path to the logging pipe. Set to an empty string (
-
--wrapper-socket, -s
- Description: Path to the logging socket. Set to
nullto disable. - Default:
/tmp/fpm-wrapper.sock
- Description: Path to the logging socket. Set to
-
--line-buffer-size
- Description: Specifies the maximum log line size in bytes.
- Default:
16384(16*1024 bytes)
-
--listen
- Description: The address for Prometheus to scrape metrics.
- Default:
:8080
-
--metrics-path
- Description: The path to expose Prometheus metrics.
- Default:
/metrics
-
--shutdown-delay
- Description: Delay before the process shutdown to allow graceful termination. The wrapper waits on SIGTERM and SIGQUIT, then sends SIGQUIT to php-fpm.
- Default:
500ms
-
--session-cleanup-interval
- Description: Interval of the removal of expired session files. Set to
0to disable. - Default:
0
- Description: Interval of the removal of expired session files. Set to
The wrapper does not parse the php-fpm config itself. At start, it does these steps:
- It runs
php-fpm -ttand reads the config that php-fpm shows: the pools after all includes,$poolvariables, and defaults. - It writes an overlay config to
--run-dir. The overlay includes the config from--fpm-configand moveserror_logand theslowlogof each pool to FIFOs in--run-dir. - It starts php-fpm with the overlay config and parses the FIFOs.
The wrapper does not change the files of the config, and php-fpm does not need write access to the directories of error_log and slowlog.
error_log = /proc/self/fd/2 of the official Docker images and error_log = syslog also go through the proxy.
Use --fpm-no-errlog or --fpm-no-slowlog to keep the paths of the config.
php-fpm -tt opens the slowlog of each pool with request_slowlog_timeout. The user of the wrapper must have write access to these paths.
The wrapper works with php-fpm of PHP 5.6 and later. It does not need root.
Debian packages of PHP set session.gc_probability = 0 and remove the expired session files with a cron job.
A container usually has no cron. Set --session-cleanup-interval (for example, 30m) to let the wrapper do this work:
- The wrapper reads the session directives from
php-fpm -iand from thephp_valueandphp_admin_valuedirectives of each pool. - It cleans up the
session.save_pathdirectories of the pools withsession.save_handler = files, also the subdirectories ofN;/path. - It removes the
sess_*files that are older thansession.gc_maxlifetime. When two or more pools use one directory, the longest lifetime applies. - It does not remove the session files that php-fpm has open. It updates their modification time.
Do not enable the cleanup when the application sets session.gc_maxlifetime with ini_set() at run time.
The wrapper cannot see this value and can remove live sessions.
Run the checks before you push:
make lintrunsgolangci-lint.make testruns the unit tests.make e2eruns the end-to-end tests. These tests need Docker.
The end-to-end tests start the wrapper with php-fpm in a container (testcontainers-go).
The default image is php:8.5-fpm-alpine. CI also runs the tests on PHP 5.6, 7.4, and 8.2–8.5.
The PHP scripts of the tests must work on PHP 5.6. To use a different image, set E2E_PHP_IMAGE:
E2E_PHP_IMAGE=php:8.2-fpm make e2e