Symfony 3.4 记录器服务

Symfony 3.4 logger service

当我调用 logger 服务时,在日志文件中获取了这条信息消息,它工作正常,但在日志文件中写入了这条消息:

php.INFO: User Deprecated: The "logger" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. {"exception":"[object] (ErrorException(code: 0): User Deprecated: The \"logger\" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. at /home/****/###/PROJECT/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php:275)"} []

我的 symfony 版本:3.4.1

$this->container->get('logger') 失败,因为记录器现在(从 3.2 开始)被标记为私有服务,默认情况下所有服务都是私有的,这意味着这些服务不能从容器中返回,必须改为依赖注入(class构造函数必须将logger作为参数,成为class的属性才能访问),或者在服务配置中标记为public,由于记录器是一个 symfony 组件,服务配置在 symfony 项目中,您必须将记录器配置从 symfony 复制到您的项目服务配置并添加 public: true,以从容器访问记录器实例.

如 Symfony 3.4 所述,MonologBundle 提供的 logger 服务和所有其他服务默认设置为私有。 [sic]

要解决此问题,推荐的方法是使用依赖注入。 http://symfony.com/doc/3.4/logging.html

namespace AppBundle\Controller;

use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
     public function indexAction(LoggerInterface $logger)
     {
        $logger->info('Your Message');
     }
}

源码参考:https://github.com/symfony/monolog-bundle/blob/v3.1.0/Resources/config/monolog.xml#L17

对于服务定义,启用 autowire 时依赖注入可用。 [sic]

#app/config/services.yml

services:
    # default configuration for services in *this* file
    _defaults:
        # automatically injects dependencies in your services
        autowire: true
        # automatically registers your services as commands, event subscribers, etc.
        autoconfigure: true
        # this means you cannot fetch services directly from the container via $container->get()
        # if you need to do this, you can override this setting on individual services
        public: false

    # makes classes in src/AppBundle available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    AppBundle\:
        resource: '../../src/AppBundle/*'
        # you can exclude directories or files
        # but if a service is unused, it's removed anyway
        exclude: '../../src/AppBundle/{Entity,Repository,Tests}'

    #enables dependency injection in controller actions
    AppBundle\Controller\:
        resource: '../../src/AppBundle/Controller'
        public: true
        tags: ['controller.service_arguments']

   #all of your custom services should be below this line
   #which will override the above configurations

    #optionally declare an individual service as public
    #AppBundle\Service\MyService: 
    #    public: true

    #alternatively declare the namespace explicitly as public
    #AppBundle\Service\:
    #    resource: '../../src/AppBundle/Service/*'
    #    public: true

然后将依赖项注入服务,将参数的类型提示添加到构造函数。

namespace AppBundle\Service;

use Psr\Log\LoggerInterface;

class MyService
{

    private $logger;
    
    public function __construct(LoggerInterface $logger)
    {
         $this->logger = $logger;
    }
   
}

如果 autowire 被禁用,您可以手动定义您的服务以注入记录器别名。

#app/config/services.yml

services:

    AppBundle\Service\MyService:
        arguments: ['@logger']
        public: true

或者,要强制记录器别名public只能从容器访问,您可以在应用程序服务配置中重新声明服务别名。

#app/config/services.yml

services:

    #...
    
    logger:
        alias: 'monolog.logger'
        public: true

除了覆盖配置中的值,您还可以在编译器传递中将记录器设置为 public 服务。 https://symfony.com/doc/4.4/service_container/compiler_passes.html

Symfony Flex

// src/Kernel.php
namespace App;

use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;

class Kernel extends BaseKernel implements CompilerPassInterface
{
    use MicroKernelTrait;

     public function process(ContainerBuilder $container)
     {
        // in this method you can manipulate the service container:
        // for example, changing some container service:
        $container->getDefinition('logger')->setPublic(true);
    }

}

Symfony 捆绑包

// src/AppBundle/AppBundle.php
namespace AppBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use AppBundle\DependencyInjection\Compiler\CustomPass;

class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(new CustomPass());
    }
}
// src/AppBundle/DependencyInjection/Compiler/CustomPass.php
namespace AppBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class CustomPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $container->getDefinition('logger')->setPublic(true);
    }
}