使用错误权限创建 Laravel 日志

我有一个脚本,我运行使用 php Artisan (与 用户) ,有时它会导致每日日志文件创建之前的 apache Www-data用户-这意味着,当一个真正的用户使用我的 Web 应用程序,我得到的文件夹权限错误:

未能打开流: 拒绝权限

我每次都将权限修改回 Www-data,但是我希望通过让日志文件始终使用正确的权限创建来解决这个问题。

我考虑过创建一个 cron 作业来创建文件或者接触它,以确保它每天都有正确的权限,但是我正在寻找一个不依赖于其他脚本的更好的解决方案。

我们还考虑了在另一个脚本中包装 php artian,以确保它始终使用 Www-data凭据运行,但是我们想要做的事情实际上是不允许 apache 执行的 过程。

还有什么建议吗?

103428 次浏览

app/start/artisan.php文件的开头添加如下代码(这是 Laravel 4的代码) :

// If effectively root, touch the log file and make sure it belongs to www-data
if (posix_geteuid() === 0) {
$file = storage_path() . '/logs/laravel.log';
touch($file);
chown($file, 'www-data');
chgrp($file, 'www-data');
chmod($file, 0664);
}

如果您提到的日志文件不是标准的 Laravel 日志文件,则调整路径。您也可能不希望像我在这里所做的那样更改组或设置权限。上面将组设置为 www-data并设置组写权限。然后,我将我的常规用户添加到 www-data组,以便作为常规用户运行 artian 命令仍然可以写入日志。

一个相关的调整是将以下内容放在 app/start/global.php文件的开头:

umask(0002);

如果这样做,上面的 chmod行就变得没有意义了。将 umask 设置为这样,PHP (因此也是 Laravel)创建的任何新文件都将被屏蔽,只有这样“其他”用户才不会拥有写权限。这意味着目录将以 rwxrwxr-x开始,文件以 rw-rw-r--开始。因此,如果 www-data运行 PHP,那么它生成的任何缓存和日志文件默认情况下都可以被该用户的主组(即 www-data)中的任何人写入。

为此,您应该对文件和目录使用高级 ACL。setfacl就是你的答案。如果您希望授予 Www-data用户对特定目录中的 根茎文件进行写操作的权限,您可以这样做:

setfacl -d -m default:www-data:you-chosen-group:rwx /my/folder

发出此命令后,您将 Www-data用户对 /my/folder/中所有文件的权限设置为 rwx,无论是谁创建了这些文件。请参考 这个这个问题。另外,你可以检查 setfacl的文件

如果有帮助就告诉我。

重点 这个答案与 laravel5.5 + 不兼容。请参阅这个答案: < a href = “ https://stackoverflow. com/questions/50305186/Custom- Dynamic-log-file-name-with-laravel5.6”> 用 laravel5.6自定义(动态)日志文件名

让我们从常量开始。

您有一个 php artisan命令,由 root运行。

可以安全地假设这个命令每天都执行。

解决方案一:

假设创建文件的用户是默认情况下拥有写入权限的用户,那么我们可以按用户分隔日志:

App/start/global.php

/*
|--------------------------------------------------------------------------
| Application Error Logger
|--------------------------------------------------------------------------
|
| Here we will configure the error logger setup for the application which
| is built on top of the wonderful Monolog library. By default we will
| build a basic log file setup which creates a single file for logs.
|
*/


Log::useDailyFiles(storage_path().'/logs/laravel-'.posix_getpwuid(posix_geteuid())['name'].'.log');

如果您的 Www-data用户要创建错误日志,那么结果将是: storage/logs/laravel-www-data-2015-4-27.log

如果您的 用户要创建错误日志,那么结果将是: storage/logs/laravel-root-2015-4-27.log

解决方案二:

在 php 脚本中更改您的 Artisan 命令使用的日志。

run()函数中,在开始处添加以下代码行:

Log::useFiles(storage_path().'/logs/laravel-'.__CLASS__.'-'.Carbon::now()->format('Y-m-d').'.log');

如果您的类的名称是 ArtisanRunner,那么您的日志文件将是:

storage/logs/laravel-ArtisanRunner-2015-4-27.log.

结论: 解决方案1更好,因为它根据用户描述日志,因此不会发生错误。

编辑: 正如 Jason 所指出的,get_current_user()返回脚本的所有者名称。因此,为了应用解决方案1,chown您的工匠类文件到所需的用户名。

对于 Laravel 5.1,我在 bootstrap/app.php的底部使用以下代码(如 那些文件中提到的) :

/**
* Configure Monolog.
*/
$app->configureMonologUsing(function(Monolog\Logger $monolog) {
$filename = storage_path('logs/laravel-'.php_sapi_name().'.log');
$handler = new Monolog\Handler\RotatingFileHandler($filename);
$monolog->pushHandler($handler);
});

当然,您可以使用许多其他处理程序。

Laravel 5.1

在我们的示例中,我们希望创建所有日志文件,以便 deploy组中的所有内容都具有读/写权限。因此,我们需要创建所有具有 0664权限的新文件,而不是 0644默认权限。

我们还添加了一个格式化程序来添加新行,以提高可读性:

$app->configureMonologUsing(function(Monolog\Logger $monolog) {
$filename = storage_path('/logs/laravel.log');
$handler = new Monolog\Handler\RotatingFileHandler($filename, 0, \Monolog\Logger::DEBUG, true, 0664);
$handler->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true, true));
$monolog->pushHandler($handler);
});

也可以把这个和公认的答案结合起来

$app->configureMonologUsing(function(Monolog\Logger $monolog) {
$filename = storage_path('/logs/laravel-' . php_sapi_name() . '.log');
$handler = new Monolog\Handler\RotatingFileHandler($filename, 0, \Monolog\Logger::DEBUG, true, 0664);
$handler->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true, true));
$monolog->pushHandler($handler);
});

对我来说,这个问题不仅仅是日志权限的问题... ... 我遇到了任何与引导/缓存和存储文件夹有关的问题,在这些文件夹中,一个用户会创建一个文件/文件夹,而另一个则由于标准的644和755权限而无法编辑/删除。

典型的情况是:

  • 由 apache 用户创建但是在执行作曲家安装命令时不能被作曲家用户编辑的 bootstrap/cache/build.php 文件

  • 创建缓存的 apache 用户无法使用编写器用户清除缓存

  • 上面描述的可怕的日志竞争条件。

无论哪个用户创建文件/文件夹,需要访问的其他用户都拥有与原始作者完全相同的权限。

DR?

接下来这么做。

我们需要创建一个名为 laravel 的共享用户组,该组由所有需要访问存储和引导/缓存目录的用户组成。 接下来,我们需要确保新创建的文件和文件夹分别具有 laravel 组和664和775权限。

对现有的文件/目录这样做很容易,但是需要一点魔法来调整默认的文件/文件夹创建规则..。

## create user group
sudo groupadd laravel


## add composer user to group
sudo gpasswd -a composer-user laravel


## add web server to group
sudo gpasswd -a apache laravel


## jump to laravel path
sudo cd /path/to/your/beautiful/laravel-application


## optional: temporary disable any daemons that may read/write files/folders
## For example Apache & Queues


## optional: if you've been playing around with permissions
## consider resetting all files and directories to the default
sudo find ./ -type d -exec chmod 755 {} \;
sudo find ./ -type f -exec chmod 644 {} \;


## give users part of the laravel group the standard RW and RWX
## permissions for the existing files and folders respectively
sudo chown -R :laravel ./storage
sudo chown -R :laravel ./bootstrap/cache
sudo find ./storage -type d -exec chmod 775 {} \;
sudo find ./bootstrap/cache -type d -exec chmod 775 {} \;
sudo find ./storage -type f -exec chmod 664 {} \;
sudo find ./bootstrap/cache -type f -exec chmod 664 {} \;




## give the newly created files/directories the group of the parent directory
## e.g. the laravel group
sudo find ./bootstrap/cache -type d -exec chmod g+s {} \;
sudo find ./storage -type d -exec chmod g+s {} \;


## let newly created files/directories inherit the default owner
## permissions up to maximum permission of rwx e.g. new files get 664,
## folders get 775
sudo setfacl -R -d -m g::rwx ./storage
sudo setfacl -R -d -m g::rwx ./bootstrap/cache


## Reboot so group file permissions refresh (required on Debian and Centos)
sudo shutdown now -r


## optional: enable any daemons we disabled like Apache & Queues

纯粹出于调试的目的,我发现将日志分割成 cli/web + 用户是有益的,所以我稍微修改了 Sam Wilson 的答案。我的用例是在它自己的用户下运行的队列,因此它有助于区分使用 cli 的编写器用户(例如单元测试)和队列守护进程。

$app->configureMonologUsing(function(MonologLogger $monolog) {
$processUser = posix_getpwuid(posix_geteuid());
$processName= $processUser['name'];


$filename = storage_path('logs/laravel-'.php_sapi_name().'-'.$processName.'.log');
$handler = new MonologHandlerRotatingFileHandler($filename);
$monolog->pushHandler($handler);
});

我发现最好的方法是,抖动建议,http://fideloper.com/laravel-log-file-name,您可以设置幼鱼日志配置没有触摸日志类。 有不同的名称为控制台程序和 Http 程序,我认为,是最好的解决方案。

Laravel 5.4

日志: : getMonolog ()-> popHandler () ; UseDailyFiles (Storage _ path (’/Log/laravel-’) . get _ current _ user () .’. Log’) ;

添加到 AppServiceProvider中的 boot函数

Laravel 5.5

将此代码添加到 bootstrap/app.php:

$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, 30));
$handler->setFilenameFormat('laravel-{date}-{filename}', 'Y-m-d');
$formatter = new \Monolog\Formatter\LineFormatter(null, null, true, true);
$formatter->includeStacktraces();
$handler->setFormatter($formatter);
});
  • 它将存储这样的文件: laravel-2018-01-27-cli-raph.loglaravel-2018-01-27-fpm-cgi-raph.log,这是更具可读性。
  • 保留新行(作为缺省 Laravel 行为)
  • 它与 Laravel 日志查看器协同工作

Laravel 5.6

你为你的记录器 必须创建一个类:

<?php


namespace App;


use Monolog\Logger as MonologLogger;


class Logger {
public function __invoke(array $config)
{
$monolog = new MonologLogger('my-logger');
$filename = storage_path('logs/' . php_sapi_name() . '-' . posix_getpwuid(posix_geteuid())['name'] . '.log');
$monolog->pushHandler($handler = new \Monolog\Handler\RotatingFileHandler($filename, 30));
$handler->setFilenameFormat('laravel-{date}-{filename}', 'Y-m-d');
$formatter = new \Monolog\Formatter\LineFormatter(null, null, true, true);
$formatter->includeStacktraces();
$handler->setFormatter($formatter);
return $monolog;
}
}

然后,你必须在 config/logging.php中注册:

'channels' => [
'custom' => [
'driver' => 'custom',
'via' => App\Logging\CreateCustomLogger::class,
],
],

与5.5相同的行为:

  • 它将存储这样的文件: laravel-2018-01-27-cli-raph.loglaravel-2018-01-27-fpm-cgi-raph.log,这是更具可读性。
  • 保留新行(作为缺省 Laravel 行为)
  • 它与 Laravel 日志查看器协同工作

我有一个非常简单的方法:

我在 Laravel 5.6上也遇到了同样的问题

config/logging.php我只是更新了每日通道的路径值与 php_sapi_name()在它。

这将为不同的 php _ sapi _ name 创建单独的目录,并将带有时间戳的日志文件放入其特定目录。

'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/' . php_sapi_name() . '/laravel.log'),
'level' => 'debug',
'days' => 7,
]

所以对我来说,

  • 日志文件在 fpm-fcgi目录下创建: 来自网站的日志,owner: www-data
  • 日志文件是在 cli目录下创建的: from the artian command (cronjob) . owner: root

更多关于 Laravel 5.6日志记录的信息: https://laravel.com/docs/5.6/logging

这是我的 config/logging.php文件:

<?php


return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that gets used when writing
| messages to the logs. The name specified in this option should match
| one of the channels defined in the "channels" configuration array.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Drivers: "single", "daily", "slack", "syslog",
|                    "errorlog", "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily'],
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/' . php_sapi_name() . '/laravel.log'),
'level' => 'debug',
'days' => 7,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'level' => 'critical',
],
'syslog' => [
'driver' => 'syslog',
'level' => 'debug',
],
'errorlog' => [
'driver' => 'errorlog',
'level' => 'debug',
],
],
];

Laravel 版本5.6.10及更高版本在 singledaily驱动程序的配置(config/logging.php)中支持 permission元素:

    'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 7,
'permission' => 0664,
],

不需要在引导脚本中使用 Monolog。

具体来说,在 https://github.com/laravel/framework/commit/4d31633dca9594c9121afbbaa0190210de28fed8中添加了支持。

一种非 Laravel 的方法是简单地执行 cronjob 作为 www-data。

例如 https://askubuntu.com/questions/189189/how-to-run-crontab-as-userwww-data

/etc/crontab


*/5 * * * * www-data php /var/www/public/voto_m/artisan top >/dev/null 2>&1

(Laravel 5.6)我最近遇到了同样的问题,我只是简单地设置了一个在 /app/Console/Kernel.php中运行的预定命令。

$schedule->exec('chown -R www-data:www-data /var/www/**********/storage/logs')->everyMinute();

我知道这有点过火了,但是它很有效,从那以后就没有任何问题了。

Laravel 5.8

Laravel5.8允许您在 config/logging.php中设置日志名称。

因此,使用以前的答案和注释,如果您希望同时使用实际 posix 用户名和 php_sapi_name()值来命名日志,那么只需更改日志名称集。使用每日驱动程序允许根据用户/api 组合运行日志轮换,这将确保日志始终由可以修改日志的帐户进行轮换。

我还为 posx 函数添加了一个检查,这些函数可能不存在于您的本地环境中,在这种情况下,日志名只是默认为标准。

假设你使用默认的日志频道‘ daily’,你可以这样修改你的‘ channel’键:

# config/logging.php
'channels' => [
...
'daily' => [
'driver' => 'daily',
'path'   => storage_path(
function_exists('posix_getpwuid')
&& function_exists('posix_geteuid')
? 'logs/laravel'
. '-' . php_sapi_name()
. '-' . posix_getpwuid(posix_geteuid())['name']
. '.log'
: 'logs/laravel.log'),
'level'  => 'debug',
'days'   => 15,
],
...

这将导致日志名称对于每个组合(如 laravel-cli-sfscs-2019-05-15.loglaravel-apache2handler-apache-2019-05-15.log)应该是唯一的,具体取决于您的访问点。

您可以简单地更改您的工匠命令中的日志文件的权限:

$path = storage_path('log/daily.log');
chown($path, get_current_user());

其中 Get _ current _ user ()将返回当前脚本的用户。

换句话说,即使将脚本初始化为 root用户,daily.log始终将 www-data作为其所有者。

这个解决方案肯定可以在 Laravel V5.1-V6.x 上运行

这个错误的原因:

  • 主要原因是许可问题
  • 根目录中找不到环境变量或 .env文件
  • PHP 扩展问题
  • 数据库有问题

解决方案:

  • 设置正确的权限:
    • 运行这些命令(Ubuntu/Debian)
find /path/to/your/root/dir/ -type f -exec chmod 644 {} \;
find /path/to/your/root/dir/ -type d -exec chmod 755 {} \;


chown -R www-data:www-data /path/to/your/root/dir/


chgrp -R www-data storage bootstrap/cache
chmod -R ug+rwx storage bootstrap/cache
  • 如果不存在 。 env文件,则通过 touch .env创建一个并粘贴环境变量,然后运行
   php artisan key:generate
php artisan cache:clear
php artisan config:clear
composer dump-autoload
php artisan migrate //only if not already migrated

如果您正在使用 Laravel 使者,以下是在 Linux 中使用 ACL 的一个可能的修复方法:

1. 首先,在服务器上运行具有 root权限的以下脚本:

在这两个脚本中,您都需要按照下面的指示替换变量:

  • \{\{ MASTER _ PATH }} : 虚拟主机目录的路径(例如,包含应用程序的文件夹 >)。
  • \{\{ WEB _ SERVER _ USER }} : WEB 服务器使用的用户。
  • \{\{ DEPLOYMENT _ USER }} : 运行部署脚本的用户。
#!/bin/bash


DIRS="storage current/bootstrap/cache"
MASTER_PATH=\{\{MASTER_PATH}}


if [ -d $MASTER_PATH ]; then
cd $MASTER_PATH
for p in `ls $MASTER_PATH`; do
if [ -d $MASTER_PATH/$p ]; then
cd $MASTER_PATH/$p
echo "Project: $p -> $MASTER_PATH/$p"
for i in $DIRS; do
echo "- directory: $i"
if [ -d $i ]; then
echo "-- checking ACL..."
HAS_ACL=`getfacl -p $i | grep "^user:\{\{WEB_SERVER_USER}}:.*w" | wc -l`
if [  $HAS_ACL -eq 0 ]; then
echo "--- applying $i"
setfacl -L -R -m u:\{\{WEB_SERVER_USER}}:rwX -m u:\{\{DEPLOYMENT_USER}}:rwX $i
setfacl -dL -R -m u:\{\{WEB_SERVER_USER}}:rwX -m u:\{\{DEPLOYMENT_USER}}:rwX $i
else
echo "--- skipping $i"
fi
fi
done
echo "--------------"
fi
done
else
echo "No $MASTER_PATH - skipping overall"
fi

2. 在「启动新版本」 > 「此行动前」下,为使者设定以下部署挂钩

PROJECT_DIRS="storage"
RELEASE_DIRS="bootstrap/cache"
 

cd \{\{ project }}
 

for i in $PROJECT_DIRS; do
if [ -d $i ]; then
HAS_ACL=`getfacl -p $i | grep "^user:\{\{WEB_SERVER_USER}}:.*w" | wc -l`
if [  $HAS_ACL -eq 0 ]; then
echo "ACL set for directory \{\{project}}/$i"
setfacl -L -R -m u:\{\{WEB_SERVER_USER}}:rwX -m u:\{\{DEPLOYMENT_USER}}:rwX $i
setfacl -dL -R -m u:\{\{WEB_SERVER_USER}}:rwX -m u:\{\{DEPLOYMENT_USER}}:rwX $i
fi
fi
done
 

cd \{\{ release }}
 

for i in $RELEASE_DIRS; do
if [ -d $i ]; then
HAS_ACL=`getfacl -p $i | grep "^user:\{\{WEB_SERVER_USER}}:.*w" | wc -l`
if [  $HAS_ACL -eq 0 ]; then
echo "ACL set for directory \{\{project}}/$i"
setfacl -L -R -m u:\{\{WEB_SERVER_USER}}:rwX -m u:\{\{DEPLOYMENT_USER}}:rwX $i
setfacl -dL -R -m u:\{\{WEB_SERVER_USER}}:rwX -m u:\{\{DEPLOYMENT_USER}}:rwX $i
fi
fi
done

3. 重新部署应用程序

现在重新部署您的应用程序,它应该可以继续工作。

注意: 在 1.中定义的脚本应该在每次向计算机添加新项目时运行。

cd /path/to/project
chown -R www-data:root .
chmod -R g+s .