diff --git a/ci/image-tests/Checks/ContainerEnvironmentChecks.php b/ci/image-tests/Checks/ContainerEnvironmentChecks.php index 92cf65113..cb67f3c68 100644 --- a/ci/image-tests/Checks/ContainerEnvironmentChecks.php +++ b/ci/image-tests/Checks/ContainerEnvironmentChecks.php @@ -42,7 +42,8 @@ public function testTimeZoneFromTheEnvironment(): void /** * TZ=Europe/Berlin: cron runs a job for the current hour in Berlin, which is not the hour in UTC. The job comes from - * Hooks/ and is there before cron starts; the test works out its hours, see ImageTestCase::cronHours(). Only in the + * Hooks/ and is there before cron starts; the test works out its hours, see ImageTestCase::cronHours(). + * SERVICE_CRON_OPTS: cron logs the job at its log level, through syslog-ng into the file of LOG_STDOUT. Only in the * images that start cron. */ public function testCronRunsJobsInTheTimeZoneFromTheEnvironment(): void @@ -56,6 +57,30 @@ public function testCronRunsJobsInTheTimeZoneFromTheEnvironment(): void $this->waitFor(function () use ($container): void { self::assertSame("ran\n", $this->stdout('cat /tmp/image-tests/cron-job 2>&1 || true', $container)); }, 25, 3); + $this->waitFor(function () use ($container): void { + self::assertMatchesRegularExpression( + self::image()->isAlpine() ? '/crond\[\d+\]: USER root pid +\d+ cmd echo ran/' : '/CRON\[\d+\]: \(root\) END \(\[\d+\] echo ran/', + $this->stdout('cat ' . static::ENVIRONMENT['LOG_STDOUT'], $container), + 'SERVICE_CRON_OPTS=' . self::cronOptions(), + ); + }, 10, 1); + } + + /** + * syslog-ng writes the messages of the programs in the container into the file of LOG_STDOUT. The environment + * container starts it where the image does not. + */ + public function testSyslogMessagesReachTheFileFromTheEnvironment(): void + { + $container = $this->environmentContainer(); + if (!in_array('syslog', self::services(), true)) { + $this->assertCommandSucceeds('docker-service enable syslog && docker-service start syslog', $container); + } + + $this->eventually(function () use ($container): void { + $this->assertCommandSucceeds('logger -t image-test syslog-marker', $container); + self::assertStringContainsString('image-test: syslog-marker', $this->stdout('cat ' . static::ENVIRONMENT['LOG_STDOUT'], $container)); + }); } /** diff --git a/ci/image-tests/Checks/ServiceChecks.php b/ci/image-tests/Checks/ServiceChecks.php index d2b089229..e12cd6b9b 100644 --- a/ci/image-tests/Checks/ServiceChecks.php +++ b/ci/image-tests/Checks/ServiceChecks.php @@ -13,8 +13,6 @@ use function array_combine; use function array_keys; use function in_array; -use function preg_grep; -use function preg_quote; use function ksort; use function preg_match_all; use function sleep; @@ -94,7 +92,7 @@ public static function serviceOptions(): iterable { yield 'supervisord' => ['supervisord']; foreach (self::services() as $service) { - if (in_array($service, ['cron', 'syslog', 'ssh', 'apache', 'php-fpm'], true)) { + if (in_array($service, ['syslog', 'ssh', 'apache', 'php-fpm'], true)) { yield $service => [$service]; } } @@ -103,8 +101,8 @@ public static function serviceOptions(): iterable /** * What the SERVICE_*_OPTS of the environment container do: supervisord logs at the level "trace"; syslog-ng writes * its process id to the file of --pidfile; sshd listens on port 2222; Apache defines IMAGE_TEST, for which the - * configuration from Hooks/ sends a header; PHP-FPM names the session cookie IMAGETEST. Cron gets a log level of - * its own, which shows only in its command line. + * configuration from Hooks/ sends a header; PHP-FPM names the session cookie IMAGETEST. The log level of cron is + * checked by ContainerEnvironmentChecks. */ #[DataProvider('serviceOptions')] public function testServiceOptionsFromTheEnvironment(string $service): void @@ -129,10 +127,6 @@ public function testServiceOptionsFromTheEnvironment(string $service): void 'SERVICE_APACHE_OPTS=-DIMAGE_TEST', ), 'php-fpm' => self::assertSame('IMAGETEST', $this->phpFpmBody($container, 'test=session_name'), 'SERVICE_PHPFPM_OPTS=-dsession.name=IMAGETEST'), - 'cron' => self::assertNotSame([], preg_grep( - '#^/usr/sbin/' . (self::image()->isAlpine() ? 'crond' : 'cron') . ' -f ' . preg_quote(self::cronOptions(), '#') . '$#', - $this->commandLines($container), - ), 'SERVICE_CRON_OPTS'), default => throw new LogicException('No check of the options of ' . $service), }; }); diff --git a/ci/image-tests/ImageTestCase.php b/ci/image-tests/ImageTestCase.php index 79d7a90d0..cdda8d80f 100644 --- a/ci/image-tests/ImageTestCase.php +++ b/ci/image-tests/ImageTestCase.php @@ -310,11 +310,12 @@ protected function environmentContainer(): Container } /** - * A log level option of cron in Debian and Ubuntu, of BusyBox crond in Alpine. + * A log level option of cron in Debian and Ubuntu, of BusyBox crond in Alpine, at which cron logs each job it runs: + * Debian also logs its end, BusyBox its start. */ protected static function cronOptions(): string { - return self::image()->isAlpine() ? '-l 8' : '-L 15'; + return self::image()->isAlpine() ? '-l 0' : '-L 15'; } /** diff --git a/docker/base/alpine/conf/etc/syslog-ng/syslog-ng.conf b/docker/base/alpine/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/base/alpine/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/base/alpine/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/base/ubuntu-22.04/conf/etc/syslog-ng/syslog-ng.conf b/docker/base/ubuntu-22.04/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/base/ubuntu-22.04/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/base/ubuntu-22.04/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.2-alpine/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.2-alpine/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.2-alpine/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.2-alpine/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.2/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.2/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.2/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.2/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.3-alpine/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.3-alpine/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.3-alpine/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.3-alpine/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.3/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.3/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.3/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.3/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.4-alpine/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.4-alpine/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.4-alpine/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.4-alpine/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.4/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.4/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.4/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.4/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.5-alpine/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.5-alpine/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.5-alpine/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.5-alpine/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docker/php-official/8.5/conf/etc/syslog-ng/syslog-ng.conf b/docker/php-official/8.5/conf/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/docker/php-official/8.5/conf/etc/syslog-ng/syslog-ng.conf +++ b/docker/php-official/8.5/conf/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END }; diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 0e2274bac..5797615c9 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -35,7 +35,7 @@ Images: `base`, `base-app`, `apache`, `nginx`, `php-*`. | Variable | Default | Effect | | --- | --- | --- | | `CLI_SCRIPT` | not set | Command the `cli` entrypoint runs as `APPLICATION_USER`. Works in `base-app` and `php-*` only; in the other images `cli` fails because `APPLICATION_USER` is not set. See [Customization](customization.md#commands). | -| `LOG_STDOUT` | container stdout | File that `/docker.stdout` points to. Nginx and Apache write their access logs there, syslog-ng the messages of the services, for example of cron and Postfix. PHP-FPM does not, see [Known issues](known-issues.md#log_stdout-and-log_stderr-for-php-fpm); on Alpine, syslog-ng gets no messages, see [syslog on Alpine](known-issues.md#syslog-on-alpine). | +| `LOG_STDOUT` | container stdout | File that `/docker.stdout` points to. Nginx and Apache write their access logs there, syslog-ng the messages of the services, for example of cron and Postfix. PHP-FPM does not, see [Known issues](known-issues.md#log_stdout-and-log_stderr-for-php-fpm). | | `LOG_STDERR` | container stderr | File that `/docker.stderr` points to. Nginx and Apache write their error logs there. | | `TZ` | not set (UTC) | Time zone of the container, for example `Europe/Berlin`. It applies to `date`, cron and the time stamps supervisord writes to the container log. PHP uses `PHP_DATE_TIMEZONE` instead, see [Time zone](customization.md#time-zone). | diff --git a/docs/known-issues.md b/docs/known-issues.md index ae63190b8..88357d3f8 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -28,16 +28,6 @@ These options and tools are part of the images but do not work, or work only in - **Images:** `php-*`, Debian and Alpine. - **Instead:** Read them with `docker compose logs`. To have PHP-FPM's messages and the PHP errors in the file of `LOG_STDERR`, also set `fpm.global.error_log: /docker.stderr`. The access log needs a pool file that sorts after `docker.conf`, see [`fpm.pool.`](#fpmpoolkey-for-the-directives-of-dockerconf). -### syslog on Alpine - -- **Effect:** syslog-ng takes messages on `/dev/log` only as a stream, but programs on Alpine send them as datagrams. The messages of cron, Postfix and `logger` are lost: they reach neither the container log nor the file of `LOG_STDOUT`. -- **Images:** the Alpine images that run syslog-ng: `php-*` with an `-alpine` tag, and `base`, `base-app`, `nginx` and `apache` once you enable the service. -- **Instead:** Let syslog-ng take datagrams, in a [service hook](customization.md#service-hooks) such as `/opt/docker/bin/service.d/syslog-ng.d/50-dgram.sh`: - - ```bash - sed -i 's#unix-stream("/dev/log")#unix-dgram("/dev/log")#' /opt/docker/etc/syslog-ng/syslog-ng.conf - ``` - ### No `/app` in `nginx` and `apache` - **Effect:** The images have no `/app`, the default document root. Without your application there, Nginx answers every request with `500` (`rewrite or internal redirection cycle`), and Apache exits at the start with `DocumentRoot '/app' is not a directory` and is restarted over and over. diff --git a/provisioning/base/general/etc/syslog-ng/syslog-ng.conf b/provisioning/base/general/etc/syslog-ng/syslog-ng.conf index c69841581..36fcb09c9 100644 --- a/provisioning/base/general/etc/syslog-ng/syslog-ng.conf +++ b/provisioning/base/general/etc/syslog-ng/syslog-ng.conf @@ -22,7 +22,8 @@ options { source s_src { # syslog-s_src START - unix-stream("/dev/log"); + # a datagram socket: syslog() of glibc tries datagrams first, the one of musl on Alpine only sends datagrams + unix-dgram("/dev/log"); internal(); # syslog-s_src END };