PHP 依赖注入中如何为不同类配置 PSR LoggerInterface?
数据阿明
结论
通常可以在类的构造函数中注入 Psr\Log\LoggerInterface,再由应用组合根(例如依赖注入容器配置)创建并配置 logger。PSR-3 定义了日志接口,没有定义 LoggerFactoryInterface。是否需要自定义工厂,要看不同类是否确实需要不同的 logger 配置。
示例代码通过 LoggerFactory::getLogger(__CLASS__) 按类名获取 logger。改用依赖注入后,可以在容器配置或自定义工厂中决定使用哪个 logger、如何设置阈值,让业务类只依赖日志接口。
所有类使用相同配置时
直接注入一个实现 LoggerInterface 的实例:
use Psr\Log\LoggerInterface;
final class ReportService
{
public function __construct(
private LoggerInterface $logger,
) {
}
public function generate(): void
{
$this->logger->info('开始生成报告');
}
}
容器负责提供这个实例。绑定方式取决于使用的依赖注入容器和项目采用的日志库。
不同类需要不同阈值时
先确认是否需要按类配置。如果差异涉及日志通道、处理器或阈值,可以在容器配置中为不同服务创建不同的 logger 实例,再分别注入。以下配置仅作示意,具体语法需按所用容器调整:
$container->set(ReportService::class, function ($container) {
return new ReportService(
$container->get('logger.reports'),
);
});
$container->set(ImportService::class, function ($container) {
return new ImportService(
$container->get('logger.imports'),
);
});
logger.reports 和 logger.imports 是应用配置的服务名,不是 PSR-3 规定的名称。阈值的配置方式取决于所选日志库的实现。
如果需要按类名动态获取 logger,可以定义项目自己的工厂接口:
use Psr\Log\LoggerInterface;
interface LoggerFactoryInterface
{
public function getLogger(string $name): LoggerInterface;
}
在应用层实现该接口,再通过容器注入。消费者可以这样使用:
final class ReportService
{
private LoggerInterface $logger;
public function __construct(LoggerFactoryInterface $loggerFactory)
{
$this->logger = $loggerFactory->getLogger(self::class);
}
}
工厂可以根据传入的名称返回不同配置的 logger。这个接口由项目定义,PSR-3 没有规定此类工厂接口或其行为。
选择方式时的注意事项
- 如果差异只在日志上下文中的类名,通常不必为每个类创建不同的 logger。日志库或处理器可能可以补充上下文,具体取决于实现。
- 如果不同类确实需要不同的阈值、处理器或输出位置,应在应用配置中明确规则,并确保服务取得对应配置的 logger。
- 避免让业务类直接创建具体日志库对象或自行决定全局配置,以免创建逻辑散落在业务代码中。
- 对配置固定的依赖,构造函数注入
LoggerInterface更合适;需要按名称动态选择 logger 时,可以使用自定义工厂。
备注:内容仅供参考。
