Laravel 4 - 记录 SQL 查询


Laravel 4 - logging SQL queries

关于

在Laravel 4中记录SQL查询已经有几个问题。但是我已经尝试了几乎所有方法,但它仍然没有按照我想要的方式工作。

这是我的情况

  1. 在我的 php 视图文件中,我向服务器发出 AJAX 请求
  2. 接收 AJAX 请求并运行 RAW 参数化的 Postgres SQL 查询(例如

    DB::select('select * from my_table where id=?', array(1((

如果我使用

Event::listen('illuminate.query', function($sql)
{
  Log::error($sql);
});

我只是得到"从id=my_table中选择*?"作为日志消息,而没有实际填充ID值。

如果我使用

$queries = DB::getQueryLog();
$last_query = end($queries);
Log::error(print_r($last_query, true));

我仍然没有填充 ID 的最终 SQL 查询。

最后,如果我使用像 https://github.com/loic-sharma/profiler 这样的日志记录工具 - 它不会显示任何内容,因为我正在发出 AJAX 请求。

我用尽了我的选择吗?还有别的办法吗?

这是我目前用于记录 sql 查询的内容。您应该能够将其放入主路由文件中,然后将"log"=> true添加到数据库配置中。

if (Config::get('database.log', false))
{           
    Event::listen('illuminate.query', function($query, $bindings, $time, $name)
    {
        $data = compact('bindings', 'time', 'name');
        // Format binding data for sql insertion
        foreach ($bindings as $i => $binding)
        {   
            if ($binding instanceof 'DateTime)
            {   
                $bindings[$i] = $binding->format('''Y-m-d H:i:s''');
            }
            else if (is_string($binding))
            {   
                $bindings[$i] = "'$binding'";
            }   
        }       
        // Insert bindings into query
        $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
        $query = vsprintf($query, $bindings); 
        Log::info($query, $data);
    });
}

感谢 Jeemusu 回答有关将绑定插入准备好的语句的一点。

您应该能够通过传递 $bindings 作为 Event 函数的第二个参数来找到绑定。

Event::listen('illuminate.query', function($sql, $bindings, $time){
    echo $sql;          // select * from my_table where id=? 
    print_r($bindings); // Array ( [0] => 4 )
    echo $time;         // 0.58 
    // To get the full sql query with bindings inserted
    $sql = str_replace(array('%', '?'), array('%%', '%s'), $sql);
    $full_sql = vsprintf($sql, $bindings);
});

在 Laravel 3.x 中,我认为事件侦听器被称为laravel.query

继续@Collin詹姆斯的回答。

如果您只想记录到 sql 的单独文件,则可以执行以下操作:

if (Config::get('database.log', false)) {
    Event::listen('illuminate.query', function($query, $bindings, $time, $name) {
        $data = compact('bindings', 'time', 'name');
        // Format binding data for sql insertion
        foreach ($bindings as $i => $binding) {
            if ($binding instanceof 'DateTime) {
                $bindings[$i] = $binding->format('''Y-m-d H:i:s''');
            } else if (is_string($binding)) {
                $bindings[$i] = "'$binding'";
            }
        }
        // Insert bindings into query
        $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
        $query = vsprintf($query, $bindings);
        $log = new Logger('sql');
        $log->pushHandler(new StreamHandler(storage_path().'/logs/sql-' . date('Y-m-d') . '.log', Logger::INFO));
        // add records to the log
        $log->addInfo($query, $data);
    });
}

在文件顶部显示以下内容:

use Monolog'Logger;
use Monolog'Handler'StreamHandler;

这会将您的所有查询记录到名为 sql-YYYY-mm-dd.log in storage/logs/ 的文件中。

虽然接受的答案是正确的,但这个答案解释了在使用jQuery发出Ajax请求时如何更新loic-sharma分析器。使用这种方法不需要读取文件日志。

步骤 1

第一个问题是在每个 Ajax 请求时将更新的探查器数据发送到客户端。这可以使用Laravel应用程序的"之后"事件来解决。

应用/过滤器.php:

App::after(function($request, $response)
{
    // If it's a JsonResponse and the profiler is enabled, include it in the response.
    if($response instanceof 'Illuminate'Http'JsonResponse && Profiler::isEnabled()) {
        $profiler = Profiler::getFacadeRoot();
        $profilerJson = json_encode($profiler->render());
        $content = substr($response->getContent(), 0, -1) . ',"profiler":' . $profilerJson . '}';
        $response->setContent($content);
    }
});

App::after过滤器将在每次Laravel请求时运行。上面闭包的第一行确保仅当响应类型为 JsonResponse 并且启用了探查器时,它才会继续。如果是这种情况,请呈现探查器并将 HTML 追加到 JSON 对象。

注意:此代码假定返回的 JSON 是一个对象。所以数组会失败:Response::json(array(1,2,3)).

步骤 2

现在更新的探查器 HTML 已发送到客户端,我们必须使用 javascript 使用新的探查器 HTML 更新 DOM。每次客户端收到 JSON 响应时都应发生这种情况。jQuery提供了全局Ajax事件处理程序,这是实现这一目标的完美选择。

$(document).ajaxSuccess(function(event, request, settings) {
    try {
        json = jQuery.parseJSON(request.responseText);
        if(json.profiler) {
            renderProfiler(json.profiler);
        }
    } catch(error) {
        // Its not JSON.
        return;
    }
});

下面是将旧探查器替换为新探查器的方法:

renderProfiler = function(data) {
    // Remove previous 
    $anbu = $('.anbu');
    $anbu.prev().remove(); // Removes <style> tag of profiler
    $anbu.next().next().remove(); // Removes the inline script tag of profiler
    $anbu.next().remove(); // Removes jQuery script tag by the profiler
    $anbu.remove(); // Removes the <div> tag of profiler
    $(document.body).append(data);
};

使用它

现在,它就像返回响应一样简单,如下所示:

return Response::json(array(
    'user' => Auth::user()
));

Laravel将附加分析器HTML。javascript将捕获JSON响应并更新DOM。您将在网页上拥有SQL查询和计时。

注意

在测试代码时,可能会有一两个错误。这也不是我这样做的。我没有在 json 响应中发送 HTML,而是使用探查器中的实际数据扩展对象。在客户端,我使用胡须模板渲染分析器。

虽然这个问题最初是针对Laravel 4的,但我仍然通过谷歌来到这里,但我使用的是Laravel 5。

有新的方法可以使用中间件记录Laravel 5中的所有查询,但是如果您更喜欢相同的方法,这里是Collin James提供的相同代码,但适用于Laravel 5

if (Config::get('database.log', false))
{
    Event::listen('Illuminate'Database'Events'QueryExecuted', function($query)
    {
        $bindings = $query->bindings;
        $time = $query->time;
        $name = $query->connection->getName();
        $data = compact('bindings', 'time', 'name');
        // Format binding data for sql insertion
        foreach ($bindings as $i => $binding)
        {
            if ($binding instanceof 'DateTime)
            {
                $bindings[$i] = $binding->format('''Y-m-d H:i:s''');
            }
            else if (is_string($binding))
            {
                $bindings[$i] = "'$binding'";
            }
        }
        // Insert bindings into query
        $query = str_replace(array('%', '?'), array('%%', '%s'), $query->sql);
        $query = vsprintf($query, $bindings);
        Log::info($query, $data);
    });
}

这就是我一直在使用的东西:

DB::listen(function ($query, $bindings, $time, $connection) {
    $fullQuery = vsprintf(str_replace(array('%', '?'), array('%%', '%s'), $query), $bindings);
    $result = $connection . ' (' . $time . '): ' . $fullQuery;
    dump($result);
    // You can of course log the $result
});