Symfony 3.4 日志记录服务

15
当我调用logger服务在日志文件中获取信息消息时,它可以工作,但是会写入以下消息到日志文件:

php.INFO: 用户已过时:自从Symfony 3.2开始,从容器中获取“logger”服务已弃用并将在4.0中失败。您应该使服务公开,或停止直接使用容器并改用依赖注入。{"exception":"[object] (ErrorException(code: 0): 用户已过时:自从Symfony 3.2开始,从容器中获取“logger”服务已弃用,并在4.0中将失败。您应该使服务公开,或停止直接使用容器并改用依赖注入。/home/****/###/PROJECT/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php中的at)"}[]

我的Symfony版本为3.4.1。

没有你的代码,我无法为你提供具体帮助,但错误信息告诉了你出了什么问题。Symfony服务和依赖注入在3.2和3.4之间发生了重大变化。https://symfony.com/doc/current/service_container/3.3-di-changes.html - MEmerson
抱歉,在代码中我获取了日志记录器服务并调用了信息方法。 - A.Seddighi
所以不要这样做。考虑更新你的问题,附上一些代码,展示你从哪里获取了记录器。也许我们可以建议一些更改。 - Cerad
你能分享一下代码在使用 ->get("logger") 时出错的部分吗? - Tomas Votruba
2个回答

30

正如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

或者,为了强制使记录器别名从容器中公开访问,您可以在应用程序服务配置中重新声明服务别名。

#app/config/services.yml

services:

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

除了在配置中覆盖值之外,您还可以将记录器设置为编译器传递中的公共服务。

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

你知道命令的替代方法吗?我尝试过 new ConsoleLogger($output),但这并不包括有用的上下文信息。 - Adambean
@Adambean 同样的过程,只需将 LoggerInterface $logger 传递给您的命令的 __construct。请参阅:如何将命令定义为服务 否则,您具体想要实现什么? - Will B.
我确实尝试过,但是一旦存在构造函数,该命令将不再可见。我认为这是因为我的命令扩展自抽象的“CoreCommand”,在其中放置了我的常见命令功能。然而,使用Symfony\Component\Console\Logger\ConsoleLogger类,我可以在我的核心命令的preExecute()函数中运行$this->log = new ConsoleLogger($this->output);来实现类似的结果。(消息更短/更干净,但缺少上下文数据。) - Adambean

2

$this->container->get('logger') 运行失败,因为在3.2版本中,logger被标记为私有服务,所有服务默认都是私有的,这意味着这些服务不能从容器中返回,必须通过依赖注入(类构造函数必须将logger作为参数,并成为类的属性以便访问),或者在服务配置中标记为public。由于logger是Symfony组件,所以服务配置在Symfony项目中,您需要将logger配置从Symfony复制到您的项目服务配置中,并添加public: true,才能从容器中访问logger实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接