web-chefs/db-lojack

Laravel 数据库查询记录器和调试助手。

2.3 2020-03-10 16:00 UTC

README

Latest Version on Packagist Software License Total Downloads

支持基本参数替换的 Laravel 数据库查询记录器和调试助手。

特性

  1. 基本的查询绑定替换
  2. 中间件和监听器处理程序
  3. 在生产环境和预发布环境中隐藏安全信息
  4. 请求查询计数记录
  5. 记录网页和控制台查询
  6. DBLog 门面,便于开发者查询调试
  7. 可配置
  8. 默认文本记录器支持日志文件轮转,无需依赖其他包

版本

已验证为正常工作

  • Laravel 5.3 在 PHP 5.6 上
  • Laravel 5.4 在 PHP 5.6, 7.0 上
  • Laravel 5.5 在 PHP 7.0, 7.1 上
  • Laravel 5.6 在 PHP 7.1 上

安装

通过 Composer

$ composer require web-chefs/db-lojack

将服务提供者添加到 config/app.php

'providers' => [
   // Other Service Providers
   WebChefs\DBLoJack\DBLoJackServiceProvider::class,
];

可选地添加 DB LoJack Facade

'aliases' => [
   'DBLog' => WebChefs\DBLoJack\Facades\DbLogger::class,
];

处理程序

有两种处理程序可用,各有优缺点。

中间件(默认)

使用 Laravel HTTP 内核中间件来启用数据库查询记录,并在请求结束时记录所有查询作为单个块。

优点

  1. 将所有查询记录为单个块,允许您跟踪特定请求的查询
  2. 记录每个查询的查询时间(毫秒)

缺点

  1. 不适用于控制台命令(请参阅事件监听器)
  2. 如果请求中执行了大量查询,Laravel 对查询数据的跟踪可能会消耗大量内存,中间件方法不适用于长时间运行的过程或请求
  3. 如果不是第一个中间件,一些查询可能会丢失,应将其设置为高于任何执行数据库调用的中间件

设置

将处理程序配置为 middleware

将中间件添加到 App\Http\Kernel

    protected $middleware = [
        \WebChefs\DBLoJack\Middleware\QueryLoggingMiddleware::class,
    ];

事件监听器

使用 Laravel 事件监听器数据库事件。无论配置如何,此方法在控制台/artisan 应用程序中使用。

优点

  1. 一次处理一个查询,因此更节省内存
  2. 记录所有查询,不受配置影响

缺点

  1. 不一致地记录查询时间,通常由 Laravel 提供
  2. 对于单个请求的多个查询,记录为单独的,使其更难以跟踪到特定请求

设置

将处理程序配置为 listener

开发者使用(门面)

# Get formated query builder query
$query = DB::table('users')->where('id', 1);
print DBLog::formatQuery($query);

# Get formated model query
$model = \App\User::where('id', 1);
print DBLog::formatQuery($model);

# Format raw sql and bindings
print DBLog::formatSql($query->toSql(), $query->getBindings());

# Logs single query or model
DBLog::logQuery($query);

# Debug query using dd();
DBLog::debugQuery($query);

# Check if enabled
DBLog::isEnabled();

# Check if will log for specific handler
DBLog::isLogging('middleware');

# Get configured handler
DBLog::getHandler();

# Check if an object is queryable and loggable throwing an exception
DBLog::isQueryable($query);

# Check if an object is queryable and loggable returning a boolean
DBLog::isQueryable($query, false);

日志

查询日志

查询日志将单个查询记录到每天轮转的文本文件中。

例如: storage/logs/db/db_query.console.2018-06-06.log

性能看门狗日志

一个 PerformanceWatchdog 类将收集统计信息和查询,如果达到或超过指标阈值,则写入日志。

汇总日志

记录每个请求的总计一行。

20180608 12:06:12: Time: 5ms, Version: 7.0.14, DB Queries: 8, Memory: 14 MiB, Request: "CLI: ./vendor/bin/phpunit"
20180608 12:07:35: Time: 10ms, Version: 7.0.14, DB Queries: 25, Memory: 35 MiB, Request: "https:///login"

详细日志

每个运行的查询的日志,包括每个查询的计数器和每个唯一路径到该查询的迷你堆栈跟踪。

START===========================================================================
Date:           20180608 12:06:12
Time:           54ms
Version:        7.0.14
Request:        "CLI: ./vendor/bin/phpunit"
Memory Start:       10 MiB
Memory Current:     14 MiB
Memory Usage:       3.18 MiB
Memory Max Peak:    13.18 MiB
DB Queries:         8
--------------------------------------------------------------------------------
Usage Count:        2
Trace Count:        2
--------------------------------------------------------------------------------
select * from sqlite_master where type = 'table' and name = ?
--------------------------------------------------------------------------------

1) /var/www/site/vendor/WebChefs/DB-LoJack/src/PerformanceWatchdog.php(497): Illuminate\Support\Facades\Facade::__callStatic('simpleTrace', Array)
    2) /var/www/site/vendor/WebChefs/DB-LoJack/src/DBLoJackServiceProvider.php(70): WebChefs\DBLoJack\PerformanceWatchdog->logQuery('select * from s...')
    3) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(348): WebChefs\DBLoJack\DBLoJackServiceProvider->WebChefs\DBLoJack\{closure}(Object(Illuminate\Database\Events\QueryExecuted))
    4) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(199): Illuminate\Events\Dispatcher->Illuminate\Events\{closure}('Illuminate\\Data...', Array)
    5) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(808): Illuminate\Events\Dispatcher->dispatch('Illuminate\\Data...')
    6) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(665): Illuminate\Database\Connection->event(Object(Illuminate\Database\Events\QueryExecuted))
    7) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(618): Illuminate\Database\Connection->logQuery('select * from s...', Array, 0.52)
    8) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(326): Illuminate\Database\Connection->run('select * from s...', Array, Object(Closure))
    9) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php(72): Illuminate\Database\Connection->select('select * from s...', Array)
    10) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php(154): Illuminate\Database\Schema\Builder->hasTable('migrations')

1) /var/www/site/vendor/WebChefs/DB-LoJack/src/PerformanceWatchdog.php(497): Illuminate\Support\Facades\Facade::__callStatic('simpleTrace', Array)
    2) /var/www/site/vendor/WebChefs/DB-LoJack/src/DBLoJackServiceProvider.php(70): WebChefs\DBLoJack\PerformanceWatchdog->logQuery('select * from s...')
    3) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(348): WebChefs\DBLoJack\DBLoJackServiceProvider->WebChefs\DBLoJack\{closure}(Object(Illuminate\Database\Events\QueryExecuted))
    4) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(199): Illuminate\Events\Dispatcher->Illuminate\Events\{closure}('Illuminate\\Data...', Array)
    5) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(808): Illuminate\Events\Dispatcher->dispatch('Illuminate\\Data...')
    6) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(665): Illuminate\Database\Connection->event(Object(Illuminate\Database\Events\QueryExecuted))
    7) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(618): Illuminate\Database\Connection->logQuery('select * from s...', Array, 0.06)
    8) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(326): Illuminate\Database\Connection->run('select * from s...', Array, Object(Closure))
    9) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php(72): Illuminate\Database\Connection->select('select * from s...', Array)
    10) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php(154): Illuminate\Database\Schema\Builder->hasTable('migrations')

--------------------------------------------------------------------------------
Usage Count:        2
Trace Count:        2
--------------------------------------------------------------------------------
delete from "jobs" where "id" = ?
--------------------------------------------------------------------------------

1) /var/www/site/vendor/WebChefs/DB-LoJack/src/PerformanceWatchdog.php(497): Illuminate\Support\Facades\Facade::__callStatic('simpleTrace', Array)
    2) /var/www/site/vendor/WebChefs/DB-LoJack/src/DBLoJackServiceProvider.php(70): WebChefs\DBLoJack\PerformanceWatchdog->logQuery('delete from "jo...')
    3) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(348): WebChefs\DBLoJack\DBLoJackServiceProvider->WebChefs\DBLoJack\{closure}(Object(Illuminate\Database\Events\QueryExecuted))
    4) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(199): Illuminate\Events\Dispatcher->Illuminate\Events\{closure}('Illuminate\\Data...', Array)
    5) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(808): Illuminate\Events\Dispatcher->dispatch('Illuminate\\Data...')
    6) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(665): Illuminate\Database\Connection->event(Object(Illuminate\Database\Events\QueryExecuted))
    7) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(618): Illuminate\Database\Connection->logQuery('delete from "jo...', Array, 0.05)
    8) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(477): Illuminate\Database\Connection->run('delete from "jo...', Array, Object(Closure))
    9) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(428): Illuminate\Database\Connection->affectingStatement('delete from "jo...', Array)
    10) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2225): Illuminate\Database\Connection->delete('delete from "jo...', Array)

1) /var/www/site/vendor/WebChefs/DB-LoJack/src/PerformanceWatchdog.php(497): Illuminate\Support\Facades\Facade::__callStatic('simpleTrace', Array)
    2) /var/www/site/vendor/WebChefs/DB-LoJack/src/DBLoJackServiceProvider.php(70): WebChefs\DBLoJack\PerformanceWatchdog->logQuery('delete from "jo...')
    3) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(348): WebChefs\DBLoJack\DBLoJackServiceProvider->WebChefs\DBLoJack\{closure}(Object(Illuminate\Database\Events\QueryExecuted))
    4) /var/www/site/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(199): Illuminate\Events\Dispatcher->Illuminate\Events\{closure}('Illuminate\\Data...', Array)
    5) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(808): Illuminate\Events\Dispatcher->dispatch('Illuminate\\Data...')
    6) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(665): Illuminate\Database\Connection->event(Object(Illuminate\Database\Events\QueryExecuted))
    7) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(618): Illuminate\Database\Connection->logQuery('delete from "jo...', Array, 0.02)
    8) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(477): Illuminate\Database\Connection->run('delete from "jo...', Array, Object(Closure))
    9) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Connection.php(428): Illuminate\Database\Connection->affectingStatement('delete from "jo...', Array)
    10) /var/www/site/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2225): Illuminate\Database\Connection->delete('delete from "jo...', Array)

END=============================================================================

配置

要自定义配置,请发布 config/db-lojack.php

php artisan vendor:publish --provider="WebChefs\DBLoJack\DBLoJackServiceProvider" --tag="config"

默认配置

return [

    /*
     |--------------------------------------------------------------------------
     | Lo-Jack Query Log
     |--------------------------------------------------------------------------
     |
     | To debug database queries by logging to a text file found in
     | storage/logs. We should avoid running this in production.
     |
     */

    'query_log' => [

        // Enable query logging
        'enabled'    => env('APP_DEBUG', false) && env('APP_ENV', 'local') == 'local',

        // If enabled, when running in console the listener handler will be forced
        'console_logging' => false,

        // Max files number of files to keep, logs are rotated daily
        'max_files'       => 10,

        // Type of handler to collect the query lots and action the log writer:
        // Options middleware or listener
        'handler'         => 'middleware',

        // Default logging location
        'log_path'        => storage_path('logs/db'),

        // Connections to log
        // Comma separated database connection names eg: mysql,pgsql,test
        // all            = all configured connections
        'connection'      => 'all',

        /*
         |----------------------------------------------------------------------
         | Log Formatters
         |----------------------------------------------------------------------
         |
         | Available tokens:
         |
         | All
         | - :env        = environment config variable at time
         | - :date       = date and time the log was writen
         | - :connection = database connection
         | - :label      = request label, URL for http requests, argv for console
         | - :handler    = DBLoJack handler, middleware or listener
         |
         | Query Only
         | - :time       = execution time of a query (not always available)
         | - :query      = formatted query
         |
         | Boundary entires
         | - :boundary    = boundary type, before or after
         |
         */

        // String format for single query (listener)
        'log_foramt_single' => '[:date] [:connection] [:env] :time ms ":query" ":label"',
        // String format for multiple queries being log at once (middleware)
        'log_foramt_multi'  => '[:connection] [:env] :time ms ":query"',

        // Log entries showing for grouping all the logs for single request
        // Leave empty or null to skip boundary
        'log_before_boundary' => '---------BOUNDARY :boundary-:handler [:env]---------' . "\n[:date] :label",
        'log_after_boundary'  => '---------BOUNDARY :boundary---------',

    ],

    /*
     |--------------------------------------------------------------------------
     | Lo-Jack Performance Watchdog
     |--------------------------------------------------------------------------
     |
     | Implements a monitoring system were logs are generated if a configured
     | threshold is exceeded.
     |
     */

    'performance_watchdog' => [

        // Default logging location
        'log_path'    => storage_path('logs/performance'),

        // Operating mode
        // - false      = turns off all monitoring
        // - 'summary'  = most light weight logs a single line per a request
        // - 'detailed' = logs debug details of every database query, good for development
        // - 'both'     = log summary and detailed
        'mode'        => false,

        // Size of mini back trace used in detailed log.
        // Can be integer or false to not log traces
        'trace'       => 10,

        // The minimum number of times a queries should be run before logging.
        'min_queries' => 2,

        // Threshold counters
        'threshold'   => [
            'time'    => 1000,   // request total time in ms
            'queries' => 100,    // total query count per request
            'memory'  => '35MB', // request max memory
         ],

    ],

];

安全性与信息泄露

通常,在生产环境中记录完整的数据库查询(具有实际参数/绑定)是一个非常糟糕的想法,因为这会导致将敏感信息(如用户名、密码和会话 ID)记录到通常安全性较低的位置,形式为应用程序日志。

因此,如果Laravel环境设置为生产测试,查询将被记录,但不会替换绑定,查询将留下?占位符。

贡献

所有代码提交将仅作为拉取请求进行评估和接受。如果您有任何问题或发现任何错误,请随时提出问题。

鸣谢

许可证

MIT许可证(MIT)。更多信息请参阅许可证文件