Laravel每日日志创建权限错误


111

我有一个使用php artisan(root)运行的脚本用户)有时它会导致在Apache www-data用户执行操作之前创建每日日志文件-这意味着当真实用户使用我的Web应用程序时,文件夹权限错误:

无法打开流:权限被拒绝

我将权限更改回 每次都 www-data,但是我想通过始终使用正确的权限创建日志文件来解决此问题。

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

我们还考虑过将php artisan包装在另一个脚本中,以确保它始终以www-data凭据运行,但实际上,我们要执行的操作是root不应允许apache过程。

还有其他建议吗?


每天午夜(当然,在正确的用户下)将cron作业设置到touch新的日志文件中。
Ben Harold

@BenHarold谢谢,我们已经考虑过了,但是我不想再涉及更多脚本。
NiRR 2014年

2
在这种情况下,您需要以php artisan要创建日志文件的用户身份运行。
Ben Harold

@BenHarold再次感谢,我们也考虑了这一点,这可能是最好的方法,但是我已经更新了问题以解释为什么这也不理想。
NiRR 2014年

2
什么工作对我来说是与执行的cron为www数据的用户sudo crontab -u www-data -e
无Llisterri

Answers:


66

让我们从常量开始。

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

可以安全地假定每天都要执行此命令。

解决方案1:

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

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-'.get_current_user().'.log');

如果您的www数据用户要创建错误日志,则会导致:storage/logs/laravel-www-data-2015-4-27.log

如果您的root用户将创建错误日志,则将导致:storage/logs/laravel-root-2015-4-27.log

解决方案2:

在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更好,因为它可以按用户描述您的日志,因此不会发生任何错误。

编辑:杰森指出,get_current_user()返回脚本的所有者名称。因此,对于要应用的解决方案1,chown您的工匠类文件已输入所需的用户名。


12
请注意,get_current_user()返回当前PHP脚本的所有者(根据php.net),而不是当前正在运行该脚本的用户。我php_sapi_name()改用了它,它给出了将以不同用户身份运行的php处理程序的名称(例如,apache或cli)。
杰森

1
我是否可以建议同时使用执行脚本的用户名和php_sapi_name的建议,因为许多用户可以从CLI执行Laravel,例如,一些DBA访问您的服务器,或者您可能希望Laravel CRON以apache的身份运行。使用posix_getpwuid(posix_geteuid())['name']获取执行此脚本的进程名称。请参阅下面的完整文章。
安德鲁

需要针对最新的Laravel版本进行更新:v5 +
Andrew

105

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

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

无需在引导脚本中摆弄Monolog。

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


8
这应该在官方文档中!
odupont

3
此答案中缺少撇号。它应该是“ permission” =>“ 0664”。那么这个答案就很好!
Phil

2
@Phil Nope-这只是Monologs流处理程序的包装,该处理程序接受int的权限。Monolog包装php.net/manual/en/function.chmod.php-注意,必须使用前导0来确保它是八进制值
Chris

7
'permission' => 0664为我工作(无引号)
Syclone

2
@Friedrich(如果您使用“ root”作为文件所有者创建日志文件),则可能表明您在设置网络服务器方面存在更大的问题
kjones

62

对于Laravel 5.1,我在底部使用了以下内容bootstrap/app.php(如docs所述):

/**
 * 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);
});

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


1
我非常喜欢这个答案,因为1)它已更新为5.1,2)在文档中使用了一种方法来扩展日志行为。
迪伦·皮尔斯

太好了,不需要额外的前向闪光灯,但仍然可以使用。它应该显示为... $ filename = storage_path('logs / laravel-'。php_sapi_name()。'。log');
Andrew

我是否可以建议同时使用执行脚本的用户名和php_sapi_name的建议,因为许多用户可以从CLI执行Laravel,例如,一些DBA访问您的服务器,或者您可能希望Laravel CRON以apache的身份运行。使用posix_getpwuid(posix_geteuid())['name']获取执行此脚本的进程名称。请参阅下面的完整文章。
安德鲁

1
如何在Laravel 5.6中使用它?因为Laravel 5.6具有全新的日志记录系统。
Hamed Kamrava '18

26

为此,您应该在文件和目录上使用高级ACL。setfacl这将是您的答案。如果您想授予www-data用户写特定目录中文件的权限,可以这样:

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

发出此命令后,无论谁创建rwxwww-data用户,都将对所有文件的www-data用户设置权限/my/folder/。请参阅这个问题以供参考。另外,您可以检查文档中的setfacl

让我知道是否有帮助。


3
以下命令对我有用:setfacl -d -m g:www-data:rw /full/path/to/laravel/storage/logs后跟php artisan cache:clearcomposer dump-autoload
Sawny

17

我的工作方式非常简单:

我在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
  • 日志文件是在cliartisan命令(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',
        ],
    ],
];

很好...我们的解决方案更干净..我正在对其进行测试
新浪Miandashti

1
正如另一条评论所指出的,日志只是故事的一部分。有编译的视图,数据缓存,预缓存的源代码,其中任何一个都可以由Web或cli用户创建为本地文件。
杰森

2
如果您使用来缓存配置artisan config:cache,则此方法不起作用,因为它将使用cli SAPI创建配置缓存,该缓存将用于CLI和Web请求。
leeb

1
这对我有用,尝试get_current_user不起作用,但php_sapi_name可以起作用(尽管看起来较丑)
Richard Fu

我认为这是最快最好的方法。修改配置不会修改Laravel的基本结构,而只是修改配置。
William Prigol Lopes

12

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

典型方案是:

  • bootstrap / cache / compiled.php文件由apache用户创建,但在执行composer install命令时无法被composer用户编辑

  • apache用户创建的缓存,无法使用composer用户清除

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

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

TL; 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);
}); 

这是非常好的。是您的configureMonologUsing代码仍然是必要的,虽然,一旦你运行setfacl命令?
jeff-h

7

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);
});


5

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.log并且laravel-2018-01-27-fpm-cgi-raph.log更具可读性。
  • 保留新行(作为默认的Laravel行为)
  • 它与Laravel Log Viewer一起使用

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.log并且laravel-2018-01-27-fpm-cgi-raph.log更具可读性。
  • 保留新行(作为默认的Laravel行为)
  • 它与Laravel Log Viewer一起使用

最佳答案!荣誉
Shahid Karimi

4

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组中,以便运行artisan命令仍可以写入日志。

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

umask(0002);

如果执行此操作,则chmod上面的行无意义。将umask设置为此,PHP(以及Laravel)创建的任何新文件将仅被屏蔽其权限,以便“其他”用户将不具有写入权限。这意味着目录将以开头rwxrwxr-x,文件将以开头rw-rw-r--。因此,如果www-data运行的是PHP,则默认情况下,该用户的主组()中的任何人都可以写入该缓存和日志文件www-data


4

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

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

我知道这有点矫kill过正,但它的作用就像一种魅力,此后再没有任何问题。


有用 ?是的,但这是最佳做法吗?我觉得不是。
Pablo Papalardo

3

Laravel 5.4

\Log::getMonolog()->popHandler(); \Log::useDailyFiles(storage_path('/logs/laravel-').get_current_user().'.log');

添加到boot功能AppServiceProvider


1

Laravel 5.8

Laravel 5.8允许您在中设置日志名称config/logging.php

因此,使用先前的答案和注释,如果要使用实际的posix用户名和php_sapi_name()值来命名日志,则只需更改日志名称集即可。使用每日驱动程序允许按用户/ API组合运行日志轮换,这将确保始终由可以修改日志的帐户轮换日志。

我还为本地环境中可能不存在的posix函数添加了检查,在这种情况下,日志名称仅默认为标准。

假设您使用默认的日志通道“每日”,则可以这样修改“通道”键:

# 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取决于您的访问点)应该是唯一的。


0

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

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

其中get_current_user()将返回当前脚本的用户。

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


0

如果您使用的是Laravel Envoyer,则可以在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.在“激活新版本”>“此操作之前”下,在envoyer上设置以下部署挂钩

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.中定义的脚本。



-1

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


-1

该解决方案一定会在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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.