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.