Firesphere/silverstripe-solr-search

View on GitHub
src/Helpers/SolrLogger.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php
/**
 * class SolrLogger|Firesphere\SolrSearch\Helpers\SolrLogger Log errors to the Database
 *
 * @package Firesphere\Solr\Search
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
 */

namespace Firesphere\SolrSearch\Helpers;

use Countable;
use Firesphere\SolrSearch\Models\SolrLog;
use Firesphere\SolrSearch\Services\SolrCoreService;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Debug;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\ValidationException;
use Solarium\Exception\HttpException;

/**
 * Class SolrLogger
 *
 * Log information from Solr to the CMS for reference
 *
 * @package Firesphere\Solr\Search
 */
class SolrLogger
{
    /**
     * @var Client Guzzle base client to communicate with Solr
     */
    protected $client;

    /**
     * @var array Default options
     */
    protected $options = [];

    /**
     * SolrLogger constructor.
     *
     * @param null|Countable $handler
     */
    public function __construct($handler = null)
    {
        $config = SolrCoreService::config()->get('config');
        $hostConfig = array_shift($config['endpoint']);
        $guzzleConfig = [
            'base_uri' => $hostConfig['host'] . ':' . $hostConfig['port'],
        ];
        if ($handler) {
            $guzzleConfig['handler'] = $handler;
        }

        if (isset($hostConfig['username']) && isset($hostConfig['password'])) {
            $this->options = [
                'auth' => [
                    $hostConfig['username'],
                    $hostConfig['password']
                ]
            ];
        }


        $this->client = new Client($guzzleConfig);
    }

    /**
     * Log the given message and dump it out.
     * Also boot the Log to get the latest errors from Solr
     *
     * @param string $type
     * @param string $message
     * @throws HTTPException
     * @throws ValidationException
     */
    public static function logMessage($type, $message): void
    {
        $solrLogger = new self();
        $solrLogger->saveSolrLog($type);
        /** @var SolrLog $lastError */
        $lastError = SolrLog::get()->last();

        $err = ($lastError === null) ? 'Unknown' : $lastError->getLastErrorLine();
        $errTime = ($lastError === null) ? 'Unknown' : $lastError->Timestamp;
        $message .= sprintf('%sLast known Solr error:%s%s: %s', PHP_EOL, PHP_EOL, $errTime, $err);
        /** @var LoggerInterface $logger */
        $logger = Injector::inst()->get(LoggerInterface::class);
        $logger->alert($message);
        if (Director::is_cli() || Controller::curr()->getRequest()->getVar('unittest')) {
            Debug::dump($message);
        }
    }

    /**
     * Save the latest Solr errors to the log
     *
     * @param string $type
     * @throws HTTPException
     * @throws ValidationException
     */
    public function saveSolrLog($type = 'Query'): void
    {
        $options = array_merge($this->options, [
            'query' => [
                'since' => 0,
                'wt'    => 'json',
            ],
        ]);
        $response = $this->client->get('solr/admin/info/logging', $options);

        $arrayResponse = json_decode($response->getBody(), true);

        foreach ($arrayResponse['history']['docs'] as $error) {
            $filter = [
                'Timestamp' => $error['time'],
                'Index'     => $error['core'] ?? 'x:Unknown',
                'Level'     => $error['level'],
            ];
            $this->findOrCreateLog($type, $filter, $error);
        }
    }

    /**
     * Attempt to find, otherwise create, a log object
     *
     * @param $type
     * @param array $filter
     * @param $error
     * @throws ValidationException
     */
    private function findOrCreateLog($type, array $filter, $error): void
    {
        // Not covered in tests. It's only here to make sure the connection isn't closed by a child process
        $conn = DB::is_active();
        // @codeCoverageIgnoreStart
        if (!$conn) {
            $config = DB::getConfig();
            DB::connect($config);
        }
        // @codeCoverageIgnoreEnd
        if (!SolrLog::get()->filter($filter)->exists()) {
            $logData = [
                'Message' => $error['message'],
                'Type'    => $type,
            ];
            $log = array_merge($filter, $logData);
            SolrLog::create($log)->write();
            if (Director::is_cli() || Controller::curr()->getRequest()->getVar('unittest')) {
                /** @var LoggerInterface $logger */
                $logger = Injector::inst()->get(LoggerInterface::class);
                $logger->error($error['message']);
            }
        }
    }

    /**
     * Return the Guzzle Client
     *
     * @return Client
     */
    public function getClient(): Client
    {
        return $this->client;
    }

    /**
     * Set the Guzzle client
     *
     * @param Client $client
     * @return SolrLogger
     */
    public function setClient(Client $client): self
    {
        $this->client = $client;

        return $this;
    }

    /**
     * Get the options for Guzzle
     *
     * @return array
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    /**
     * Set custom options for Guzzle
     *
     * @param array $options
     */
    public function setOptions(array $options): void
    {
        $this->options = $options;
    }
}