Skip to content

Commit fc6ce17

Browse files
committed
feat(logs): introduce logging for symfony
1 parent 3ac4547 commit fc6ce17

File tree

13 files changed

+456
-4
lines changed

13 files changed

+456
-4
lines changed

src/EventListener/ConsoleListener.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Sentry\Event;
88
use Sentry\EventHint;
99
use Sentry\ExceptionMechanism;
10+
use Sentry\Logs\Logs;
1011
use Sentry\State\HubInterface;
1112
use Sentry\State\Scope;
1213
use Symfony\Component\Console\Event\ConsoleCommandEvent;
@@ -35,8 +36,8 @@ class ConsoleListener
3536
/**
3637
* Constructor.
3738
*
38-
* @param HubInterface $hub The current hub
39-
* @param bool $captureErrors Whether to capture console errors
39+
* @param HubInterface $hub The current hub
40+
* @param bool $captureErrors Whether to capture console errors
4041
*/
4142
public function __construct(HubInterface $hub, bool $captureErrors = true)
4243
{
@@ -60,7 +61,7 @@ public function handleConsoleCommandEvent(ConsoleCommandEvent $event): void
6061
}
6162

6263
if ($input instanceof ArgvInput) {
63-
$scope->setExtra('Full command', (string) $input);
64+
$scope->setExtra('Full command', (string)$input);
6465
}
6566
}
6667

@@ -71,6 +72,7 @@ public function handleConsoleCommandEvent(ConsoleCommandEvent $event): void
7172
*/
7273
public function handleConsoleTerminateEvent(ConsoleTerminateEvent $event): void
7374
{
75+
Logs::getInstance()->flush();
7476
$this->hub->popScope();
7577
}
7678

@@ -82,7 +84,7 @@ public function handleConsoleTerminateEvent(ConsoleTerminateEvent $event): void
8284
public function handleConsoleErrorEvent(ConsoleErrorEvent $event): void
8385
{
8486
$this->hub->configureScope(function (Scope $scope) use ($event): void {
85-
$scope->setTag('console.command.exit_code', (string) $event->getExitCode());
87+
$scope->setTag('console.command.exit_code', (string)$event->getExitCode());
8688

8789
if ($this->captureErrors) {
8890
$hint = EventHint::fromArray([
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\EventListener;
4+
5+
use Sentry\Logs\Logs;
6+
use Symfony\Component\HttpKernel\Event\TerminateEvent;
7+
8+
/**
9+
* RequestListener for sentry log related events.
10+
*/
11+
class LogRequestListener
12+
{
13+
14+
/**
15+
* Flushes the logs on kernel termination.
16+
*
17+
* @param TerminateEvent $event
18+
* @return void
19+
*/
20+
public function handleKernelTerminateEvent(TerminateEvent $event)
21+
{
22+
Logs::getInstance()->flush();
23+
}
24+
25+
}

src/Monolog/LogsHandler.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Monolog;
6+
7+
use Monolog\Handler\HandlerInterface;
8+
use Monolog\Logger as MonologLogger;
9+
use Sentry\Monolog\CompatibilityLogLevelTrait;
10+
use Sentry\Monolog\LogsHandler as BaseLogsHandler;
11+
12+
/**
13+
* Wrapper for the Sentry LogsHandler that uses Monolog constants for initialization.
14+
* Sentry's LogLevel classes cannot be properly used in Symfony configuration so this acts as
15+
* a service facade that can be instantiated using yml/xml config files.
16+
*/
17+
class LogsHandler implements HandlerInterface
18+
{
19+
use CompatibilityLogLevelTrait;
20+
21+
/**
22+
* @var BaseLogsHandler
23+
*/
24+
private $logsHandler;
25+
26+
public function __construct(int $level = MonologLogger::DEBUG, bool $bubble = true)
27+
{
28+
$logLevel = self::getSentryLogLevelFromMonologLevel($level);
29+
$this->logsHandler = new BaseLogsHandler($logLevel, $bubble);
30+
}
31+
32+
public function isHandling(array $record): bool
33+
{
34+
return $this->logsHandler->isHandling($record);
35+
}
36+
37+
public function handle(array $record): bool
38+
{
39+
// Extra check required here because `isHandling` is not guaranteed to
40+
// be called, and we might accidentally capture log messages that should be filtered.
41+
if ($this->logsHandler->isHandling($record)) {
42+
return $this->logsHandler->handle($record);
43+
}
44+
return false;
45+
}
46+
47+
public function handleBatch(array $records): void
48+
{
49+
$this->logsHandler->handleBatch($records);
50+
}
51+
52+
public function close(): void
53+
{
54+
$this->logsHandler->close();
55+
}
56+
}

src/Resources/config/services.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
<tag name="kernel.event_listener" event="kernel.request" method="handleKernelRequestEvent" />
8686
</service>
8787

88+
<service id="Sentry\SentryBundle\EventListener\LogRequestListener" class="Sentry\SentryBundle\EventListener\LogRequestListener">
89+
<tag name="kernel.event_listener" event="kernel.terminate" method="handleKernelTerminateEvent" priority="10" />
90+
</service>
91+
8892
<service id="Sentry\SentryBundle\Command\SentryTestCommand" class="Sentry\SentryBundle\Command\SentryTestCommand">
8993
<argument type="service" id="Sentry\State\HubInterface" />
9094

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\Tests\End2End\App\Command;
4+
5+
use Psr\Log\LoggerInterface;
6+
use Symfony\Component\Console\Command\Command;
7+
use Symfony\Component\Console\Input\ArgvInput;
8+
use Symfony\Component\Console\Input\InputInterface;
9+
use Symfony\Component\Console\Output\OutputInterface;
10+
11+
class CrashCommand extends Command
12+
{
13+
14+
/**
15+
* @var LoggerInterface
16+
*/
17+
private $logger;
18+
19+
/**
20+
* @var string
21+
*/
22+
private $subcommand;
23+
24+
public function __construct(LoggerInterface $logger, string $subcommand = null)
25+
{
26+
parent::__construct();
27+
$this->logger = $logger;
28+
$this->subcommand = $subcommand;
29+
}
30+
31+
protected function execute(InputInterface $input, OutputInterface $output)
32+
{
33+
$this->logger->warning("Executing subcommand if exists");
34+
35+
if ($this->subcommand !== null) {
36+
$this->getApplication()->doRun(new ArgvInput(['bin/console', 'log:test']), $output);
37+
}
38+
39+
$this->logger->emergency("About to crash");
40+
41+
throw new \RuntimeException("Crash in command");
42+
}
43+
44+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\Tests\End2End\App\Command;
4+
5+
use Psr\Log\LoggerInterface;
6+
use Symfony\Component\Console\Command\Command;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Output\OutputInterface;
9+
10+
class LoggingCommand extends Command
11+
{
12+
/**
13+
* @var LoggerInterface
14+
*/
15+
private $logger;
16+
17+
public function __construct(LoggerInterface $logger)
18+
{
19+
parent::__construct();
20+
$this->logger = $logger;
21+
}
22+
23+
protected function execute(InputInterface $input, OutputInterface $output)
24+
{
25+
$this->logger->debug("Debug Log");
26+
27+
$this->logger->info("Info Log");
28+
29+
$this->logger->warning("Warn Log");
30+
31+
$this->logger->error("Error Log");
32+
33+
return 0;
34+
}
35+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\Tests\End2End\App\Controller;
4+
5+
use Psr\Log\LoggerInterface;
6+
use Symfony\Component\HttpFoundation\Response;
7+
8+
class LoggingController
9+
{
10+
11+
/**
12+
* @var LoggerInterface
13+
*/
14+
private $logger;
15+
16+
public function __construct(LoggerInterface $logger)
17+
{
18+
$this->logger = $logger;
19+
}
20+
21+
public function justLogging()
22+
{
23+
// LogLevel::fatal()
24+
$this->logger->emergency("Emergency Log");
25+
$this->logger->critical("Critical Log");
26+
// LogLevel::error()
27+
$this->logger->error("Error Log");
28+
// LogLevel::warn()
29+
$this->logger->warning("Warn Log");
30+
// LogLevel::info()
31+
$this->logger->info("Info Log");
32+
$this->logger->notice("Notice Log");
33+
// LogLevel::debug()
34+
$this->logger->debug("Debug Log");
35+
36+
return new Response();
37+
}
38+
39+
public function loggingWithError()
40+
{
41+
$this->logger->emergency("Something is not right");
42+
$this->logger->error("About to crash");
43+
throw new \RuntimeException("Crash");
44+
}
45+
46+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\Tests\End2End\App;
4+
5+
use Symfony\Component\Config\Loader\LoaderInterface;
6+
7+
class KernelWithLogging extends Kernel
8+
{
9+
10+
public function registerContainerConfiguration(LoaderInterface $loader): void
11+
{
12+
parent::registerContainerConfiguration($loader);
13+
14+
$loader->load(__DIR__ . '/logging.yml');
15+
}
16+
}

tests/End2End/App/config.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ services:
3636
Sentry\SentryBundle\Tests\End2End\App\Command\MainCommand:
3737
tags: [{ name: 'console.command', command: 'main-command' }]
3838

39+
Sentry\SentryBundle\Tests\End2End\App\Command\LoggingCommand:
40+
autowire: true
41+
tags: [ { name: 'console.command', command: 'log:test' } ]
42+
43+
Sentry\SentryBundle\Tests\End2End\App\Command\CrashCommand.nosub:
44+
autowire: true
45+
class: Sentry\SentryBundle\Tests\End2End\App\Command\CrashCommand
46+
arguments:
47+
$subcommand: null
48+
tags: [ { name: 'console.command', command: 'log:nosub' } ]
49+
50+
Sentry\SentryBundle\Tests\End2End\App\Command\CrashCommand.sub:
51+
autowire: true
52+
class: Sentry\SentryBundle\Tests\End2End\App\Command\CrashCommand
53+
arguments:
54+
$subcommand: 'log:test'
55+
tags: [ { name: 'console.command', command: 'log:sub' } ]
56+
3957
monolog:
4058
handlers:
4159
main:

tests/End2End/App/logging.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
sentry:
2+
options:
3+
enable_logs: true
4+
5+
monolog:
6+
handlers:
7+
sentry_logs:
8+
type: service
9+
id: Sentry\SentryBundle\Monolog\LogsHandler
10+
11+
services:
12+
Sentry\SentryBundle\Monolog\LogsHandler:
13+
arguments:
14+
- !php/const Monolog\Logger::WARNING
15+
16+
Sentry\SentryBundle\Tests\End2End\App\Controller\LoggingController:
17+
autowire: true
18+
tags:
19+
- { name: controller.service_arguments }

0 commit comments

Comments
 (0)