vendor/rollbar/rollbar/src/RollbarLogger.php line 216

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Rollbar;
  3. use Exception;
  4. use Psr\Log\InvalidArgumentException;
  5. use Psr\Log\LoggerInterface;
  6. use Stringable;
  7. use Throwable;
  8. use Psr\Log\AbstractLogger;
  9. use Rollbar\Payload\Payload;
  10. use Rollbar\Payload\Level;
  11. use Rollbar\Truncation\Truncation;
  12. use Rollbar\Payload\EncodedPayload;
  13. class RollbarLogger extends AbstractLogger
  14. {
  15.     /**
  16.      * @var Config $config The logger configuration instance.
  17.      */
  18.     private Config $config;
  19.     /**
  20.      * @var Truncation The payload truncation manager for the logger.
  21.      */
  22.     private Truncation $truncation;
  23.     /**
  24.      * @var array $queue The queue for sending payloads in batched mode.
  25.      */
  26.     private array $queue;
  27.     /**
  28.      * @var int $reportCount The number of reports already sent or queued.
  29.      */
  30.     private int $reportCount 0;
  31.     /**
  32.      * Creates a new instance of the RollbarLogger.
  33.      *
  34.      * @param array $config The array of configs to use for the logger.
  35.      *
  36.      * @throws Exception If a custom truncation strategy class does not implement {@see StrategyInterface}.
  37.      */
  38.     public function __construct(array $config)
  39.     {
  40.         $this->config     = new Config($config);
  41.         $this->truncation = new Truncation($this->config);
  42.         $this->queue      = array();
  43.     }
  44.     /**
  45.      * Returns the configs object.
  46.      *
  47.      * @return Config
  48.      *
  49.      * @since 3.0
  50.      */
  51.     public function getConfig(): Config
  52.     {
  53.         return $this->config;
  54.     }
  55.     /**
  56.      * Enables logging of errors to Rollbar.
  57.      *
  58.      * @return void
  59.      */
  60.     public function enable(): void
  61.     {
  62.         $this->config->enable();
  63.     }
  64.     /**
  65.      * Disables logging of errors to Rollbar.
  66.      *
  67.      * @return void
  68.      */
  69.     public function disable(): void
  70.     {
  71.         $this->config->disable();
  72.     }
  73.     /**
  74.      * Returns true if the Rollbar logger is enabled.
  75.      *
  76.      * @return bool
  77.      */
  78.     public function enabled(): bool
  79.     {
  80.         return $this->config->enabled();
  81.     }
  82.     /**
  83.      * Returns true if the Rollbar logger is disabled.
  84.      *
  85.      * @return bool
  86.      */
  87.     public function disabled(): bool
  88.     {
  89.         return $this->config->disabled();
  90.     }
  91.     /**
  92.      * Updates the existing configurations. All existing configurations will be kept unless explicitly updated in the
  93.      * $config array.
  94.      *
  95.      * @param array $config The new configurations to add or overwrite the existing configurations.
  96.      *
  97.      * @return void
  98.      */
  99.     public function configure(array $config): void
  100.     {
  101.         $this->config->configure($config);
  102.     }
  103.     /**
  104.      * Returns a new {@see RollbarLogger} instance with a combination of the configs from the current logger updated
  105.      * with the extra $configs array.
  106.      *
  107.      * @param array $config Additional configurations to update or overwrite the existing configurations.
  108.      *
  109.      * @return RollbarLogger
  110.      * @throws Exception
  111.      */
  112.     public function scope(array $config): RollbarLogger
  113.     {
  114.         return new RollbarLogger($this->extend($config));
  115.     }
  116.     /**
  117.      * Deeply combines the provided $config array and the existing configurations and returns the combined array of
  118.      * configs.
  119.      *
  120.      * @param array $config The new configs to update or overwrite the existing configurations.
  121.      *
  122.      * @return array
  123.      */
  124.     public function extend(array $config): array
  125.     {
  126.         return $this->config->extend($config);
  127.     }
  128.     /**
  129.      * Adds a new key / value pair that will be sent with the payload to Rollbar. If the key already exists in the
  130.      * custom data array the existing value will be overwritten.
  131.      *
  132.      * @param string $key  The key to store this value in the custom array.
  133.      * @param mixed  $data The value that is going to be stored. Must be a primitive or JSON serializable.
  134.      *
  135.      * @return void
  136.      */
  137.     public function addCustom(string $keymixed $data): void
  138.     {
  139.         $this->config->addCustom($key$data);
  140.     }
  141.     /**
  142.      * Removes a key from the custom data array that is sent with the payload to Rollbar.
  143.      *
  144.      * @param string $key The key to remove.
  145.      *
  146.      * @return void
  147.      */
  148.     public function removeCustom(string $key): void
  149.     {
  150.         $this->config->removeCustom($key);
  151.     }
  152.     /**
  153.      * Returns the array of key / value pairs that will be sent with the payload to Rollbar.
  154.      *
  155.      * @return array|null
  156.      */
  157.     public function getCustom(): mixed
  158.     {
  159.         return $this->config->getCustom();
  160.     }
  161.     /**
  162.      * Logs a message to the Rollbar service with the specified level.
  163.      *
  164.      * @param Level|string      $level   The severity level of the message.
  165.      *                                   Must be one of the levels as defined in
  166.      *                                   the {@see Level} constants.
  167.      * @param string|Stringable $message The log message.
  168.      * @param array             $context Arbitrary data.
  169.      *
  170.      * @return void
  171.      *
  172.      * @throws InvalidArgumentException If $level is not a valid level.
  173.      * @throws Throwable Rethrown $message if it is {@see Throwable} and {@see Config::raiseOnError} is true.
  174.      */
  175.     public function log($level$message, array $context = array()): void
  176.     {
  177.         $this->report($level$message$context);
  178.     }
  179.     /**
  180.      * Creates the {@see Response} object and reports the message to the Rollbar
  181.      * service.
  182.      *
  183.      * @param string|Level      $level      The severity level to send to Rollbar.
  184.      * @param string|Stringable $message    The log message.
  185.      * @param array             $context    Any additional context data.
  186.      * @param bool              $isUncaught True if the error or exception was captured by a Rollbar handler. Thus, it
  187.      *                                      was not caught by the application.
  188.      *
  189.      * @return Response
  190.      *
  191.      * @throws InvalidArgumentException If $level is not a valid level.
  192.      * @throws Throwable Rethrown $message if it is {@see Throwable} and {@see Config::raiseOnError} is true.
  193.      *
  194.      * @since  4.0.0
  195.      */
  196.     public function report(
  197.         string|Level $level,
  198.         string|Stringable $message,
  199.         array $context = array(),
  200.         bool $isUncaught false
  201.     ): Response {
  202.         if ($this->disabled()) {
  203.             $this->verboseLogger()->notice('Rollbar is disabled');
  204.             return new Response(0"Disabled");
  205.         }
  206.         // Convert a Level proper into a string proper, as the code paths that
  207.         // follow have allowed both only by virtue that a Level downcasts to a
  208.         // string. With strict types, that no longer happens. We should consider
  209.         // tightening the boundary so that we convert from string to Level
  210.         // enum here, and work with Level enum through protected level.
  211.         if ($level instanceof Level) {
  212.             $level = (string)$level;
  213.         } elseif (!LevelFactory::isValidLevel($level)) {
  214.             $exception = new InvalidArgumentException("Invalid log level '$level'.");
  215.             $this->verboseLogger()->error($exception->getMessage());
  216.             throw $exception;
  217.         }
  218.         $this->verboseLogger()->info("Attempting to log: [$level] " $message);
  219.         if ($this->config->internalCheckIgnored($level$message)) {
  220.             $this->verboseLogger()->info('Occurrence ignored');
  221.             return new Response(0"Ignored");
  222.         }
  223.         $accessToken $this->getAccessToken();
  224.         $payload     $this->getPayload($accessToken$level$message$context);
  225.         if ($this->config->checkIgnored($payload$message$isUncaught)) {
  226.             $this->verboseLogger()->info('Occurrence ignored');
  227.             $response = new Response(0"Ignored");
  228.         } else {
  229.             $serialized $payload->serialize($this->config->getMaxNestingDepth());
  230.             $scrubbed $this->scrub($serialized);
  231.             $encoded $this->encode($scrubbed);
  232.             $truncated $this->truncate($encoded);
  233.             $response $this->send($truncated$accessToken);
  234.         }
  235.         $this->handleResponse($payload$response);
  236.         if ($response->getStatus() === 0) {
  237.             $this->verboseLogger()->error('Occurrence rejected by the SDK: ' $response);
  238.         } elseif ($response->getStatus() >= 400) {
  239.             $info $response->getInfo();
  240.             $this->verboseLogger()->error(
  241.                 'Occurrence rejected by the API: with status ' $response->getStatus() . ': '
  242.                 . ($info['message'] ?? 'message not set')
  243.             );
  244.         } else {
  245.             $this->verboseLogger()->info('Occurrence successfully logged');
  246.         }
  247.         if ($message instanceof Throwable && $this->config->getRaiseOnError()) {
  248.             throw $message;
  249.         }
  250.         return $response;
  251.     }
  252.     /**
  253.      * Sends and flushes the batch payload queue.
  254.      *
  255.      * @return Response|null
  256.      */
  257.     public function flush(): ?Response
  258.     {
  259.         if ($this->getQueueSize() > 0) {
  260.             $batch       $this->queue;
  261.             $this->queue = array();
  262.             return $this->config->sendBatch($batch$this->getAccessToken());
  263.         }
  264.         $this->verboseLogger()->debug('Queue flushed');
  265.         return new Response(0"Queue empty");
  266.     }
  267.     /**
  268.      * Sends and flushes the batch payload queue, and waits for the requests to be sent.
  269.      *
  270.      * @return void
  271.      */
  272.     public function flushAndWait(): void
  273.     {
  274.         $this->flush();
  275.         $this->config->wait($this->getAccessToken());
  276.     }
  277.     /**
  278.      * Returns true if the error level should be ignored because of error level or sampling rates.
  279.      *
  280.      * @param int $errno The PHP error level bitmask.
  281.      *
  282.      * @return bool
  283.      */
  284.     public function shouldIgnoreError(int $errno): bool
  285.     {
  286.         return $this->config->shouldIgnoreError($errno);
  287.     }
  288.     /**
  289.      * Returns the number of report payloads currently in batch queue.
  290.      *
  291.      * @return int
  292.      */
  293.     public function getQueueSize(): int
  294.     {
  295.         return count($this->queue);
  296.     }
  297.     /**
  298.      * Sends a report to the Rollbar service.
  299.      *
  300.      * @param EncodedPayload $payload     The prepared payload to send.
  301.      * @param string         $accessToken The API access token.
  302.      *
  303.      * @return Response
  304.      */
  305.     protected function send(EncodedPayload $payloadstring $accessToken): Response
  306.     {
  307.         if ($this->reportCount >= $this->config->getMaxItems()) {
  308.             $response = new Response(
  309.                 0,
  310.                 "Maximum number of items per request has been reached. If you " .
  311.                 "want to report more items, please use `max_items` " .
  312.                 "configuration option."
  313.             );
  314.             $this->verboseLogger()->warning($response->getInfo());
  315.             return $response;
  316.         } else {
  317.             $this->reportCount++;
  318.         }
  319.         if ($this->config->getBatched()) {
  320.             $response = new Response(0"Pending");
  321.             if ($this->getQueueSize() >= $this->config->getBatchSize()) {
  322.                 $response $this->flush();
  323.             }
  324.             $this->queue[] = $payload;
  325.             $this->verboseLogger()->debug("Added payload to the queue (running in `batched` mode).");
  326.             return $response;
  327.         }
  328.         return $this->config->send($payload$accessToken);
  329.     }
  330.     /**
  331.      * Creates a payload from a log level and message or error.
  332.      *
  333.      * @param string            $accessToken The API access token.
  334.      * @param string            $level       The log level. Should be one of the {@see Level} constants.
  335.      * @param string|Stringable $toLog       The message or error to be sent to Rollbar.
  336.      * @param array             $context     Additional data to send with the message.
  337.      *
  338.      * @return Payload
  339.      */
  340.     protected function getPayload(
  341.         string $accessToken,
  342.         string $level,
  343.         string|Stringable $toLog,
  344.         array $context,
  345.     ): Payload {
  346.         $data    $this->config->getRollbarData($level$toLog$context);
  347.         $payload = new Payload($data$accessToken);
  348.         return $this->config->transform($payload$level$toLog$context);
  349.     }
  350.     /**
  351.      * Returns the access token for the logger instance.
  352.      *
  353.      * @return string
  354.      */
  355.     protected function getAccessToken(): string
  356.     {
  357.         return $this->config->getAccessToken();
  358.     }
  359.     /**
  360.      * Returns the configured DataBuilder instance.
  361.      *
  362.      * @return DataBuilder
  363.      */
  364.     public function getDataBuilder(): DataBuilder
  365.     {
  366.         return $this->config->getDataBuilder();
  367.     }
  368.     /**
  369.      * Returns the logger responsible for logging request payload and response dumps, if enabled.
  370.      *
  371.      * @return LoggerInterface
  372.      */
  373.     public function logPayloadLogger(): LoggerInterface
  374.     {
  375.         return $this->config->logPayloadLogger();
  376.     }
  377.     /**
  378.      * Returns the verbose logger instance.
  379.      *
  380.      * @return LoggerInterface
  381.      */
  382.     public function verboseLogger(): LoggerInterface
  383.     {
  384.         return $this->config->verboseLogger();
  385.     }
  386.     /**
  387.      * Calls the custom 'responseHandler', config if it exists.
  388.      *
  389.      * @param Payload  $payload  The payload that was sent.
  390.      * @param Response $response The response from the Rollbar service.
  391.      *
  392.      * @return void
  393.      */
  394.     protected function handleResponse(Payload $payloadResponse $response): void
  395.     {
  396.         $this->config->handleResponse($payload$response);
  397.     }
  398.     /**
  399.      * Tries to remove sensitive data from the payload before it is sent.
  400.      *
  401.      * @param array $serializedPayload The multidimensional array of data to be sent.
  402.      *
  403.      * @return array
  404.      */
  405.     protected function scrub(array &$serializedPayload): array
  406.     {
  407.         $serializedPayload['data'] = $this->config->getScrubber()->scrub($serializedPayload['data']);
  408.         return $serializedPayload;
  409.     }
  410.     /**
  411.      * Reduces the size of the data, so it does not exceed size limits.
  412.      *
  413.      * @param EncodedPayload $payload The payload to possibly truncate.
  414.      *
  415.      * @return EncodedPayload
  416.      */
  417.     protected function truncate(EncodedPayload $payload): EncodedPayload
  418.     {
  419.         return $this->truncation->truncate($payload);
  420.     }
  421.     /**
  422.      * JSON serializes the payload.
  423.      *
  424.      * @param array $payload The data payload to serialize.
  425.      *
  426.      * @return EncodedPayload
  427.      * @throws Exception If JSON serialization fails.
  428.      */
  429.     protected function encode(array $payload): EncodedPayload
  430.     {
  431.         $encoded = new EncodedPayload($payload);
  432.         $encoded->encode();
  433.         return $encoded;
  434.     }
  435. }