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:
<?php
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
Monolog is great library to handle logs in your application. It’s easy and elastic in configuration.
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)
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('stdout');
$log->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
$log->debug('Foo');
You just need to configure StreamHandler, to write to the php://stdout
file.
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
Laravel use the arrays for configuration so the same thing will look like this:
# config/logging.php
<?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.
I think that stderr is more popular in some examples, also Fabien Potencier set it as default in his minimalistic logger, so I think we can assume that this one is better.
Personally, I always used the stdout, so that’s the reason why I use it in this post’s examples. If I will find a great reason for using one of them over another I will update this post.