DebugPanel.php 5.35 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\elasticsearch;

use yii\debug\Panel;
11
use yii\helpers\ArrayHelper;
12
use yii\helpers\Url;
13 14 15 16 17 18 19 20 21 22 23 24
use yii\log\Logger;
use yii\helpers\Html;
use yii\web\View;

/**
 * Debugger panel that collects and displays elasticsearch queries performed.
 *
 * @author Carsten Brandt <mail@cebe.cc>
 * @since 2.0
 */
class DebugPanel extends Panel
{
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
    public $db = 'elasticsearch';

    public function init()
    {
        $this->actions['elasticsearch-query'] = [
            'class' => 'yii\\elasticsearch\\DebugAction',
            'panel' => $this,
            'db' => $this->db,
        ];
    }

    public function getName()
    {
        return 'Elasticsearch';
    }

    public function getSummary()
    {
        $timings = $this->calculateTimings();
        $queryCount = count($timings);
        $queryTime = 0;
        foreach ($timings as $timing) {
            $queryTime += $timing[3];
        }
        $queryTime = number_format($queryTime * 1000) . ' ms';
        $url = $this->getUrl();
        $output = <<<EOD
52
<div class="yii-debug-toolbar-block">
53 54 55
    <a href="$url" title="Executed $queryCount elasticsearch queries which took $queryTime.">
        ES <span class="label">$queryCount</span> <span class="label">$queryTime</span>
    </a>
56 57
</div>
EOD;
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

        return $queryCount > 0 ? $output : '';
    }

    public function getDetail()
    {
        $timings = $this->calculateTimings();
        ArrayHelper::multisort($timings, 3, SORT_DESC);
        $rows = [];
        $i = 0;
        foreach ($timings as $logId => $timing) {
            $duration = sprintf('%.1f ms', $timing[3] * 1000);
            $message = $timing[1];
            $traces = $timing[4];
            if (($pos = mb_strpos($message, "#")) !== false) {
                $url = mb_substr($message, 0, $pos);
                $body = mb_substr($message, $pos + 1);
            } else {
                $url = $message;
                $body = null;
            }
            $traceString = '';
            if (!empty($traces)) {
                $traceString .= Html::ul($traces, [
                    'class' => 'trace',
                    'item' => function ($trace) {
                        return "<li>{$trace['file']}({$trace['line']})</li>";
                    },
                ]);
            }
            $ajaxUrl = Url::to(['elasticsearch-query', 'logId' => $logId, 'tag' => $this->tag]);
            \Yii::$app->view->registerJs(<<<JS
$('#elastic-link-$i').on('click', function () {
    var result = $('#elastic-result-$i');
    result.html('Sending request...');
    result.parent('tr').show();
    $.ajax({
        type: "POST",
        url: "$ajaxUrl",
        success: function (data) {
            $('#elastic-time-$i').html(data.time);
            $('#elastic-result-$i').html(data.result);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            $('#elastic-time-$i').html('');
            $('#elastic-result-$i').html('<span style="color: #c00;">Error: ' + errorThrown + ' - ' + textStatus + '</span><br />' + jqXHR.responseText);
        },
        dataType: "json"
    });

    return false;
109 110 111
});
JS
, View::POS_READY);
112 113
            $runLink = Html::a('run query', '#', ['id' => "elastic-link-$i"]) . '<br/>';
            $rows[] = <<<HTML
114
<tr>
115 116 117
    <td style="width: 10%;">$duration</td>
    <td style="width: 75%;"><div><b>$url</b><br/><p>$body</p>$traceString</div></td>
    <td style="width: 15%;">$runLink</td>
118 119 120
</tr>
<tr style="display: none;"><td id="elastic-time-$i"></td><td colspan="3" id="elastic-result-$i"></td></tr>
HTML;
121 122 123 124 125
            $i++;
        }
        $rows = implode("\n", $rows);

        return <<<HTML
126 127 128 129 130
<h1>Elasticsearch Queries</h1>

<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead>
<tr>
131 132 133
    <th style="width: 10%;">Time</th>
    <th style="width: 75%;">Url / Query</th>
    <th style="width: 15%;">Run Query on node</th>
134 135 136 137 138 139 140
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
HTML;
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    }

    private $_timings;

    public function calculateTimings()
    {
        if ($this->_timings !== null) {
            return $this->_timings;
        }
        $messages = $this->data['messages'];
        $timings = [];
        $stack = [];
        foreach ($messages as $i => $log) {
            list($token, $level, $category, $timestamp) = $log;
            $log[5] = $i;
            if ($level == Logger::LEVEL_PROFILE_BEGIN) {
                $stack[] = $log;
            } elseif ($level == Logger::LEVEL_PROFILE_END) {
                if (($last = array_pop($stack)) !== null && $last[0] === $token) {
                    $timings[$last[5]] = [count($stack), $token, $last[3], $timestamp - $last[3], $last[4]];
                }
            }
        }

        $now = microtime(true);
        while (($last = array_pop($stack)) !== null) {
            $delta = $now - $last[3];
            $timings[$last[5]] = [count($stack), $last[0], $last[2], $delta, $last[4]];
        }
        ksort($timings);

        return $this->_timings = $timings;
    }

    public function save()
    {
        $target = $this->module->logTarget;
        $messages = $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, ['yii\elasticsearch\Connection::httpRequest']);

        return ['messages' => $messages];
    }
182
}