Configuration.php 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  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 Symfony\Component\Config\Definition\BaseNode;
  12. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  13. use Symfony\Component\Config\Definition\ConfigurationInterface;
  14. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  15. use Monolog\Logger;
  16. /**
  17. * This class contains the configuration information for the bundle
  18. *
  19. * This information is solely responsible for how the different configuration
  20. * sections are normalized, and merged.
  21. *
  22. * Possible handler types and related configurations (brackets indicate optional params):
  23. *
  24. * - service:
  25. * - id
  26. *
  27. * - stream:
  28. * - path: string
  29. * - [level]: level name or int value, defaults to DEBUG
  30. * - [bubble]: bool, defaults to true
  31. * - [file_permission]: int|null, defaults to null (0644)
  32. * - [use_locking]: bool, defaults to false
  33. *
  34. * - console:
  35. * - [verbosity_levels]: level => verbosity configuration
  36. * - [level]: level name or int value, defaults to DEBUG
  37. * - [bubble]: bool, defaults to true
  38. * - [console_formatter_options]: array
  39. *
  40. * - firephp:
  41. * - [level]: level name or int value, defaults to DEBUG
  42. * - [bubble]: bool, defaults to true
  43. *
  44. * - browser_console:
  45. * - [level]: level name or int value, defaults to DEBUG
  46. * - [bubble]: bool, defaults to true
  47. *
  48. * - gelf:
  49. * - publisher: {id: ...} or {hostname: ..., port: ..., chunk_size: ...}
  50. * - [level]: level name or int value, defaults to DEBUG
  51. * - [bubble]: bool, defaults to true
  52. *
  53. * - chromephp:
  54. * - [level]: level name or int value, defaults to DEBUG
  55. * - [bubble]: bool, defaults to true
  56. *
  57. * - rotating_file:
  58. * - path: string
  59. * - [max_files]: files to keep, defaults to zero (infinite)
  60. * - [level]: level name or int value, defaults to DEBUG
  61. * - [bubble]: bool, defaults to true
  62. * - [file_permission]: string|null, defaults to null
  63. * - [use_locking]: bool, defaults to false
  64. * - [filename_format]: string, defaults to '{filename}-{date}'
  65. * - [date_format]: string, defaults to 'Y-m-d'
  66. *
  67. * - mongo:
  68. * - mongo:
  69. * - id: optional if host is given
  70. * - host: database host name, optional if id is given
  71. * - [port]: defaults to 27017
  72. * - [user]: database user name
  73. * - pass: mandatory only if user is present
  74. * - [database]: defaults to monolog
  75. * - [collection]: defaults to logs
  76. * - [level]: level name or int value, defaults to DEBUG
  77. * - [bubble]: bool, defaults to true
  78. *
  79. * - elasticsearch:
  80. * - elasticsearch:
  81. * - id: optional if host is given
  82. * - host: elastic search host name. Do not prepend with http(s)://
  83. * - [port]: defaults to 9200
  84. * - [transport]: transport protocol (http by default)
  85. * - [user]: elastic search user name
  86. * - [password]: elastic search user password
  87. * - [index]: index name, defaults to monolog
  88. * - [document_type]: document_type, defaults to logs
  89. * - [level]: level name or int value, defaults to DEBUG
  90. * - [bubble]: bool, defaults to true
  91. *
  92. * - redis:
  93. * - redis:
  94. * - id: optional if host is given
  95. * - host: 127.0.0.1
  96. * - password: null
  97. * - port: 6379
  98. * - database: 0
  99. * - key_name: monolog_redis
  100. *
  101. * - predis:
  102. * - redis:
  103. * - id: optional if host is given
  104. * - host: tcp://10.0.0.1:6379
  105. * - key_name: monolog_redis
  106. *
  107. * - fingers_crossed:
  108. * - handler: the wrapped handler's name
  109. * - [action_level|activation_strategy]: minimum level or service id to activate the handler, defaults to WARNING
  110. * - [excluded_404s]: if set, the strategy will be changed to one that excludes 404s coming from URLs matching any of those patterns
  111. * - [excluded_http_codes]: if set, the strategy will be changed to one that excludes specific HTTP codes (requires Symfony Monolog bridge 4.1+)
  112. * - [buffer_size]: defaults to 0 (unlimited)
  113. * - [stop_buffering]: bool to disable buffering once the handler has been activated, defaults to true
  114. * - [passthru_level]: level name or int value for messages to always flush, disabled by default
  115. * - [bubble]: bool, defaults to true
  116. *
  117. * - filter:
  118. * - handler: the wrapped handler's name
  119. * - [accepted_levels]: list of levels to accept
  120. * - [min_level]: minimum level to accept (only used if accepted_levels not specified)
  121. * - [max_level]: maximum level to accept (only used if accepted_levels not specified)
  122. * - [bubble]: bool, defaults to true
  123. *
  124. * - buffer:
  125. * - handler: the wrapped handler's name
  126. * - [buffer_size]: defaults to 0 (unlimited)
  127. * - [level]: level name or int value, defaults to DEBUG
  128. * - [bubble]: bool, defaults to true
  129. * - [flush_on_overflow]: bool, defaults to false
  130. *
  131. * - deduplication:
  132. * - handler: the wrapped handler's name
  133. * - [store]: The file/path where the deduplication log should be kept, defaults to %kernel.cache_dir%/monolog_dedup_*
  134. * - [deduplication_level]: The minimum logging level for log records to be looked at for deduplication purposes, defaults to ERROR
  135. * - [time]: The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through, defaults to 60
  136. * - [bubble]: bool, defaults to true
  137. *
  138. * - group:
  139. * - members: the wrapped handlers by name
  140. * - [bubble]: bool, defaults to true
  141. *
  142. * - whatfailuregroup:
  143. * - members: the wrapped handlers by name
  144. * - [bubble]: bool, defaults to true
  145. *
  146. * - syslog:
  147. * - ident: string
  148. * - [facility]: defaults to 'user', use any of the LOG_* facility constant but without LOG_ prefix, e.g. user for LOG_USER
  149. * - [logopts]: defaults to LOG_PID
  150. * - [level]: level name or int value, defaults to DEBUG
  151. * - [bubble]: bool, defaults to true
  152. *
  153. * - syslogudp:
  154. * - host: syslogd host name
  155. * - [port]: defaults to 514
  156. * - [facility]: defaults to 'user', use any of the LOG_* facility constant but without LOG_ prefix, e.g. user for LOG_USER
  157. * - [logopts]: defaults to LOG_PID
  158. * - [level]: level name or int value, defaults to DEBUG
  159. * - [bubble]: bool, defaults to true
  160. * - [ident]: string, defaults to
  161. *
  162. * - swift_mailer:
  163. * - from_email: optional if email_prototype is given
  164. * - to_email: optional if email_prototype is given
  165. * - subject: optional if email_prototype is given
  166. * - [email_prototype]: service id of a message, defaults to a default message with the three fields above
  167. * - [content_type]: optional if email_prototype is given, defaults to text/plain
  168. * - [mailer]: mailer service, defaults to mailer
  169. * - [level]: level name or int value, defaults to DEBUG
  170. * - [bubble]: bool, defaults to true
  171. * - [lazy]: use service lazy loading, bool, defaults to true
  172. *
  173. * - native_mailer:
  174. * - from_email: string
  175. * - to_email: string
  176. * - subject: string
  177. * - [level]: level name or int value, defaults to DEBUG
  178. * - [bubble]: bool, defaults to true
  179. * - [headers]: optional array containing additional headers: ['Foo: Bar', '...']
  180. *
  181. * - symfony_mailer:
  182. * - from_email: optional if email_prototype is given
  183. * - to_email: optional if email_prototype is given
  184. * - subject: optional if email_prototype is given
  185. * - [email_prototype]: service id of a message, defaults to a default message with the three fields above
  186. * - [mailer]: mailer service id, defaults to mailer.mailer
  187. * - [level]: level name or int value, defaults to DEBUG
  188. * - [bubble]: bool, defaults to true
  189. *
  190. * - socket:
  191. * - connection_string: string
  192. * - [timeout]: float
  193. * - [connection_timeout]: float
  194. * - [persistent]: bool
  195. * - [level]: level name or int value, defaults to DEBUG
  196. * - [bubble]: bool, defaults to true
  197. *
  198. * - pushover:
  199. * - token: pushover api token
  200. * - user: user id or array of ids
  201. * - [title]: optional title for messages, defaults to the server hostname
  202. * - [level]: level name or int value, defaults to DEBUG
  203. * - [bubble]: bool, defaults to true
  204. * - [timeout]: float
  205. * - [connection_timeout]: float
  206. *
  207. * - raven / sentry:
  208. * - dsn: connection string
  209. * - client_id: Raven client custom service id (optional)
  210. * - [release]: release number of the application that will be attached to logs, defaults to null
  211. * - [level]: level name or int value, defaults to DEBUG
  212. * - [bubble]: bool, defaults to true
  213. * - [auto_log_stacks]: bool, defaults to false
  214. * - [environment]: string, default to null (no env specified)
  215. *
  216. * - sentry:
  217. * - hub_id: Sentry hub custom service id (optional)
  218. *
  219. * - newrelic:
  220. * - [level]: level name or int value, defaults to DEBUG
  221. * - [bubble]: bool, defaults to true
  222. * - [app_name]: new relic app name, default null
  223. *
  224. * - hipchat:
  225. * - token: hipchat api token
  226. * - room: room id or name
  227. * - [notify]: defaults to false
  228. * - [nickname]: defaults to Monolog
  229. * - [level]: level name or int value, defaults to DEBUG
  230. * - [bubble]: bool, defaults to true
  231. * - [use_ssl]: bool, defaults to true
  232. * - [message_format]: text or html, defaults to text
  233. * - [host]: defaults to "api.hipchat.com"
  234. * - [api_version]: defaults to "v1"
  235. * - [timeout]: float
  236. * - [connection_timeout]: float
  237. *
  238. * - slack:
  239. * - token: slack api token
  240. * - channel: channel name (with starting #)
  241. * - [bot_name]: defaults to Monolog
  242. * - [icon_emoji]: defaults to null
  243. * - [use_attachment]: bool, defaults to true
  244. * - [use_short_attachment]: bool, defaults to false
  245. * - [include_extra]: bool, defaults to false
  246. * - [level]: level name or int value, defaults to DEBUG
  247. * - [bubble]: bool, defaults to true
  248. * - [timeout]: float
  249. * - [connection_timeout]: float
  250. *
  251. * - slackwebhook:
  252. * - webhook_url: slack webhook URL
  253. * - channel: channel name (with starting #)
  254. * - [bot_name]: defaults to Monolog
  255. * - [icon_emoji]: defaults to null
  256. * - [use_attachment]: bool, defaults to true
  257. * - [use_short_attachment]: bool, defaults to false
  258. * - [include_extra]: bool, defaults to false
  259. * - [level]: level name or int value, defaults to DEBUG
  260. * - [bubble]: bool, defaults to true
  261. *
  262. * - slackbot:
  263. * - team: slack team slug
  264. * - token: slackbot token
  265. * - channel: channel name (with starting #)
  266. * - [level]: level name or int value, defaults to DEBUG
  267. * - [bubble]: bool, defaults to true
  268. *
  269. * - cube:
  270. * - url: http/udp url to the cube server
  271. * - [level]: level name or int value, defaults to DEBUG
  272. * - [bubble]: bool, defaults to true
  273. *
  274. * - amqp:
  275. * - exchange: service id of an AMQPExchange
  276. * - [exchange_name]: string, defaults to log
  277. * - [level]: level name or int value, defaults to DEBUG
  278. * - [bubble]: bool, defaults to true
  279. *
  280. * - error_log:
  281. * - [message_type]: int 0 or 4, defaults to 0
  282. * - [level]: level name or int value, defaults to DEBUG
  283. * - [bubble]: bool, defaults to true
  284. *
  285. * - null:
  286. * - [level]: level name or int value, defaults to DEBUG
  287. * - [bubble]: bool, defaults to true
  288. *
  289. * - test:
  290. * - [level]: level name or int value, defaults to DEBUG
  291. * - [bubble]: bool, defaults to true
  292. *
  293. * - debug:
  294. * - [level]: level name or int value, defaults to DEBUG
  295. * - [bubble]: bool, defaults to true
  296. *
  297. * - loggly:
  298. * - token: loggly api token
  299. * - [level]: level name or int value, defaults to DEBUG
  300. * - [bubble]: bool, defaults to true
  301. * - [tags]: tag names
  302. *
  303. * - logentries:
  304. * - token: logentries api token
  305. * - [use_ssl]: whether or not SSL encryption should be used, defaults to true
  306. * - [level]: level name or int value, defaults to DEBUG
  307. * - [bubble]: bool, defaults to true
  308. * - [timeout]: float
  309. * - [connection_timeout]: float
  310. *
  311. * - insightops:
  312. * - token: Log token supplied by InsightOps
  313. * - region: Region where InsightOps account is hosted. Could be 'us' or 'eu'. Defaults to 'us'
  314. * - [use_ssl]: whether or not SSL encryption should be used, defaults to true
  315. * - [level]: level name or int value, defaults to DEBUG
  316. * - [bubble]: bool, defaults to true
  317. *
  318. * - flowdock:
  319. * - token: flowdock api token
  320. * - source: human readable identifier of the application
  321. * - from_email: email address of the message sender
  322. * - [level]: level name or int value, defaults to DEBUG
  323. * - [bubble]: bool, defaults to true
  324. *
  325. * - rollbar:
  326. * - id: RollbarNotifier service (mandatory if token is not provided)
  327. * - token: rollbar api token (skip if you provide a RollbarNotifier service id)
  328. * - [config]: config values from https://github.com/rollbar/rollbar-php#configuration-reference
  329. * - [level]: level name or int value, defaults to DEBUG
  330. * - [bubble]: bool, defaults to true
  331. *
  332. * - server_log:
  333. * - host: server log host. ex: 127.0.0.1:9911
  334. * - [level]: level name or int value, defaults to DEBUG
  335. * - [bubble]: bool, defaults to true
  336. *
  337. * All handlers can also be marked with `nested: true` to make sure they are never added explicitly to the stack
  338. *
  339. * @author Jordi Boggiano <j.boggiano@seld.be>
  340. * @author Christophe Coevoet <stof@notk.org>
  341. */
  342. class Configuration implements ConfigurationInterface
  343. {
  344. /**
  345. * Generates the configuration tree builder.
  346. *
  347. * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
  348. */
  349. public function getConfigTreeBuilder()
  350. {
  351. $treeBuilder = new TreeBuilder('monolog');
  352. $rootNode = method_exists(TreeBuilder::class, 'getRootNode') ? $treeBuilder->getRootNode() : $treeBuilder->root('monolog');
  353. $rootNode
  354. ->fixXmlConfig('channel')
  355. ->fixXmlConfig('handler')
  356. ->children()
  357. ->scalarNode('use_microseconds')->defaultTrue()->end()
  358. ->arrayNode('channels')
  359. ->canBeUnset()
  360. ->prototype('scalar')->end()
  361. ->end()
  362. ->arrayNode('handlers')
  363. ->canBeUnset()
  364. ->useAttributeAsKey('name')
  365. ->prototype('array')
  366. ->fixXmlConfig('member')
  367. ->fixXmlConfig('excluded_404')
  368. ->fixXmlConfig('excluded_http_code')
  369. ->fixXmlConfig('tag')
  370. ->fixXmlConfig('accepted_level')
  371. ->fixXmlConfig('header')
  372. ->canBeUnset()
  373. ->children()
  374. ->scalarNode('type')
  375. ->isRequired()
  376. ->treatNullLike('null')
  377. ->beforeNormalization()
  378. ->always()
  379. ->then(function ($v) { return strtolower($v); })
  380. ->end()
  381. ->end()
  382. ->scalarNode('id')->end() // service & rollbar
  383. ->scalarNode('priority')->defaultValue(0)->end()
  384. ->scalarNode('level')->defaultValue('DEBUG')->end()
  385. ->booleanNode('bubble')->defaultTrue()->end()
  386. ->scalarNode('app_name')->defaultNull()->end()
  387. ->booleanNode('include_stacktraces')->defaultFalse()->end()
  388. ->booleanNode('process_psr_3_messages')->defaultNull()->end()
  389. ->scalarNode('path')->defaultValue('%kernel.logs_dir%/%kernel.environment%.log')->end() // stream and rotating
  390. ->scalarNode('file_permission') // stream and rotating
  391. ->defaultNull()
  392. ->beforeNormalization()
  393. ->ifString()
  394. ->then(function ($v) {
  395. if (substr($v, 0, 1) === '0') {
  396. return octdec($v);
  397. }
  398. return (int) $v;
  399. })
  400. ->end()
  401. ->end()
  402. ->booleanNode('use_locking')->defaultFalse()->end() // stream and rotating
  403. ->scalarNode('filename_format')->defaultValue('{filename}-{date}')->end() //rotating
  404. ->scalarNode('date_format')->defaultValue('Y-m-d')->end() //rotating
  405. ->scalarNode('ident')->defaultFalse()->end() // syslog and syslogudp
  406. ->scalarNode('logopts')->defaultValue(LOG_PID)->end() // syslog
  407. ->scalarNode('facility')->defaultValue('user')->end() // syslog
  408. ->scalarNode('max_files')->defaultValue(0)->end() // rotating
  409. ->scalarNode('action_level')->defaultValue('WARNING')->end() // fingers_crossed
  410. ->scalarNode('activation_strategy')->defaultNull()->end() // fingers_crossed
  411. ->booleanNode('stop_buffering')->defaultTrue()->end()// fingers_crossed
  412. ->scalarNode('passthru_level')->defaultNull()->end() // fingers_crossed
  413. ->arrayNode('excluded_404s') // fingers_crossed
  414. ->canBeUnset()
  415. ->prototype('scalar')->end()
  416. ->end()
  417. ->arrayNode('excluded_http_codes') // fingers_crossed
  418. ->canBeUnset()
  419. ->beforeNormalization()
  420. ->always(function ($values) {
  421. return array_map(function ($value) {
  422. /*
  423. * Allows YAML:
  424. * excluded_http_codes: [403, 404, { 400: ['^/foo', '^/bar'] }]
  425. *
  426. * and XML:
  427. * <monolog:excluded-http-code code="403">
  428. * <monolog:url>^/foo</monolog:url>
  429. * <monolog:url>^/bar</monolog:url>
  430. * </monolog:excluded-http-code>
  431. * <monolog:excluded-http-code code="404" />
  432. */
  433. if (is_array($value)) {
  434. return isset($value['code']) ? $value : ['code' => key($value), 'urls' => current($value)];
  435. }
  436. return ['code' => $value, 'urls' => []];
  437. }, $values);
  438. })
  439. ->end()
  440. ->prototype('array')
  441. ->children()
  442. ->scalarNode('code')->end()
  443. ->arrayNode('urls')
  444. ->prototype('scalar')->end()
  445. ->end()
  446. ->end()
  447. ->end()
  448. ->end()
  449. ->arrayNode('accepted_levels') // filter
  450. ->canBeUnset()
  451. ->prototype('scalar')->end()
  452. ->end()
  453. ->scalarNode('min_level')->defaultValue('DEBUG')->end() // filter
  454. ->scalarNode('max_level')->defaultValue('EMERGENCY')->end() //filter
  455. ->scalarNode('buffer_size')->defaultValue(0)->end() // fingers_crossed and buffer
  456. ->booleanNode('flush_on_overflow')->defaultFalse()->end() // buffer
  457. ->scalarNode('handler')->end() // fingers_crossed and buffer
  458. ->scalarNode('url')->end() // cube
  459. ->scalarNode('exchange')->end() // amqp
  460. ->scalarNode('exchange_name')->defaultValue('log')->end() // amqp
  461. ->scalarNode('room')->end() // hipchat
  462. ->scalarNode('message_format')->defaultValue('text')->end() // hipchat
  463. ->scalarNode('api_version')->defaultNull()->end() // hipchat
  464. ->scalarNode('channel')->defaultNull()->end() // slack & slackwebhook & slackbot
  465. ->scalarNode('bot_name')->defaultValue('Monolog')->end() // slack & slackwebhook
  466. ->scalarNode('use_attachment')->defaultTrue()->end() // slack & slackwebhook
  467. ->scalarNode('use_short_attachment')->defaultFalse()->end() // slack & slackwebhook
  468. ->scalarNode('include_extra')->defaultFalse()->end() // slack & slackwebhook
  469. ->scalarNode('icon_emoji')->defaultNull()->end() // slack & slackwebhook
  470. ->scalarNode('webhook_url')->end() // slackwebhook
  471. ->scalarNode('team')->end() // slackbot
  472. ->scalarNode('notify')->defaultFalse()->end() // hipchat
  473. ->scalarNode('nickname')->defaultValue('Monolog')->end() // hipchat
  474. ->scalarNode('token')->end() // pushover & hipchat & loggly & logentries & flowdock & rollbar & slack & slackbot & insightops
  475. ->scalarNode('region')->end() // insightops
  476. ->scalarNode('source')->end() // flowdock
  477. ->booleanNode('use_ssl')->defaultTrue()->end() // logentries & hipchat & insightops
  478. ->variableNode('user') // pushover
  479. ->validate()
  480. ->ifTrue(function ($v) {
  481. return !is_string($v) && !is_array($v);
  482. })
  483. ->thenInvalid('User must be a string or an array.')
  484. ->end()
  485. ->end()
  486. ->scalarNode('title')->defaultNull()->end() // pushover
  487. ->scalarNode('host')->defaultNull()->end() // syslogudp & hipchat
  488. ->scalarNode('port')->defaultValue(514)->end() // syslogudp
  489. ->arrayNode('publisher')
  490. ->canBeUnset()
  491. ->beforeNormalization()
  492. ->ifString()
  493. ->then(function ($v) { return ['id' => $v]; })
  494. ->end()
  495. ->children()
  496. ->scalarNode('id')->end()
  497. ->scalarNode('hostname')->end()
  498. ->scalarNode('port')->defaultValue(12201)->end()
  499. ->scalarNode('chunk_size')->defaultValue(1420)->end()
  500. ->end()
  501. ->validate()
  502. ->ifTrue(function ($v) {
  503. return !isset($v['id']) && !isset($v['hostname']);
  504. })
  505. ->thenInvalid('What must be set is either the hostname or the id.')
  506. ->end()
  507. ->end() // gelf
  508. ->arrayNode('mongo')
  509. ->canBeUnset()
  510. ->beforeNormalization()
  511. ->ifString()
  512. ->then(function ($v) { return ['id' => $v]; })
  513. ->end()
  514. ->children()
  515. ->scalarNode('id')->end()
  516. ->scalarNode('host')->end()
  517. ->scalarNode('port')->defaultValue(27017)->end()
  518. ->scalarNode('user')->end()
  519. ->scalarNode('pass')->end()
  520. ->scalarNode('database')->defaultValue('monolog')->end()
  521. ->scalarNode('collection')->defaultValue('logs')->end()
  522. ->end()
  523. ->validate()
  524. ->ifTrue(function ($v) {
  525. return !isset($v['id']) && !isset($v['host']);
  526. })
  527. ->thenInvalid('What must be set is either the host or the id.')
  528. ->end()
  529. ->validate()
  530. ->ifTrue(function ($v) {
  531. return isset($v['user']) && !isset($v['pass']);
  532. })
  533. ->thenInvalid('If you set user, you must provide a password.')
  534. ->end()
  535. ->end() // mongo
  536. ->arrayNode('elasticsearch')
  537. ->canBeUnset()
  538. ->beforeNormalization()
  539. ->ifString()
  540. ->then(function ($v) { return ['id' => $v]; })
  541. ->end()
  542. ->children()
  543. ->scalarNode('id')->end()
  544. ->scalarNode('host')->end()
  545. ->scalarNode('port')->defaultValue(9200)->end()
  546. ->scalarNode('transport')->defaultValue('Http')->end()
  547. ->scalarNode('user')->defaultNull()->end()
  548. ->scalarNode('password')->defaultNull()->end()
  549. ->end()
  550. ->validate()
  551. ->ifTrue(function ($v) {
  552. return !isset($v['id']) && !isset($v['host']);
  553. })
  554. ->thenInvalid('What must be set is either the host or the id.')
  555. ->end()
  556. ->end() // elasticsearch
  557. ->scalarNode('index')->defaultValue('monolog')->end() // elasticsearch
  558. ->scalarNode('document_type')->defaultValue('logs')->end() // elasticsearch
  559. ->scalarNode('ignore_error')->defaultValue(false)->end() // elasticsearch
  560. ->arrayNode('redis')
  561. ->canBeUnset()
  562. ->beforeNormalization()
  563. ->ifString()
  564. ->then(function ($v) { return ['id' => $v]; })
  565. ->end()
  566. ->children()
  567. ->scalarNode('id')->end()
  568. ->scalarNode('host')->end()
  569. ->scalarNode('password')->defaultNull()->end()
  570. ->scalarNode('port')->defaultValue(6379)->end()
  571. ->scalarNode('database')->defaultValue(0)->end()
  572. ->scalarNode('key_name')->defaultValue('monolog_redis')->end()
  573. ->end()
  574. ->validate()
  575. ->ifTrue(function ($v) {
  576. return !isset($v['id']) && !isset($v['host']);
  577. })
  578. ->thenInvalid('What must be set is either the host or the service id of the Redis client.')
  579. ->end()
  580. ->end() // redis
  581. ->arrayNode('predis')
  582. ->canBeUnset()
  583. ->beforeNormalization()
  584. ->ifString()
  585. ->then(function ($v) { return ['id' => $v]; })
  586. ->end()
  587. ->children()
  588. ->scalarNode('id')->end()
  589. ->scalarNode('host')->end()
  590. ->end()
  591. ->validate()
  592. ->ifTrue(function ($v) {
  593. return !isset($v['id']) && !isset($v['host']);
  594. })
  595. ->thenInvalid('What must be set is either the host or the service id of the Predis client.')
  596. ->end()
  597. ->end() // predis
  598. ->arrayNode('config')
  599. ->canBeUnset()
  600. ->prototype('scalar')->end()
  601. ->end() // rollbar
  602. ->arrayNode('members') // group, whatfailuregroup
  603. ->canBeUnset()
  604. ->performNoDeepMerging()
  605. ->prototype('scalar')->end()
  606. ->end()
  607. ->scalarNode('from_email')->end() // swift_mailer, native_mailer, symfony_mailer and flowdock
  608. ->arrayNode('to_email') // swift_mailer, native_mailer and symfony_mailer
  609. ->prototype('scalar')->end()
  610. ->beforeNormalization()
  611. ->ifString()
  612. ->then(function ($v) { return [$v]; })
  613. ->end()
  614. ->end()
  615. ->scalarNode('subject')->end() // swift_mailer, native_mailer and symfony_mailer
  616. ->scalarNode('content_type')->defaultNull()->end() // swift_mailer and symfony_mailer
  617. ->arrayNode('headers') // native_mailer
  618. ->canBeUnset()
  619. ->scalarPrototype()->end()
  620. ->end()
  621. ->scalarNode('mailer')->defaultNull()->end() // swift_mailer and symfony_mailer
  622. ->arrayNode('email_prototype') // swift_mailer and symfony_mailer
  623. ->canBeUnset()
  624. ->beforeNormalization()
  625. ->ifString()
  626. ->then(function ($v) { return ['id' => $v]; })
  627. ->end()
  628. ->children()
  629. ->scalarNode('id')->isRequired()->end()
  630. ->scalarNode('method')->defaultNull()->end()
  631. ->end()
  632. ->end()
  633. ->booleanNode('lazy')->defaultValue(true)->end() // swift_mailer
  634. ->scalarNode('connection_string')->end() // socket_handler
  635. ->scalarNode('timeout')->end() // socket_handler, logentries, pushover, hipchat & slack
  636. ->scalarNode('time')->defaultValue(60)->end() // deduplication
  637. ->scalarNode('deduplication_level')->defaultValue(Logger::ERROR)->end() // deduplication
  638. ->scalarNode('store')->defaultNull()->end() // deduplication
  639. ->scalarNode('connection_timeout')->end() // socket_handler, logentries, pushover, hipchat & slack
  640. ->booleanNode('persistent')->end() // socket_handler
  641. ->scalarNode('dsn')->end() // raven_handler, sentry_handler
  642. ->scalarNode('hub_id')->defaultNull()->end() // sentry_handler
  643. ->scalarNode('client_id')->defaultNull()->end() // raven_handler, sentry_handler
  644. ->scalarNode('auto_log_stacks')->defaultFalse()->end() // raven_handler
  645. ->scalarNode('release')->defaultNull()->end() // raven_handler, sentry_handler
  646. ->scalarNode('environment')->defaultNull()->end() // raven_handler, sentry_handler
  647. ->scalarNode('message_type')->defaultValue(0)->end() // error_log
  648. ->arrayNode('tags') // loggly
  649. ->beforeNormalization()
  650. ->ifString()
  651. ->then(function ($v) { return explode(',', $v); })
  652. ->end()
  653. ->beforeNormalization()
  654. ->ifArray()
  655. ->then(function ($v) { return array_filter(array_map('trim', $v)); })
  656. ->end()
  657. ->prototype('scalar')->end()
  658. ->end()
  659. // console
  660. ->variableNode('console_formater_options')
  661. ->setDeprecated(...$this->getDeprecationMsg('"%path%.%node%" is deprecated, use "%path%.console_formatter_options" instead.', 3.7))
  662. ->validate()
  663. ->ifTrue(function ($v) {
  664. return !is_array($v);
  665. })
  666. ->thenInvalid('The console_formater_options must be an array.')
  667. ->end()
  668. ->end()
  669. ->variableNode('console_formatter_options')
  670. ->defaultValue([])
  671. ->validate()
  672. ->ifTrue(static function ($v) { return !is_array($v); })
  673. ->thenInvalid('The console_formatter_options must be an array.')
  674. ->end()
  675. ->end()
  676. ->arrayNode('verbosity_levels') // console
  677. ->beforeNormalization()
  678. ->ifArray()
  679. ->then(function ($v) {
  680. $map = [];
  681. $verbosities = ['VERBOSITY_QUIET', 'VERBOSITY_NORMAL', 'VERBOSITY_VERBOSE', 'VERBOSITY_VERY_VERBOSE', 'VERBOSITY_DEBUG'];
  682. // allow numeric indexed array with ascendning verbosity and lowercase names of the constants
  683. foreach ($v as $verbosity => $level) {
  684. if (is_int($verbosity) && isset($verbosities[$verbosity])) {
  685. $map[$verbosities[$verbosity]] = strtoupper($level);
  686. } else {
  687. $map[strtoupper($verbosity)] = strtoupper($level);
  688. }
  689. }
  690. return $map;
  691. })
  692. ->end()
  693. ->children()
  694. ->scalarNode('VERBOSITY_QUIET')->defaultValue('ERROR')->end()
  695. ->scalarNode('VERBOSITY_NORMAL')->defaultValue('WARNING')->end()
  696. ->scalarNode('VERBOSITY_VERBOSE')->defaultValue('NOTICE')->end()
  697. ->scalarNode('VERBOSITY_VERY_VERBOSE')->defaultValue('INFO')->end()
  698. ->scalarNode('VERBOSITY_DEBUG')->defaultValue('DEBUG')->end()
  699. ->end()
  700. ->validate()
  701. ->always(function ($v) {
  702. $map = [];
  703. foreach ($v as $verbosity => $level) {
  704. $verbosityConstant = 'Symfony\Component\Console\Output\OutputInterface::'.$verbosity;
  705. if (!defined($verbosityConstant)) {
  706. throw new InvalidConfigurationException(sprintf(
  707. 'The configured verbosity "%s" is invalid as it is not defined in Symfony\Component\Console\Output\OutputInterface.',
  708. $verbosity
  709. ));
  710. }
  711. if (!is_numeric($level)) {
  712. $levelConstant = 'Monolog\Logger::'.$level;
  713. if (!defined($levelConstant)) {
  714. throw new InvalidConfigurationException(sprintf(
  715. 'The configured minimum log level "%s" for verbosity "%s" is invalid as it is not defined in Monolog\Logger.',
  716. $level, $verbosity
  717. ));
  718. }
  719. $level = constant($levelConstant);
  720. } else {
  721. $level = (int) $level;
  722. }
  723. $map[constant($verbosityConstant)] = $level;
  724. }
  725. return $map;
  726. })
  727. ->end()
  728. ->end()
  729. ->arrayNode('channels')
  730. ->fixXmlConfig('channel', 'elements')
  731. ->canBeUnset()
  732. ->beforeNormalization()
  733. ->ifString()
  734. ->then(function ($v) { return ['elements' => [$v]]; })
  735. ->end()
  736. ->beforeNormalization()
  737. ->ifTrue(function ($v) { return is_array($v) && is_numeric(key($v)); })
  738. ->then(function ($v) { return ['elements' => $v]; })
  739. ->end()
  740. ->validate()
  741. ->ifTrue(function ($v) { return empty($v); })
  742. ->thenUnset()
  743. ->end()
  744. ->validate()
  745. ->always(function ($v) {
  746. $isExclusive = null;
  747. if (isset($v['type'])) {
  748. $isExclusive = 'exclusive' === $v['type'];
  749. }
  750. $elements = [];
  751. foreach ($v['elements'] as $element) {
  752. if (0 === strpos($element, '!')) {
  753. if (false === $isExclusive) {
  754. throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list.');
  755. }
  756. $elements[] = substr($element, 1);
  757. $isExclusive = true;
  758. } else {
  759. if (true === $isExclusive) {
  760. throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list');
  761. }
  762. $elements[] = $element;
  763. $isExclusive = false;
  764. }
  765. }
  766. if (!count($elements)) {
  767. return null;
  768. }
  769. return ['type' => $isExclusive ? 'exclusive' : 'inclusive', 'elements' => $elements];
  770. })
  771. ->end()
  772. ->children()
  773. ->scalarNode('type')
  774. ->validate()
  775. ->ifNotInArray(['inclusive', 'exclusive'])
  776. ->thenInvalid('The type of channels has to be inclusive or exclusive')
  777. ->end()
  778. ->end()
  779. ->arrayNode('elements')
  780. ->prototype('scalar')->end()
  781. ->end()
  782. ->end()
  783. ->end()
  784. ->scalarNode('formatter')->end()
  785. ->booleanNode('nested')->defaultFalse()->end()
  786. ->end()
  787. ->beforeNormalization()
  788. ->always(static function ($v) {
  789. if (empty($v['console_formatter_options']) && !empty($v['console_formater_options'])) {
  790. $v['console_formatter_options'] = $v['console_formater_options'];
  791. }
  792. return $v;
  793. })
  794. ->end()
  795. ->validate()
  796. ->always(static function ($v) { unset($v['console_formater_options']); return $v; })
  797. ->end()
  798. ->validate()
  799. ->ifTrue(function ($v) { return 'service' === $v['type'] && !empty($v['formatter']); })
  800. ->thenInvalid('Service handlers can not have a formatter configured in the bundle, you must reconfigure the service itself instead')
  801. ->end()
  802. ->validate()
  803. ->ifTrue(function ($v) { return ('fingers_crossed' === $v['type'] || 'buffer' === $v['type'] || 'filter' === $v['type']) && empty($v['handler']); })
  804. ->thenInvalid('The handler has to be specified to use a FingersCrossedHandler or BufferHandler or FilterHandler')
  805. ->end()
  806. ->validate()
  807. ->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_404s']) && !empty($v['activation_strategy']); })
  808. ->thenInvalid('You can not use excluded_404s together with a custom activation_strategy in a FingersCrossedHandler')
  809. ->end()
  810. ->validate()
  811. ->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_http_codes']) && !empty($v['activation_strategy']); })
  812. ->thenInvalid('You can not use excluded_http_codes together with a custom activation_strategy in a FingersCrossedHandler')
  813. ->end()
  814. ->validate()
  815. ->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_http_codes']) && !empty($v['excluded_404s']); })
  816. ->thenInvalid('You can not use excluded_http_codes together with excluded_404s in a FingersCrossedHandler')
  817. ->end()
  818. ->validate()
  819. ->ifTrue(function ($v) { return 'fingers_crossed' !== $v['type'] && (!empty($v['excluded_http_codes']) || !empty($v['excluded_404s'])); })
  820. ->thenInvalid('You can only use excluded_http_codes/excluded_404s with a FingersCrossedHandler definition')
  821. ->end()
  822. ->validate()
  823. ->ifTrue(function ($v) { return 'filter' === $v['type'] && "DEBUG" !== $v['min_level'] && !empty($v['accepted_levels']); })
  824. ->thenInvalid('You can not use min_level together with accepted_levels in a FilterHandler')
  825. ->end()
  826. ->validate()
  827. ->ifTrue(function ($v) { return 'filter' === $v['type'] && "EMERGENCY" !== $v['max_level'] && !empty($v['accepted_levels']); })
  828. ->thenInvalid('You can not use max_level together with accepted_levels in a FilterHandler')
  829. ->end()
  830. ->validate()
  831. ->ifTrue(function ($v) { return 'rollbar' === $v['type'] && !empty($v['id']) && !empty($v['token']); })
  832. ->thenInvalid('You can not use both an id and a token in a RollbarHandler')
  833. ->end()
  834. ->validate()
  835. ->ifTrue(function ($v) { return 'rollbar' === $v['type'] && empty($v['id']) && empty($v['token']); })
  836. ->thenInvalid('The id or the token has to be specified to use a RollbarHandler')
  837. ->end()
  838. ->validate()
  839. ->ifTrue(function ($v) { return 'swift_mailer' === $v['type'] && empty($v['email_prototype']) && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); })
  840. ->thenInvalid('The sender, recipient and subject or an email prototype have to be specified to use a SwiftMailerHandler')
  841. ->end()
  842. ->validate()
  843. ->ifTrue(function ($v) { return 'native_mailer' === $v['type'] && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); })
  844. ->thenInvalid('The sender, recipient and subject have to be specified to use a NativeMailerHandler')
  845. ->end()
  846. ->validate()
  847. ->ifTrue(function ($v) { return 'symfony_mailer' === $v['type'] && empty($v['email_prototype']) && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); })
  848. ->thenInvalid('The sender, recipient and subject or an email prototype have to be specified to use the Symfony MailerHandler')
  849. ->end()
  850. ->validate()
  851. ->ifTrue(function ($v) { return 'service' === $v['type'] && !isset($v['id']); })
  852. ->thenInvalid('The id has to be specified to use a service as handler')
  853. ->end()
  854. ->validate()
  855. ->ifTrue(function ($v) { return 'syslogudp' === $v['type'] && !isset($v['host']); })
  856. ->thenInvalid('The host has to be specified to use a syslogudp as handler')
  857. ->end()
  858. ->validate()
  859. ->ifTrue(function ($v) { return 'gelf' === $v['type'] && !isset($v['publisher']); })
  860. ->thenInvalid('The publisher has to be specified to use a GelfHandler')
  861. ->end()
  862. ->validate()
  863. ->ifTrue(function ($v) { return 'socket' === $v['type'] && !isset($v['connection_string']); })
  864. ->thenInvalid('The connection_string has to be specified to use a SocketHandler')
  865. ->end()
  866. ->validate()
  867. ->ifTrue(function ($v) { return 'pushover' === $v['type'] && (empty($v['token']) || empty($v['user'])); })
  868. ->thenInvalid('The token and user have to be specified to use a PushoverHandler')
  869. ->end()
  870. ->validate()
  871. ->ifTrue(function ($v) { return 'raven' === $v['type'] && !array_key_exists('dsn', $v) && null === $v['client_id']; })
  872. ->thenInvalid('The DSN has to be specified to use a RavenHandler')
  873. ->end()
  874. ->validate()
  875. ->ifTrue(function ($v) { return 'sentry' === $v['type'] && !array_key_exists('dsn', $v) && null === $v['hub_id'] && null === $v['client_id']; })
  876. ->thenInvalid('The DSN has to be specified to use Sentry\'s handler')
  877. ->end()
  878. ->validate()
  879. ->ifTrue(function ($v) { return 'sentry' === $v['type'] && null !== $v['hub_id'] && null !== $v['client_id']; })
  880. ->thenInvalid('You can not use both a hub_id and a client_id in a Sentry handler')
  881. ->end()
  882. ->validate()
  883. ->ifTrue(function ($v) { return 'hipchat' === $v['type'] && (empty($v['token']) || empty($v['room'])); })
  884. ->thenInvalid('The token and room have to be specified to use a HipChatHandler')
  885. ->end()
  886. ->validate()
  887. ->ifTrue(function ($v) { return 'hipchat' === $v['type'] && !in_array($v['message_format'], ['text', 'html']); })
  888. ->thenInvalid('The message_format has to be "text" or "html" in a HipChatHandler')
  889. ->end()
  890. ->validate()
  891. ->ifTrue(function ($v) { return 'hipchat' === $v['type'] && null !== $v['api_version'] && !in_array($v['api_version'], ['v1', 'v2'], true); })
  892. ->thenInvalid('The api_version has to be "v1" or "v2" in a HipChatHandler')
  893. ->end()
  894. ->validate()
  895. ->ifTrue(function ($v) { return 'slack' === $v['type'] && (empty($v['token']) || empty($v['channel'])); })
  896. ->thenInvalid('The token and channel have to be specified to use a SlackHandler')
  897. ->end()
  898. ->validate()
  899. ->ifTrue(function ($v) { return 'slackwebhook' === $v['type'] && (empty($v['webhook_url'])); })
  900. ->thenInvalid('The webhook_url have to be specified to use a SlackWebhookHandler')
  901. ->end()
  902. ->validate()
  903. ->ifTrue(function ($v) { return 'slackbot' === $v['type'] && (empty($v['team']) || empty($v['token']) || empty($v['channel'])); })
  904. ->thenInvalid('The team, token and channel have to be specified to use a SlackbotHandler')
  905. ->end()
  906. ->validate()
  907. ->ifTrue(function ($v) { return 'cube' === $v['type'] && empty($v['url']); })
  908. ->thenInvalid('The url has to be specified to use a CubeHandler')
  909. ->end()
  910. ->validate()
  911. ->ifTrue(function ($v) { return 'mongo' === $v['type'] && !isset($v['mongo']); })
  912. ->thenInvalid('The mongo configuration has to be specified to use a MongoHandler')
  913. ->end()
  914. ->validate()
  915. ->ifTrue(function ($v) { return 'amqp' === $v['type'] && empty($v['exchange']); })
  916. ->thenInvalid('The exchange has to be specified to use a AmqpHandler')
  917. ->end()
  918. ->validate()
  919. ->ifTrue(function ($v) { return 'loggly' === $v['type'] && empty($v['token']); })
  920. ->thenInvalid('The token has to be specified to use a LogglyHandler')
  921. ->end()
  922. ->validate()
  923. ->ifTrue(function ($v) { return 'loggly' === $v['type'] && !empty($v['tags']); })
  924. ->then(function ($v) {
  925. $invalidTags = preg_grep('/^[a-z0-9][a-z0-9\.\-_]*$/i', $v['tags'], PREG_GREP_INVERT);
  926. if (!empty($invalidTags)) {
  927. throw new InvalidConfigurationException(sprintf('The following Loggly tags are invalid: %s.', implode(', ', $invalidTags)));
  928. }
  929. return $v;
  930. })
  931. ->end()
  932. ->validate()
  933. ->ifTrue(function ($v) { return 'logentries' === $v['type'] && empty($v['token']); })
  934. ->thenInvalid('The token has to be specified to use a LogEntriesHandler')
  935. ->end()
  936. ->validate()
  937. ->ifTrue(function ($v) { return 'insightops' === $v['type'] && empty($v['token']); })
  938. ->thenInvalid('The token has to be specified to use a InsightOpsHandler')
  939. ->end()
  940. ->validate()
  941. ->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['token']); })
  942. ->thenInvalid('The token has to be specified to use a FlowdockHandler')
  943. ->end()
  944. ->validate()
  945. ->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['from_email']); })
  946. ->thenInvalid('The from_email has to be specified to use a FlowdockHandler')
  947. ->end()
  948. ->validate()
  949. ->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['source']); })
  950. ->thenInvalid('The source has to be specified to use a FlowdockHandler')
  951. ->end()
  952. ->validate()
  953. ->ifTrue(function ($v) { return 'server_log' === $v['type'] && empty($v['host']); })
  954. ->thenInvalid('The host has to be specified to use a ServerLogHandler')
  955. ->end()
  956. ->validate()
  957. ->ifTrue(function ($v) { return 'redis' === $v['type'] && empty($v['redis']); })
  958. ->thenInvalid('The host has to be specified to use a RedisLogHandler')
  959. ->end()
  960. ->validate()
  961. ->ifTrue(function ($v) { return 'predis' === $v['type'] && empty($v['redis']); })
  962. ->thenInvalid('The host has to be specified to use a RedisLogHandler')
  963. ->end()
  964. ->end()
  965. ->validate()
  966. ->ifTrue(function ($v) { return isset($v['debug']); })
  967. ->thenInvalid('The "debug" name cannot be used as it is reserved for the handler of the profiler')
  968. ->end()
  969. ->example([
  970. 'syslog' => [
  971. 'type' => 'stream',
  972. 'path' => '/var/log/symfony.log',
  973. 'level' => 'ERROR',
  974. 'bubble' => 'false',
  975. 'formatter' => 'my_formatter',
  976. ],
  977. 'main' => [
  978. 'type' => 'fingers_crossed',
  979. 'action_level' => 'WARNING',
  980. 'buffer_size' => 30,
  981. 'handler' => 'custom',
  982. ],
  983. 'custom' => [
  984. 'type' => 'service',
  985. 'id' => 'my_handler',
  986. ]
  987. ])
  988. ->end()
  989. ->end()
  990. ;
  991. return $treeBuilder;
  992. }
  993. /**
  994. * Returns the correct deprecation param's as an array for setDeprecated.
  995. *
  996. * Symfony/Config v5.1 introduces a deprecation notice when calling
  997. * setDeprecation() with less than 3 args and the getDeprecation method was
  998. * introduced at the same time. By checking if getDeprecation() exists,
  999. * we can determine the correct param count to use when calling setDeprecated.
  1000. *
  1001. * @return array{0:string}|array{0:string, 1: numeric-string, string}
  1002. */
  1003. private function getDeprecationMsg(string $message, string $version): array
  1004. {
  1005. if (method_exists(BaseNode::class, 'getDeprecation')) {
  1006. return [
  1007. 'symfony/monolog-bundle',
  1008. $version,
  1009. $message,
  1010. ];
  1011. }
  1012. return [$message];
  1013. }
  1014. }