Laravel 7 在作业中动态设置日志路径 class
Laravel 7 set log path dynamically in Job class
我正在 Laravel 7.3 上同时使用多个 运行 作业构建项目。
我需要让每个作业将日志写入不同的每日轮换文件。日志文件的名称应基于作业正在处理的模型。
问题是我找不到智能解决方案。
我尝试过的:
1) 在 config/logging.php
中创建多个频道。
按预期工作,但目前大约有 50 个不同的职位,而且数量还在不断增加。方法丑陋且难以维护。
2) 设置 Config(['logging.channels.CUSTOMCHANNEL.path' => storage_path('logs/platform/'.$this->platform->name.'.log')]);
。
乱用 Config 变量是个坏主意,因为很多作业 运行 一次。结果,来自一个作业的消息经常被写入另一个作业日志中。
3) 使用 Log::useDailyFiles()
似乎从 laravel 5.5 或 5.6 开始就停止工作了。刚收到错误 Call to undefined method Monolog\Logger::useDailyFiles()
。关于如何在 laravel 7 中工作有什么想法吗?
4) 在 config/logging.php
中对通道使用 tap
参数。
Example in laravel docs
不知道如何将模型名称传递到 CustomizeFormatter 以设置文件名。
我几乎可以肯定有智能解决方案,我只是遗漏了一些东西。
有什么建议吗?谢谢!
您可以继承日志管理器以允许动态配置
<?php
namespace App\Log;
use Illuminate\Support\Str;
use Illuminate\Log\LogManager as BaseLogManager;
class LogManager extends BaseLogManager
{
/**
* Get the log connection configuration.
*
* @param string $name
* @return array
*/
protected function configurationFor($name)
{
if (!Str::contains($name, ':')) {
return parent::configurationFor($name);
}
[$baseName, $model] = explode(':', $name, 2);
$baseConfig = parent::configurationFor($baseName);
$baseConfig['path'] = ...; //your logic
return $baseConfig;
}
}
类似Laravel的日志服务提供者,除了这个可以完全替换
<?php
namespace App\Log;
use Illuminate\Support\ServiceProvider;
class LogServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('log', function ($app) {
return new LogManager($app);
});
}
}
编辑:我刚刚看到 Laravel 的日志服务提供者在 config/app.php
中丢失了,这是因为应用程序 "hard-loaded"。您仍然可以通过继承应用程序本身来替换它
<?php
namespace App\Foundation;
use App\Log\LogServiceProvider;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Routing\RoutingServiceProvider;
use Illuminate\Foundation\Application as BaseApplication;
class Application extends BaseApplication
{
/**
* Register all of the base service providers.
*
* @return void
*/
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
}
最后在 bootstrap/app.php
中,将 Illuminate\Foundation\Application
替换为 App\Foundation\Application
例如,如果你试试这个
app('log')->channel('single:users')->debug('test');
如果您的解析逻辑是 ,Laravel 将使用 single
频道的配置并写入 users.log
$baseConfig['path'] = $model + '.log';
我得到了一个自 Laravel 4 以来一直在使用的解决方案,尽管它不遵循 'Laravel' 的做事方式。
class UserTrackLogger
{
/**
* @var $full_path string
*/
protected $full_path;
/**
* @var $tenant string
*/
protected $tenant;
/**
* @var $user User
*/
protected $user;
/**
* @var $request Request
*/
protected $request;
public static function log(string $message, Request $request, User $user, array $data = []): void
{
/** @noinspection PhpVariableNamingConventionInspection */
$userTrack = new static($request, $user);
$userTrack->write($message, $data);
}
protected function __construct(Request $request, User $user)
{
$this->request = $request;
$this->user = $user;
$this->tenant = app()->make('tenant')->tenant__name;
$path = storage_path() . "/logs/{$this->tenant}/users";
$filename = $this->user->username_with_name;
$this->full_path = Formatter::formatPath("{$path}/{$filename}.log");
self::makeFolder($this->full_path);
}
protected function write(string $message, array $data = []): void
{
$formatter = $this->getFormat();
$record = [
'message' => $message,
'context' => $data,
'extra' => [],
'datetime' => date(Utility::DATETIME_FORMAT_DEFAULT),
'level_name' => 'TRACK',
'channel' => '',
];
file_put_contents($this->full_path, $formatter->format($record), FILE_APPEND);
}
protected function getFormat(): FormatterInterface
{
$ip = $this->request->getClientIp();
$method = strtoupper($this->request->method());
$format = "[%datetime%][{$this->tenant}][{$this->user->username}][{$this->user->name}]: $ip $method %message% %context%\n";
return new LineFormatter($format, null, true);
}
protected static function makeFolder(string $full_path): bool
{
$path = dirname($full_path);
if ( !is_dir($path) ) {
return mkdir($path, 0755, true);
}
return false;
}
}
当我想记录一些东西时,我会 UserTrackLogger::log($request->fullUrl(), $request, $user, $data);
我建议创建一个与此类似但扩展了 RotatingFileHandler
.
的记录器
我正在 Laravel 7.3 上同时使用多个 运行 作业构建项目。 我需要让每个作业将日志写入不同的每日轮换文件。日志文件的名称应基于作业正在处理的模型。
问题是我找不到智能解决方案。
我尝试过的:
1) 在 config/logging.php
中创建多个频道。
按预期工作,但目前大约有 50 个不同的职位,而且数量还在不断增加。方法丑陋且难以维护。
2) 设置 Config(['logging.channels.CUSTOMCHANNEL.path' => storage_path('logs/platform/'.$this->platform->name.'.log')]);
。
乱用 Config 变量是个坏主意,因为很多作业 运行 一次。结果,来自一个作业的消息经常被写入另一个作业日志中。
3) 使用 Log::useDailyFiles()
似乎从 laravel 5.5 或 5.6 开始就停止工作了。刚收到错误 Call to undefined method Monolog\Logger::useDailyFiles()
。关于如何在 laravel 7 中工作有什么想法吗?
4) 在 config/logging.php
中对通道使用 tap
参数。
Example in laravel docs 不知道如何将模型名称传递到 CustomizeFormatter 以设置文件名。
我几乎可以肯定有智能解决方案,我只是遗漏了一些东西。 有什么建议吗?谢谢!
您可以继承日志管理器以允许动态配置
<?php
namespace App\Log;
use Illuminate\Support\Str;
use Illuminate\Log\LogManager as BaseLogManager;
class LogManager extends BaseLogManager
{
/**
* Get the log connection configuration.
*
* @param string $name
* @return array
*/
protected function configurationFor($name)
{
if (!Str::contains($name, ':')) {
return parent::configurationFor($name);
}
[$baseName, $model] = explode(':', $name, 2);
$baseConfig = parent::configurationFor($baseName);
$baseConfig['path'] = ...; //your logic
return $baseConfig;
}
}
类似Laravel的日志服务提供者,除了这个可以完全替换
<?php
namespace App\Log;
use Illuminate\Support\ServiceProvider;
class LogServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('log', function ($app) {
return new LogManager($app);
});
}
}
编辑:我刚刚看到 Laravel 的日志服务提供者在 config/app.php
中丢失了,这是因为应用程序 "hard-loaded"。您仍然可以通过继承应用程序本身来替换它
<?php
namespace App\Foundation;
use App\Log\LogServiceProvider;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Routing\RoutingServiceProvider;
use Illuminate\Foundation\Application as BaseApplication;
class Application extends BaseApplication
{
/**
* Register all of the base service providers.
*
* @return void
*/
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
}
最后在 bootstrap/app.php
中,将 Illuminate\Foundation\Application
替换为 App\Foundation\Application
例如,如果你试试这个
app('log')->channel('single:users')->debug('test');
如果您的解析逻辑是 ,Laravel 将使用 single
频道的配置并写入 users.log
$baseConfig['path'] = $model + '.log';
我得到了一个自 Laravel 4 以来一直在使用的解决方案,尽管它不遵循 'Laravel' 的做事方式。
class UserTrackLogger
{
/**
* @var $full_path string
*/
protected $full_path;
/**
* @var $tenant string
*/
protected $tenant;
/**
* @var $user User
*/
protected $user;
/**
* @var $request Request
*/
protected $request;
public static function log(string $message, Request $request, User $user, array $data = []): void
{
/** @noinspection PhpVariableNamingConventionInspection */
$userTrack = new static($request, $user);
$userTrack->write($message, $data);
}
protected function __construct(Request $request, User $user)
{
$this->request = $request;
$this->user = $user;
$this->tenant = app()->make('tenant')->tenant__name;
$path = storage_path() . "/logs/{$this->tenant}/users";
$filename = $this->user->username_with_name;
$this->full_path = Formatter::formatPath("{$path}/{$filename}.log");
self::makeFolder($this->full_path);
}
protected function write(string $message, array $data = []): void
{
$formatter = $this->getFormat();
$record = [
'message' => $message,
'context' => $data,
'extra' => [],
'datetime' => date(Utility::DATETIME_FORMAT_DEFAULT),
'level_name' => 'TRACK',
'channel' => '',
];
file_put_contents($this->full_path, $formatter->format($record), FILE_APPEND);
}
protected function getFormat(): FormatterInterface
{
$ip = $this->request->getClientIp();
$method = strtoupper($this->request->method());
$format = "[%datetime%][{$this->tenant}][{$this->user->username}][{$this->user->name}]: $ip $method %message% %context%\n";
return new LineFormatter($format, null, true);
}
protected static function makeFolder(string $full_path): bool
{
$path = dirname($full_path);
if ( !is_dir($path) ) {
return mkdir($path, 0755, true);
}
return false;
}
}
当我想记录一些东西时,我会 UserTrackLogger::log($request->fullUrl(), $request, $user, $data);
我建议创建一个与此类似但扩展了 RotatingFileHandler
.