Completed
Push — 8.x-2.x ( eae9c1...c6d484 )
by Frédéric G.
03:41
created

Logger::eventLoad()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 6
rs 9.4285
1
<?php
2
3
namespace Drupal\mongodb_watchdog;
4
5
use Drupal\Component\Utility\Unicode;
6
use Drupal\Core\Logger\LogMessageParserInterface;
7
use MongoDB\Database;
8
use MongoDB\Driver\Exception\InvalidArgumentException;
9
use Psr\Log\AbstractLogger;
10
11
/**
12
 * Class Logger is a PSR/3 Logger using a MongoDB data store.
13
 *
14
 * @package Drupal\mongodb_watchdog
15
 */
16
class Logger extends AbstractLogger {
17
18
  const TEMPLATE_COLLECTION = 'watchdog';
19
  const EVENT_COLLECTION_PREFIX = 'watchdog_event_';
20
  const EVENT_COLLECTIONS_PATTERN = '^watchdog_event_[[:xdigit:]]{24}$';
21
22
  /**
23
   * The logger storage.
24
   *
25
   * @var \MongoDB\Database
26
   */
27
  protected $database;
28
29
  /**
30
   * The message's placeholders parser.
31
   *
32
   * @var \Drupal\Core\Logger\LogMessageParserInterface
33
   */
34
  protected $parser;
35
36
  /**
37
   * Constructs a Logger object.
38
   *
39
   * @param \MongoDB\Database $database
40
   *   The database object.
41
   * @param \Drupal\Core\Logger\LogMessageParserInterface $parser
42
   *   The parser to use when extracting message variables.
43
   */
44
  public function __construct(Database $database, LogMessageParserInterface $parser) {
45
    $this->database = $database;
46
    $this->parser = $parser;
47
  }
48
49
  /**
50
   * {@inheritdoc}
51
   */
52
  public function log($level, $message, array $context = []) {
53
    // Remove any backtraces since they may contain an unserializable variable.
54
    unset($context['backtrace']);
55
56
    // Convert PSR3-style messages to SafeMarkup::format() style, so they can be
57
    // translated too in runtime.
58
    $message_placeholders = $this->parser->parseMessagePlaceholders($message, $context);
59
60
    $template_result = $this->database
61
      ->selectCollection(static::TEMPLATE_COLLECTION)
62
      ->insertOne([
63
        'type' => Unicode::substr($context['channel'], 0, 64),
64
        'message' => $message,
65
        'severity' => $level,
66
      ]);
67
    $template_id = $template_result->getInsertedId();
68
69
    $event_collection = $this->eventCollection($template_id);
70
    $event = [
71
      'hostname' => Unicode::substr($context['ip'], 0, 128),
72
      'link' => $context['link'],
73
      'location' => $context['request_uri'],
74
      'referer' => $context['referer'],
75
      'timestamp' => $context['timestamp'],
76
      'user' => ['uid' => $context['uid']],
77
      'variables' => $message_placeholders,
78
    ];
79
    $event_collection->insertOne($event);
80
  }
81
82
  /**
83
   * List the event collections.
84
   *
85
   * @return \MongoDB\Collection[]
86
   *   The collections with a name matching the event pattern.
87
   */
88
  public function eventCollections() {
89
    echo static::EVENT_COLLECTIONS_PATTERN;
90
    $options = [
91
      'filter' => [
92
        'name' => ['$regex' => static::EVENT_COLLECTIONS_PATTERN],
93
      ],
94
    ];
95
    $result = iterator_to_array($this->database->listCollections($options));
96
    return $result;
97
  }
98
99
  /**
100
   * Return a collection, given its template id.
101
   *
102
   * @param string $template_id
103
   *   The string representation of a template \MongoId.
104
   *
105
   * @return \MongoDB\Collection
106
   *   A collection object for the specified template id.
107
   */
108
  public function eventCollection($template_id) {
109
    $collection_name = static::EVENT_COLLECTION_PREFIX . $template_id;
110
    if (!preg_match('/' . static::EVENT_COLLECTIONS_PATTERN . '/', $collection_name)) {
111
      throw new InvalidArgumentException(t('Invalid watchdog template id `@id`.', [
112
        '@id' => $collection_name,
113
      ]));
114
    }
115
    $collection = $this->database->selectCollection($collection_name);
116
    return $collection;
117
  }
118
119
  /**
120
   * Ensure indexes are set on the collections.
121
   *
122
   * First index is on <line, timestamp> instead of <function, line, timestamp>,
123
   * because we write to this collection a lot, and the smaller index on two
124
   * numbers should be much faster to create than one with a string included.
125
   */
126
  public function ensureIndexes() {
127
    $templates = $this->database->selectCollection(static::TEMPLATE_COLLECTION);
128
    $indexes = [
129
      // Index for adding/updating increments.
130
      [
131
        'name' => 'for-increments',
132
        'key' => ['line' => 1, 'timestamp' => -1],
133
      ],
134
135
      // Index for admin page without filters.
136
      [
137
        'name' => 'admin-no-filters',
138
        'key' => ['timestamp' => -1],
139
      ],
140
141
      // Index for admin page filtering by type.
142
      [
143
        'name' => 'admin-by-type',
144
        'key' => ['type' => 1, 'timestamp' => -1],
145
      ],
146
147
      // Index for admin page filtering by severity.
148
      [
149
        'name' => 'admin-by-severity',
150
        'key' => ['severity' => 1, 'timestamp' => -1],
151
      ],
152
153
      // Index for admin page filtering by type and severity.
154
      [
155
        'name' => 'admin-by-both',
156
        'key' => ['type' => 1, 'severity' => 1, 'timestamp' => -1],
157
      ],
158
    ];
159
    $templates->createIndexes($indexes);
160
  }
161
162
}
0 ignored issues
show
Coding Style introduced by
As per coding style, files should not end with a newline character.

This check marks files that end in a newline character, i.e. an empy line.

Loading history...
163