Initial commit

This commit is contained in:
2020-10-07 10:37:15 +02:00
commit ce5f440392
28157 changed files with 4429172 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
/**
* Registers processors in Monolog loggers or handlers.
*
* @author Christophe Coevoet <stof@notk.org>
*/
class AddProcessorsPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('monolog.logger')) {
return;
}
foreach ($container->findTaggedServiceIds('monolog.processor') as $id => $tags) {
foreach ($tags as $tag) {
if (!empty($tag['channel']) && !empty($tag['handler'])) {
throw new \InvalidArgumentException(sprintf('you cannot specify both the "handler" and "channel" attributes for the "monolog.processor" tag on service "%s"', $id));
}
if (!empty($tag['handler'])) {
$definition = $container->findDefinition(sprintf('monolog.handler.%s', $tag['handler']));
} elseif (!empty($tag['channel'])) {
if ('app' === $tag['channel']) {
$definition = $container->getDefinition('monolog.logger');
} else {
$definition = $container->getDefinition(sprintf('monolog.logger.%s', $tag['channel']));
}
} else {
$definition = $container->getDefinition('monolog.logger_prototype');
}
if (!empty($tag['method'])) {
$processor = array(new Reference($id), $tag['method']);
} else {
// If no method is defined, fallback to use __invoke
$processor = new Reference($id);
}
$definition->addMethodCall('pushProcessor', array($processor));
}
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
/**
* Sets the transport for Swiftmailer handlers depending on the existing
* container definitions.
*
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
*/
class AddSwiftMailerTransportPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$handlers = $container->getParameter('monolog.swift_mailer.handlers');
foreach ($handlers as $id) {
$definition = $container->getDefinition($id);
$mailerId = (string) $definition->getArgument(0);
// Try to fetch the transport for a non-default mailer first, then go with the default swiftmailer
$possibleServices = array(
$mailerId.'.transport.real',
$mailerId.'.transport',
'swiftmailer.transport.real',
'swiftmailer.transport',
);
foreach ($possibleServices as $serviceId) {
if ($container->hasAlias($serviceId) || $container->hasDefinition($serviceId)) {
$definition->addMethodCall(
'setTransport',
array(new Reference($serviceId))
);
break;
}
}
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Definition;
use Monolog\Logger;
/**
* Adds the DebugHandler when the profiler is enabled and kernel.debug is true.
*
* @author Christophe Coevoet <stof@notk.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @deprecated since version 2.12, to be removed in 4.0. Use AddDebugLogProcessorPass in FrameworkBundle instead.
*/
class DebugHandlerPass implements CompilerPassInterface
{
private $channelPass;
public function __construct(LoggerChannelPass $channelPass)
{
// Trigger the deprecation only when using a Symfony version supporting the new feature (i.e. 3.2+)
if (class_exists('Symfony\Bridge\Monolog\Processor\DebugProcessor') && class_exists('Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddDebugLogProcessorPass')) {
@trigger_error('The '.__CLASS__.' class is deprecated since version 2.12 and will be removed in 4.0. Use AddDebugLogProcessorPass in FrameworkBundle instead.', E_USER_DEPRECATED);
}
$this->channelPass = $channelPass;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('profiler')) {
return;
}
if (!$container->getParameter('kernel.debug')) {
return;
}
$debugHandler = new Definition('Symfony\Bridge\Monolog\Handler\DebugHandler', array(Logger::DEBUG, true));
$container->setDefinition('monolog.handler.debug', $debugHandler);
foreach ($this->channelPass->getChannels() as $channel) {
$container
->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel)
->addMethodCall('pushHandler', array(new Reference('monolog.handler.debug')));
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Fixes loggers with no handlers (by registering a "null" one).
*
* Monolog 1.x adds a default handler logging on STDERR when a logger has
* no registered handlers. This is NOT what what we want in Symfony, so in such
* cases, we add a "null" handler to avoid the issue.
*
* Note that Monolog 2.x does not register a default handler anymore, so this pass can
* be removed when MonologBundle minimum version of Monolog is bumped to 2.0.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @see https://github.com/Seldaek/monolog/commit/ad37b7b2d11f300cbace9f5e84f855d329519e28
*/
class FixEmptyLoggerPass implements CompilerPassInterface
{
private $channelPass;
public function __construct(LoggerChannelPass $channelPass)
{
$this->channelPass = $channelPass;
}
public function process(ContainerBuilder $container)
{
$container->register('monolog.handler.null_internal', 'Monolog\Handler\NullHandler');
foreach ($this->channelPass->getChannels() as $channel) {
$def = $container->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel);
foreach ($def->getMethodCalls() as $method) {
if ('pushHandler' === $method[0]) {
continue 2;
}
}
$def->addMethodCall('pushHandler', array(new Reference('monolog.handler.null_internal')));
}
}
}

View File

@@ -0,0 +1,155 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* Replaces the default logger by another one with its own channel for tagged services.
*
* @author Christophe Coevoet <stof@notk.org>
*/
class LoggerChannelPass implements CompilerPassInterface
{
protected $channels = array('app');
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('monolog.logger')) {
return;
}
// create channels necessary for the handlers
foreach ($container->findTaggedServiceIds('monolog.logger') as $id => $tags) {
foreach ($tags as $tag) {
if (empty($tag['channel']) || 'app' === $tag['channel']) {
continue;
}
$resolvedChannel = $container->getParameterBag()->resolveValue($tag['channel']);
$definition = $container->getDefinition($id);
$loggerId = sprintf('monolog.logger.%s', $resolvedChannel);
$this->createLogger($resolvedChannel, $loggerId, $container);
foreach ($definition->getArguments() as $index => $argument) {
if ($argument instanceof Reference && 'logger' === (string) $argument) {
$definition->replaceArgument($index, $this->changeReference($argument, $loggerId));
}
}
$calls = $definition->getMethodCalls();
foreach ($calls as $i => $call) {
foreach ($call[1] as $index => $argument) {
if ($argument instanceof Reference && 'logger' === (string) $argument) {
$calls[$i][1][$index] = $this->changeReference($argument, $loggerId);
}
}
}
$definition->setMethodCalls($calls);
if (\method_exists($definition, 'getBindings')) {
$binding = new BoundArgument(new Reference($loggerId));
// Mark the binding as used already, to avoid reporting it as unused if the service does not use a
// logger injected through the LoggerInterface alias.
$values = $binding->getValues();
$values[2] = true;
$binding->setValues($values);
$bindings = $definition->getBindings();
$bindings['Psr\Log\LoggerInterface'] = $binding;
$definition->setBindings($bindings);
}
}
}
// create additional channels
foreach ($container->getParameter('monolog.additional_channels') as $chan) {
$loggerId = sprintf('monolog.logger.%s', $chan);
$this->createLogger($chan, $loggerId, $container);
$container->getDefinition($loggerId)->setPublic(true);
}
$container->getParameterBag()->remove('monolog.additional_channels');
// wire handlers to channels
$handlersToChannels = $container->getParameter('monolog.handlers_to_channels');
foreach ($handlersToChannels as $handler => $channels) {
foreach ($this->processChannels($channels) as $channel) {
try {
$logger = $container->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel);
} catch (InvalidArgumentException $e) {
$msg = 'Monolog configuration error: The logging channel "'.$channel.'" assigned to the "'.substr($handler, 16).'" handler does not exist.';
throw new \InvalidArgumentException($msg, 0, $e);
}
$logger->addMethodCall('pushHandler', array(new Reference($handler)));
}
}
}
public function getChannels()
{
return $this->channels;
}
protected function processChannels($configuration)
{
if (null === $configuration) {
return $this->channels;
}
if ('inclusive' === $configuration['type']) {
return $configuration['elements'] ?: $this->channels;
}
return array_diff($this->channels, $configuration['elements']);
}
protected function createLogger($channel, $loggerId, ContainerBuilder $container)
{
if (!in_array($channel, $this->channels)) {
if (class_exists('Symfony\Component\DependencyInjection\ChildDefinition')) {
$logger = new ChildDefinition('monolog.logger_prototype');
} else {
$logger = new DefinitionDecorator('monolog.logger_prototype');
}
$logger->replaceArgument(0, $channel);
$container->setDefinition($loggerId, $logger);
$this->channels[] = $channel;
}
}
/**
* Creates a copy of a reference and alters the service ID.
*
* @param Reference $reference
* @param string $serviceId
*
* @return Reference
*/
private function changeReference(Reference $reference, $serviceId)
{
if (method_exists($reference, 'isStrict')) {
// Stay compatible with Symfony 2
return new Reference($serviceId, $reference->getInvalidBehavior(), $reference->isStrict(false));
}
return new Reference($serviceId, $reference->getInvalidBehavior());
}
}

View File

@@ -0,0 +1,860 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Monolog\Logger;
/**
* This class contains the configuration information for the bundle
*
* This information is solely responsible for how the different configuration
* sections are normalized, and merged.
*
* Possible handler types and related configurations (brackets indicate optional params):
*
* - service:
* - id
*
* - stream:
* - path: string
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - console:
* - [verbosity_levels]: level => verbosity configuration
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - firephp:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - browser_console:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - gelf:
* - publisher: {id: ...} or {hostname: ..., port: ..., chunk_size: ...}
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - chromephp:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - rotating_file:
* - path: string
* - [max_files]: files to keep, defaults to zero (infinite)
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [file_permission]: string|null, defaults to null
* - [filename_format]: string, defaults to '{filename}-{date}'
* - [date_format]: string, defaults to 'Y-m-d'
*
* - mongo:
* - mongo:
* - id: optional if host is given
* - host: database host name, optional if id is given
* - [port]: defaults to 27017
* - [user]: database user name
* - pass: mandatory only if user is present
* - [database]: defaults to monolog
* - [collection]: defaults to logs
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - elasticsearch:
* - elasticsearch:
* - id: optional if host is given
* - host: elastic search host name
* - [port]: defaults to 9200
* - [index]: index name, defaults to monolog
* - [document_type]: document_type, defaults to logs
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - fingers_crossed:
* - handler: the wrapped handler's name
* - [action_level|activation_strategy]: minimum level or service id to activate the handler, defaults to WARNING
* - [excluded_404s]: if set, the strategy will be changed to one that excludes 404s coming from URLs matching any of those patterns
* - [excluded_http_codes]: if set, the strategy will be changed to one that excludes specific HTTP codes (requires Symfony Monolog bridge 4.1+)
* - [buffer_size]: defaults to 0 (unlimited)
* - [stop_buffering]: bool to disable buffering once the handler has been activated, defaults to true
* - [passthru_level]: level name or int value for messages to always flush, disabled by default
* - [bubble]: bool, defaults to true
*
* - filter:
* - handler: the wrapped handler's name
* - [accepted_levels]: list of levels to accept
* - [min_level]: minimum level to accept (only used if accepted_levels not specified)
* - [max_level]: maximum level to accept (only used if accepted_levels not specified)
* - [bubble]: bool, defaults to true
*
* - buffer:
* - handler: the wrapped handler's name
* - [buffer_size]: defaults to 0 (unlimited)
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [flush_on_overflow]: bool, defaults to false
*
* - deduplication:
* - handler: the wrapper handler's name
* - [store]: The file/path where the deduplication log should be kept, defaults to %kernel.cache_dir%/monolog_dedup_*
* - [deduplication_level]: The minimum logging level for log records to be looked at for deduplication purposes, defaults to ERROR
* - [time]: The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through, defaults to 60
* - [bubble]: bool, defaults to true
*
* - group:
* - members: the wrapped handlers by name
* - [bubble]: bool, defaults to true
*
* - whatfailuregroup:
* - members: the wrapped handlers by name
* - [bubble]: bool, defaults to true
*
* - syslog:
* - ident: string
* - [facility]: defaults to 'user', use any of the LOG_* facility constant but without LOG_ prefix, e.g. user for LOG_USER
* - [logopts]: defaults to LOG_PID
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - syslogudp:
* - host: syslogd host name
* - [port]: defaults to 514
* - [facility]: defaults to 'user', use any of the LOG_* facility constant but without LOG_ prefix, e.g. user for LOG_USER
* - [logopts]: defaults to LOG_PID
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - swift_mailer:
* - from_email: optional if email_prototype is given
* - to_email: optional if email_prototype is given
* - subject: optional if email_prototype is given
* - [email_prototype]: service id of a message, defaults to a default message with the three fields above
* - [content_type]: optional if email_prototype is given, defaults to text/plain
* - [mailer]: mailer service, defaults to mailer
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [lazy]: use service lazy loading, bool, defaults to true
*
* - native_mailer:
* - from_email: string
* - to_email: string
* - subject: string
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - socket:
* - connection_string: string
* - [timeout]: float
* - [connection_timeout]: float
* - [persistent]: bool
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - pushover:
* - token: pushover api token
* - user: user id or array of ids
* - [title]: optional title for messages, defaults to the server hostname
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [timeout]: float
* - [connection_timeout]: float
*
* - raven:
* - dsn: connection string
* - client_id: Raven client custom service id (optional)
* - [release]: release number of the application that will be attached to logs, defaults to null
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [auto_log_stacks]: bool, defaults to false
*
* - newrelic:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [app_name]: new relic app name, default null
*
* - hipchat:
* - token: hipchat api token
* - room: room id or name
* - [notify]: defaults to false
* - [nickname]: defaults to Monolog
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [use_ssl]: bool, defaults to true
* - [message_format]: text or html, defaults to text
* - [host]: defaults to "api.hipchat.com"
* - [api_version]: defaults to "v1"
* - [timeout]: float
* - [connection_timeout]: float
*
* - slack:
* - token: slack api token
* - channel: channel name (with starting #)
* - [bot_name]: defaults to Monolog
* - [icon_emoji]: defaults to null
* - [use_attachment]: bool, defaults to true
* - [use_short_attachment]: bool, defaults to false
* - [include_extra]: bool, defaults to false
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [timeout]: float
* - [connection_timeout]: float
*
* - slackwebhook:
* - webhook_url: slack webhook URL
* - channel: channel name (with starting #)
* - [bot_name]: defaults to Monolog
* - [icon_emoji]: defaults to null
* - [use_attachment]: bool, defaults to true
* - [use_short_attachment]: bool, defaults to false
* - [include_extra]: bool, defaults to false
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - slackbot:
* - team: slack team slug
* - token: slackbot token
* - channel: channel name (with starting #)
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - cube:
* - url: http/udp url to the cube server
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - amqp:
* - exchange: service id of an AMQPExchange
* - [exchange_name]: string, defaults to log
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - error_log:
* - [message_type]: int 0 or 4, defaults to 0
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - null:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - test:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - debug:
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - loggly:
* - token: loggly api token
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [tags]: tag names
*
* - logentries:
* - token: logentries api token
* - [use_ssl]: whether or not SSL encryption should be used, defaults to true
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
* - [timeout]: float
* - [connection_timeout]: float
*
* - flowdock:
* - token: flowdock api token
* - source: human readable identifier of the application
* - from_email: email address of the message sender
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - rollbar:
* - id: RollbarNotifier service (mandatory if token is not provided)
* - token: rollbar api token (skip if you provide a RollbarNotifier service id)
* - [config]: config values from https://github.com/rollbar/rollbar-php#configuration-reference
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* - server_log:
* - host: server log host. ex: 127.0.0.1:9911
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org>
*/
class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('monolog');
$rootNode = method_exists(TreeBuilder::class, 'getRootNode') ? $treeBuilder->getRootNode() : $treeBuilder->root('monolog');
$rootNode
->fixXmlConfig('channel')
->fixXmlConfig('handler')
->children()
->scalarNode('use_microseconds')->defaultTrue()->end()
->arrayNode('channels')
->canBeUnset()
->prototype('scalar')->end()
->end()
->arrayNode('handlers')
->canBeUnset()
->useAttributeAsKey('name')
->prototype('array')
->fixXmlConfig('member')
->fixXmlConfig('excluded_404')
->fixXmlConfig('excluded_http_code')
->fixXmlConfig('tag')
->fixXmlConfig('accepted_level')
->canBeUnset()
->children()
->scalarNode('type')
->isRequired()
->treatNullLike('null')
->beforeNormalization()
->always()
->then(function ($v) { return strtolower($v); })
->end()
->end()
->scalarNode('id')->end() // service & rollbar
->scalarNode('priority')->defaultValue(0)->end()
->scalarNode('level')->defaultValue('DEBUG')->end()
->booleanNode('bubble')->defaultTrue()->end()
->scalarNode('app_name')->defaultNull()->end()
->booleanNode('include_stacktraces')->defaultFalse()->end()
->booleanNode('process_psr_3_messages')->defaultTrue()->end()
->scalarNode('path')->defaultValue('%kernel.logs_dir%/%kernel.environment%.log')->end() // stream and rotating
->scalarNode('file_permission') // stream and rotating
->defaultNull()
->beforeNormalization()
->ifString()
->then(function ($v) {
if (substr($v, 0, 1) === '0') {
return octdec($v);
}
return (int) $v;
})
->end()
->end()
->scalarNode('filename_format')->defaultValue('{filename}-{date}')->end() //rotating
->scalarNode('date_format')->defaultValue('Y-m-d')->end() //rotating
->scalarNode('ident')->defaultFalse()->end() // syslog
->scalarNode('logopts')->defaultValue(LOG_PID)->end() // syslog
->scalarNode('facility')->defaultValue('user')->end() // syslog
->scalarNode('max_files')->defaultValue(0)->end() // rotating
->scalarNode('action_level')->defaultValue('WARNING')->end() // fingers_crossed
->scalarNode('activation_strategy')->defaultNull()->end() // fingers_crossed
->booleanNode('stop_buffering')->defaultTrue()->end()// fingers_crossed
->scalarNode('passthru_level')->defaultNull()->end() // fingers_crossed
->arrayNode('excluded_404s') // fingers_crossed
->canBeUnset()
->prototype('scalar')->end()
->end()
->arrayNode('excluded_http_codes') // fingers_crossed
->canBeUnset()
->beforeNormalization()
->always(function ($values) {
return array_map(function ($value) {
/*
* Allows YAML:
* excluded_http_codes: [403, 404, { 400: ['^/foo', '^/bar'] }]
*
* and XML:
* <monolog:excluded-http-code code="403">
* <monolog:url>^/foo</monolog:url>
* <monolog:url>^/bar</monolog:url>
* </monolog:excluded-http-code>
* <monolog:excluded-http-code code="404" />
*/
if (is_array($value)) {
return isset($value['code']) ? $value : array('code' => key($value), 'urls' => current($value));
}
return array('code' => $value, 'urls' => array());
}, $values);
})
->end()
->prototype('array')
->children()
->scalarNode('code')->end()
->arrayNode('urls')
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->arrayNode('accepted_levels') // filter
->canBeUnset()
->prototype('scalar')->end()
->end()
->scalarNode('min_level')->defaultValue('DEBUG')->end() // filter
->scalarNode('max_level')->defaultValue('EMERGENCY')->end() //filter
->scalarNode('buffer_size')->defaultValue(0)->end() // fingers_crossed and buffer
->booleanNode('flush_on_overflow')->defaultFalse()->end() // buffer
->scalarNode('handler')->end() // fingers_crossed and buffer
->scalarNode('url')->end() // cube
->scalarNode('exchange')->end() // amqp
->scalarNode('exchange_name')->defaultValue('log')->end() // amqp
->scalarNode('room')->end() // hipchat
->scalarNode('message_format')->defaultValue('text')->end() // hipchat
->scalarNode('api_version')->defaultNull()->end() // hipchat
->scalarNode('channel')->defaultNull()->end() // slack & slackwebhook & slackbot
->scalarNode('bot_name')->defaultValue('Monolog')->end() // slack & slackwebhook
->scalarNode('use_attachment')->defaultTrue()->end() // slack & slackwebhook
->scalarNode('use_short_attachment')->defaultFalse()->end() // slack & slackwebhook
->scalarNode('include_extra')->defaultFalse()->end() // slack & slackwebhook
->scalarNode('icon_emoji')->defaultNull()->end() // slack & slackwebhook
->scalarNode('webhook_url')->end() // slackwebhook
->scalarNode('team')->end() // slackbot
->scalarNode('notify')->defaultFalse()->end() // hipchat
->scalarNode('nickname')->defaultValue('Monolog')->end() // hipchat
->scalarNode('token')->end() // pushover & hipchat & loggly & logentries & flowdock & rollbar & slack & slackbot
->scalarNode('source')->end() // flowdock
->booleanNode('use_ssl')->defaultTrue()->end() // logentries & hipchat
->variableNode('user') // pushover
->validate()
->ifTrue(function ($v) {
return !is_string($v) && !is_array($v);
})
->thenInvalid('User must be a string or an array.')
->end()
->end()
->scalarNode('title')->defaultNull()->end() // pushover
->scalarNode('host')->defaultNull()->end() // syslogudp & hipchat
->scalarNode('port')->defaultValue(514)->end() // syslogudp
->arrayNode('publisher')
->canBeUnset()
->beforeNormalization()
->ifString()
->then(function ($v) { return array('id' => $v); })
->end()
->children()
->scalarNode('id')->end()
->scalarNode('hostname')->end()
->scalarNode('port')->defaultValue(12201)->end()
->scalarNode('chunk_size')->defaultValue(1420)->end()
->end()
->validate()
->ifTrue(function ($v) {
return !isset($v['id']) && !isset($v['hostname']);
})
->thenInvalid('What must be set is either the hostname or the id.')
->end()
->end() // gelf
->arrayNode('mongo')
->canBeUnset()
->beforeNormalization()
->ifString()
->then(function ($v) { return array('id' => $v); })
->end()
->children()
->scalarNode('id')->end()
->scalarNode('host')->end()
->scalarNode('port')->defaultValue(27017)->end()
->scalarNode('user')->end()
->scalarNode('pass')->end()
->scalarNode('database')->defaultValue('monolog')->end()
->scalarNode('collection')->defaultValue('logs')->end()
->end()
->validate()
->ifTrue(function ($v) {
return !isset($v['id']) && !isset($v['host']);
})
->thenInvalid('What must be set is either the host or the id.')
->end()
->validate()
->ifTrue(function ($v) {
return isset($v['user']) && !isset($v['pass']);
})
->thenInvalid('If you set user, you must provide a password.')
->end()
->end() // mongo
->arrayNode('elasticsearch')
->canBeUnset()
->beforeNormalization()
->ifString()
->then(function ($v) { return array('id' => $v); })
->end()
->children()
->scalarNode('id')->end()
->scalarNode('host')->end()
->scalarNode('port')->defaultValue(9200)->end()
->scalarNode('transport')->defaultValue('Http')->end()
->scalarNode('user')->defaultNull()->end()
->scalarNode('password')->defaultNull()->end()
->end()
->validate()
->ifTrue(function ($v) {
return !isset($v['id']) && !isset($v['host']);
})
->thenInvalid('What must be set is either the host or the id.')
->end()
->end() // elasticsearch
->scalarNode('index')->defaultValue('monolog')->end() // elasticsearch
->scalarNode('document_type')->defaultValue('logs')->end() // elasticsearch
->scalarNode('ignore_error')->defaultValue(false)->end() // elasticsearch
->arrayNode('config')
->canBeUnset()
->prototype('scalar')->end()
->end() // rollbar
->arrayNode('members') // group, whatfailuregroup
->canBeUnset()
->performNoDeepMerging()
->prototype('scalar')->end()
->end()
->scalarNode('from_email')->end() // swift_mailer, native_mailer and flowdock
->arrayNode('to_email') // swift_mailer and native_mailer
->prototype('scalar')->end()
->beforeNormalization()
->ifString()
->then(function ($v) { return array($v); })
->end()
->end()
->scalarNode('subject')->end() // swift_mailer and native_mailer
->scalarNode('content_type')->defaultNull()->end() // swift_mailer
->scalarNode('mailer')->defaultValue('mailer')->end() // swift_mailer
->arrayNode('email_prototype') // swift_mailer
->canBeUnset()
->beforeNormalization()
->ifString()
->then(function ($v) { return array('id' => $v); })
->end()
->children()
->scalarNode('id')->isRequired()->end()
->scalarNode('method')->defaultNull()->end()
->end()
->end()
->booleanNode('lazy')->defaultValue(true)->end() // swift_mailer
->scalarNode('connection_string')->end() // socket_handler
->scalarNode('timeout')->end() // socket_handler, logentries, pushover, hipchat & slack
->scalarNode('time')->defaultValue(60)->end() // deduplication
->scalarNode('deduplication_level')->defaultValue(Logger::ERROR)->end() // deduplication
->scalarNode('store')->defaultNull()->end() // deduplication
->scalarNode('connection_timeout')->end() // socket_handler, logentries, pushover, hipchat & slack
->booleanNode('persistent')->end() // socket_handler
->scalarNode('dsn')->end() // raven_handler
->scalarNode('client_id')->defaultNull()->end() // raven_handler
->scalarNode('auto_log_stacks')->defaultFalse()->end() // raven_handler
->scalarNode('release')->defaultNull()->end() // raven_handler
->scalarNode('message_type')->defaultValue(0)->end() // error_log
->arrayNode('tags') // loggly
->beforeNormalization()
->ifString()
->then(function ($v) { return explode(',', $v); })
->end()
->beforeNormalization()
->ifArray()
->then(function ($v) { return array_filter(array_map('trim', $v)); })
->end()
->prototype('scalar')->end()
->end()
->arrayNode('verbosity_levels') // console
->beforeNormalization()
->ifArray()
->then(function ($v) {
$map = array();
$verbosities = array('VERBOSITY_QUIET', 'VERBOSITY_NORMAL', 'VERBOSITY_VERBOSE', 'VERBOSITY_VERY_VERBOSE', 'VERBOSITY_DEBUG');
// allow numeric indexed array with ascendning verbosity and lowercase names of the constants
foreach ($v as $verbosity => $level) {
if (is_int($verbosity) && isset($verbosities[$verbosity])) {
$map[$verbosities[$verbosity]] = strtoupper($level);
} else {
$map[strtoupper($verbosity)] = strtoupper($level);
}
}
return $map;
})
->end()
->children()
->scalarNode('VERBOSITY_QUIET')->defaultValue('ERROR')->end()
->scalarNode('VERBOSITY_NORMAL')->defaultValue('WARNING')->end()
->scalarNode('VERBOSITY_VERBOSE')->defaultValue('NOTICE')->end()
->scalarNode('VERBOSITY_VERY_VERBOSE')->defaultValue('INFO')->end()
->scalarNode('VERBOSITY_DEBUG')->defaultValue('DEBUG')->end()
->end()
->validate()
->always(function ($v) {
$map = array();
foreach ($v as $verbosity => $level) {
$verbosityConstant = 'Symfony\Component\Console\Output\OutputInterface::'.$verbosity;
if (!defined($verbosityConstant)) {
throw new InvalidConfigurationException(sprintf(
'The configured verbosity "%s" is invalid as it is not defined in Symfony\Component\Console\Output\OutputInterface.',
$verbosity
));
}
if (!is_numeric($level)) {
$levelConstant = 'Monolog\Logger::'.$level;
if (!defined($levelConstant)) {
throw new InvalidConfigurationException(sprintf(
'The configured minimum log level "%s" for verbosity "%s" is invalid as it is not defined in Monolog\Logger.',
$level, $verbosity
));
}
$level = constant($levelConstant);
} else {
$level = (int) $level;
}
$map[constant($verbosityConstant)] = $level;
}
return $map;
})
->end()
->end()
->arrayNode('channels')
->fixXmlConfig('channel', 'elements')
->canBeUnset()
->beforeNormalization()
->ifString()
->then(function ($v) { return array('elements' => array($v)); })
->end()
->beforeNormalization()
->ifTrue(function ($v) { return is_array($v) && is_numeric(key($v)); })
->then(function ($v) { return array('elements' => $v); })
->end()
->validate()
->ifTrue(function ($v) { return empty($v); })
->thenUnset()
->end()
->validate()
->always(function ($v) {
$isExclusive = null;
if (isset($v['type'])) {
$isExclusive = 'exclusive' === $v['type'];
}
$elements = array();
foreach ($v['elements'] as $element) {
if (0 === strpos($element, '!')) {
if (false === $isExclusive) {
throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list.');
}
$elements[] = substr($element, 1);
$isExclusive = true;
} else {
if (true === $isExclusive) {
throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list');
}
$elements[] = $element;
$isExclusive = false;
}
}
if (!count($elements)) {
return null;
}
return array('type' => $isExclusive ? 'exclusive' : 'inclusive', 'elements' => $elements);
})
->end()
->children()
->scalarNode('type')
->validate()
->ifNotInArray(array('inclusive', 'exclusive'))
->thenInvalid('The type of channels has to be inclusive or exclusive')
->end()
->end()
->arrayNode('elements')
->prototype('scalar')->end()
->end()
->end()
->end()
->scalarNode('formatter')->end()
->booleanNode('nested')->defaultFalse()->end()
->end()
->validate()
->ifTrue(function ($v) { return 'service' === $v['type'] && !empty($v['formatter']); })
->thenInvalid('Service handlers can not have a formatter configured in the bundle, you must reconfigure the service itself instead')
->end()
->validate()
->ifTrue(function ($v) { return ('fingers_crossed' === $v['type'] || 'buffer' === $v['type'] || 'filter' === $v['type']) && empty($v['handler']); })
->thenInvalid('The handler has to be specified to use a FingersCrossedHandler or BufferHandler or FilterHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_404s']) && !empty($v['activation_strategy']); })
->thenInvalid('You can not use excluded_404s together with a custom activation_strategy in a FingersCrossedHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_http_codes']) && !empty($v['activation_strategy']); })
->thenInvalid('You can not use excluded_http_codes together with a custom activation_strategy in a FingersCrossedHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_http_codes']) && !empty($v['excluded_404s']); })
->thenInvalid('You can not use excluded_http_codes together with excluded_404s in a FingersCrossedHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'filter' === $v['type'] && "DEBUG" !== $v['min_level'] && !empty($v['accepted_levels']); })
->thenInvalid('You can not use min_level together with accepted_levels in a FilterHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'filter' === $v['type'] && "EMERGENCY" !== $v['max_level'] && !empty($v['accepted_levels']); })
->thenInvalid('You can not use max_level together with accepted_levels in a FilterHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'rollbar' === $v['type'] && !empty($v['id']) && !empty($v['token']); })
->thenInvalid('You can not use both an id and a token in a RollbarHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'rollbar' === $v['type'] && empty($v['id']) && empty($v['token']); })
->thenInvalid('The id or the token has to be specified to use a RollbarHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'swift_mailer' === $v['type'] && empty($v['email_prototype']) && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); })
->thenInvalid('The sender, recipient and subject or an email prototype have to be specified to use a SwiftMailerHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'native_mailer' === $v['type'] && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); })
->thenInvalid('The sender, recipient and subject have to be specified to use a NativeMailerHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'service' === $v['type'] && !isset($v['id']); })
->thenInvalid('The id has to be specified to use a service as handler')
->end()
->validate()
->ifTrue(function ($v) { return 'syslogudp' === $v['type'] && !isset($v['host']); })
->thenInvalid('The host has to be specified to use a syslogudp as handler')
->end()
->validate()
->ifTrue(function ($v) { return 'gelf' === $v['type'] && !isset($v['publisher']); })
->thenInvalid('The publisher has to be specified to use a GelfHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'socket' === $v['type'] && !isset($v['connection_string']); })
->thenInvalid('The connection_string has to be specified to use a SocketHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'pushover' === $v['type'] && (empty($v['token']) || empty($v['user'])); })
->thenInvalid('The token and user have to be specified to use a PushoverHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'raven' === $v['type'] && !array_key_exists('dsn', $v) && null === $v['client_id']; })
->thenInvalid('The DSN has to be specified to use a RavenHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'hipchat' === $v['type'] && (empty($v['token']) || empty($v['room'])); })
->thenInvalid('The token and room have to be specified to use a HipChatHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'hipchat' === $v['type'] && !in_array($v['message_format'], array('text', 'html')); })
->thenInvalid('The message_format has to be "text" or "html" in a HipChatHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'hipchat' === $v['type'] && null !== $v['api_version'] && !in_array($v['api_version'], array('v1', 'v2'), true); })
->thenInvalid('The api_version has to be "v1" or "v2" in a HipChatHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'slack' === $v['type'] && (empty($v['token']) || empty($v['channel'])); })
->thenInvalid('The token and channel have to be specified to use a SlackHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'slackwebhook' === $v['type'] && (empty($v['webhook_url'])); })
->thenInvalid('The webhook_url have to be specified to use a SlackWebhookHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'slackbot' === $v['type'] && (empty($v['team']) || empty($v['token']) || empty($v['channel'])); })
->thenInvalid('The team, token and channel have to be specified to use a SlackbotHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'cube' === $v['type'] && empty($v['url']); })
->thenInvalid('The url has to be specified to use a CubeHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'mongo' === $v['type'] && !isset($v['mongo']); })
->thenInvalid('The mongo configuration has to be specified to use a MongoHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'amqp' === $v['type'] && empty($v['exchange']); })
->thenInvalid('The exchange has to be specified to use a AmqpHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'loggly' === $v['type'] && empty($v['token']); })
->thenInvalid('The token has to be specified to use a LogglyHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'loggly' === $v['type'] && !empty($v['tags']); })
->then(function ($v) {
$invalidTags = preg_grep('/^[a-z0-9][a-z0-9\.\-_]*$/i', $v['tags'], PREG_GREP_INVERT);
if (!empty($invalidTags)) {
throw new InvalidConfigurationException(sprintf('The following Loggly tags are invalid: %s.', implode(', ', $invalidTags)));
}
return $v;
})
->end()
->validate()
->ifTrue(function ($v) { return 'logentries' === $v['type'] && empty($v['token']); })
->thenInvalid('The token has to be specified to use a LogEntriesHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['token']); })
->thenInvalid('The token has to be specified to use a FlowdockHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['from_email']); })
->thenInvalid('The from_email has to be specified to use a FlowdockHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['source']); })
->thenInvalid('The source has to be specified to use a FlowdockHandler')
->end()
->validate()
->ifTrue(function ($v) { return 'server_log' === $v['type'] && empty($v['host']); })
->thenInvalid('The host has to be specified to use a ServerLogHandler')
->end()
->end()
->validate()
->ifTrue(function ($v) { return isset($v['debug']); })
->thenInvalid('The "debug" name cannot be used as it is reserved for the handler of the profiler')
->end()
->example(array(
'syslog' => array(
'type' => 'stream',
'path' => '/var/log/symfony.log',
'level' => 'ERROR',
'bubble' => 'false',
'formatter' => 'my_formatter',
),
'main' => array(
'type' => 'fingers_crossed',
'action_level' => 'WARNING',
'buffer_size' => 30,
'handler' => 'custom',
),
'custom' => array(
'type' => 'service',
'id' => 'my_handler',
)
))
->end()
->end()
;
return $treeBuilder;
}
}

View File

@@ -0,0 +1,799 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MonologBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* MonologExtension is an extension for the Monolog library.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org>
*/
class MonologExtension extends Extension
{
private $nestedHandlers = array();
private $swiftMailerHandlers = array();
private function levelToMonologConst($level)
{
return is_int($level) ? $level : constant('Monolog\Logger::'.strtoupper($level));
}
/**
* Loads the Monolog configuration.
*
* @param array $configs An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
if (isset($config['handlers'])) {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('monolog.xml');
$container->setParameter('monolog.use_microseconds', $config['use_microseconds']);
// always autowire the main logger, require Symfony >= 2.8, < 3.3
if (!method_exists('Symfony\Component\DependencyInjection\ContainerBuilder', 'fileExists') && method_exists('Symfony\Component\DependencyInjection\Definition', 'addAutowiringType')) {
$container->getDefinition('monolog.logger')->addAutowiringType('Psr\Log\LoggerInterface');
}
$handlers = array();
foreach ($config['handlers'] as $name => $handler) {
$handlers[$handler['priority']][] = array(
'id' => $this->buildHandler($container, $name, $handler),
'channels' => empty($handler['channels']) ? null : $handler['channels'],
);
}
$container->setParameter(
'monolog.swift_mailer.handlers',
$this->swiftMailerHandlers
);
ksort($handlers);
$sortedHandlers = array();
foreach ($handlers as $priorityHandlers) {
foreach (array_reverse($priorityHandlers) as $handler) {
$sortedHandlers[] = $handler;
}
}
$handlersToChannels = array();
foreach ($sortedHandlers as $handler) {
if (!in_array($handler['id'], $this->nestedHandlers)) {
$handlersToChannels[$handler['id']] = $handler['channels'];
}
}
$container->setParameter('monolog.handlers_to_channels', $handlersToChannels);
if (PHP_VERSION_ID < 70000) {
$this->addClassesToCompile(array(
'Monolog\\Formatter\\FormatterInterface',
'Monolog\\Formatter\\LineFormatter',
'Monolog\\Handler\\HandlerInterface',
'Monolog\\Handler\\AbstractHandler',
'Monolog\\Handler\\AbstractProcessingHandler',
'Monolog\\Handler\\StreamHandler',
'Monolog\\Handler\\FingersCrossedHandler',
'Monolog\\Handler\\FilterHandler',
'Monolog\\Handler\\TestHandler',
'Monolog\\Logger',
'Symfony\\Bridge\\Monolog\\Logger',
'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface',
'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy',
));
}
}
$container->setParameter('monolog.additional_channels', isset($config['channels']) ? $config['channels'] : array());
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/monolog';
}
private function buildHandler(ContainerBuilder $container, $name, array $handler)
{
$handlerId = $this->getHandlerId($name);
if ('service' === $handler['type']) {
$container->setAlias($handlerId, $handler['id']);
return $handlerId;
}
$definition = new Definition($this->getHandlerClassByType($handler['type']));
$handler['level'] = $this->levelToMonologConst($handler['level']);
if ($handler['include_stacktraces']) {
$definition->setConfigurator(array('Symfony\\Bundle\\MonologBundle\\MonologBundle', 'includeStacktraces'));
}
if ($handler['process_psr_3_messages']) {
$processorId = 'monolog.processor.psr_log_message';
if (!$container->hasDefinition($processorId)) {
$processor = new Definition('Monolog\\Processor\\PsrLogMessageProcessor');
$processor->setPublic(false);
$container->setDefinition($processorId, $processor);
}
$definition->addMethodCall('pushProcessor', array(new Reference($processorId)));
}
switch ($handler['type']) {
case 'stream':
$definition->setArguments(array(
$handler['path'],
$handler['level'],
$handler['bubble'],
$handler['file_permission'],
));
break;
case 'console':
$definition->setArguments(array(
null,
$handler['bubble'],
isset($handler['verbosity_levels']) ? $handler['verbosity_levels'] : array(),
));
$definition->addTag('kernel.event_subscriber');
break;
case 'firephp':
$definition->setArguments(array(
$handler['level'],
$handler['bubble'],
));
$definition->addTag('kernel.event_listener', array('event' => 'kernel.response', 'method' => 'onKernelResponse'));
break;
case 'gelf':
if (isset($handler['publisher']['id'])) {
$publisher = new Reference($handler['publisher']['id']);
} elseif (class_exists('Gelf\Transport\UdpTransport')) {
$transport = new Definition("Gelf\Transport\UdpTransport", array(
$handler['publisher']['hostname'],
$handler['publisher']['port'],
$handler['publisher']['chunk_size'],
));
$transport->setPublic(false);
$publisher = new Definition('Gelf\Publisher', array());
$publisher->addMethodCall('addTransport', array($transport));
$publisher->setPublic(false);
} elseif (class_exists('Gelf\MessagePublisher')) {
$publisher = new Definition('Gelf\MessagePublisher', array(
$handler['publisher']['hostname'],
$handler['publisher']['port'],
$handler['publisher']['chunk_size'],
));
$publisher->setPublic(false);
} else {
throw new \RuntimeException('The gelf handler requires the graylog2/gelf-php package to be installed');
}
$definition->setArguments(array(
$publisher,
$handler['level'],
$handler['bubble'],
));
break;
case 'mongo':
if (isset($handler['mongo']['id'])) {
$client = new Reference($handler['mongo']['id']);
} else {
$server = 'mongodb://';
if (isset($handler['mongo']['user'])) {
$server .= $handler['mongo']['user'].':'.$handler['mongo']['pass'].'@';
}
$server .= $handler['mongo']['host'].':'.$handler['mongo']['port'];
$client = new Definition('MongoClient', array(
$server,
));
$client->setPublic(false);
}
$definition->setArguments(array(
$client,
$handler['mongo']['database'],
$handler['mongo']['collection'],
$handler['level'],
$handler['bubble'],
));
break;
case 'elasticsearch':
if (isset($handler['elasticsearch']['id'])) {
$elasticaClient = new Reference($handler['elasticsearch']['id']);
} else {
// elastica client new definition
$elasticaClient = new Definition('Elastica\Client');
$elasticaClientArguments = array(
'host' => $handler['elasticsearch']['host'],
'port' => $handler['elasticsearch']['port'],
'transport' => $handler['elasticsearch']['transport'],
);
if (isset($handler['elasticsearch']['user']) && isset($handler['elasticsearch']['password'])) {
$elasticaClientArguments = array_merge(
$elasticaClientArguments,
array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode($handler['elasticsearch']['user'] . ':' . $handler['elasticsearch']['password'])
)
)
);
}
$elasticaClient->setArguments(array(
$elasticaClientArguments
));
$elasticaClient->setPublic(false);
}
// elastica handler definition
$definition->setArguments(array(
$elasticaClient,
array(
'index' => $handler['index'],
'type' => $handler['document_type'],
'ignore_error' => $handler['ignore_error']
),
$handler['level'],
$handler['bubble'],
));
break;
case 'chromephp':
$definition->setArguments(array(
$handler['level'],
$handler['bubble'],
));
$definition->addTag('kernel.event_listener', array('event' => 'kernel.response', 'method' => 'onKernelResponse'));
break;
case 'rotating_file':
$definition->setArguments(array(
$handler['path'],
$handler['max_files'],
$handler['level'],
$handler['bubble'],
$handler['file_permission'],
));
$definition->addMethodCall('setFilenameFormat', array(
$handler['filename_format'],
$handler['date_format'],
));
break;
case 'fingers_crossed':
$handler['action_level'] = $this->levelToMonologConst($handler['action_level']);
if (null !== $handler['passthru_level']) {
$handler['passthru_level'] = $this->levelToMonologConst($handler['passthru_level']);
}
$nestedHandlerId = $this->getHandlerId($handler['handler']);
$this->markNestedHandler($nestedHandlerId);
if (isset($handler['activation_strategy'])) {
$activation = new Reference($handler['activation_strategy']);
} elseif (!empty($handler['excluded_404s'])) {
$activationDef = new Definition('Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy', array(
new Reference('request_stack'),
$handler['excluded_404s'],
$handler['action_level']
));
$container->setDefinition($handlerId.'.not_found_strategy', $activationDef);
$activation = new Reference($handlerId.'.not_found_strategy');
} elseif (!empty($handler['excluded_http_codes'])) {
if (!class_exists('Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy')) {
throw new \LogicException('"excluded_http_codes" cannot be used as your version of Monolog bridge does not support it.');
}
$activationDef = new Definition('Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy', array(
new Reference('request_stack'),
$handler['excluded_http_codes'],
$handler['action_level']
));
$container->setDefinition($handlerId.'.http_code_strategy', $activationDef);
$activation = new Reference($handlerId.'.http_code_strategy');
} else {
$activation = $handler['action_level'];
}
$definition->setArguments(array(
new Reference($nestedHandlerId),
$activation,
$handler['buffer_size'],
$handler['bubble'],
$handler['stop_buffering'],
$handler['passthru_level'],
));
break;
case 'filter':
$handler['min_level'] = $this->levelToMonologConst($handler['min_level']);
$handler['max_level'] = $this->levelToMonologConst($handler['max_level']);
foreach (array_keys($handler['accepted_levels']) as $k) {
$handler['accepted_levels'][$k] = $this->levelToMonologConst($handler['accepted_levels'][$k]);
}
$nestedHandlerId = $this->getHandlerId($handler['handler']);
$this->markNestedHandler($nestedHandlerId);
$minLevelOrList = !empty($handler['accepted_levels']) ? $handler['accepted_levels'] : $handler['min_level'];
$definition->setArguments(array(
new Reference($nestedHandlerId),
$minLevelOrList,
$handler['max_level'],
$handler['bubble'],
));
break;
case 'buffer':
$nestedHandlerId = $this->getHandlerId($handler['handler']);
$this->markNestedHandler($nestedHandlerId);
$definition->setArguments(array(
new Reference($nestedHandlerId),
$handler['buffer_size'],
$handler['level'],
$handler['bubble'],
$handler['flush_on_overflow'],
));
break;
case 'deduplication':
$nestedHandlerId = $this->getHandlerId($handler['handler']);
$this->markNestedHandler($nestedHandlerId);
$defaultStore = '%kernel.cache_dir%/monolog_dedup_'.sha1($handlerId);
$definition->setArguments(array(
new Reference($nestedHandlerId),
isset($handler['store']) ? $handler['store'] : $defaultStore,
$handler['deduplication_level'],
$handler['time'],
$handler['bubble'],
));
break;
case 'group':
case 'whatfailuregroup':
$references = array();
foreach ($handler['members'] as $nestedHandler) {
$nestedHandlerId = $this->getHandlerId($nestedHandler);
$this->markNestedHandler($nestedHandlerId);
$references[] = new Reference($nestedHandlerId);
}
$definition->setArguments(array(
$references,
$handler['bubble'],
));
break;
case 'syslog':
$definition->setArguments(array(
$handler['ident'],
$handler['facility'],
$handler['level'],
$handler['bubble'],
$handler['logopts'],
));
break;
case 'syslogudp':
$definition->setArguments(array(
$handler['host'],
$handler['port'],
$handler['facility'],
$handler['level'],
$handler['bubble'],
));
break;
case 'swift_mailer':
if (isset($handler['email_prototype'])) {
if (!empty($handler['email_prototype']['method'])) {
$prototype = array(new Reference($handler['email_prototype']['id']), $handler['email_prototype']['method']);
} else {
$prototype = new Reference($handler['email_prototype']['id']);
}
} else {
$messageFactory = new Definition('Symfony\Bundle\MonologBundle\SwiftMailer\MessageFactory');
$messageFactory->setLazy(true);
$messageFactory->setPublic(false);
$messageFactory->setArguments(array(
new Reference($handler['mailer']),
$handler['from_email'],
$handler['to_email'],
$handler['subject'],
$handler['content_type']
));
$messageFactoryId = sprintf('%s.mail_message_factory', $handlerId);
$container->setDefinition($messageFactoryId, $messageFactory);
// set the prototype as a callable
$prototype = array(new Reference($messageFactoryId), 'createMessage');
}
$definition->setArguments(array(
new Reference($handler['mailer']),
$prototype,
$handler['level'],
$handler['bubble'],
));
$this->swiftMailerHandlers[] = $handlerId;
$definition->addTag('kernel.event_listener', array('event' => 'kernel.terminate', 'method' => 'onKernelTerminate'));
$definition->addTag('kernel.event_listener', array('event' => 'console.terminate', 'method' => 'onCliTerminate'));
break;
case 'native_mailer':
$definition->setArguments(array(
$handler['to_email'],
$handler['subject'],
$handler['from_email'],
$handler['level'],
$handler['bubble'],
));
break;
case 'socket':
$definition->setArguments(array(
$handler['connection_string'],
$handler['level'],
$handler['bubble'],
));
if (isset($handler['timeout'])) {
$definition->addMethodCall('setTimeout', array($handler['timeout']));
}
if (isset($handler['connection_timeout'])) {
$definition->addMethodCall('setConnectionTimeout', array($handler['connection_timeout']));
}
if (isset($handler['persistent'])) {
$definition->addMethodCall('setPersistent', array($handler['persistent']));
}
break;
case 'pushover':
$definition->setArguments(array(
$handler['token'],
$handler['user'],
$handler['title'],
$handler['level'],
$handler['bubble'],
));
if (isset($handler['timeout'])) {
$definition->addMethodCall('setTimeout', array($handler['timeout']));
}
if (isset($handler['connection_timeout'])) {
$definition->addMethodCall('setConnectionTimeout', array($handler['connection_timeout']));
}
break;
case 'hipchat':
$definition->setArguments(array(
$handler['token'],
$handler['room'],
$handler['nickname'],
$handler['notify'],
$handler['level'],
$handler['bubble'],
$handler['use_ssl'],
$handler['message_format'],
!empty($handler['host']) ? $handler['host'] : 'api.hipchat.com',
!empty($handler['api_version']) ? $handler['api_version'] : 'v1',
));
if (isset($handler['timeout'])) {
$definition->addMethodCall('setTimeout', array($handler['timeout']));
}
if (isset($handler['connection_timeout'])) {
$definition->addMethodCall('setConnectionTimeout', array($handler['connection_timeout']));
}
break;
case 'slack':
$definition->setArguments(array(
$handler['token'],
$handler['channel'],
$handler['bot_name'],
$handler['use_attachment'],
$handler['icon_emoji'],
$handler['level'],
$handler['bubble'],
$handler['use_short_attachment'],
$handler['include_extra'],
));
if (isset($handler['timeout'])) {
$definition->addMethodCall('setTimeout', array($handler['timeout']));
}
if (isset($handler['connection_timeout'])) {
$definition->addMethodCall('setConnectionTimeout', array($handler['connection_timeout']));
}
break;
case 'slackwebhook':
$definition->setArguments(array(
$handler['webhook_url'],
$handler['channel'],
$handler['bot_name'],
$handler['use_attachment'],
$handler['icon_emoji'],
$handler['use_short_attachment'],
$handler['include_extra'],
$handler['level'],
$handler['bubble'],
));
break;
case 'slackbot':
$definition->setArguments(array(
$handler['team'],
$handler['token'],
urlencode($handler['channel']),
$handler['level'],
$handler['bubble'],
));
break;
case 'cube':
$definition->setArguments(array(
$handler['url'],
$handler['level'],
$handler['bubble'],
));
break;
case 'amqp':
$definition->setArguments(array(
new Reference($handler['exchange']),
$handler['exchange_name'],
$handler['level'],
$handler['bubble'],
));
break;
case 'error_log':
$definition->setArguments(array(
$handler['message_type'],
$handler['level'],
$handler['bubble'],
));
break;
case 'raven':
if (null !== $handler['client_id']) {
$clientId = $handler['client_id'];
} else {
$client = new Definition('Raven_Client', array(
$handler['dsn'],
array('auto_log_stacks' => $handler['auto_log_stacks'])
));
$client->setPublic(false);
$clientId = 'monolog.raven.client.'.sha1($handler['dsn']);
$container->setDefinition($clientId, $client);
}
$definition->setArguments(array(
new Reference($clientId),
$handler['level'],
$handler['bubble'],
));
if (!empty($handler['release'])) {
$definition->addMethodCall('setRelease', array($handler['release']));
}
break;
case 'loggly':
$definition->setArguments(array(
$handler['token'],
$handler['level'],
$handler['bubble'],
));
if (!empty($handler['tags'])) {
$definition->addMethodCall('setTag', array(implode(',', $handler['tags'])));
}
break;
case 'logentries':
$definition->setArguments(array(
$handler['token'],
$handler['use_ssl'],
$handler['level'],
$handler['bubble'],
));
if (isset($handler['timeout'])) {
$definition->addMethodCall('setTimeout', array($handler['timeout']));
}
if (isset($handler['connection_timeout'])) {
$definition->addMethodCall('setConnectionTimeout', array($handler['connection_timeout']));
}
break;
case 'flowdock':
$definition->setArguments(array(
$handler['token'],
$handler['level'],
$handler['bubble'],
));
if (empty($handler['formatter'])) {
$formatter = new Definition("Monolog\Formatter\FlowdockFormatter", array(
$handler['source'],
$handler['from_email'],
));
$formatterId = 'monolog.flowdock.formatter.'.sha1($handler['source'].'|'.$handler['from_email']);
$formatter->setPublic(false);
$container->setDefinition($formatterId, $formatter);
$definition->addMethodCall('setFormatter', array(new Reference($formatterId)));
}
break;
case 'rollbar':
if (!empty($handler['id'])) {
$rollbarId = $handler['id'];
} else {
$config = $handler['config'] ?: array();
$config['access_token'] = $handler['token'];
$rollbar = new Definition('RollbarNotifier', array(
$config,
));
$rollbarId = 'monolog.rollbar.notifier.'.sha1(json_encode($config));
$rollbar->setPublic(false);
$container->setDefinition($rollbarId, $rollbar);
}
$definition->setArguments(array(
new Reference($rollbarId),
$handler['level'],
$handler['bubble'],
));
break;
case 'newrelic':
$definition->setArguments(array(
$handler['level'],
$handler['bubble'],
$handler['app_name'],
));
break;
case 'server_log':
if (!class_exists('Symfony\Bridge\Monolog\Handler\ServerLogHandler')) {
throw new \RuntimeException('The ServerLogHandler is not available. Please update "symfony/monolog-bridge" to 3.3.');
}
$definition->setArguments(array(
$handler['host'],
$handler['level'],
$handler['bubble'],
));
break;
// Handlers using the constructor of AbstractHandler without adding their own arguments
case 'browser_console':
case 'test':
case 'null':
case 'debug':
$definition->setArguments(array(
$handler['level'],
$handler['bubble'],
));
break;
default:
throw new \InvalidArgumentException(sprintf('Invalid handler type "%s" given for handler "%s"', $handler['type'], $name));
}
if (!empty($handler['nested']) && true === $handler['nested']) {
$this->markNestedHandler($handlerId);
}
if (!empty($handler['formatter'])) {
$definition->addMethodCall('setFormatter', array(new Reference($handler['formatter'])));
}
$container->setDefinition($handlerId, $definition);
return $handlerId;
}
private function markNestedHandler($nestedHandlerId)
{
if (in_array($nestedHandlerId, $this->nestedHandlers)) {
return;
}
$this->nestedHandlers[] = $nestedHandlerId;
}
private function getHandlerId($name)
{
return sprintf('monolog.handler.%s', $name);
}
private function getHandlerClassByType($handlerType)
{
$typeToClassMapping = array(
'stream' => 'Monolog\Handler\StreamHandler',
'console' => 'Symfony\Bridge\Monolog\Handler\ConsoleHandler',
'group' => 'Monolog\Handler\GroupHandler',
'buffer' => 'Monolog\Handler\BufferHandler',
'deduplication' => 'Monolog\Handler\DeduplicationHandler',
'rotating_file' => 'Monolog\Handler\RotatingFileHandler',
'syslog' => 'Monolog\Handler\SyslogHandler',
'syslogudp' => 'Monolog\Handler\SyslogUdpHandler',
'null' => 'Monolog\Handler\NullHandler',
'test' => 'Monolog\Handler\TestHandler',
'gelf' => 'Monolog\Handler\GelfHandler',
'rollbar' => 'Monolog\Handler\RollbarHandler',
'flowdock' => 'Monolog\Handler\FlowdockHandler',
'browser_console' => 'Monolog\Handler\BrowserConsoleHandler',
'firephp' => 'Symfony\Bridge\Monolog\Handler\FirePHPHandler',
'chromephp' => 'Symfony\Bridge\Monolog\Handler\ChromePhpHandler',
'debug' => 'Symfony\Bridge\Monolog\Handler\DebugHandler',
'swift_mailer' => 'Symfony\Bridge\Monolog\Handler\SwiftMailerHandler',
'native_mailer' => 'Monolog\Handler\NativeMailerHandler',
'socket' => 'Monolog\Handler\SocketHandler',
'pushover' => 'Monolog\Handler\PushoverHandler',
'raven' => 'Monolog\Handler\RavenHandler',
'newrelic' => 'Monolog\Handler\NewRelicHandler',
'hipchat' => 'Monolog\Handler\HipChatHandler',
'slack' => 'Monolog\Handler\SlackHandler',
'slackwebhook' => 'Monolog\Handler\SlackWebhookHandler',
'slackbot' => 'Monolog\Handler\SlackbotHandler',
'cube' => 'Monolog\Handler\CubeHandler',
'amqp' => 'Monolog\Handler\AmqpHandler',
'error_log' => 'Monolog\Handler\ErrorLogHandler',
'loggly' => 'Monolog\Handler\LogglyHandler',
'logentries' => 'Monolog\Handler\LogEntriesHandler',
'whatfailuregroup' => 'Monolog\Handler\WhatFailureGroupHandler',
'fingers_crossed' => 'Monolog\Handler\FingersCrossedHandler',
'filter' => 'Monolog\Handler\FilterHandler',
'mongo' => 'Monolog\Handler\MongoDBHandler',
'elasticsearch' => 'Monolog\Handler\ElasticSearchHandler',
'server_log' => 'Symfony\Bridge\Monolog\Handler\ServerLogHandler',
);
if (!isset($typeToClassMapping[$handlerType])) {
throw new \InvalidArgumentException(sprintf('There is no handler class defined for handler "%s".', $handlerType));
}
return $typeToClassMapping[$handlerType];
}
}