分隔 Laravel log file 以解決 Permission Deny 問題

Laravel 理當會將發生的例外錯誤(Exception)寫到 storage/logs/laravel-yyyy-mm-dd.log,但查詢該檔案,卻看不到應有的錯誤內容!

環境

Docker 安裝的內容,

  1. Ubuntu 18.04
  2. Nginx 1.14
  3. php7.3-fpm

權限問題 Permission Deny

1
2
3
4
5
6
$ ll storage/logs/

-rw-r--r--. 1 www-data www-data 30283 Apr 16 23:55 laravel-2019-04-16.log
-rw-r--r--. 1 www-data www-data 30079 Apr 17 23:55 laravel-2019-04-17.log
-rw-r--r--. 1 root root 30283 Apr 18 23:55 laravel-2019-04-18.log
-rw-r--r--. 1 root root 38283 Apr 19 23:55 laravel-2019-04-19.log

系統有 crontab 定時處理事務,在處理的同時會以 Log::info() 輸出,所以產生的 log 檔案,其 owner 會是 root。
網站程式運行的 user 是 www-data,所以若發生程式的 Exception 就會因為 Permission Deny 而無法寫入,

解決方式

成功的解法 Success

bootstrap/app.php 檔案內加入以下程式碼,

1
2
3
4
5
6
7
8
9
10
11

// isolate artisan console log and laravel service log
// https://stackoverflow.com/questions/27674597/laravel-daily-log-created-with-wrong-permissions
$app->configureMonologUsing(function (Monolog\Logger $monolog) {
$filename = storage_path('logs/' . php_sapi_name() . '-' . posix_getpwuid(posix_geteuid())['name'] . '.log');
$monolog->pushHandler($handler = new Monolog\Handler\RotatingFileHandler($filename, 60));
$handler->setFilenameFormat('laravel-{date}-{filename}', 'Y-m-d');
$formatter = new LineFormatter(null, null, true, true);
$formatter->includeStacktraces();
$handler->setFormatter($formatter);
});

php artisan command 執行或是 crontab 所產生的 log file,其 owner 都會是 root
如果是網站運行時的 Exception 所建立的 log file,則 owner 會是 www-data,符合要的結果。 😄

1
2
3
4
$ ll storage/logs/

-rw-r--r--. 1 root root 2652 Apr 24 06:30 laravel-2019-04-24-cli-root.log
-rw-r--r--. 1 www-data www-data 78B Apr 24 04:24 laravel-2019-04-24-fpm-fcgi-www-data.log

無法得到預期效果的處理方式 failed

用 www-data 執行

www-data user 運行 php artisan command

切換執行的 user 身份

1
2
3
$ su www-data

This account is currently not available.

查看 www-data user

1
2
3
$ cat /etc/passwd | grep www

www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

/usr/sbin/nologin 的關係,所以無法切換 user,
修改成

1
2
3
$ vim /etc/passwd

www-data:x:33:33:www-data:/var/www:/bin/bash

然後以 www-data 身份執行 php artisan 以產生 log file,會得到 owner 為 www-data 的 log file,

1
2
3
$ su www-data
$ php artisan command:xxxx
$ ll storage/logs/

BUT crontab 執行產生的 log file 仍然 root,Orz

修改 crontab

1
2
3
4

$ crontab -e

* * * * * /usr/bin/php /home/www/code/artisan schedule:run >> /dev/null 2>&1

修改成

1
* * * * * www-data /usr/bin/php /home/www/code/artisan schedule:run >> /dev/null 2>&1

然後執行

1
$ sudo service cron reload

結果 cron 無法運作,😳

可能需要這樣做(未驗證)

www-data 建立自己的 crontab 排程,可參考 [关于 Laravel 日志权限] 1 下方留言

1
2
$ su www-data
$ crontab -u www-data -e

放棄測試

因環境是在 Docker 內,上述的步驟已經無法輕易以撰寫 shell script 達成要的結果,若需要人工介入修改,則不符合使用 Docker 無痛開發原則 XD。

Reference