laravel8 服者提供者,中间件,自定义日志通道实践--QueryListener

自定义日志

自定义日志格式

App\Services\QueryFormatter

<?php


namespace App\Services;


use Illuminate\Log\Logger;

class QueryFormatter
{
    public function __invoke(Logger $logger)
    {
        $mongoLogger = $logger->getLogger();
        foreach ($mongoLogger->getHandlers() as $handler) {
            $handler->setFormatter(new QueryFormat());
        }
    }

}

App\Services\QueryFormat

<?php


namespace App\Services;


use Illuminate\Support\Str;
use Monolog\Formatter\LineFormatter;

class QueryFormat extends LineFormatter
{

    public function format(array $recode): string
    {
        $vars = $this->normalize($recode);
        $queries = data_get($vars, 'context.query');
        if (empty($queries)) {
            return '';
        }
        $extra = data_get($vars, 'context.extra');
        $output = "[ SQL ] [ ? ] [ ? ] [ RunTime:? ms ]\n";
        $line = '';
        $totalRuntime=0;
        foreach ($queries as $key => $query) {
            if (!isset($query['bindings']) || !isset($query['sql']) || !isset($query['time'])) {
                return $line;
            }
            $sql = Str::replaceArray('?', $query['bindings'], $query['sql']);
            $line .= Str::replaceArray('?', [$key + 1, $sql, $query['time']], $output);
        }
        $str = str_repeat('-', 60) . "\n";
        $header = '';
        if (!empty($extra)) {  
            $headerFormat = "[?] ? ? ?\n";
            $header = Str::replaceArray('?', array_values($extra), $headerFormat);
        }
        return $header . $line . $str;
    }

}

自定义的 Monolog 通道

config/loggin.php添加

        'sql'   => [
            'driver' => 'daily',
            'path'   => storage_path('logs/sql/sql.log'),
            'level'  => 'debug',
            'days'   => 14,
            'tap'    => [App\Services\QueryFormatter::class],
        ],
        'cli'   => [
            'driver' => 'daily',
            'path'   => storage_path('logs/sql/cli.log'),
            'level'  => 'debug',
            'days'   => 14,
            'tap'    => [App\Services\QueryFormatter::class],
        ],
注册QueryListen监听器
<?php

namespace App\Providers;

use App\Contracts\QueryListen;
use App\Services\QueryListenService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
        $this->app->singleton(QueryListen::class, function () {
            return new QueryListenService();
        });
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
        //监听DB Query
        DB::listen(function ($query) {
            $listener = $this->app->make(QueryListen::class);
            $listener->push([
                'time'     => $query->time,
                'sql'      => $query->sql,
                'bindings' => $query->bindings,
            ]);
        });
    }
}

SQL契约

app/Contracts/QueryListen.php

<?php


namespace App\Contracts;


interface QueryListen
{
    public function push(array $query=[]);

    public function write(array $extra=[]);

}

SQL监听服务类

app/Services/QueryListenService.php

<?php


namespace App\Services;


use App\Contracts\QueryListen;
use Illuminate\Support\Facades\Log;

class QueryListenService implements QueryListen
{
    private $query;
    private static $instance;

    public function __construct()
    {
        $this->query = collect();
    }

    public static function getInstance()
    {
        $instance = self::$instance;
        if (empty($instance)) {
            $instance = new static();
            self::$instance = $instance;
        }
        return $instance;
    }

    public function push(array $query = [])
    {
        $this->query->push($query);
    }

    public function write(array $extra = [])
    {
        $channel = is_cli() ? 'cli' : 'sql';
        Log::channel($channel)->info('', [
            'query' => $this->query->toArray(),
            'extra' => $extra,
        ]);
    }
}
注册全局中件间

QueryListenerMiddleware

<?php

namespace App\Http\Middleware;

use App\Contracts\QueryListen;
use Closure;
use Illuminate\Http\Request;

class QueryListenerMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $service = resolve(QueryListen::class);
        $time = time();
        $response = $next($request);
        $service->write([
            'time'   => date('Y-m-d H:i:s', $time),
            'ip'     => $request->getClientIp(),
            'method' => is_cli() ? 'cli' : $request->method(),
            'url'    => $request->getHost() . $request->getRequestUri(),
        ]);
        return $response;
    }
}
全局路由

app/Http/Kernel.php$middleware属性加入一行

 \App\Http\Middleware\QueryListenerMiddleware::class,

基本完成

运行控制器进行CURD操作,能看到类似效果
storage\logs\sql目录下生成sql-2020-09-18.log类似文件

[2020-09-18 14:07:49] 127.0.0.1 GET www.demo.com/query
[ SQL ] [ 1 ] [ select * from `wja_user` where `la_user`.`user_id` = 1 limit 1 ] [ RunTime:8.3 ms ]
[ SQL ] [ 2 ] [ select * from `wja_user` where `la_user`.`user_id` = 2 limit 1 ] [ RunTime:0.63 ms ]

在控制台命令模式下没有记录,所以不要灰心,继续折腾,几经周折,在artisan文件修改如下:

$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$time = time();
$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    $output = new Symfony\Component\Console\Output\ConsoleOutput
);

/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/

$kernel->terminate($input, $status);
//获取SQL监听器
$queryListen = $app->make(\App\Contracts\QueryListen::class);
//写入监听到的SQL日志
$queryListen->write([
    'time'   => date('Y-m-d H:i:s', $time),
    'ip'     => '127.0.0.1',
    'method' => '',
    'cmd'    => implode(' ', $_SERVER['argv']),
]);
exit($status);

最效效果

storage\logs\sql目录下发现cli-2020-09-18.log类似文件
打开cli-2020-09-18.log

------------------------------------------------------------
[2020-09-18 14:07:24] 127.0.0.1  artisan test
[ SQL ] [ 1 ] [ select `username` from `wja_user` where `user_id` = 1 limit 1 ] [ RunTime:4.85 ms ]
------------------------------------------------------------

备注

routes\console.php下注册测试命令

Artisan::command('test', function () {
    $userID = \App\Models\Users::where('user_id', 1)->value('username');
    $this->comment($userID);
})->purpose("test cmd");

`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。