Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Monolog/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
* hub instance.
*
* @deprecated since version 4.24. To be removed in version 5.0. Use {@see LogsHandler}
* with the `enable_logs` SDK option instead for logging. {@see ExceptionToSentryIssueHandler}
* to send monolog exceptions to Sentry.
* with the `enable_logs` SDK option for Sentry logs, {@see ExceptionToSentryIssueHandler}
* to send Monolog exceptions to Sentry issues, and {@see LogToSentryIssueHandler}
* to send Monolog log messages to Sentry issues.
*
* @author Stefano Arlandini <sarlandini@alice.it>
*/
Expand Down
118 changes: 118 additions & 0 deletions src/Monolog/LogToSentryIssueHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types=1);

namespace Sentry\Monolog;

use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Level;
use Monolog\Logger;
use Monolog\LogRecord;
use Psr\Log\LogLevel;
use Sentry\Event;
use Sentry\EventHint;
use Sentry\State\HubInterface;
use Sentry\State\Scope;

/**
* This Monolog handler captures log messages as Sentry issues.
*/
class LogToSentryIssueHandler extends AbstractProcessingHandler
{
use CompatibilityProcessingHandlerTrait;

private const CONTEXT_EXCEPTION_KEY = 'exception';

/**
* @var HubInterface
*/
private $hub;

/**
* @var bool
*/
private $fillExtraContext;

/**
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
*/
public function __construct(HubInterface $hub, $level = Logger::DEBUG, bool $bubble = true, bool $fillExtraContext = false)
{
$this->hub = $hub;
$this->fillExtraContext = $fillExtraContext;

parent::__construct($level, $bubble);
}

/**
* @param array<string, mixed>|LogRecord $record
*/
public function handle($record): bool
{
/** @phpstan-ignore-next-line */
if (!$this->isHandling($record) || $this->hasThrowable($record)) {
return false;
}

/** @phpstan-ignore-next-line */
return parent::handle($record);
}

/**
* @param array<string, mixed>|LogRecord $record
*/
protected function doWrite($record): void
{
$event = Event::createEvent();
$event->setLevel(self::getSeverityFromLevel($record['level']));
$event->setMessage($record['message']);
$event->setLogger(\sprintf('monolog.%s', $record['channel']));

$hint = new EventHint();

$this->hub->withScope(function (Scope $scope) use ($record, $event, $hint): void {
$scope->setExtra('monolog.channel', $record['channel']);
$scope->setExtra('monolog.level', $record['level_name']);

if ($this->fillExtraContext) {
$monologContextData = $this->getArrayFieldFromRecord($record, 'context');

if ($monologContextData !== []) {
$scope->setExtra('monolog.context', $monologContextData);
}

$monologExtraData = $this->getArrayFieldFromRecord($record, 'extra');

if ($monologExtraData !== []) {
$scope->setExtra('monolog.extra', $monologExtraData);
}
}

$this->hub->captureEvent($event, $hint);
});
}

/**
* @param array<string, mixed>|LogRecord $record
*/
private function hasThrowable($record): bool
{
$exception = $this->getArrayFieldFromRecord($record, 'context')[self::CONTEXT_EXCEPTION_KEY] ?? null;

return $exception instanceof \Throwable;
}
Comment thread
cursor[bot] marked this conversation as resolved.

/**
* @param array<string, mixed>|LogRecord $record
*
* @return array<string, mixed>
*/
private function getArrayFieldFromRecord($record, string $field): array
{
if (isset($record[$field]) && \is_array($record[$field])) {
return $record[$field];
}

return [];
}
}
2 changes: 1 addition & 1 deletion src/Monolog/LogsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function handle($record): bool
if (!$this->isHandling($record)) {
return false;
}
// Do not collect logs for exceptions, they should be handled seperately by the `Handler` or `captureException`
// Do not collect logs for exceptions, they should be handled separately by `ExceptionToSentryIssueHandler` or `captureException`
if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
return false;
}
Expand Down
Loading
Loading