Laravel多域名场景下运行时设置默认日志通道的优化方案
更优实现方案
方案1:分层统一配置(适配不同入口类型)
针对HTTP请求、命令、队列Job三类入口做统一处理,避免重复编写切换代码:
1.1 HTTP请求:用中间件自动绑定
创建SetDomainLogChannel中间件,根据控制器命名空间判断所属域名模块,自动切换日志通道:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class SetDomainLogChannel { public function handle(Request $request, Closure $next) { $controllerAction = $request->route()->getAction(); $namespace = $controllerAction['namespace'] ?? ''; // 假设域名模块命名空间为App\Domains\App1、App\Domains\App2 if (str_contains($namespace, 'App\Domains\App1')) { config(['logging.default' => 'app-1']); } elseif (str_contains($namespace, 'App\Domains\App2')) { config(['logging.default' => 'app-2']); } return $next($request); } }
将该中间件注册到对应域名模块的路由组中,无需每个控制器手动设置。
1.2 命令:自定义基类统一处理
给每个域名模块的命令创建专属基类,在基类的handle方法开头统一配置日志通道:
<?php namespace App\Domains\App1\Console; use Illuminate\Console\Command; abstract class App1BaseCommand extends Command { public function handle() { config(['logging.default' => 'app-1']); return $this->handleApp1Command(); } // 子类需实现此方法编写业务逻辑 abstract protected function handleApp1Command(); }
后续该域名下的所有命令都继承这个基类即可。
1.3 队列Job:基类统一配置
和命令逻辑一致,给每个域名模块的Job创建基类:
<?php namespace App\Domains\App1\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; abstract class App1BaseJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle() { config(['logging.default' => 'app-1']); return $this->handleApp1Job(); } abstract protected function handleApp1Job(); }
该域名下的所有Job继承此基类,无需重复配置。
方案2:全局动态日志通道解析
自定义日志逻辑,让Laravel自动根据当前执行的域名模块选择日志通道:
2.1 自定义DomainAwareLogger类
继承Laravel默认Logger,构造时动态判断所属域名并切换通道:
<?php namespace App\Logging; use Illuminate\Log\Logger; use Monolog\Logger as MonologLogger; class DomainAwareLogger extends Logger { public function __construct(MonologLogger $monolog) { parent::__construct($monolog); $channel = $this->determineDomainChannel(); $this->setDefaultDriver($channel); } protected function determineDomainChannel(): string { $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2] ?? []; $class = $caller['class'] ?? ''; if (str_contains($class, 'App\Domains\App1')) { return 'app-1'; } elseif (str_contains($class, 'App\Domains\App2')) { return 'app-2'; } return config('logging.default'); } }
2.2 替换默认日志服务绑定
在AppServiceProvider的register方法中替换默认Logger:
public function register() { $this->app->bind('log', function ($app) { return new \App\Logging\DomainAwareLogger($app['monolog']); }); }
此后全局日志会自动根据执行代码所属域名模块选择对应通道,无需手动干预。
方案3:Monolog处理器实现多文件写入
若需同时保留全局日志和域名独立日志,可通过Monolog处理器动态分流:
// 在config/logging.php的stack通道中添加自定义处理器 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['single'], 'processors' => [\App\Logging\DomainLogProcessor::class], ], // 其他通道配置... ],
创建处理器类:
<?php namespace App\Logging; use Monolog\Handler\StreamHandler; use Monolog\Processor\ProcessorInterface; class DomainLogProcessor implements ProcessorInterface { public function __invoke(array $record): array { $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1] ?? []; $class = $caller['class'] ?? ''; $domain = 'default'; if (str_contains($class, 'App\Domains\App1')) { $domain = 'app-1'; } elseif (str_contains($class, 'App\Domains\App2')) { $domain = 'app-2'; } // 动态添加对应域名的日志文件处理器 $handler = new StreamHandler(storage_path("logs/{$domain}.log")); $record['logger']->pushHandler($handler); return $record; } }
此方案可实现单条日志同时写入全局文件和域名专属文件。
内容的提问来源于stack exchange,提问作者CXJ
相关产品推荐
相关产品推荐

