Docker Logs
We cover the docker logs command. We need to make a tweak so everything outputs to stdout or stderr. If we do that successfully, we can use Docker’s logging mechanism to see output from Nginx, PHP, and Supervisord.
The only change we need to make:
PHP-FPM
In our php-fpm.conf file, adjust the error_log to error_log = /proc/self/fd/2 .
Nginx
Nginx is easier — we can just adjust the Dockerfile by symlinking the nginx error and access logs to stdout and stderr:
FROM ubuntu:18.04 LABEL maintainer="Chris Fidao" ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ && apt-get install -y gnupg tzdata \ && echo "UTC" > /etc/timezone \ && dpkg-reconfigure -f noninteractive tzdata RUN apt-get update \ && apt-get install -y curl zip unzip git supervisor sqlite3 \ nginx php7.2-fpm php7.2-cli \ php7.2-pgsql php7.2-sqlite3 php7.2-gd \ php7.2-curl php7.2-memcached \ php7.2-imap php7.2-mysql php7.2-mbstring \ php7.2-xml php7.2-zip php7.2-bcmath php7.2-soap \ php7.2-intl php7.2-readline php7.2-xdebug \ php-msgpack php-igbinary \ && php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \ && mkdir /run/php \ && apt-get -y autoremove \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ && echo "daemon off;" >> /etc/nginx/nginx.conf RUN ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log ADD default /etc/nginx/sites-available/default ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf ADD php-fpm.conf /etc/php/7.2/fpm/php-fpm.conf CMD ["nginx"]
And of course, we need to rebuild the image:
docker build -t shippingdocker/app:latest \ -f docker/app/Dockerfile \ docker/app
Looking for a deeper dive into Docker?
Sign up here to get a preview of the Shipping Docker course! Learn how to integrate Docker into your applications and develop a workflow to make using Docker a breeze!
How to configure PHP logs for Docker
If you are using docker and cloud services to run your application live, you should manage your logs.
The most common method to store them is to put them in the text file. It’s the default configuration for most backend frameworks. This option is ok if you run your application locally or on the VPS server for test.
When you run your application in a production environment, you should choose a better option to manage your logs. Almost every cloud has a tool for rotating logs or if not, you can use for example Grafana Loki or ELK stack. Those solutions are better because give you interfaces to rotate and search your logs. Also, you have easy access to them, you no need to connect to your server to review them.
If you are using Docker containers, and you running your application in cloud services, often they will be automatically writing the logs of your containers to tools like AWS CloudWatch or GCloud Stackdriver. But first, you need to redirect your log streams to the output of the Docker container to be able to use them.
Linux streams
Docker containers are running the Linux processes. In linux every running process has 3 streams, STDIN , STDOUT , STDERR . STDIN it’s command input stream, that you can provide for ex. by your keyboard. STDOUT is the stream where the running command may print the output. STDERR is the standard error stream, but the name I think is a bit confusing, because it is basically intended for diagnostic output. When you run the docker logs [container] command in your terminal, you will see the output of STDOUT and STDERR streams. So our goal is to redirect our logs to one of those streams. Official docker documentation page
PHP-FPM
In PHP we are often running our application using the PHP-FPM (Process Manager). If you run your docker with FPM inside a docker container, and you run the docker logs command, you should see the output with processed requests, or errors. So the PHP-FPM is already writing its output to STDOUT .
The PHP-FPM allow us to catch workers output and forward them to the STDOUT . To do that we need to make sure that the FPM is configured properly. You can create new config file, and push it for example to the /usr/local/etc/php-fpm.d/logging.conf file:
[global] error_log = /proc/self/fd/2 [www] access.log = /proc/self/fd/2 catch_workers_output = yes decorate_workers_output = no
The error_log and access.log parameters are configuration of streams of logs output.
The catch_workers_output option is turning on the worker’s output caching. The decorate_workers_output is the option that turns off the output decoration. If you leave this option turned on, FPM will decorate your application output like this:
[21-Mar-2016 14:10:02] WARNING: [pool www] child 12 said into stdout: "[your log line]"
Remember that decorate_workers_output option is available only for PHP 7.3.0 and higher. If you are using official docker php-fpm image, this configuration is already set in the /usr/local/etc/php-fpm.d/docker.conf file, so you no need to do anything more 😎
PHP application configuration
Right now everything that will be put to the stdout from PHP workers will be shown in our docker logs. But when logs are forwarded to that stream in PHP? To write something to STDIN on PHP level, we need to just write to the php://stdout stream. In the simplest way you can do this like that:
file_put_contents('php://stdout', 'Hello world');
When you execute this code in php cli, you will get the Hello world text on the output. But it’s not the optimal way to push your logs to the STDOUT . Every modern framework should have a PSR-3 Logger. I think that the most popular now is the monolog, so I will show you how to configure it in Symfony, Laravel, and in pure usage.
Monolog
Basic monolog configuration
If you are using monolog in your project with manual configuration, you need to configure handler in this way: (Modified documentation example)
use Monolog\Logger; use Monolog\Handler\StreamHandler; $log = new Logger('stdout'); $log->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG)); $log->debug('Foo');
Symfony
Symfony Kernel since the Flex was provided, is using minimalist PSR-3 logger, that logs everything to php://stderr by default. In Symfony, monolog as other components is configured in YAML files. So the same configuration will look like this:
# config/packages/monolog.yaml monolog: handlers: stdout: type: stream path: "php://stdout" level: debug
Laravel
# config/logging.php use Monolog\Handler\StreamHandler; return [ 'channels' => 'stdout' => [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'level' => env('LOG_LEVEL', 'debug'), 'with' => [ 'stream' => 'php://stdout', ], ], ];
STDERR or STDOUT
In some articles on the internet, you can read that someone uses stderr, and someone uses stdout streams to write logs there. Right now I cannot fin any reasons to choose one of them which is better.
The only information that I found on this topic is that post.