diff --git a/config/optional/monitoring.sensor_config.php_notices.yml b/config/optional/monitoring.sensor_config.php_notices.yml
new file mode 100644
index 0000000..6bb0bed
--- /dev/null
+++ b/config/optional/monitoring.sensor_config.php_notices.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - dblog
+id: php_notices
+label: 'PHP notices'
+description: 'Php notices logged by watchdog'
+category: Watchdog
+plugin_id: php_notices
+result_class: null
+value_label: 'Watchdog events'
+value_type: number
+caching_time: 3600
+settings:
+  table: watchdog
+  time_interval_field: 'timestamp'
+  time_interval_value: 86400
+thresholds:
+  type: none
+  warning: 10
+  critical: 100
diff --git a/config/schema/monitoring.schema.yml b/config/schema/monitoring.schema.yml
index 8f49d6d..ce80101 100644
--- a/config/schema/monitoring.schema.yml
+++ b/config/schema/monitoring.schema.yml
@@ -197,6 +197,10 @@ monitoring.settings.watchdog_aggregator:
   type: monitoring.settings.database_aggregator
   label: 'Watchdog sensor settings'
 
+monitoring.settings.php_notices:
+  type: monitoring.settings.watchdog_aggregator
+  label: 'PHP notices sensor settings'
+
 monitoring.settings.dblog_404:
   type: monitoring.settings.database_aggregator
   label: '404 settings'
diff --git a/src/Plugin/monitoring/SensorPlugin/DatabaseAggregatorSensorPlugin.php b/src/Plugin/monitoring/SensorPlugin/DatabaseAggregatorSensorPlugin.php
index eef80c9..6172303 100644
--- a/src/Plugin/monitoring/SensorPlugin/DatabaseAggregatorSensorPlugin.php
+++ b/src/Plugin/monitoring/SensorPlugin/DatabaseAggregatorSensorPlugin.php
@@ -295,7 +295,7 @@ class DatabaseAggregatorSensorPlugin extends DatabaseAggregatorSensorPluginBase
     $form['output_table']['keys'] = array(
       '#type' => 'textarea',
       '#tree' => FALSE,
-      '#default_value' => implode("\n", $this->sensorConfig->getSetting('keys')),
+      '#default_value' => implode("\n", $this->sensorConfig->getSetting('keys', ['variables'])),
       '#maxlength' => 255,
       '#title' => t('Keys'),
       '#required' => TRUE,
diff --git a/src/Plugin/monitoring/SensorPlugin/PhpNoticesSensorPlugin.php b/src/Plugin/monitoring/SensorPlugin/PhpNoticesSensorPlugin.php
new file mode 100644
index 0000000..3b4d729
--- /dev/null
+++ b/src/Plugin/monitoring/SensorPlugin/PhpNoticesSensorPlugin.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\monitoring\Plugin\monitoring\SensorPlugin\PhpNoticesSensorPlugin.
+ */
+
+namespace Drupal\monitoring\Plugin\monitoring\SensorPlugin;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\monitoring\Result\SensorResultInterface;
+
+/**
+ * Monitors php notices logged in the watchdog.
+ *
+ * @SensorPlugin(
+ *   id = "php_notices",
+ *   provider = "dblog",
+ *   label = @Translation("PHP notices (database log)"),
+ *   description = @Translation("Monitors PHP notices from database log."),
+ *   addable = FALSE
+ * )
+ *
+ * Displays PHP notice with highest occurrence as message.
+ */
+class PhpNoticesSensorPlugin extends WatchdogAggregatorSensorPlugin {
+  /**
+   * {@inheritdoc}
+   */
+  public function runSensor(SensorResultInterface $result) {
+    parent::runSensor($result);
+    if (!empty($this->fetchedObject->variables)) {
+      $result->setMessage('@count times: @error', ['@count' => (int) $this->fetchedObject->records_count, '@error' => SafeMarkup::xssFilter(SafeMarkup::format('%type: !message in %function (line %line of %file).', unserialize($this->fetchedObject->variables)), Xss::getAdminTagList())]);
+    };
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    unset($form['conditions_table']);
+    unset($form['output_table']);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAggregateQuery() {
+    $query = parent::getAggregateQuery();
+    $query->addField('watchdog', 'variables');
+    $query->condition('type', 'php', NULL);
+    // The message is the most recurring php error.
+    $query->groupBy('variables');
+    $query->orderBy('records_count', 'DESC');
+    $query->range(0, 10);
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuery() {
+    $query = parent::getQuery();
+    $query->addField('watchdog', 'variables');
+    $this->addAggregateExpression($query);
+    $query->condition('type', 'php', NULL);
+    $query->groupBy('variables');
+    $query->orderBy('records_count', 'DESC');
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function verboseResultUnaggregated(array &$output) {
+    parent::verboseResultUnaggregated($output);
+    if (!empty($output['result']['#rows'])) {
+      $rows = $output['result']['#rows'];
+      $output['result']['#rows'] = [];
+      $output['result']['#header'] = ['Type', 'Message', 'Function', 'File', 'Line'];
+      foreach ($rows as $row) {
+        $variables = unserialize($row['data']['variables']);
+        $row_result['type'] = ['data' => $variables['%type'], 'class' => 'entity'];
+        $row_result['message'] = ['data' => SafeMarkup::xssFilter($variables['!message'], Xss::getAdminTagList()), 'class' => 'entity'];
+        $row_result['function'] = ['data' => $variables['%function'], 'class' => 'entity'];
+        $row_result['file'] = ['data' => $variables['%file'], 'class' => 'entity'];
+        $row_result['line'] = ['data' => $variables['%line'], 'class' => 'entity'];
+        $output['result']['#rows'][] = $row_result;
+      }
+    };
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resultVerbose(SensorResultInterface $result) {
+    $output = parent::resultVerbose($result);
+
+    return $output;
+  }
+
+}
diff --git a/src/Plugin/monitoring/SensorPlugin/WatchdogAggregatorSensorPlugin.php b/src/Plugin/monitoring/SensorPlugin/WatchdogAggregatorSensorPlugin.php
index 03bf1f9..8208248 100644
--- a/src/Plugin/monitoring/SensorPlugin/WatchdogAggregatorSensorPlugin.php
+++ b/src/Plugin/monitoring/SensorPlugin/WatchdogAggregatorSensorPlugin.php
@@ -6,13 +6,10 @@
 
 namespace Drupal\monitoring\Plugin\monitoring\SensorPlugin;
 
-use Drupal\Core\Database\DatabaseExceptionWrapper;
-use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Component\Utility\Xss;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\monitoring\Result\SensorResultInterface;
 use Drupal\monitoring\SensorPlugin\ExtendedInfoSensorPluginInterface;
-use Drupal\monitoring\SensorPlugin\DatabaseAggregatorSensorPluginBase;
-use Drupal\Core\Entity\DependencyTrait;
 use Drupal\Component\Utility\SafeMarkup;
 
 /**
@@ -24,7 +21,6 @@ use Drupal\Component\Utility\SafeMarkup;
  *   description = @Translation("Simple aggregator able to query the watchdog table."),
  *   addable = TRUE
  * )
- *
  */
 class WatchdogAggregatorSensorPlugin extends DatabaseAggregatorSensorPlugin implements ExtendedInfoSensorPluginInterface {
   /**
@@ -32,15 +28,14 @@ class WatchdogAggregatorSensorPlugin extends DatabaseAggregatorSensorPlugin impl
    */
   public function verboseResultUnaggregated(array &$output) {
     parent::verboseResultUnaggregated($output);
-    if (isset($output['result']['#rows'])) {
-      if (array_key_exists('message', $output['result']['#header']) && array_key_exists('variables', $output['result']['#header'])) {
-        unset($output['result']['#header']['variables']);
-      }
+    // If sensor has message and variables, remove variables header.
+    if (isset($output['result']['#rows']) && array_key_exists('message', $output['result']['#header']) && array_key_exists('variables', $output['result']['#header'])) {
+      unset($output['result']['#header']['variables']);
+      // Replace the message for every row.
       foreach ($output['result']['#rows'] as $delta => $row) {
-        if (array_key_exists('message', $row['data']) && array_key_exists('variables', $row['data'])) {
-          $output['result']['#rows'][$delta]['data']['message'] = SafeMarkup::format($row['data']['message'], unserialize($row['data']['variables']));
-          unset($output['result']['#rows'][$delta]['data']['variables']);
-        };
+        $output['result']['#rows'][$delta]['data']['message'] = Safemarkup::xssFilter(SafeMarkup::format($row['data']['message'], unserialize($row['data']['variables']), Xss::getAdminTagList()));
+        // Do not render the variables in the row.
+        unset($output['result']['#rows'][$delta]['data']['variables']);
       };
     }
   }
@@ -52,7 +47,9 @@ class WatchdogAggregatorSensorPlugin extends DatabaseAggregatorSensorPlugin impl
     $form = parent::buildConfigurationForm($form, $form_state);
     $form['#title'] = t('Watchdog Sensor plugin settings');
     // The following fields should not be edited, so we disable them.
+    $form['table']['#default_value'] = 'watchdog';
     $form['table']['#disabled'] = TRUE;
+    $form['aggregation']['time_interval_field']['#default_value'] = 'timestamp';
     $form['aggregation']['time_interval_field']['#disabled'] = TRUE;
     return $form;
   }
diff --git a/src/Tests/MonitoringCoreKernelTest.php b/src/Tests/MonitoringCoreKernelTest.php
index c38dbd0..2d7195a 100644
--- a/src/Tests/MonitoringCoreKernelTest.php
+++ b/src/Tests/MonitoringCoreKernelTest.php
@@ -125,6 +125,55 @@ class MonitoringCoreKernelTest extends MonitoringUnitTestBase {
   }
 
   /**
+   * Tests php notices watchdog sensor.
+   */
+  public function testPhpNoticesSensor() {
+
+    // Prepare a fake PHP error.
+    $error = [
+      '%type' => 'Recoverable fatal error',
+      '!message' => 'Argument 1 passed to Drupal\Core\Form\ConfigFormBase::buildForm() must be of the type array, null given, called in /usr/local/var/www/d8/www/core/modules/system/src/Form/CronForm.php on line 127 and defined',
+      '%function' => 'Drupal\Core\Form\ConfigFormBase->buildForm()',
+      '%line' => '42',
+      '%file' => '/usr/local/var/www/d8/www/core/lib/Drupal/Core/Form/ConfigFormBase.php',
+      'severity_level' => 3,
+    ];
+    // Log it twice.
+    \Drupal::logger('php')->log($error['severity_level'], '%type: !message in %function (line %line of %file).', $error);
+    \Drupal::logger('php')->log($error['severity_level'], '%type: !message in %function (line %line of %file).', $error);
+
+    $result = $this->runSensor('php_notices');
+    $message = $result->getMessage();
+    // Assert the message has been set and replaced successfully.
+    $this->assertEqual($message, SafeMarkup::format('2 times: %type: !message in %function (line %line of %file).', $error), 'PHP notice was found and replaced successfully.');
+
+    // Prepare another fake PHP notice.
+    $new_error = [
+      '%type' => 'Notice',
+      '!message' => 'Use of undefined constant B - assumed \'B\'',
+      '%function' => 'Drupal\system\Form\CronForm->buildForm()',
+      '%line' => '126',
+      '%file' => '/usr/local/var/www/d8/www/core/modules/system/src/Form/CronForm.php',
+      'severity_level' => 5,
+    ];
+    \Drupal::logger('php')->log($new_error['severity_level'], '%type: !message in %function (line %line of %file).', $new_error);
+    $result = $this->runSensor('php_notices');
+    $message = $result->getMessage();
+    // Assert the message is still the one from above and not the new message.
+    $this->assertEqual($message, SafeMarkup::format('2 times: %type: !message in %function (line %line of %file).', $error), 'The sensor message is still the old message.');
+    $this->assertNotEqual($message, SafeMarkup::format('%type: !message in %function (line %line of %file).', $new_error), 'The sensor message is not the new message.');
+
+    // Log the new error twice more, check it is now the sensor message.
+    \Drupal::logger('php')->log($new_error['severity_level'], '%type: !message in %function (line %line of %file).', $new_error);
+    \Drupal::logger('php')->log($new_error['severity_level'], '%type: !message in %function (line %line of %file).', $new_error);
+    $result = $this->runSensor('php_notices');
+    $message = $result->getMessage();
+    // Assert the new message is returned as a message.
+    $this->assertEqual($message, SafeMarkup::format('3 times: %type: !message in %function (line %line of %file).', $new_error), 'The new message is now the sensor message.');
+    $this->assertNotEqual($message, SafeMarkup::format('2 times: %type: !message in %function (line %line of %file).', $error), 'The old message is not the sensor message anymore.');
+  }
+
+  /**
    * Tests dblog 404 errors sensor.
    *
    * Logged through watchdog.
