|
|
@@ -147,6 +147,11 @@ class Logger implements LoggerInterface, ResettableInterface
|
|
|
*/
|
|
|
protected $exceptionHandler;
|
|
|
|
|
|
+ /**
|
|
|
+ * @var int Keeps track of depth to prevent infinite logging loops
|
|
|
+ */
|
|
|
+ private $logDepth = 0;
|
|
|
+
|
|
|
/**
|
|
|
* @psalm-param array<callable(array): array> $processors
|
|
|
*
|
|
|
@@ -291,30 +296,51 @@ class Logger implements LoggerInterface, ResettableInterface
|
|
|
*/
|
|
|
public function addRecord(int $level, string $message, array $context = []): bool
|
|
|
{
|
|
|
- $record = null;
|
|
|
+ $this->logDepth += 1;
|
|
|
+ if ($this->logDepth === 3) {
|
|
|
+ $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.');
|
|
|
+ return false;
|
|
|
+ } elseif ($this->logDepth >= 5) { // log depth 4 is let through so we can log the warning above
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- foreach ($this->handlers as $handler) {
|
|
|
- if (null === $record) {
|
|
|
- // skip creating the record as long as no handler is going to handle it
|
|
|
- if (!$handler->isHandling(['level' => $level])) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ try {
|
|
|
+ $record = null;
|
|
|
|
|
|
- $levelName = static::getLevelName($level);
|
|
|
+ foreach ($this->handlers as $handler) {
|
|
|
+ if (null === $record) {
|
|
|
+ // skip creating the record as long as no handler is going to handle it
|
|
|
+ if (!$handler->isHandling(['level' => $level])) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- $record = [
|
|
|
- 'message' => $message,
|
|
|
- 'context' => $context,
|
|
|
- 'level' => $level,
|
|
|
- 'level_name' => $levelName,
|
|
|
- 'channel' => $this->name,
|
|
|
- 'datetime' => new DateTimeImmutable($this->microsecondTimestamps, $this->timezone),
|
|
|
- 'extra' => [],
|
|
|
- ];
|
|
|
+ $levelName = static::getLevelName($level);
|
|
|
+
|
|
|
+ $record = [
|
|
|
+ 'message' => $message,
|
|
|
+ 'context' => $context,
|
|
|
+ 'level' => $level,
|
|
|
+ 'level_name' => $levelName,
|
|
|
+ 'channel' => $this->name,
|
|
|
+ 'datetime' => new DateTimeImmutable($this->microsecondTimestamps, $this->timezone),
|
|
|
+ 'extra' => [],
|
|
|
+ ];
|
|
|
+
|
|
|
+ try {
|
|
|
+ foreach ($this->processors as $processor) {
|
|
|
+ $record = $processor($record);
|
|
|
+ }
|
|
|
+ } catch (Throwable $e) {
|
|
|
+ $this->handleException($e, $record);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ // once the record exists, send it to all handlers as long as the bubbling chain is not interrupted
|
|
|
try {
|
|
|
- foreach ($this->processors as $processor) {
|
|
|
- $record = $processor($record);
|
|
|
+ if (true === $handler->handle($record)) {
|
|
|
+ break;
|
|
|
}
|
|
|
} catch (Throwable $e) {
|
|
|
$this->handleException($e, $record);
|
|
|
@@ -322,17 +348,8 @@ class Logger implements LoggerInterface, ResettableInterface
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // once the record exists, send it to all handlers as long as the bubbling chain is not interrupted
|
|
|
- try {
|
|
|
- if (true === $handler->handle($record)) {
|
|
|
- break;
|
|
|
- }
|
|
|
- } catch (Throwable $e) {
|
|
|
- $this->handleException($e, $record);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
+ } finally {
|
|
|
+ $this->logDepth--;
|
|
|
}
|
|
|
|
|
|
return null !== $record;
|