Symfony 3.4 logger service
As stated in Symfony 3.4, the logger
service provided by the MonologBundle
and all other services, are set to private by default. [sic]
To workaround the issue, the recommended method is to use Dependency Injection.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'); }}
Source code Reference: https://github.com/symfony/monolog-bundle/blob/v3.1.0/Resources/config/monolog.xml#L17
For service definitions Dependency Injection is available when autowire
is enabled. [sic]
#app/config/services.ymlservices: # 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
Then to Inject the Dependency into the service, you add the type hint for the argument to the constructor.
namespace AppBundle\Service;use Psr\Log\LoggerInterface;class MyService{ private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } }
if autowire
is disabled, you can manually define your services to inject the logger alias.
#app/config/services.ymlservices: AppBundle\Service\MyService: arguments: ['@logger'] public: true
Alternatively, to force the logger alias to be publicly accessible from the container, you can re-declare the service alias in your application services config.
#app/config/services.ymlservices: #... logger: alias: 'monolog.logger' public: true
Instead of overriding the value in the configuration, you can also set logger as a public service in a compiler pass. https://symfony.com/doc/4.4/service_container/compiler_passes.html
Symfony Flex
// src/Kernel.phpnamespace 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 Bundle
// src/AppBundle/AppBundle.phpnamespace 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.phpnamespace 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); }}
$this->container->get('logger')
fails because logger is now (as of 3.2) marked as a private service, all services are private by default, that means that these services can not be returned from the container, and must instead be dependency injected (The class constructor must take the logger as a parameter and become a property of the class to be accessible), or marked as public in the service configuration, and since the logger is a symfony component, the service configuration is within the symfony project, you'd have to copy the logger configuration from symfony to your project service configuration and add public: true
, to access the logger instance from the container.