vendor/symfony/monolog-bundle/DependencyInjection/MonologExtension.php line 965

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bundle\MonologBundle\DependencyInjection;
  11. use Monolog\Attribute\AsMonologProcessor;
  12. use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
  13. use Monolog\Handler\HandlerInterface;
  14. use Monolog\Logger;
  15. use Monolog\Processor\ProcessorInterface;
  16. use Monolog\Processor\PsrLogMessageProcessor;
  17. use Monolog\ResettableInterface;
  18. use Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy;
  19. use Symfony\Bridge\Monolog\Processor\SwitchUserTokenProcessor;
  20. use Symfony\Bridge\Monolog\Processor\TokenProcessor;
  21. use Symfony\Bridge\Monolog\Processor\WebProcessor;
  22. use Symfony\Bundle\FullStack;
  23. use Symfony\Component\Config\FileLocator;
  24. use Symfony\Component\DependencyInjection\Argument\BoundArgument;
  25. use Symfony\Component\DependencyInjection\ChildDefinition;
  26. use Symfony\Component\DependencyInjection\ContainerBuilder;
  27. use Symfony\Component\DependencyInjection\Definition;
  28. use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
  29. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  30. use Symfony\Component\DependencyInjection\Reference;
  31. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  32. use Symfony\Component\HttpKernel\Kernel;
  33. use Symfony\Contracts\HttpClient\HttpClientInterface;
  34. /**
  35.  * MonologExtension is an extension for the Monolog library.
  36.  *
  37.  * @author Jordi Boggiano <j.boggiano@seld.be>
  38.  * @author Christophe Coevoet <stof@notk.org>
  39.  */
  40. class MonologExtension extends Extension
  41. {
  42.     private $nestedHandlers = [];
  43.     private $swiftMailerHandlers = [];
  44.     private function levelToMonologConst($levelContainerBuilder $container)
  45.     {
  46.         if (null === $level || is_numeric($level)) {
  47.             return $level;
  48.         }
  49.         if (defined('Monolog\Logger::'.strtoupper($level))) {
  50.             return constant('Monolog\Logger::' strtoupper($level));
  51.         }
  52.         if ($container->hasParameter($level)) {
  53.             return $this->levelToMonologConst($container->getParameter($level), $container);
  54.         }
  55.         try {
  56.             $logLevel $container->resolveEnvPlaceholders($leveltrue);
  57.         } catch (ParameterNotFoundException $notFoundException) {
  58.             throw new \InvalidArgumentException(sprintf('Could not match "%s" to a log level.'$level));
  59.         }
  60.         if ($logLevel !== '' && $logLevel !== $level) {
  61.             return $this->levelToMonologConst($logLevel$container);
  62.         }
  63.         throw new \InvalidArgumentException(sprintf('Could not match "%s" to a log level.'$level));
  64.     }
  65.     /**
  66.      * Loads the Monolog configuration.
  67.      *
  68.      * @param array            $configs   An array of configuration settings
  69.      * @param ContainerBuilder $container A ContainerBuilder instance
  70.      */
  71.     public function load(array $configsContainerBuilder $container)
  72.     {
  73.         if (class_exists(FullStack::class) && Kernel::MAJOR_VERSION && Logger::API >= 2) {
  74.             throw new \RuntimeException('Symfony 5 is required for Monolog 2 support. Please downgrade Monolog to version 1.');
  75.         }
  76.         $configuration $this->getConfiguration($configs$container);
  77.         $config $this->processConfiguration($configuration$configs);
  78.         if (isset($config['handlers'])) {
  79.             $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  80.             $loader->load('monolog.xml');
  81.             $container->setParameter('monolog.use_microseconds'$config['use_microseconds']);
  82.             $handlers = [];
  83.             foreach ($config['handlers'] as $name => $handler) {
  84.                 $handlers[$handler['priority']][] = [
  85.                     'id' => $this->buildHandler($container$name$handler),
  86.                     'channels' => empty($handler['channels']) ? null $handler['channels'],
  87.                 ];
  88.             }
  89.             $container->setParameter(
  90.                 'monolog.swift_mailer.handlers',
  91.                 $this->swiftMailerHandlers
  92.             );
  93.             ksort($handlers);
  94.             $sortedHandlers = [];
  95.             foreach ($handlers as $priorityHandlers) {
  96.                 foreach (array_reverse($priorityHandlers) as $handler) {
  97.                     $sortedHandlers[] = $handler;
  98.                 }
  99.             }
  100.             $handlersToChannels = [];
  101.             foreach ($sortedHandlers as $handler) {
  102.                 if (!in_array($handler['id'], $this->nestedHandlers)) {
  103.                     $handlersToChannels[$handler['id']] = $handler['channels'];
  104.                 }
  105.             }
  106.             $container->setParameter('monolog.handlers_to_channels'$handlersToChannels);
  107.         }
  108.         $container->setParameter('monolog.additional_channels', isset($config['channels']) ? $config['channels'] : []);
  109.         if (method_exists($container'registerForAutoconfiguration')) {
  110.             if (interface_exists(ProcessorInterface::class)) {
  111.                 $container->registerForAutoconfiguration(ProcessorInterface::class)
  112.                     ->addTag('monolog.processor');
  113.             } else {
  114.                 $container->registerForAutoconfiguration(WebProcessor::class)
  115.                     ->addTag('monolog.processor');
  116.             }
  117.             if (interface_exists(ResettableInterface::class)) {
  118.                 $container->registerForAutoconfiguration(ResettableInterface::class)
  119.                     ->addTag('kernel.reset', ['method' => 'reset']);
  120.             }
  121.             $container->registerForAutoconfiguration(TokenProcessor::class)
  122.                 ->addTag('monolog.processor');
  123.             if (interface_exists(HttpClientInterface::class)) {
  124.                 $handlerAutoconfiguration $container->registerForAutoconfiguration(HandlerInterface::class);
  125.                 $handlerAutoconfiguration->setBindings($handlerAutoconfiguration->getBindings() + [
  126.                     HttpClientInterface::class => new BoundArgument(new Reference('monolog.http_client'), false),
  127.                 ]);
  128.             }
  129.         }
  130.         if (80000 <= \PHP_VERSION_ID && method_exists($container'registerAttributeForAutoconfiguration')) {
  131.             $container->registerAttributeForAutoconfiguration(AsMonologProcessor::class, static function (ChildDefinition $definitionAsMonologProcessor $attribute\Reflector $reflector): void {
  132.                 $tagAttributes get_object_vars($attribute);
  133.                 if ($reflector instanceof \ReflectionMethod) {
  134.                     if (isset($tagAttributes['method'])) {
  135.                         throw new \LogicException(sprintf('AsMonologProcessor attribute cannot declare a method on "%s::%s()".'$reflector->class$reflector->name));
  136.                     }
  137.                     $tagAttributes['method'] = $reflector->getName();
  138.                 }
  139.                 $definition->addTag('monolog.processor'$tagAttributes);
  140.             });
  141.         }
  142.     }
  143.     /**
  144.      * Returns the base path for the XSD files.
  145.      *
  146.      * @return string The XSD base path
  147.      */
  148.     public function getXsdValidationBasePath()
  149.     {
  150.         return __DIR__.'/../Resources/config/schema';
  151.     }
  152.     public function getNamespace()
  153.     {
  154.         return 'http://symfony.com/schema/dic/monolog';
  155.     }
  156.     private function buildHandler(ContainerBuilder $container$name, array $handler)
  157.     {
  158.         $handlerId $this->getHandlerId($name);
  159.         if ('service' === $handler['type']) {
  160.             $container->setAlias($handlerId$handler['id']);
  161.             if (!empty($handler['nested']) && true === $handler['nested']) {
  162.                 $this->markNestedHandler($handlerId);
  163.             }
  164.             return $handlerId;
  165.         }
  166.         $handlerClass $this->getHandlerClassByType($handler['type']);
  167.         $definition = new Definition($handlerClass);
  168.         $handler['level'] = $this->levelToMonologConst($handler['level'], $container);
  169.         if ($handler['include_stacktraces']) {
  170.             $definition->setConfigurator(['Symfony\\Bundle\\MonologBundle\\MonologBundle''includeStacktraces']);
  171.         }
  172.         if (null === $handler['process_psr_3_messages']['enabled']) {
  173.             $handler['process_psr_3_messages']['enabled'] = !isset($handler['handler']) && !$handler['members'];
  174.         }
  175.         if ($handler['process_psr_3_messages']['enabled'] && method_exists($handlerClass'pushProcessor')) {
  176.             $processorId $this->buildPsrLogMessageProcessor($container$handler['process_psr_3_messages']);
  177.             $definition->addMethodCall('pushProcessor', [new Reference($processorId)]);
  178.         }
  179.         switch ($handler['type']) {
  180.         case 'stream':
  181.             $definition->setArguments([
  182.                 $handler['path'],
  183.                 $handler['level'],
  184.                 $handler['bubble'],
  185.                 $handler['file_permission'],
  186.                 $handler['use_locking'],
  187.             ]);
  188.             break;
  189.         case 'console':
  190.             $definition->setArguments([
  191.                 null,
  192.                 $handler['bubble'],
  193.                 isset($handler['verbosity_levels']) ? $handler['verbosity_levels'] : [],
  194.                 $handler['console_formatter_options']
  195.             ]);
  196.             $definition->addTag('kernel.event_subscriber');
  197.             break;
  198.         case 'chromephp':
  199.         case 'firephp':
  200.             $definition->setArguments([
  201.                 $handler['level'],
  202.                 $handler['bubble'],
  203.             ]);
  204.             $definition->addTag('kernel.event_listener', ['event' => 'kernel.response''method' => 'onKernelResponse']);
  205.             break;
  206.         case 'gelf':
  207.             if (isset($handler['publisher']['id'])) {
  208.                 $publisher = new Reference($handler['publisher']['id']);
  209.             } elseif (class_exists('Gelf\Transport\UdpTransport')) {
  210.                 $transport = new Definition("Gelf\Transport\UdpTransport", [
  211.                     $handler['publisher']['hostname'],
  212.                     $handler['publisher']['port'],
  213.                     $handler['publisher']['chunk_size'],
  214.                 ]);
  215.                 $transport->setPublic(false);
  216.                 $publisher = new Definition('Gelf\Publisher', []);
  217.                 $publisher->addMethodCall('addTransport', [$transport]);
  218.                 $publisher->setPublic(false);
  219.             } elseif (class_exists('Gelf\MessagePublisher')) {
  220.                 $publisher = new Definition('Gelf\MessagePublisher', [
  221.                     $handler['publisher']['hostname'],
  222.                     $handler['publisher']['port'],
  223.                     $handler['publisher']['chunk_size'],
  224.                 ]);
  225.                 $publisher->setPublic(false);
  226.             } else {
  227.                 throw new \RuntimeException('The gelf handler requires the graylog2/gelf-php package to be installed');
  228.             }
  229.             $definition->setArguments([
  230.                 $publisher,
  231.                 $handler['level'],
  232.                 $handler['bubble'],
  233.             ]);
  234.             break;
  235.         case 'mongo':
  236.             if (isset($handler['mongo']['id'])) {
  237.                 $client = new Reference($handler['mongo']['id']);
  238.             } else {
  239.                 $server 'mongodb://';
  240.                 if (isset($handler['mongo']['user'])) {
  241.                     $server .= $handler['mongo']['user'].':'.$handler['mongo']['pass'].'@';
  242.                 }
  243.                 $server .= $handler['mongo']['host'].':'.$handler['mongo']['port'];
  244.                 $client = new Definition('MongoDB\Client', [
  245.                     $server,
  246.                 ]);
  247.                 $client->setPublic(false);
  248.             }
  249.             $definition->setArguments([
  250.                 $client,
  251.                 $handler['mongo']['database'],
  252.                 $handler['mongo']['collection'],
  253.                 $handler['level'],
  254.                 $handler['bubble'],
  255.             ]);
  256.             break;
  257.         case 'elasticsearch':
  258.             @trigger_error('The "elasticsearch" handler type is deprecated in MonologBundle since version 3.8.0, use the "elastica" type instead, or switch to the official Elastic client using the "elastic_search" type.'E_USER_DEPRECATED);
  259.             // no break
  260.         case 'elastica':
  261.         case 'elastic_search':
  262.             if (isset($handler['elasticsearch']['id'])) {
  263.                 $client = new Reference($handler['elasticsearch']['id']);
  264.             } else {
  265.                 if ($handler['type'] === 'elastic_search') {
  266.                     // v8 has a new Elastic\ prefix
  267.                     $client = new Definition(class_exists('Elastic\Elasticsearch\Client') ? 'Elastic\Elasticsearch\Client' 'Elasticsearch\Client');
  268.                     $factory class_exists('Elastic\Elasticsearch\ClientBuilder') ? 'Elastic\Elasticsearch\ClientBuilder' 'Elasticsearch\ClientBuilder';
  269.                     $client->setFactory([$factory'fromConfig']);
  270.                     $clientArguments = [
  271.                         'host' => $handler['elasticsearch']['host'],
  272.                     ];
  273.                     if (isset($handler['elasticsearch']['user'], $handler['elasticsearch']['password'])) {
  274.                         $clientArguments['basicAuthentication'] = [$handler['elasticsearch']['user'], $handler['elasticsearch']['password']];
  275.                     }
  276.                 } else {
  277.                     $client = new Definition('Elastica\Client');
  278.                     $clientArguments = [
  279.                         'host' => $handler['elasticsearch']['host'],
  280.                         'port' => $handler['elasticsearch']['port'],
  281.                         'transport' => $handler['elasticsearch']['transport'],
  282.                     ];
  283.                     if (isset($handler['elasticsearch']['user'], $handler['elasticsearch']['password'])) {
  284.                         $clientArguments['headers'] = [
  285.                             'Authorization' => 'Basic ' base64_encode($handler['elasticsearch']['user'] . ':' $handler['elasticsearch']['password'])
  286.                         ];
  287.                     }
  288.                 }
  289.                 $client->setArguments([
  290.                     $clientArguments
  291.                 ]);
  292.                 $client->setPublic(false);
  293.             }
  294.             // elastica handler definition
  295.             $definition->setArguments([
  296.                 $client,
  297.                 [
  298.                     'index' => $handler['index'],
  299.                     'type' => $handler['document_type'],
  300.                     'ignore_error' => $handler['ignore_error']
  301.                 ],
  302.                 $handler['level'],
  303.                 $handler['bubble'],
  304.             ]);
  305.             break;
  306.         case 'telegram':
  307.             if (!class_exists('Monolog\Handler\TelegramBotHandler')) {
  308.                 throw new \RuntimeException('The TelegramBotHandler is not available. Please update "monolog/monolog" to 2.2.0');
  309.             }
  310.             $definition->setArguments([
  311.                 $handler['token'],
  312.                 $handler['channel'],
  313.                 $handler['level'],
  314.                 $handler['bubble'],
  315.                 $handler['parse_mode'],
  316.                 $handler['disable_webpage_preview'],
  317.                 $handler['disable_notification'],
  318.                 $handler['split_long_messages'],
  319.                 $handler['delay_between_messages'],
  320.             ]);
  321.             break;
  322.         case 'redis':
  323.         case 'predis':
  324.             if (isset($handler['redis']['id'])) {
  325.                 $clientId $handler['redis']['id'];
  326.             } elseif ('redis' === $handler['type']) {
  327.                 if (!class_exists(\Redis::class)) {
  328.                     throw new \RuntimeException('The \Redis class is not available.');
  329.                 }
  330.                 $client = new Definition(\Redis::class);
  331.                 $client->addMethodCall('connect', [$handler['redis']['host'], $handler['redis']['port']]);
  332.                 $client->addMethodCall('auth', [$handler['redis']['password']]);
  333.                 $client->addMethodCall('select', [$handler['redis']['database']]);
  334.                 $client->setPublic(false);
  335.                 $clientId uniqid('monolog.redis.client.'true);
  336.                 $container->setDefinition($clientId$client);
  337.             } else {
  338.                 if (!class_exists(\Predis\Client::class)) {
  339.                     throw new \RuntimeException('The \Predis\Client class is not available.');
  340.                 }
  341.                 $client = new Definition(\Predis\Client::class);
  342.                 $client->setArguments([
  343.                     $handler['redis']['host'],
  344.                 ]);
  345.                 $client->setPublic(false);
  346.                 $clientId uniqid('monolog.predis.client.'true);
  347.                 $container->setDefinition($clientId$client);
  348.             }
  349.             $definition->setArguments([
  350.                 new Reference($clientId),
  351.                 $handler['redis']['key_name'],
  352.                 $handler['level'],
  353.                 $handler['bubble'],
  354.             ]);
  355.             break;
  356.         case 'rotating_file':
  357.             $definition->setArguments([
  358.                 $handler['path'],
  359.                 $handler['max_files'],
  360.                 $handler['level'],
  361.                 $handler['bubble'],
  362.                 $handler['file_permission'],
  363.                 $handler['use_locking'],
  364.             ]);
  365.             $definition->addMethodCall('setFilenameFormat', [
  366.                 $handler['filename_format'],
  367.                 $handler['date_format'],
  368.             ]);
  369.             break;
  370.         case 'fingers_crossed':
  371.             $handler['action_level'] = $this->levelToMonologConst($handler['action_level'], $container);
  372.             if (null !== $handler['passthru_level']) {
  373.                 $handler['passthru_level'] = $this->levelToMonologConst($handler['passthru_level'], $container);
  374.             }
  375.             $nestedHandlerId $this->getHandlerId($handler['handler']);
  376.             $this->markNestedHandler($nestedHandlerId);
  377.             $activation $handler['action_level'];
  378.             if (class_exists(SwitchUserTokenProcessor::class)) {
  379.                 $activation = new Definition(ErrorLevelActivationStrategy::class, [$activation]);
  380.             }
  381.             if (isset($handler['activation_strategy'])) {
  382.                 $activation = new Reference($handler['activation_strategy']);
  383.             } elseif (!empty($handler['excluded_404s'])) {
  384.                 if (class_exists(HttpCodeActivationStrategy::class)) {
  385.                     @trigger_error('The "excluded_404s" option is deprecated in MonologBundle since version 3.4.0, you should rely on the "excluded_http_codes" option instead.'E_USER_DEPRECATED);
  386.                 }
  387.                 $activationDef = new Definition('Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy', [
  388.                     new Reference('request_stack'),
  389.                     $handler['excluded_404s'],
  390.                     $activation
  391.                 ]);
  392.                 $container->setDefinition($handlerId.'.not_found_strategy'$activationDef);
  393.                 $activation = new Reference($handlerId.'.not_found_strategy');
  394.             } elseif (!empty($handler['excluded_http_codes'])) {
  395.                 if (!class_exists('Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy')) {
  396.                     throw new \LogicException('"excluded_http_codes" cannot be used as your version of Monolog bridge does not support it.');
  397.                 }
  398.                 $activationDef = new Definition('Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy', [
  399.                     new Reference('request_stack'),
  400.                     $handler['excluded_http_codes'],
  401.                     $activation
  402.                 ]);
  403.                 $container->setDefinition($handlerId.'.http_code_strategy'$activationDef);
  404.                 $activation = new Reference($handlerId.'.http_code_strategy');
  405.             }
  406.             $definition->setArguments([
  407.                 new Reference($nestedHandlerId),
  408.                 $activation,
  409.                 $handler['buffer_size'],
  410.                 $handler['bubble'],
  411.                 $handler['stop_buffering'],
  412.                 $handler['passthru_level'],
  413.             ]);
  414.             break;
  415.         case 'filter':
  416.             $handler['min_level'] = $this->levelToMonologConst($handler['min_level'], $container);
  417.             $handler['max_level'] = $this->levelToMonologConst($handler['max_level'], $container);
  418.             foreach (array_keys($handler['accepted_levels']) as $k) {
  419.                 $handler['accepted_levels'][$k] = $this->levelToMonologConst($handler['accepted_levels'][$k], $container);
  420.             }
  421.             $nestedHandlerId $this->getHandlerId($handler['handler']);
  422.             $this->markNestedHandler($nestedHandlerId);
  423.             $minLevelOrList = !empty($handler['accepted_levels']) ? $handler['accepted_levels'] : $handler['min_level'];
  424.             $definition->setArguments([
  425.                 new Reference($nestedHandlerId),
  426.                 $minLevelOrList,
  427.                 $handler['max_level'],
  428.                 $handler['bubble'],
  429.             ]);
  430.             break;
  431.         case 'buffer':
  432.             $nestedHandlerId $this->getHandlerId($handler['handler']);
  433.             $this->markNestedHandler($nestedHandlerId);
  434.             $definition->setArguments([
  435.                 new Reference($nestedHandlerId),
  436.                 $handler['buffer_size'],
  437.                 $handler['level'],
  438.                 $handler['bubble'],
  439.                 $handler['flush_on_overflow'],
  440.             ]);
  441.             break;
  442.         case 'deduplication':
  443.             $nestedHandlerId $this->getHandlerId($handler['handler']);
  444.             $this->markNestedHandler($nestedHandlerId);
  445.             $defaultStore '%kernel.cache_dir%/monolog_dedup_'.sha1($handlerId);
  446.             $definition->setArguments([
  447.                 new Reference($nestedHandlerId),
  448.                 isset($handler['store']) ? $handler['store'] : $defaultStore,
  449.                 $handler['deduplication_level'],
  450.                 $handler['time'],
  451.                 $handler['bubble'],
  452.             ]);
  453.             break;
  454.         case 'group':
  455.         case 'whatfailuregroup':
  456.         case 'fallbackgroup':
  457.             $references = [];
  458.             foreach ($handler['members'] as $nestedHandler) {
  459.                 $nestedHandlerId $this->getHandlerId($nestedHandler);
  460.                 $this->markNestedHandler($nestedHandlerId);
  461.                 $references[] = new Reference($nestedHandlerId);
  462.             }
  463.             $definition->setArguments([
  464.                 $references,
  465.                 $handler['bubble'],
  466.             ]);
  467.             break;
  468.         case 'syslog':
  469.             $definition->setArguments([
  470.                 $handler['ident'],
  471.                 $handler['facility'],
  472.                 $handler['level'],
  473.                 $handler['bubble'],
  474.                 $handler['logopts'],
  475.             ]);
  476.             break;
  477.         case 'syslogudp':
  478.             $definition->setArguments([
  479.                 $handler['host'],
  480.                 $handler['port'],
  481.                 $handler['facility'],
  482.                 $handler['level'],
  483.                 $handler['bubble'],
  484.             ]);
  485.             if ($handler['ident']) {
  486.                 $definition->addArgument($handler['ident']);
  487.             }
  488.             break;
  489.         case 'swift_mailer':
  490.             $mailer $handler['mailer'] ?: 'mailer';
  491.             if (isset($handler['email_prototype'])) {
  492.                 if (!empty($handler['email_prototype']['method'])) {
  493.                     $prototype = [new Reference($handler['email_prototype']['id']), $handler['email_prototype']['method']];
  494.                 } else {
  495.                     $prototype = new Reference($handler['email_prototype']['id']);
  496.                 }
  497.             } else {
  498.                 $messageFactory = new Definition('Symfony\Bundle\MonologBundle\SwiftMailer\MessageFactory');
  499.                 $messageFactory->setLazy(true);
  500.                 $messageFactory->setPublic(false);
  501.                 $messageFactory->setArguments([
  502.                     new Reference($mailer),
  503.                     $handler['from_email'],
  504.                     $handler['to_email'],
  505.                     $handler['subject'],
  506.                     $handler['content_type']
  507.                 ]);
  508.                 $messageFactoryId sprintf('%s.mail_message_factory'$handlerId);
  509.                 $container->setDefinition($messageFactoryId$messageFactory);
  510.                 // set the prototype as a callable
  511.                 $prototype = [new Reference($messageFactoryId), 'createMessage'];
  512.             }
  513.             $definition->setArguments([
  514.                 new Reference($mailer),
  515.                 $prototype,
  516.                 $handler['level'],
  517.                 $handler['bubble'],
  518.             ]);
  519.             $this->swiftMailerHandlers[] = $handlerId;
  520.             $definition->addTag('kernel.event_listener', ['event' => 'kernel.terminate''method' => 'onKernelTerminate']);
  521.             $definition->addTag('kernel.event_listener', ['event' => 'console.terminate''method' => 'onCliTerminate']);
  522.             break;
  523.         case 'native_mailer':
  524.             $definition->setArguments([
  525.                 $handler['to_email'],
  526.                 $handler['subject'],
  527.                 $handler['from_email'],
  528.                 $handler['level'],
  529.                 $handler['bubble'],
  530.             ]);
  531.             if (!empty($handler['headers'])) {
  532.                 $definition->addMethodCall('addHeader', [$handler['headers']]);
  533.             }
  534.             break;
  535.         case 'symfony_mailer':
  536.             $mailer $handler['mailer'] ?: 'mailer.mailer';
  537.             if (isset($handler['email_prototype'])) {
  538.                 if (!empty($handler['email_prototype']['method'])) {
  539.                     $prototype = [new Reference($handler['email_prototype']['id']), $handler['email_prototype']['method']];
  540.                 } else {
  541.                     $prototype = new Reference($handler['email_prototype']['id']);
  542.                 }
  543.             } else {
  544.                 $prototype = (new Definition('Symfony\Component\Mime\Email'))
  545.                     ->setPublic(false)
  546.                     ->addMethodCall('from', [$handler['from_email']])
  547.                     ->addMethodCall('to'$handler['to_email'])
  548.                     ->addMethodCall('subject', [$handler['subject']]);
  549.             }
  550.             $definition->setArguments([
  551.                 new Reference($mailer),
  552.                 $prototype,
  553.                 $handler['level'],
  554.                 $handler['bubble'],
  555.             ]);
  556.             break;
  557.         case 'socket':
  558.             $definition->setArguments([
  559.                 $handler['connection_string'],
  560.                 $handler['level'],
  561.                 $handler['bubble'],
  562.             ]);
  563.             if (isset($handler['timeout'])) {
  564.                 $definition->addMethodCall('setTimeout', [$handler['timeout']]);
  565.             }
  566.             if (isset($handler['connection_timeout'])) {
  567.                 $definition->addMethodCall('setConnectionTimeout', [$handler['connection_timeout']]);
  568.             }
  569.             if (isset($handler['persistent'])) {
  570.                 $definition->addMethodCall('setPersistent', [$handler['persistent']]);
  571.             }
  572.             break;
  573.         case 'pushover':
  574.             $definition->setArguments([
  575.                 $handler['token'],
  576.                 $handler['user'],
  577.                 $handler['title'],
  578.                 $handler['level'],
  579.                 $handler['bubble'],
  580.             ]);
  581.             if (isset($handler['timeout'])) {
  582.                 $definition->addMethodCall('setTimeout', [$handler['timeout']]);
  583.             }
  584.             if (isset($handler['connection_timeout'])) {
  585.                 $definition->addMethodCall('setConnectionTimeout', [$handler['connection_timeout']]);
  586.             }
  587.             break;
  588.         case 'hipchat':
  589.             $definition->setArguments([
  590.                 $handler['token'],
  591.                 $handler['room'],
  592.                 $handler['nickname'],
  593.                 $handler['notify'],
  594.                 $handler['level'],
  595.                 $handler['bubble'],
  596.                 $handler['use_ssl'],
  597.                 $handler['message_format'],
  598.                 !empty($handler['host']) ? $handler['host'] : 'api.hipchat.com',
  599.                 !empty($handler['api_version']) ? $handler['api_version'] : 'v1',
  600.             ]);
  601.             if (isset($handler['timeout'])) {
  602.                 $definition->addMethodCall('setTimeout', [$handler['timeout']]);
  603.             }
  604.             if (isset($handler['connection_timeout'])) {
  605.                 $definition->addMethodCall('setConnectionTimeout', [$handler['connection_timeout']]);
  606.             }
  607.             break;
  608.         case 'slack':
  609.             $definition->setArguments([
  610.                 $handler['token'],
  611.                 $handler['channel'],
  612.                 $handler['bot_name'],
  613.                 $handler['use_attachment'],
  614.                 $handler['icon_emoji'],
  615.                 $handler['level'],
  616.                 $handler['bubble'],
  617.                 $handler['use_short_attachment'],
  618.                 $handler['include_extra'],
  619.             ]);
  620.             if (isset($handler['timeout'])) {
  621.                 $definition->addMethodCall('setTimeout', [$handler['timeout']]);
  622.             }
  623.             if (isset($handler['connection_timeout'])) {
  624.                 $definition->addMethodCall('setConnectionTimeout', [$handler['connection_timeout']]);
  625.             }
  626.             break;
  627.         case 'slackwebhook':
  628.             $definition->setArguments([
  629.                 $handler['webhook_url'],
  630.                 $handler['channel'],
  631.                 $handler['bot_name'],
  632.                 $handler['use_attachment'],
  633.                 $handler['icon_emoji'],
  634.                 $handler['use_short_attachment'],
  635.                 $handler['include_extra'],
  636.                 $handler['level'],
  637.                 $handler['bubble'],
  638.             ]);
  639.             break;
  640.         case 'slackbot':
  641.             $definition->setArguments([
  642.                 $handler['team'],
  643.                 $handler['token'],
  644.                 urlencode($handler['channel']),
  645.                 $handler['level'],
  646.                 $handler['bubble'],
  647.             ]);
  648.             break;
  649.         case 'cube':
  650.             $definition->setArguments([
  651.                 $handler['url'],
  652.                 $handler['level'],
  653.                 $handler['bubble'],
  654.             ]);
  655.             break;
  656.         case 'amqp':
  657.             $definition->setArguments([
  658.                 new Reference($handler['exchange']),
  659.                 $handler['exchange_name'],
  660.                 $handler['level'],
  661.                 $handler['bubble'],
  662.             ]);
  663.             break;
  664.         case 'error_log':
  665.             $definition->setArguments([
  666.                 $handler['message_type'],
  667.                 $handler['level'],
  668.                 $handler['bubble'],
  669.             ]);
  670.             break;
  671.         case 'sentry':
  672.             if (null !== $handler['hub_id']) {
  673.                 $hub = new Reference($handler['hub_id']);
  674.             } else {
  675.                 if (null !== $handler['client_id']) {
  676.                     $clientId $handler['client_id'];
  677.                 } else {
  678.                     $options = new Definition(
  679.                         'Sentry\\Options',
  680.                         [['dsn' => $handler['dsn']]]
  681.                     );
  682.                     if (!empty($handler['environment'])) {
  683.                         $options->addMethodCall('setEnvironment', [$handler['environment']]);
  684.                     }
  685.                     if (!empty($handler['release'])) {
  686.                         $options->addMethodCall('setRelease', [$handler['release']]);
  687.                     }
  688.                     $builder = new Definition('Sentry\\ClientBuilder', [$options]);
  689.                     $client = new Definition('Sentry\\Client');
  690.                     $client->setFactory([$builder'getClient']);
  691.                     $clientId 'monolog.sentry.client.'.sha1($handler['dsn']);
  692.                     $container->setDefinition($clientId$client);
  693.                     if (!$container->hasAlias('Sentry\\ClientInterface')) {
  694.                         $container->setAlias('Sentry\\ClientInterface'$clientId);
  695.                     }
  696.                 }
  697.                 $hub = new Definition(
  698.                     'Sentry\\State\\Hub',
  699.                     [new Reference($clientId)]
  700.                 );
  701.                 $container->setDefinition(sprintf('monolog.handler.%s.hub'$name), $hub);
  702.                 // can't set the hub to the current hub, getting into a recursion otherwise...
  703.                 //$hub->addMethodCall('setCurrent', array($hub));
  704.             }
  705.             $definition->setArguments([
  706.                 $hub,
  707.                 $handler['level'],
  708.                 $handler['bubble'],
  709.                 $handler['fill_extra_context'],
  710.             ]);
  711.             break;
  712.         case 'raven':
  713.             if (null !== $handler['client_id']) {
  714.                 $clientId $handler['client_id'];
  715.             } else {
  716.                 $client = new Definition('Raven_Client', [
  717.                     $handler['dsn'],
  718.                     [
  719.                         'auto_log_stacks' => $handler['auto_log_stacks'],
  720.                         'environment' => $handler['environment']
  721.                     ]
  722.                 ]);
  723.                 $client->setPublic(false);
  724.                 $clientId 'monolog.raven.client.'.sha1($handler['dsn']);
  725.                 $container->setDefinition($clientId$client);
  726.             }
  727.             $definition->setArguments([
  728.                 new Reference($clientId),
  729.                 $handler['level'],
  730.                 $handler['bubble'],
  731.             ]);
  732.             if (!empty($handler['release'])) {
  733.                 $definition->addMethodCall('setRelease', [$handler['release']]);
  734.             }
  735.             break;
  736.         case 'loggly':
  737.             $definition->setArguments([
  738.                 $handler['token'],
  739.                 $handler['level'],
  740.                 $handler['bubble'],
  741.             ]);
  742.             if (!empty($handler['tags'])) {
  743.                 $definition->addMethodCall('setTag', [implode(','$handler['tags'])]);
  744.             }
  745.             break;
  746.         case 'logentries':
  747.             $definition->setArguments([
  748.                 $handler['token'],
  749.                 $handler['use_ssl'],
  750.                 $handler['level'],
  751.                 $handler['bubble'],
  752.             ]);
  753.             if (isset($handler['timeout'])) {
  754.                 $definition->addMethodCall('setTimeout', [$handler['timeout']]);
  755.             }
  756.             if (isset($handler['connection_timeout'])) {
  757.                 $definition->addMethodCall('setConnectionTimeout', [$handler['connection_timeout']]);
  758.             }
  759.             break;
  760.         case 'insightops':
  761.             $definition->setArguments([
  762.                 $handler['token'],
  763.                 $handler['region'] ? $handler['region'] : 'us',
  764.                 $handler['use_ssl'],
  765.                 $handler['level'],
  766.                 $handler['bubble'],
  767.             ]);
  768.             break;
  769.         case 'flowdock':
  770.             $definition->setArguments([
  771.                 $handler['token'],
  772.                 $handler['level'],
  773.                 $handler['bubble'],
  774.             ]);
  775.             if (empty($handler['formatter'])) {
  776.                 $formatter = new Definition("Monolog\Formatter\FlowdockFormatter", [
  777.                     $handler['source'],
  778.                     $handler['from_email'],
  779.                 ]);
  780.                 $formatterId 'monolog.flowdock.formatter.'.sha1($handler['source'].'|'.$handler['from_email']);
  781.                 $formatter->setPublic(false);
  782.                 $container->setDefinition($formatterId$formatter);
  783.                 $definition->addMethodCall('setFormatter', [new Reference($formatterId)]);
  784.             }
  785.             break;
  786.         case 'rollbar':
  787.             if (!empty($handler['id'])) {
  788.                 $rollbarId $handler['id'];
  789.             } else {
  790.                 $config $handler['config'] ?: [];
  791.                 $config['access_token'] = $handler['token'];
  792.                 $rollbar = new Definition('RollbarNotifier', [
  793.                     $config,
  794.                 ]);
  795.                 $rollbarId 'monolog.rollbar.notifier.'.sha1(json_encode($config));
  796.                 $rollbar->setPublic(false);
  797.                 $container->setDefinition($rollbarId$rollbar);
  798.             }
  799.             $definition->setArguments([
  800.                 new Reference($rollbarId),
  801.                 $handler['level'],
  802.                 $handler['bubble'],
  803.             ]);
  804.             break;
  805.         case 'newrelic':
  806.             $definition->setArguments([
  807.                 $handler['level'],
  808.                 $handler['bubble'],
  809.                 $handler['app_name'],
  810.             ]);
  811.             break;
  812.         case 'server_log':
  813.             if (!class_exists('Symfony\Bridge\Monolog\Handler\ServerLogHandler')) {
  814.                 throw new \RuntimeException('The ServerLogHandler is not available. Please update "symfony/monolog-bridge" to 3.3.');
  815.             }
  816.             $definition->setArguments([
  817.                 $handler['host'],
  818.                 $handler['level'],
  819.                 $handler['bubble'],
  820.             ]);
  821.             break;
  822.         // Handlers using the constructor of AbstractHandler without adding their own arguments
  823.         case 'browser_console':
  824.         case 'test':
  825.         case 'null':
  826.         case 'noop':
  827.         case 'debug':
  828.             $definition->setArguments([
  829.                 $handler['level'],
  830.                 $handler['bubble'],
  831.             ]);
  832.             break;
  833.         default:
  834.             $nullWarning '';
  835.             if ($handler['type'] == '') {
  836.                 $nullWarning ', if you meant to define a null handler in a yaml config, make sure you quote "null" so it does not get converted to a php null';
  837.             }
  838.             throw new \InvalidArgumentException(sprintf('Invalid handler type "%s" given for handler "%s"' $nullWarning$handler['type'], $name));
  839.         }
  840.         if (!empty($handler['nested']) && true === $handler['nested']) {
  841.             $this->markNestedHandler($handlerId);
  842.         }
  843.         if (!empty($handler['formatter'])) {
  844.             $definition->addMethodCall('setFormatter', [new Reference($handler['formatter'])]);
  845.         }
  846.         if (!in_array($handlerId$this->nestedHandlers) && is_subclass_of($handlerClassResettableInterface::class)) {
  847.             $definition->addTag('kernel.reset', ['method' => 'reset']);
  848.         }
  849.         $container->setDefinition($handlerId$definition);
  850.         return $handlerId;
  851.     }
  852.     private function markNestedHandler($nestedHandlerId)
  853.     {
  854.         if (in_array($nestedHandlerId$this->nestedHandlers)) {
  855.             return;
  856.         }
  857.         $this->nestedHandlers[] = $nestedHandlerId;
  858.     }
  859.     private function getHandlerId($name)
  860.     {
  861.         return sprintf('monolog.handler.%s'$name);
  862.     }
  863.     private function getHandlerClassByType($handlerType)
  864.     {
  865.         $typeToClassMapping = [
  866.             'stream' => 'Monolog\Handler\StreamHandler',
  867.             'console' => 'Symfony\Bridge\Monolog\Handler\ConsoleHandler',
  868.             'group' => 'Monolog\Handler\GroupHandler',
  869.             'buffer' => 'Monolog\Handler\BufferHandler',
  870.             'deduplication' => 'Monolog\Handler\DeduplicationHandler',
  871.             'rotating_file' => 'Monolog\Handler\RotatingFileHandler',
  872.             'syslog' => 'Monolog\Handler\SyslogHandler',
  873.             'syslogudp' => 'Monolog\Handler\SyslogUdpHandler',
  874.             'null' => 'Monolog\Handler\NullHandler',
  875.             'test' => 'Monolog\Handler\TestHandler',
  876.             'gelf' => 'Monolog\Handler\GelfHandler',
  877.             'rollbar' => 'Monolog\Handler\RollbarHandler',
  878.             'flowdock' => 'Monolog\Handler\FlowdockHandler',
  879.             'browser_console' => 'Monolog\Handler\BrowserConsoleHandler',
  880.             'firephp' => 'Symfony\Bridge\Monolog\Handler\FirePHPHandler',
  881.             'chromephp' => 'Symfony\Bridge\Monolog\Handler\ChromePhpHandler',
  882.             'debug' => 'Symfony\Bridge\Monolog\Handler\DebugHandler',
  883.             'swift_mailer' => 'Symfony\Bridge\Monolog\Handler\SwiftMailerHandler',
  884.             'native_mailer' => 'Monolog\Handler\NativeMailerHandler',
  885.             'symfony_mailer' => 'Symfony\Bridge\Monolog\Handler\MailerHandler',
  886.             'socket' => 'Monolog\Handler\SocketHandler',
  887.             'pushover' => 'Monolog\Handler\PushoverHandler',
  888.             'raven' => 'Monolog\Handler\RavenHandler',
  889.             'sentry' => 'Sentry\Monolog\Handler',
  890.             'newrelic' => 'Monolog\Handler\NewRelicHandler',
  891.             'hipchat' => 'Monolog\Handler\HipChatHandler',
  892.             'slack' => 'Monolog\Handler\SlackHandler',
  893.             'slackwebhook' => 'Monolog\Handler\SlackWebhookHandler',
  894.             'slackbot' => 'Monolog\Handler\SlackbotHandler',
  895.             'cube' => 'Monolog\Handler\CubeHandler',
  896.             'amqp' => 'Monolog\Handler\AmqpHandler',
  897.             'error_log' => 'Monolog\Handler\ErrorLogHandler',
  898.             'loggly' => 'Monolog\Handler\LogglyHandler',
  899.             'logentries' => 'Monolog\Handler\LogEntriesHandler',
  900.             'whatfailuregroup' => 'Monolog\Handler\WhatFailureGroupHandler',
  901.             'fingers_crossed' => 'Monolog\Handler\FingersCrossedHandler',
  902.             'filter' => 'Monolog\Handler\FilterHandler',
  903.             'mongo' => 'Monolog\Handler\MongoDBHandler',
  904.             'elasticsearch' => 'Monolog\Handler\ElasticSearchHandler',
  905.             'telegram' => 'Monolog\Handler\TelegramBotHandler',
  906.             'server_log' => 'Symfony\Bridge\Monolog\Handler\ServerLogHandler',
  907.             'redis' => 'Monolog\Handler\RedisHandler',
  908.             'predis' => 'Monolog\Handler\RedisHandler',
  909.             'insightops' => 'Monolog\Handler\InsightOpsHandler',
  910.         ];
  911.         $v2HandlerTypesAdded = [
  912.             'elastica' => 'Monolog\Handler\ElasticaHandler',
  913.             'elasticsearch' => 'Monolog\Handler\ElasticaHandler',
  914.             'elastic_search' => 'Monolog\Handler\ElasticsearchHandler',
  915.             'fallbackgroup' => 'Monolog\Handler\FallbackGroupHandler',
  916.             'noop' => 'Monolog\Handler\NoopHandler',
  917.         ];
  918.         $v2HandlerTypesRemoved = [
  919.             'hipchat',
  920.             'raven',
  921.             'slackbot',
  922.         ];
  923.         $v3HandlerTypesRemoved = [
  924.             'swift_mailer',
  925.         ];
  926.         if (Logger::API >= 2) {
  927.             $typeToClassMapping array_merge($typeToClassMapping$v2HandlerTypesAdded);
  928.             foreach($v2HandlerTypesRemoved as $v2HandlerTypeRemoved) {
  929.                 unset($typeToClassMapping[$v2HandlerTypeRemoved]);
  930.             }
  931.         }
  932.         if (Logger::API >= 3) {
  933.             foreach($v3HandlerTypesRemoved as $v3HandlerTypeRemoved) {
  934.                 unset($typeToClassMapping[$v3HandlerTypeRemoved]);
  935.             }
  936.         }
  937.         if (!isset($typeToClassMapping[$handlerType])) {
  938.             if (Logger::API === && array_key_exists($handlerType$v2HandlerTypesAdded)) {
  939.                 throw new \InvalidArgumentException(sprintf('"%s" was added in Monolog v2, please upgrade if you wish to use it.'$handlerType));
  940.             }
  941.             if (Logger::API >= && array_key_exists($handlerType$v2HandlerTypesRemoved)) {
  942.                 throw new \InvalidArgumentException(sprintf('"%s" was removed in Monolog v2.'$handlerType));
  943.             }
  944.             if (Logger::API >= && array_key_exists($handlerType$v3HandlerTypesRemoved)) {
  945.                 throw new \InvalidArgumentException(sprintf('"%s" was removed in Monolog v3.'$handlerType));
  946.             }
  947.             throw new \InvalidArgumentException(sprintf('There is no handler class defined for handler "%s".'$handlerType));
  948.         }
  949.         return $typeToClassMapping[$handlerType];
  950.     }
  951.     private function buildPsrLogMessageProcessor(ContainerBuilder $container, array $processorOptions): string
  952.     {
  953.         static $hasConstructorArguments;
  954.         if (!isset($hasConstructorArguments)) {
  955.             $reflectionConstructor = (new \ReflectionClass(PsrLogMessageProcessor::class))->getConstructor();
  956.             $hasConstructorArguments null !== $reflectionConstructor && $reflectionConstructor->getNumberOfParameters() > 0;
  957.             unset($reflectionConstructor);
  958.         }
  959.         $processorId 'monolog.processor.psr_log_message';
  960.         $processorArguments = [];
  961.         unset($processorOptions['enabled']);
  962.         if (!empty($processorOptions)) {
  963.             if (!$hasConstructorArguments) {
  964.                 throw new \RuntimeException('Monolog 1.26 or higher is required for the "date_format" and "remove_used_context_fields" options to be used.');
  965.             }
  966.             $processorArguments = [
  967.                 $processorOptions['date_format'] ?? null,
  968.                 $processorOptions['remove_used_context_fields'] ?? false,
  969.             ];
  970.             $processorId .= '.'.ContainerBuilder::hash($processorArguments);
  971.         }
  972.         if (!$container->hasDefinition($processorId)) {
  973.             $processor = new Definition(PsrLogMessageProcessor::class);
  974.             $processor->setPublic(false);
  975.             $processor->setArguments($processorArguments);
  976.             $container->setDefinition($processorId$processor);
  977.         }
  978.         return $processorId;
  979.     }
  980. }