专栏首页程序员小助手3分钟短文 | Laravel 日志全程记录 SQL 查询语句,要改写底层?

3分钟短文 | Laravel 日志全程记录 SQL 查询语句,要改写底层?

引言

Laravel 提供给了比较强大的ORM数据库操作方式,如果在数据库端考虑到性能问题, 难以打开MySQL的慢日志,或者出于审计考虑,要在系统内全程跟踪所有的SQL操作, 应该如何实现呢?不会要改写框架底层代码?

本文就来为大家提供解决的思路。

学习时间

因为考虑到是全局记录SQL语句,那么肯定不可能在所有的数据库模型操作上都手动加上日志, 那样的效率实在是令人胆寒。

所以方法只能是在独立于系统之外的地方寻求突破口。事件是个不错的想法。

因为laravel框架提供了事件监听方式,可以在系统启动过程中注册一个事件触发器收集过程数据,记录该流程内的SQL操作,应该就大差不差了。

比如注册 illuminate.query 事件。理想的情况下,代码应该是类似下面这样的:

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

开启一个事件监听,如果query发生,则将传入的SQL语句参数写入日志内。

上面的只能算是伪代码,真正实施起来,要考虑的过程因素有很多。比如首先要查看 database 配置文件内,日志功能是否打开。也就是 log 配置项是否设置为 true。

我们使用全局的 Config 类获取配置信息:

Config::get('database.log', false)

如果没有开启数据库日志,则手动处理,将上述 illuminate.query 事件的监听器写入系统内。当然传入的参数要多一些, query, bindings, time, name,分别是 SQL 语句,绑定的参数,执行的时间,以及标志名。

那么监听事件实现起来是这样的:

Event::listen('illuminate.query', function($query, $bindings, $time, $name){});

好了,既然获取到传入的数据了,就可以实现我们的处理逻辑。为了处理方便,将所有原始数据写入 Log 类方法的第二个传参, 我们将参数打包到数组:

$data = compact('bindings', 'time', 'name');

因为单个SQL语句绑定的参数有很多,所以这个 bindings 数组,需要手动处理一下:

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'";
    }
}

对于传入的日期时间对象,进行格式化;如果是时间戳或者字符串,直接存档。

我们需要做的工作,就是把位置参数和SQL语句进行还原,生成原始的带参数的SQL语句, 不得不提 vsprintf 这个函数,大家有必要深入学习一下。

$query = str_replace(array('%', '?'), array('%%', '%s'), $query);
$query = vsprintf($query, $bindings);

注意laravel生成的SQL语句占位符是问号,而vsprintf函数占位符是百分号,所以先进行转换,然后调用。最后把准备好的数据一股脑写到Log内:

Log::info($query, $data);

我们还可以利用框架 ServiceProvider 注册上述监听器。比如创建一个 DebugServiceProvider,写入下面的代码:

本文分享自微信公众号 - 程序员小助手(mql45ea),作者:黄门小李子

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 3分钟短文 ! Laravel拼装SQL子查询的最佳实现

    对于数据库DBA可能更习惯从SQL的角度出发,从SQL现有的语言结构和功能上解决问题。比如查询一个product表,要求查询条件中,product_catago...

    程序员小助手
  • 3分钟短文 | Laravel 获取模型查询生成的SQL语句

    在程序开发阶段,我们关注于业务逻辑,实现功能。而laravel提供了非常好的 debug 支持,只需在 env 文件内指定 debug = true ,就可以在...

    程序员小助手
  • 手把手教你造轮子:这个价值100万的短网址微服务,我送给你

    网上已经有产品,用着还不错。可是,作为程序员,从零开始造轮子,开发一个属于自己的短网址服务器,这想法amazing!

    程序员小助手
  • 什么叫业界良心

    不知道从什么时候开始起,国人变得特别浮躁,尤其是在商业领域。君不见,某宝上假货不断,用户投诉无门。在整体情况一般的情况下,质量或服务好的企业都被用户称之为“业界...

    光荣与梦想1987
  • 一条简单的sql在11g和12c中的不同(r5笔记第2天)

    今天在查看awr报告的时候,有一句很简单的sql语句引起了我的注意,因为它排在SQL Order by Reads的第2位。 Physical ReadsExe...

    jeanron100
  • 基于点击图模型Query和Document相关性的计算

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    张凝可
  • 一篇文章教会你使用HTML打造一款颜色配对游戏

    createjs是一个基于canvas的制作H5游戏、动画、交互的库。包括EaselJs、TweenJs、SoundJs、 PreloadJs四个部分。它基于...

    前端皮皮
  • [日常] 算法-旋转字符串-暴力移位法

    给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串...

    陶士涵
  • C/C++ 一段代码区分数组指针|指针数组|函数指针|函数指针数组

    1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<windows.h> 4 /* 举列子说明什么是...

    Gxjun
  • Python数据可视化案例二:动态更新数据

    在开发与数据监测和数据可视化有关的系统时,我们会需要根据最新的数据对图形进行更新。下面的代码模拟了这种情况,单击Start按钮时会更新数据并重新绘制图形使得曲线...

    Python小屋屋主

扫码关注云+社区

领取腾讯云代金券