1<?php declare(strict_types=1); 2 3/* 4 * This file is part of the Monolog package. 5 * 6 * (c) Jordi Boggiano <j.boggiano@seld.be> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Monolog\Handler; 13 14use Rollbar\RollbarLogger; 15use Throwable; 16use Monolog\Logger; 17 18/** 19 * Sends errors to Rollbar 20 * 21 * If the context data contains a `payload` key, that is used as an array 22 * of payload options to RollbarLogger's log method. 23 * 24 * Rollbar's context info will contain the context + extra keys from the log record 25 * merged, and then on top of that a few keys: 26 * 27 * - level (rollbar level name) 28 * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) 29 * - channel 30 * - datetime (unix timestamp) 31 * 32 * @author Paul Statezny <paulstatezny@gmail.com> 33 */ 34class RollbarHandler extends AbstractProcessingHandler 35{ 36 /** 37 * @var RollbarLogger 38 */ 39 protected $rollbarLogger; 40 41 /** @var string[] */ 42 protected $levelMap = [ 43 Logger::DEBUG => 'debug', 44 Logger::INFO => 'info', 45 Logger::NOTICE => 'info', 46 Logger::WARNING => 'warning', 47 Logger::ERROR => 'error', 48 Logger::CRITICAL => 'critical', 49 Logger::ALERT => 'critical', 50 Logger::EMERGENCY => 'critical', 51 ]; 52 53 /** 54 * Records whether any log records have been added since the last flush of the rollbar notifier 55 * 56 * @var bool 57 */ 58 private $hasRecords = false; 59 60 /** @var bool */ 61 protected $initialized = false; 62 63 /** 64 * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token 65 */ 66 public function __construct(RollbarLogger $rollbarLogger, $level = Logger::ERROR, bool $bubble = true) 67 { 68 $this->rollbarLogger = $rollbarLogger; 69 70 parent::__construct($level, $bubble); 71 } 72 73 /** 74 * {@inheritDoc} 75 */ 76 protected function write(array $record): void 77 { 78 if (!$this->initialized) { 79 // __destructor() doesn't get called on Fatal errors 80 register_shutdown_function(array($this, 'close')); 81 $this->initialized = true; 82 } 83 84 $context = $record['context']; 85 $context = array_merge($context, $record['extra'], [ 86 'level' => $this->levelMap[$record['level']], 87 'monolog_level' => $record['level_name'], 88 'channel' => $record['channel'], 89 'datetime' => $record['datetime']->format('U'), 90 ]); 91 92 if (isset($context['exception']) && $context['exception'] instanceof Throwable) { 93 $exception = $context['exception']; 94 unset($context['exception']); 95 $toLog = $exception; 96 } else { 97 $toLog = $record['message']; 98 } 99 100 // @phpstan-ignore-next-line 101 $this->rollbarLogger->log($context['level'], $toLog, $context); 102 103 $this->hasRecords = true; 104 } 105 106 public function flush(): void 107 { 108 if ($this->hasRecords) { 109 $this->rollbarLogger->flush(); 110 $this->hasRecords = false; 111 } 112 } 113 114 /** 115 * {@inheritDoc} 116 */ 117 public function close(): void 118 { 119 $this->flush(); 120 } 121 122 /** 123 * {@inheritDoc} 124 */ 125 public function reset() 126 { 127 $this->flush(); 128 129 parent::reset(); 130 } 131} 132