Skip to content

Commit 10c35d5

Browse files
committed
Add Sentry BreadcrumbHandler support
1 parent ed0e4a2 commit 10c35d5

File tree

3 files changed

+92
-16
lines changed

3 files changed

+92
-16
lines changed

DependencyInjection/Configuration.php

+11-3
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@
236236
* - hub_id: Sentry hub custom service id (optional)
237237
* - [fill_extra_context]: bool, defaults to false
238238
*
239+
* - sentry_breadcrumb:
240+
* - sentry_handler: the sentry handler's name
241+
*
239242
* - newrelic:
240243
* - [level]: level name or int value, defaults to DEBUG
241244
* - [bubble]: bool, defaults to true
@@ -534,7 +537,7 @@ public function getConfigTreeBuilder(): TreeBuilder
534537
->scalarNode('max_level')->defaultValue('EMERGENCY')->end() // filter
535538
->scalarNode('buffer_size')->defaultValue(0)->end() // fingers_crossed and buffer
536539
->booleanNode('flush_on_overflow')->defaultFalse()->end() // buffer
537-
->scalarNode('handler')->end() // fingers_crossed and buffer
540+
->scalarNode('handler')->end() // fingers_crossed, buffer, filter, deduplication, sampling
538541
->scalarNode('url')->end() // cube
539542
->scalarNode('exchange')->end() // amqp
540543
->scalarNode('exchange_name')->defaultValue('log')->end() // amqp
@@ -584,6 +587,7 @@ public function getConfigTreeBuilder(): TreeBuilder
584587
->booleanNode('persistent')->end() // socket_handler
585588
->scalarNode('dsn')->end() // raven_handler, sentry_handler
586589
->scalarNode('hub_id')->defaultNull()->end() // sentry_handler
590+
->scalarNode('sentry_handler')->defaultNull()->end() // sentry_breadcrumb
587591
->scalarNode('client_id')->defaultNull()->end() // raven_handler, sentry_handler
588592
->scalarNode('auto_log_stacks')->defaultFalse()->end() // raven_handler
589593
->scalarNode('release')->defaultNull()->end() // raven_handler, sentry_handler
@@ -658,8 +662,8 @@ public function getConfigTreeBuilder(): TreeBuilder
658662
->thenInvalid('Service handlers can not have a formatter configured in the bundle, you must reconfigure the service itself instead')
659663
->end()
660664
->validate()
661-
->ifTrue(function ($v) { return ('fingers_crossed' === $v['type'] || 'buffer' === $v['type'] || 'filter' === $v['type'] || 'sampling' === $v['type']) && empty($v['handler']); })
662-
->thenInvalid('The handler has to be specified to use a FingersCrossedHandler or BufferHandler or FilterHandler or SamplingHandler')
665+
->ifTrue(function ($v) { return ('fingers_crossed' === $v['type'] || 'buffer' === $v['type'] || 'filter' === $v['type'] || 'deduplication' === $v['type'] || 'sampling' === $v['type']) && empty($v['handler']); })
666+
->thenInvalid('The handler has to be specified to use a FingersCrossedHandler, BufferHandler, FilterHandler, DeduplicationHandler or SamplingHandler')
663667
->end()
664668
->validate()
665669
->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_404s']) && !empty($v['activation_strategy']); })
@@ -725,6 +729,10 @@ public function getConfigTreeBuilder(): TreeBuilder
725729
->ifTrue(function ($v) { return 'sentry' === $v['type'] && null !== $v['hub_id'] && null !== $v['client_id']; })
726730
->thenInvalid('You can not use both a hub_id and a client_id in a Sentry handler')
727731
->end()
732+
->validate()
733+
->ifTrue(function ($v) { return 'sentry_breadcrumb' === $v['type'] && empty($v['sentry_handler']); })
734+
->thenInvalid('The sentry_handler has to be specified to use a Sentry BreadcrumbHandler')
735+
->end()
728736
->validate()
729737
->ifTrue(function ($v) { return 'hipchat' === $v['type'] && (empty($v['token']) || empty($v['room'])); })
730738
->thenInvalid('The token and room have to be specified to use a HipChatHandler')

DependencyInjection/MonologExtension.php

+24-5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Symfony\Bridge\Monolog\Processor\TokenProcessor;
2626
use Symfony\Bridge\Monolog\Processor\WebProcessor;
2727
use Symfony\Component\Config\FileLocator;
28+
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
2829
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
2930
use Symfony\Component\DependencyInjection\ChildDefinition;
3031
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -47,6 +48,8 @@ class MonologExtension extends Extension
4748
{
4849
private $nestedHandlers = [];
4950

51+
private $sentryBreadcrumbHandlers = [];
52+
5053
private $swiftMailerHandlers = [];
5154

5255
/**
@@ -79,6 +82,11 @@ public function load(array $configs, ContainerBuilder $container)
7982
];
8083
}
8184

85+
foreach ($this->sentryBreadcrumbHandlers as $sentryBreadcrumbHandlerId => $sentryHandlerId) {
86+
$hubId = (string) $container->getDefinition($sentryHandlerId)->getArgument(0);
87+
$container->getDefinition($sentryBreadcrumbHandlerId)->replaceArgument(0, new Reference($hubId));
88+
}
89+
8290
$container->setParameter(
8391
'monolog.swift_mailer.handlers',
8492
$this->swiftMailerHandlers
@@ -724,7 +732,7 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler
724732

725733
case 'sentry':
726734
if (null !== $handler['hub_id']) {
727-
$hub = new Reference($handler['hub_id']);
735+
$hubId = $handler['hub_id'];
728736
} else {
729737
if (null !== $handler['client_id']) {
730738
$clientId = $handler['client_id'];
@@ -755,24 +763,34 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler
755763
}
756764
}
757765

758-
$hub = new Definition(
766+
$hubId = \sprintf('monolog.handler.%s.hub', $name);
767+
$hub = $container->setDefinition($hubId, new Definition(
759768
'Sentry\\State\\Hub',
760769
[new Reference($clientId)]
761-
);
762-
$container->setDefinition(\sprintf('monolog.handler.%s.hub', $name), $hub);
770+
));
763771

764772
// can't set the hub to the current hub, getting into a recursion otherwise...
765773
// $hub->addMethodCall('setCurrent', array($hub));
766774
}
767775

768776
$definition->setArguments([
769-
$hub,
777+
new Reference($hubId),
770778
$handler['level'],
771779
$handler['bubble'],
772780
$handler['fill_extra_context'],
773781
]);
774782
break;
775783

784+
case 'sentry_breadcrumb':
785+
$this->sentryBreadcrumbHandlers[$handlerId] = $this->getHandlerId($handler['sentry_handler']);
786+
787+
$definition->setArguments([
788+
new AbstractArgument('Sentry hub id'),
789+
$handler['level'],
790+
$handler['bubble'],
791+
]);
792+
break;
793+
776794
case 'raven':
777795
if (null !== $handler['client_id']) {
778796
$clientId = $handler['client_id'];
@@ -977,6 +995,7 @@ private function getHandlerClassByType($handlerType)
977995
'pushover' => 'Monolog\Handler\PushoverHandler',
978996
'raven' => 'Monolog\Handler\RavenHandler',
979997
'sentry' => 'Sentry\Monolog\Handler',
998+
'sentry_breadcrumb' => 'Sentry\Monolog\BreadcrumbHandler',
980999
'newrelic' => 'Monolog\Handler\NewRelicHandler',
9811000
'hipchat' => 'Monolog\Handler\HipChatHandler',
9821001
'slack' => 'Monolog\Handler\SlackHandler',

Tests/DependencyInjection/MonologExtensionTest.php

+57-8
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,10 @@ public function testRavenHandlerWhenAClientIsSpecified()
371371

372372
public function testSentryHandlerWhenConfigurationIsWrong()
373373
{
374-
try {
375-
$this->getContainer([['handlers' => ['sentry' => ['type' => 'sentry']]]]);
376-
$this->fail();
377-
} catch (InvalidConfigurationException $e) {
378-
$this->assertStringContainsString('DSN', $e->getMessage());
379-
}
374+
$this->expectException(InvalidConfigurationException::class);
375+
$this->expectExceptionMessage('The DSN has to be specified to use Sentry\'s handler');
376+
377+
$this->getContainer([['handlers' => ['sentry' => ['type' => 'sentry']]]]);
380378
}
381379

382380
public function testSentryHandlerWhenADSNIsSpecified()
@@ -426,7 +424,10 @@ public function testSentryHandlerWhenADSNAndAClientAreSpecified()
426424
$this->assertDICDefinitionMethodCallAt(1, $logger, 'pushHandler', [new Reference('monolog.handler.sentry')]);
427425

428426
$handler = $container->getDefinition('monolog.handler.sentry');
429-
$this->assertDICConstructorArguments($handler->getArguments()[0], [new Reference('sentry.client')]);
427+
$this->assertDICConstructorArguments($handler, [new Reference('monolog.handler.sentry.hub'), 'DEBUG', true, false]);
428+
429+
$hub = $container->getDefinition($handler->getArguments()[0]);
430+
$this->assertDICConstructorArguments($hub, [new Reference('sentry.client')]);
430431
}
431432

432433
public function testSentryHandlerWhenAClientIsSpecified()
@@ -452,7 +453,10 @@ public function testSentryHandlerWhenAClientIsSpecified()
452453
$this->assertDICDefinitionMethodCallAt(1, $logger, 'pushHandler', [new Reference('monolog.handler.sentry')]);
453454

454455
$handler = $container->getDefinition('monolog.handler.sentry');
455-
$this->assertDICConstructorArguments($handler->getArguments()[0], [new Reference('sentry.client')]);
456+
$this->assertDICConstructorArguments($handler, [new Reference('monolog.handler.sentry.hub'), 'DEBUG', true, false]);
457+
458+
$hub = $container->getDefinition($handler->getArguments()[0]);
459+
$this->assertDICConstructorArguments($hub, [new Reference('sentry.client')]);
456460
}
457461

458462
public function testSentryHandlerWhenAHubIsSpecified()
@@ -501,6 +505,51 @@ public function testSentryHandlerWhenAHubAndAClientAreSpecified()
501505
);
502506
}
503507

508+
public function testSentryBreadcrumbHandlerWhenConfigurationIsWrong()
509+
{
510+
$this->expectException(InvalidConfigurationException::class);
511+
$this->expectExceptionMessage('The sentry_handler has to be specified to use a Sentry BreadcrumbHandler');
512+
513+
$this->getContainer([['handlers' => ['sentry_breadcrumb' => ['type' => 'sentry_breadcrumb']]]]);
514+
}
515+
516+
/**
517+
* @testWith [{"dsn": "http://a:[email protected]:9000/1"}, "monolog.handler.sentry.hub"]
518+
* [{"hub_id": "sentry.hub"}, "sentry.hub"]
519+
*/
520+
public function testSentryBreadcrumbHandlerWhenConfigurationIsOk(array $config, string $hubId)
521+
{
522+
$container = $this->getContainer(
523+
[
524+
[
525+
'handlers' => [
526+
'sentry_breadcrumb' => ['type' => 'sentry_breadcrumb', 'sentry_handler' => 'sentry'],
527+
'sentry' => ['type' => 'sentry'] + $config,
528+
],
529+
],
530+
],
531+
[
532+
'sentry.hub' => new Definition(\Sentry\State\HubInterface::class),
533+
]
534+
);
535+
536+
$this->assertTrue($container->hasDefinition('monolog.logger'));
537+
$this->assertTrue($container->hasDefinition('monolog.handler.sentry'));
538+
$this->assertTrue($container->hasDefinition('monolog.handler.sentry_breadcrumb'));
539+
540+
$logger = $container->getDefinition('monolog.logger');
541+
$this->assertDICDefinitionMethodCallAt(0, $logger, 'useMicrosecondTimestamps', ['%monolog.use_microseconds%']);
542+
$this->assertDICDefinitionMethodCallAt(1, $logger, 'pushHandler', [new Reference('monolog.handler.sentry')]);
543+
$this->assertDICDefinitionMethodCallAt(2, $logger, 'pushHandler', [new Reference('monolog.handler.sentry_breadcrumb')]);
544+
545+
$handler = $container->getDefinition('monolog.handler.sentry_breadcrumb');
546+
$this->assertDICDefinitionClass($handler, 'Sentry\Monolog\BreadcrumbHandler');
547+
$this->assertDICConstructorArguments($handler, [new Reference($hubId), 'DEBUG', true]);
548+
549+
$sentry = $container->getDefinition('monolog.handler.sentry');
550+
$this->assertSame((string) $sentry->getArgument(0), (string) $handler->getArgument(0));
551+
}
552+
504553
public function testLogglyHandler()
505554
{
506555
$token = '026308d8-2b63-4225-8fe9-e01294b6e472';

0 commit comments

Comments
 (0)