You are here

public function Logger::log in MongoDB 8.2

See also

https://httpd.apache.org/docs/2.4/en/mod/mod_unique_id.html

File

modules/mongodb_watchdog/src/Logger.php, line 303

Class

Logger
Class Logger is a PSR/3 Logger using a MongoDB data store.

Namespace

Drupal\mongodb_watchdog

Code

public function log($level, $template, array $context = []) : void {

  // PSR-3 LoggerInterface documents level as "mixed", while the RFC itself
  // in §1.1 implies implementations may know about non-standard levels. In
  // the case of Drupal implementations, this includes the 8 RFC5424 levels.
  if (is_string($level)) {
    $level = $this->rfc5424levels[$level];
  }
  if ($level > $this->limit) {
    return;
  }

  // Convert PSR3-style messages to SafeMarkup::format() style, so they can be
  // translated at runtime too.
  $placeholders = $this->parser
    ->parseMessagePlaceholders($template, $context);

  // If code location information is all present, as for errors/exceptions,
  // then use it to build the message template id.
  $type = $context['channel'] ?? static::DEFAULT_CHANNEL;
  $location = [
    '%type' => 1,
    '@message' => 1,
    '%function' => 1,
    '%file' => 1,
    '%line' => 1,
  ];
  if (!empty(array_diff_key($location, $placeholders))) {
    $this
      ->enhanceLogEntry($placeholders, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10));
  }
  $file = $placeholders['%file'];
  $line = $placeholders['%line'];
  $function = $placeholders['%function'];
  $key = implode(":", [
    $type,
    $level,
    $file,
    $line,
    $function,
  ]);
  $templateId = md5($key);
  $selector = [
    '_id' => $templateId,
  ];
  $update = [
    '$inc' => [
      'count' => 1,
    ],
    '$set' => [
      '_id' => $templateId,
      'message' => $template,
      'severity' => $level,
      'changed' => $this->time
        ->getCurrentTime(),
      'type' => mb_substr($type, 0, 64),
    ],
  ];
  $options = [
    'upsert' => TRUE,
  ];
  $templateResult = $this->database
    ->selectCollection(static::TEMPLATE_COLLECTION)
    ->updateOne($selector, $update, $options);

  // Only insert each template once per request.
  if ($this->requestTracking && !isset($this->templates[$templateId])) {
    $requestId = $this->requestStack
      ->getCurrentRequest()->server
      ->get('UNIQUE_ID');
    $this->templates[$templateId] = 1;
    $track = [
      'requestId' => $requestId,
      'templateId' => $templateId,
    ];
    $this
      ->trackerCollection()
      ->insertOne($track);
  }
  else {

    // 24-byte format like mod_unique_id values.
    $requestId = '@@Not-a-valid-request@@';
  }
  $eventCollection = $this
    ->eventCollection($templateId);
  if ($templateResult
    ->getUpsertedCount()) {

    // Capped collections are actually size-based, not count-based, so "items"
    // is only a maximum, assuming event documents weigh 1kB, but the actual
    // number of items stored may be lower if items are heavier.
    // We do not use 'autoindexid' for greater speed, because:
    // - it does not work on replica sets,
    // - it is deprecated in MongoDB 3.2 and going away in 3.4.
    $options = [
      'capped' => TRUE,
      'size' => $this->items * 1024,
      'max' => $this->items,
    ];
    $this->database
      ->createCollection($eventCollection
      ->getCollectionName(), $options);

    // Do not create this index by default, as its cost is useless if request
    // tracking is not enabled.
    if ($this->requestTracking) {
      $key = [
        'requestTracking_id' => 1,
      ];
      $options = [
        'name' => 'admin-by-request',
      ];
      $eventCollection
        ->createIndex($key, $options);
    }
  }
  foreach ($placeholders as &$placeholder) {
    if ($placeholder instanceof MarkupInterface) {
      $placeholder = Xss::filterAdmin($placeholder);
    }
  }
  $event = [
    'hostname' => mb_substr($context['ip'] ?? '', 0, 128),
    'link' => $context['link'] ?? NULL,
    'location' => $context['request_uri'] ?? NULL,
    'referer' => $context['referer'] ?? NULL,
    'timestamp' => $context['timestamp'] ?? $this->time
      ->getCurrentTime(),
    'user' => [
      'uid' => $context['uid'] ?? 0,
    ],
    'variables' => $placeholders,
  ];
  if ($this->requestTracking) {

    // Fetch the current request on each event to support subrequest nesting.
    $event['requestTracking_id'] = $requestId;
    $event['requestTracking_sequence'] = $this->sequence;
    $this->sequence++;
  }
  $eventCollection
    ->insertOne($event);
}