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