
laravel 默认将错误日志写入 `storage/logs/laravel.log` 文件,而非标准输出(stdout),导致 `docker logs` 无法捕获。本文提供完整解决方案:通过配置日志通道为 `stack` + `single`、设置 `log_channel=stack`,并配合 `tail -f` 实时转发日志至容器 stdout。
在容器化 Laravel 应用(尤其是运行于 Docker 或 Kubernetes 环境)时,遵循“12-Factor App”原则要求应用将日志直接输出到 stdout/stderr,由容器运行时统一收集(如 docker logs、Fluentd、Elasticsearch 等)。但 Laravel 默认使用 single 或 daily 文件驱动,日志仅落盘,不触达容器标准流——这正是你执行 php artisan command5 报错却在 docker logs -f travellist-app 中看不到任何 Laravel 错误信息的根本原因。
✅ 正确配置步骤
1. 确保 config/logging.php 使用可输出到 stdout 的通道组合
推荐采用 stack 驱动聚合多个通道,并将 single 作为唯一子通道(注意:此处的 single 本身仍写文件,后续需靠 tail 转发;若追求纯 PHP 层直出 stdout,可改用 errorlog 驱动,见下方进阶说明):
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'], // ← 关键:只包含 single(或其他持久化通道)
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'permission' => 0660,
],
],⚠️ 注意:single 驱动本身不会自动写入 stdout,它只是确保所有日志(包括 Artisan 命令异常)都集中写入 laravel.log —— 这是后续 tail 转发的前提。
2. 设置环境变量启用 stack 通道
在 .env 文件中明确指定:
LOG_CHANNEL=stack
此配置强制 Laravel 所有日志(HTTP 请求、队列、Artisan 命令等)均经由 stack 通道处理,最终落地到 storage/logs/laravel.log。
3. 在容器启动后实时 tail 日志文件到 stdout
这是最关键的一步:让容器主进程(PID 1)的标准错误流持续接收日志内容。在 Dockerfile 的 ENTRYPOINT 或 Kubernetes 的 lifecycle.postStart 中执行:
# 推荐写法(兼容性高,避免文件不存在报错) touch /var/www/storage/logs/laravel.log && \ tail -f /var/www/storage/logs/laravel.log > /proc/1/fd/2 &
- /proc/1/fd/2 指向容器主进程(通常是 PHP-FPM master 或自定义入口脚本)的 stderr;
- & 确保 tail 后台运行,不阻塞主进程启动;
- touch 预创建日志文件,防止首次 tail 失败。
✅ Docker Compose 示例(docker-compose.yml):
services:
app:
build: .
# ... 其他配置
entrypoint: >
sh -c "
touch /var/www/storage/logs/laravel.log &&
tail -f /var/www/storage/logs/laravel.log > /proc/1/fd/2 &
exec docker-php-entrypoint php-fpm
"✅ Kubernetes Deployment 片段(如原答案所示):
lifecycle:
postStart:
exec:
command:
- /bin/bash
- -c
- |
touch /var/www/storage/logs/laravel.log &&
tail -f /var/www/storage/logs/laravel.log > /proc/1/fd/2 &? 替代方案:PHP 层直出 stdout(无需 tail)
若希望完全绕过文件 I/O,可将 single 替换为 errorlog 驱动(Laravel 8+ 原生支持):
'stdout' => [
'driver' => 'errorlog',
'level' => 'debug',
'layout' => 'line', // 可选:格式化为单行便于日志采集
],再设 LOG_CHANNEL=stdout。此方式由 PHP 的 error_log() 函数直接写入 stderr,零依赖外部命令,更符合云原生最佳实践,但需确认你的 PHP 容器已正确配置 error_log = /proc/1/fd/2(通常默认即如此)。
✅ 验证效果
重启容器后执行触发错误的 Artisan 命令:
docker exec -it travellist-app php artisan command5
立即在另一终端运行:
docker logs -f travellist-app
你将看到类似以下实时输出(含堆栈):
[2024-06-15 08:22:33] local.ERROR: Command "command5" is not defined. {"exception":"[object] (Symfony\\Component\\Console\\Exception\\CommandNotFoundException(code: 0): Command \"command5\" is not defined. at /var/www/vendor/symfony/console/Application.php:644)..."}? 总结
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| stack + single + tail -f | 兼容所有 Laravel 版本,日志文件仍可本地调试 | 依赖 shell 命令,多一层进程 | Docker/K8s 快速落地,兼容旧项目 |
| errorlog 驱动 | 纯 PHP 实现,无额外进程,性能更优 | 需 Laravel ≥8.x,丢失文件备份能力 | 新项目或可升级框架的生产环境 |
选择任一方案,即可让 Laravel 错误、警告、调试日志完整出现在 docker logs 流中,无缝接入现代可观测性体系。










