PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/SensorRunner.php

https://gitlab.com/Drulenium-bot/monitoring
PHP | 351 lines | 163 code | 36 blank | 152 comment | 24 complexity | a697a7a74f1e7f28028737d079f79a64 MD5 | raw file
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\monitoring\SensorRunner.
  5. */
  6. namespace Drupal\monitoring;
  7. use Drupal\Component\Utility\SafeMarkup;
  8. use Drupal\Component\Utility\Timer;
  9. use Drupal\Core\Cache\CacheBackendInterface;
  10. use Drupal\Core\Cache\Cache;
  11. use Drupal\Core\Config\ConfigFactoryInterface;
  12. use Drupal\monitoring\Result\SensorResultInterface;
  13. use Drupal\monitoring\Sensor\DisabledSensorException;
  14. use Drupal\monitoring\Entity\SensorConfig;
  15. use Drupal\monitoring\Sensor\SensorManager;
  16. /**
  17. * Instantiate and run requested sensors.
  18. *
  19. * @todo more
  20. */
  21. class SensorRunner {
  22. /**
  23. * The sensor manager.
  24. *
  25. * @var \Drupal\monitoring\Sensor\SensorManager
  26. */
  27. protected $sensorManager;
  28. /**
  29. * Internal sensor result cache.
  30. *
  31. * @var array
  32. */
  33. protected $sensorResultCache = array();
  34. /**
  35. * List of sensors info keyed by sensor name that are meant to run.
  36. *
  37. * @var \Drupal\monitoring\Entity\SensorConfig[]
  38. */
  39. protected $sensorConfig = array();
  40. /**
  41. * Flag to force sensor run.
  42. *
  43. * @var bool
  44. */
  45. protected $forceRun = FALSE;
  46. /**
  47. * Flag to switch the collecting of verbose output.
  48. *
  49. * @var bool
  50. */
  51. protected $verbose = FALSE;
  52. /**
  53. * @var \Drupal\Core\Cache\CacheBackendInterface
  54. */
  55. protected $cache;
  56. /**
  57. * @var \Drupal\Core\Config\Config
  58. */
  59. protected $config;
  60. /**
  61. * Constructs a SensorRunner.
  62. *
  63. * @param \Drupal\monitoring\Sensor\SensorManager $sensor_manager
  64. * @param \Drupal\Core\Cache\CacheBackendInterface $cache
  65. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  66. */
  67. public function __construct(SensorManager $sensor_manager, CacheBackendInterface $cache, ConfigFactoryInterface $config_factory) {
  68. $this->sensorManager = $sensor_manager;
  69. $this->config = $config_factory->get('monitoring.settings');
  70. $this->cache = $cache;
  71. }
  72. /**
  73. * Loads available sensor results from cache.
  74. *
  75. * @param \Drupal\monitoring\Entity\SensorConfig[] $sensors_config
  76. * List of sensor config object that we want to run.
  77. */
  78. protected function loadCache(array $sensors_config) {
  79. $cids = array();
  80. // Only load sensor caches if they define caching.
  81. foreach ($sensors_config as $sensor_config) {
  82. if ($sensor_config->getCachingTime()) {
  83. $cids[] = $this->getSensorCid($sensor_config->id());
  84. }
  85. }
  86. if ($cids) {
  87. foreach ($this->cache->getMultiple($cids) as $cache) {
  88. $this->sensorResultCache[$cache->data['name']] = $cache->data;
  89. }
  90. }
  91. }
  92. /**
  93. * Runs the defined sensors.
  94. *
  95. * @param \Drupal\monitoring\Entity\SensorConfig[] $sensors_config_all
  96. * List of sensor config object that we want to run.
  97. * @param bool $force
  98. * Force sensor execution.
  99. * @param bool $verbose
  100. * Collect verbose info.
  101. *
  102. * @return \Drupal\monitoring\Result\SensorResultInterface[]
  103. * Array of sensor results.
  104. *
  105. * @throws \Drupal\monitoring\Sensor\DisabledSensorException
  106. * Thrown if any of the passed sensors is not enabled.
  107. *
  108. * @see \Drupal\monitoring\SensorRunner::runSensor()
  109. */
  110. public function runSensors(array $sensors_config_all = array(), $force = FALSE, $verbose = FALSE) {
  111. $this->verbose = $verbose;
  112. $this->forceRun = $force;
  113. if (empty($sensors_config_all)) {
  114. $sensors_config_all = $this->sensorManager->getEnabledSensorConfig();
  115. }
  116. $this->loadCache($sensors_config_all);
  117. $results = array();
  118. foreach ($sensors_config_all as $name => $sensor_config) {
  119. if ($result = $this->runSensor($sensor_config)) {
  120. $result->setPreviousResult(monitoring_sensor_result_last($result->getSensorId()));
  121. $results[$name] = $result;
  122. }
  123. }
  124. $this->logResults($results);
  125. $this->cacheResults($results);
  126. // Trigger a hook to allow processing of sensors data.
  127. \Drupal::moduleHandler()->invokeAll('monitoring_run_sensors', [$results]);
  128. return $results;
  129. }
  130. /**
  131. * Run a single given sensor.
  132. *
  133. * @param \Drupal\monitoring\Entity\SensorConfig $sensor_config
  134. * Sensor config
  135. *
  136. * @return SensorResultInterface
  137. * Sensor result.
  138. *
  139. * @throws \Drupal\monitoring\Sensor\DisabledSensorException
  140. * Thrown if the passed sensor is not enabled.
  141. *
  142. * @see \Drupal\monitoring\Sensor\SensorInterface::runSensor()
  143. */
  144. protected function runSensor(SensorConfig $sensor_config) {
  145. $plugin = $sensor_config->getPlugin();
  146. // Check if sensor is enabled.
  147. if (!$plugin->isEnabled()) {
  148. throw new DisabledSensorException(SafeMarkup::format('Sensor @sensor_name is not enabled and must not be run.', array('@sensor_name' => $sensor_config->id())));
  149. }
  150. $result = $this->getResultObject($sensor_config);
  151. // In case result is not yet cached run sensor.
  152. if (!$result->isCached()) {
  153. Timer::start($sensor_config->id());
  154. try {
  155. $plugin->runSensor($result);
  156. } catch (\Exception $e) {
  157. // In case the sensor execution results in an exception, mark it as
  158. // critical and set the sensor status message.
  159. $result->setStatus(SensorResultInterface::STATUS_CRITICAL);
  160. $result->setMessage(get_class($e) . ': ' . $e->getMessage());
  161. // Log the error to watchdog.
  162. watchdog_exception('monitoring_exception', $e);
  163. // @todo Improve logging by e.g. integrating with past or save the
  164. // backtrace as part of the sensor verbose output.
  165. }
  166. $timer = Timer::stop($sensor_config->id());
  167. $result->setExecutionTime($timer['time']);
  168. // Capture verbose output if requested and if we are able to do so.
  169. try {
  170. if ($this->verbose && $sensor_config->isExtendedInfo()) {
  171. /** @var \Drupal\monitoring\SensorPlugin\ExtendedInfoSensorPluginInterface $plugin */
  172. $result->setVerboseOutput($plugin->resultVerbose($result));
  173. }
  174. }
  175. catch (\Exception $e) {
  176. $result->setVerboseOutput(array('#markup' => $e->getMessage()));
  177. }
  178. try {
  179. $result->compile();
  180. }
  181. catch (\Exception $e) {
  182. $result->setStatus(SensorResultInterface::STATUS_CRITICAL);
  183. $result->setMessage(get_class($e) . ': ' . $e->getMessage());
  184. }
  185. }
  186. return $result;
  187. }
  188. /**
  189. * Log results if needed.
  190. *
  191. * @param \Drupal\monitoring\Result\SensorResultInterface[] $results
  192. * Results to be saved.
  193. */
  194. protected function logResults(array $results) {
  195. foreach ($results as $result) {
  196. // Skip if the result is cached.
  197. if ($result->isCached()) {
  198. continue;
  199. }
  200. $old_status = NULL;
  201. // Try to load the previous log result for this sensor.
  202. if ($result->getPreviousResult()) {
  203. $old_status = $result->getPreviousResult()->getStatus();
  204. }
  205. // Check if we need to log the result.
  206. if ($this->needsLogging($result, $old_status, $result->getStatus())) {
  207. monitoring_sensor_result_save($result);
  208. }
  209. }
  210. }
  211. /**
  212. * Checks if sensor results should be logged.
  213. *
  214. * @param \Drupal\monitoring\Result\SensorResultInterface $result
  215. * The sensor result.
  216. * @param string $old_status
  217. * The old sensor status.
  218. * @param string $new_status
  219. * Thew new sensor status.
  220. *
  221. * @return bool
  222. * TRUE if the result should be logged, FALSE if not.
  223. */
  224. protected function needsLogging($result, $old_status = NULL, $new_status = NULL) {
  225. $log_activity = $result->getSensorConfig()->getSetting('result_logging', FALSE);
  226. // We log if requested or on status change.
  227. if ($this->config->get('sensor_call_logging') == 'on_request') {
  228. return $log_activity || ($old_status != $new_status);
  229. }
  230. // We are logging all.
  231. if ($this->config->get('sensor_call_logging') == 'all') {
  232. return TRUE;
  233. }
  234. return FALSE;
  235. }
  236. /**
  237. * Cache results if caching applies.
  238. *
  239. * @param \Drupal\monitoring\Result\SensorResultInterface[] $results
  240. * Results to be cached.
  241. */
  242. protected function cacheResults(array $results) {
  243. // @todo: Cache in a single array, with per item expiration?
  244. foreach ($results as $result) {
  245. $definition = $result->getSensorConfig();
  246. if ($definition->getCachingTime() && !$result->isCached()) {
  247. $data = array(
  248. 'name' => $result->getSensorId(),
  249. 'sensor_status' => $result->getStatus(),
  250. 'sensor_message' => $result->getMessage(),
  251. 'sensor_expected_value' => $result->getExpectedValue(),
  252. 'sensor_value' => $result->getValue(),
  253. 'execution_time' => $result->getExecutionTime(),
  254. 'timestamp' => $result->getTimestamp(),
  255. );
  256. $this->cache->set(
  257. $this->getSensorCid($result->getSensorId()),
  258. $data,
  259. REQUEST_TIME + $definition->getCachingTime(),
  260. array('monitoring_sensor_result')
  261. );
  262. }
  263. }
  264. }
  265. /**
  266. * Instantiates sensor result object.
  267. *
  268. * @param \Drupal\monitoring\Entity\SensorConfig $sensor_config
  269. * Sensor config.
  270. *
  271. * @return \Drupal\monitoring\Result\SensorResultInterface
  272. * Instantiated sensor result object.
  273. */
  274. protected function getResultObject(SensorConfig $sensor_config) {
  275. $result_class = '\Drupal\monitoring\Result\SensorResult';
  276. if (!$this->forceRun && isset($this->sensorResultCache[$sensor_config->id()])) {
  277. $result = new $result_class($sensor_config, $this->sensorResultCache[$sensor_config->id()]);
  278. }
  279. else {
  280. $result = new $result_class($sensor_config);
  281. }
  282. return $result;
  283. }
  284. /**
  285. * Gets sensor cache id.
  286. *
  287. * @param string $sensor_name
  288. *
  289. * @return string
  290. * Cache id.
  291. */
  292. protected function getSensorCid($sensor_name) {
  293. return 'monitoring_sensor_result:' . $sensor_name;
  294. }
  295. /**
  296. * Reset sensor result caches.
  297. *
  298. * @param array $sensor_names
  299. * (optional) Array of sensors to reset the cache for. An empty array clears
  300. * all results, which is the default.
  301. */
  302. public function resetCache(array $sensor_names = array()) {
  303. if (empty($sensor_names)) {
  304. // No sensor names provided, clear all caches.
  305. Cache::invalidateTags(array('monitoring_sensor_result'));
  306. }
  307. else {
  308. $cids = array();
  309. foreach ($sensor_names as $sensor_name) {
  310. $cids[] = $this->getSensorCid($sensor_name);
  311. }
  312. $this->cache->deleteMultiple($cids);
  313. }
  314. }
  315. }