Solved Please help to send php-fpm error logs to nginx error logs

Greetings,

This is what I've been struggling with for a while now.
I was wondering maybe somebody has experience with this.

So I have php 8.1.23 and nginx 1.24.0 installed from ports. Then I use php-fpm as fastcgi service to communicate with nginx.

Everything works perfectly, but errors reporting, which drives me crazy.

What I'm trying to achieve is I want to combine PHP and nginx errors in a single log file. So, instead of php-fpm using separate log files (or syslogd) I want it to send logs back to nginx so nginx can store PHP error logs within nginx error log files.

I saw some examples online, they look something like this:
1. An example of PHP code that triggers an error
PHP:
<?php error_log("MESSAGE", 0);?>
2. Nginx's error log file
Code:
2020/06/04 16:23:00 [error] 9057#9057: *26 FastCGI sent in stderr: "MESSAGE" while reading response header from upstream, client: 151.24.155.27, server: "fqdn", request: "GET /page.php HTTP/2.0", upstream: "fastcgi://127.0.0.1:9000", host: "127.0.0.1"

I did the following configuration for nginx and php-fpm, it looks like this is what people use in general to achieve my goal.
Code:
server {
...
  access_log      /var/log/nginx/domain.access_log main ;
  error_log       /var/log/nginx/domain.error_log error ;
...
  location ~ \.php$ {
      try_files      $uri  =404 ;
      fastcgi_intercept_errors off ;
      fastcgi_buffering off ;
      fastcgi_request_buffering off ;
      fastcgi_pass   unix:/var/run/php-fpm.sock;
      fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
      include        /usr/local/etc/nginx/fastcgi_params ;
  }
...
}
Code:
[global]

pid = /var/run/php-fpm.pid
;error_log = /dev/stderr
syslog.facility = daemon
syslog.ident = php-corefpm
log_buffering = no
log_level = error
emergency_restart_threshold = 1
emergency_restart_interval = 12h


[common_pool]

user = www
group = www
listen = /var/run/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0666
;listen.allowed_clients = 127.0.0.1

catch_workers_output = yes
decorate_workers_output = no
php_flag[display_errors] = off
;php_admin_value[display_errors] = stderr
;php_admin_value[error_reporting] = E_ALL
;php_admin_value[log_errors] = On
;php_admin_value[error_log] = stderr
php_admin_flag[log_errors] = on
php_admin_flag[fastcgi.logging] = off

pm = dynamic
pm.max_children = 50
pm.start_servers = 7
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500
pm.status_path = /fpmstatus

slowlog = /var/log/php/php-fpm.slow.log
request_slowlog_timeout = 10s
ping.path = /ping

This is not working for me.

At the beginning of my journey, I was confused about how nginx actually gets this information and thought it magically happens through STDERR descriptor from php-fpm to nginx, but then I did some digging into the FastCGI protocol, and it looks like backend (php-fpm) actually can send this information using FCGI_STDERR record type.

After the finding, I started to dump traffic between nginx and php-fpm, so I discovered that php-fpm is not sending FCGI_STDERR back to nginx when an error on the PHP side happens.

I checked docs of php-fpm configuration a couple of times and haven't found how to allow/force send this FCGI_STDERR back to nginx explicitly.

At this point, I'm completely lost.

This is probably not the best place to ask this kind of question, but I was wondering if this is somehow related to how php configures/complies from ports.

Will appreciate any help
 
Did you set the error_log file location in php-fpm.conf file?

Code:
; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; into a local file.
; Note: the default prefix is /var
; Default Value: log/php-fpm.log
;error_log = log/php-fpm.log
 
Did you set the error_log file location in php-fpm.conf file?

Code:
; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; into a local file.
; Note: the default prefix is /var
; Default Value: log/php-fpm.log
;error_log = log/php-fpm.log
Yes, I did try to set it with various options. Tho I don't understand how setting that option will tell php-fpm to send stderr over fastcgi
 
If the log file is locked and you can't point both of them to the same log file then use log facility and let the syslog to catch and store it for you.
 
what if you set error_log to /dev/fd/2 ?
So long story short, all comments I have in the config example above, I actually did try those. So yeah, I did try to set it to /dev/stderr (same as /dev/fd/2), tho I don't really understand how it could help in this situation even tho I saw some examples online
If the log file is locked and you can't point both of them to the same log file then use log facility and let the syslog to catch and store it for you.
So, as I said in my first message, I don't expect multiple applications to write in a single file, I want PHP-FPM to send an error message over fastcgi protocol back to nginx, so nginx could write this information to it's own error log. I know it's possible since there're a lot of examples online when people sharing logs like
Code:
2021/09/27 09:10:09 [error] 31358#31358: *607 FastCGI sent in stderr: "PHP message: PHP Warning:  fopen(/usr/share/nginx/package/learning-platform-38/learning-platform-prod/wp-content/uploads/wp-file-manager-pro/fm_backup/index.php): failed to open stream: No such file or directory in /usr/share/nginx/package/learning-platform-38/learning-platform-prod/wp-content/plugins/wp-file-manager/file_folder_manager.php on line 59PHP message: PHP Warning:  fclose() expects parameter 1 to be resource, bool given in /usr/share/nginx/package/learning-platform-38/learning-platform-prod/wp-content/plugins/wp-file-manager/file_folder_manager.php on line 60" while reading response header from upstream, client: 10.39.68.231, server: acooldomain.server, request: "GET / HTTP/2.0", upstream: "fastcgi://127.0.0.1:9000", host: "learning.acooldomain.com"
This is nginx error log, and nginx says FastCGI sent in stderr. I want PHP-FPM to send stderr messages back to nginx, so nginx could print those
 
catch_workers_output bool
Redirect worker stdout and stderr into main error log. If not set, stdout and stderr will be redirected to /dev/null according to FastCGI specs. Default value: no.

It should catch the errors if your error reprting is enabled error_reporting
 
Hey guys, I want to share my findings, finally made it work, maybe it will be helpful for somebody

The primary problem was related to the configuration of the PHP core itself. I had error_log = syslog defined in my php.ini, I also did try other options too. It's kind of a weird approach, but to make it work, we need to unset error_log = to activate the SAPI error logger, so php-fpm can actually send errors with fascgi protocol. Looks like you can not have both, e.g., syslog and SAPI working at the same time. The weird part is it is impossible to specify using SAPI something like error_log = SAPI; it won't work. There are two options:
1. comment error_log = in php.ini
2. use php_admin_value[error_log] = none in php-fpm.conf to clear a previously set value

Now, obviously, fastcgi.logging should be turned on. While I was playing with various configurations, I did disable it and completely forgot to enable it back. I have php_admin_flag[fastcgi.logging] = On in my php-fpm.conf .

Other stuff good to know:
We can safely turn off catch_workers_output since, as I mentioned before, this does not affect anything.

My final php-fpm config
Code:
[global]

pid = /var/run/php-fpm.pid
error_log = syslog
syslog.facility = daemon
syslog.ident = php-corefpm
log_buffering = no
log_level = error
emergency_restart_threshold = 1
emergency_restart_interval = 12h


[common_pool]

user = www
group = www
listen = /var/run/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0666
listen.allowed_clients = 127.0.0.1

catch_workers_output = no
decorate_workers_output = no

; https://www.php.net/manual/en/ini.list.php
; https://www.php.net/manual/en/configuration.changes.modes.php
; https://www.php.net/manual/en/configuration.changes.php
php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT
php_admin_value[error_log] = none
php_admin_flag[log_errors] = On
php_admin_flag[fastcgi.logging] = On
; If you want display errors in browser uncomment following
;php_flag[display_errors] = off

pm = dynamic
pm.max_children = 50
pm.start_servers = 7
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500
pm.status_path = /fpmstatus

slowlog = /var/log/php/php-fpm.slow.log
request_slowlog_timeout = 10s
ping.path = /ping

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include=/usr/local/etc/php-fpm/*.pool.conf
 
Back
Top