MonologExtension.php 40 KB

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