Laravel写入操作日志至数据表导致业务返回的d与表中实际的id不一致

php   2022-04-08 09:21   1127   0  

使用数据库表operation_logs 记录提交的操作日志使用,首先在AppServiceProvider::boot方法中注册数据库监听事件

$user = Auth::user();
$requestId = (string) Str::uuid();
DB::listen(function ($query) use ($user, $requestId) {
    $type = strtolower($query->connection->getName());
    $method = Routing::getMethod();
    //不记录GET请求日志
    if (strtoupper($method) == "GET" || strtoupper($type) == "SELECT") {
        return false;
    }
    $sql = str_replace('?', '"'.'%s'.'"', $query->sql);
    // 判断operation_logs 本身,否则会导致死循环
    if (strpos($sql, "operation_logs") !== false) {
        return false;
    }
    $qBindings = [];
    foreach ($query->bindings as $key => $value) {
        if (is_numeric($key)) {
            $qBindings[] = $value;
        } else {
            $sql = str_replace(':' . $key, '"' . $value . '"', $sql);
        }
    }
    $sql = vsprintf($sql, $qBindings);
    $sql = str_replace("\\", "", $sql);
    $module = Routing::module();
    $operationLog = new OperationLog();
    $operationLog->app = $module;
    $operationLog->school_id = $user ? $user->school_id : 0;
    $operationLog->request_id = $requestId;
    $operationLog->user_id = $user ? $user->id : 0;
    $operationLog->duration = $query->time;
    $operationLog->sql = $sql;
    $operationLog->method = $method;
    $operationLog->path = Routing::getPath();
    $operationLog->save();
    
    return true;
});

OperationLog模型如下

class OperationLog extends BaseModel
{
    public $timestamps = false;
    
    public array $sortable = ['id', 'duration'];
    
    public array $condition = [
        'sql' => 'like',
        'method' => '=',
        'path' => 'like',
        'app' => '=',
        'request_id' => 'like',
        'logged_at' => 'between'
    ];
    
    public function schoolUser(): BelongsTo
    {
        return $this->belongsTo(SchoolUser::class, 'user_id', 'id');
    }
}

然后在编写其他业务过程中发现,Model写入数据后返回的id不是实际的数据库表id,查了一番后才发现返回的是operation_logs表的id,再经历一番资料查阅后发现,监听的日志跟业务同时使用一个连接且立马写入数据就会发生以上情况。解决方案有2个:1.使用队列去处理操作日志写入表事件,比较麻烦这里使用第二种方式,使用独立的连接来写入操作日志

OperationLog模型中新增日志链接配置信息

// 必须使用独立的链接来监听SQL变化,否则会使业务模型主键发生错乱
public $connection = 'sqlLog';

其次在config/database.php中新增一个sqlLog的配置,复制默认的mysql相同一份即可

'sqlLog' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    ]) : [],
],

至此。搞定。



博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。