You are here

function raven_watchdog in Raven: Sentry Integration 7.4

Same name and namespace in other branches
  1. 7 raven.module \raven_watchdog()
  2. 7.2 raven.module \raven_watchdog()
  3. 7.3 raven.module \raven_watchdog()

Implements hook_watchdog().

File

./raven.module, line 134
Allows to track errors to Sentry server.

Code

function raven_watchdog($log_entry) {
  static $counter = 0;
  if (!variable_get('raven_enabled', FALSE)) {
    return;
  }
  $client = raven_get_client();
  if (!$client) {
    return;
  }
  $watchdog_levels = variable_get('raven_watchdog_levels', []);
  $levels_map = [
    WATCHDOG_EMERGENCY => Severity::FATAL,
    WATCHDOG_ALERT => Severity::FATAL,
    WATCHDOG_CRITICAL => Severity::FATAL,
    WATCHDOG_ERROR => Severity::ERROR,
    WATCHDOG_WARNING => Severity::WARNING,
    WATCHDOG_NOTICE => Severity::INFO,
    WATCHDOG_INFO => Severity::INFO,
    WATCHDOG_DEBUG => Severity::DEBUG,
  ];
  $variables = $log_entry['variables'];
  if (!$variables) {
    $variables = [];
  }
  $event = Event::createEvent();
  $event
    ->setLevel(new Severity($levels_map[$log_entry['severity']]));
  $message = html_entity_decode(strip_tags(strtr($log_entry['message'], $variables)), ENT_QUOTES, 'UTF-8');
  $event
    ->setMessage($log_entry['message'], $variables, $message);
  $extra = [
    'request_uri' => $log_entry['request_uri'],
  ];
  if ($log_entry['referer']) {
    $extra['referer'] = $log_entry['referer'];
  }
  if ($log_entry['link']) {
    $extra['link'] = $log_entry['link'];
  }
  $event
    ->setExtra($extra);
  $event
    ->setLogger($log_entry['type']);
  $user = UserDataBag::createFromUserIdentifier($log_entry['uid']);
  $user
    ->setIpAddress($log_entry['ip']);
  if ($log_entry['user'] && $log_entry['uid']) {
    if (variable_get('raven_send_user_data', FALSE)) {
      $user
        ->setEmail($log_entry['user']->mail);
      $user
        ->setUsername($log_entry['user']->name);
    }
    $user
      ->setMetadata('roles', implode(', ', $log_entry['user']->roles));
  }
  $event
    ->setUser($user);
  $filter = [
    'process' => !empty($watchdog_levels[$log_entry['severity'] + 1]),
    'log_entry' => $log_entry,
    'event' => $event,
  ];
  $ignored_types = array_map('trim', preg_split('/\\R/', variable_get('raven_ignored_types', ''), -1, PREG_SPLIT_NO_EMPTY));
  if (in_array($log_entry['type'], $ignored_types)) {
    $filter['process'] = FALSE;
  }
  drupal_alter('raven_watchdog_filter', $filter);
  if ($filter['process']) {

    // Save memory by not copying the object for each frame.
    $stack = debug_backtrace(0);

    // Ignore error handling and logging frames.
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && $stack[0]['function'] == 'raven_watchdog') {
      array_shift($stack);
    }
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && $stack[0]['function'] == 'call_user_func_array') {
      array_shift($stack);
    }
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && ($stack[0]['function'] == 'module_invoke_all' || $stack[0]['function'] == 'module_invoke')) {
      array_shift($stack);
    }
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && $stack[0]['function'] == 'watchdog' && empty($stack[1]['class']) && isset($stack[1]['function']) && $stack[1]['function'] == 'watchdog_exception') {
      array_shift($stack);
    }
    elseif (empty($stack[0]['class']) && isset($stack[0]['function']) && $stack[0]['function'] == 'watchdog' && empty($stack[1]['class']) && isset($stack[1]['function']) && $stack[1]['function'] == '_drupal_log_error') {
      array_shift($stack);
      array_shift($stack);
    }
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && $stack[0]['function'] == '_drupal_error_handler_real') {
      array_shift($stack);
    }
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && $stack[0]['function'] == '_drupal_error_handler' && empty($stack[0]['line'])) {
      array_shift($stack);
    }
    if (empty($stack[0]['class']) && isset($stack[0]['function']) && ($stack[0]['function'] == 'watchdog_exception' || $stack[0]['function'] == '_drupal_exception_handler')) {
      $arg = [
        'watchdog_exception' => 1,
        '_drupal_exception_handler' => 0,
      ];

      // Use the exception backtrace for (usually) easier debugging.
      $exception = $stack[0]['args'][$arg[$stack[0]['function']]];
      $stack = $exception
        ->getTrace();

      // Copy logic from _drupal_decode_exception().
      array_unshift($stack, [
        'line' => $exception
          ->getLine(),
        'file' => $exception
          ->getFile(),
      ]);
      if ($exception instanceof PDOException) {
        $db_functions = [
          'db_query',
          'db_query_range',
        ];
        while (!empty($stack[1]) && ($caller = $stack[1]) && (isset($caller['class']) && (strpos($caller['class'], 'Query') !== FALSE || strpos($caller['class'], 'Database') !== FALSE || strpos($caller['class'], 'PDO') !== FALSE) || in_array($caller['function'], $db_functions))) {
          array_shift($stack);
        }
      }
    }
    if (!variable_get('raven_trace', FALSE)) {
      foreach ($stack as &$frame) {
        unset($frame['args']);
      }
    }
    $stacktraceBuilder = new StacktraceBuilder($client
      ->getOptions(), new RepresentationSerializer($client
      ->getOptions()));
    $stacktrace = $stacktraceBuilder
      ->buildFromBacktrace($stack, '', 0);
    $stacktrace
      ->removeFrame(count($stacktrace
      ->getFrames()) - 1);
    $rateLimit = variable_get('raven_rate_limit', 0);
    if (!$rateLimit || $counter < $rateLimit) {
      \Sentry\captureEvent($event, EventHint::fromArray([
        'stacktrace' => $stacktrace,
      ]));
    }
    elseif ($counter == $rateLimit) {
      \Sentry\captureException(new RavenRateLimitException('Log event discarded due to rate limit exceeded; future log events will not be captured by Sentry.'));
    }
    $counter++;
  }

  // Record a breadcrumb.
  $breadcrumb = [
    'log_entry' => $log_entry,
    'process' => TRUE,
    'breadcrumb' => [
      'category' => $log_entry['type'],
      'message' => $message,
      'level' => $levels_map[$log_entry['severity']],
    ],
  ];
  foreach ([
    '%line',
    '%file',
    '%type',
    '%function',
  ] as $key) {
    if (isset($log_entry['variables'][$key])) {
      $breadcrumb['breadcrumb']['data'][substr($key, 1)] = $log_entry['variables'][$key];
    }
  }
  drupal_alter('raven_breadcrumb', $breadcrumb);
  if (!empty($breadcrumb['process'])) {
    \Sentry\addBreadcrumb(Breadcrumb::fromArray($breadcrumb['breadcrumb']));
  }
}