Polyscope——面向 Laravel 的以代理为先的开发环境

Build Custom Middleware for Query Performance Monitoring and Optimization in Laravel with MongoDB

最后更新于 经过

Build Custom Middleware for Query Performance Monitoring and Optimization in Laravel with MongoDB image

Performance issues can be one of the most challenging to solve in real-world applications because they are not bugs. Performance issues often hide in database queries, leading to situations where the application works but just does not perform great.

Often, when a route is slow, the real issues are usually inefficient queries, a missing index, or an unexpectedly expensive aggregation. Problems like this are usually difficult to identify without proper monitoring.

In this tutorial, we will build a lightweight monitoring system for Laravel and MongoDB applications. The goal is to track database query performance and request duration so we can quickly detect slow operations and point developers to them.

拉维尔 and MongoDB are a powerful pairing because they combine a highly productive PHP framework with a database built for scale and flexibility. Using the official Laravel MongoDB package, you can use Laravel's expressive Eloquent ORM to manage data without the rigid constraints of a traditional SQL schema.

By the end of this guide, you will have a working system that:

  • Tracks MongoDB query execution time
  • Identifies slow queries automatically
  • Logs performance data for later analysis
  • Automatically cleans up old logs using TTL indexes

Pre-requisites

The following prerequisites are required to follow along with the tutorial:

  • Development environment setup for Laravel and MongoDB
  • Working knowledge of the Laravel framework
  • MongoDB Atlas account

Understanding the Architecture

Let's take a moment to explore the flow of what we will build. The monitoring system would consist of two components: Request MiddlewareMongoDB Command Subscriber

Request Middleware

This is a custom Laravel middleware that we will create. It will accept requests from the client, measure the total duration of the HTTP request, and return insight into the time it takes from start to finish.

MongoDB Command Subscriber

We will create a PHP class that subscribes to the MongoDB PHP driver. The MongoDB PHP driver exposes a monitoring system that emits events whenever database commands are executed.

By subscribing to these events, we can capture query information, including the operation type ( find , insert , aggregate ), collection name, and execution time.

The image below shows the complete flow of the application from request to logging response in the MongoDB collection.

Now we have a clear picture of what we want to build. Let's get started building it.

项目设置

We will start by creating a fresh Laravel project using the command below:

作曲家 创造 项目 laravel/laravel devrel-tutorial-customMiddlewareForQueryPerformance

We need to configure the freshly created Laravel project to work with MongoDB. Ensure you have the MongoDB PHP Extension installed and added to your php.ini file. Follow the instructions in the MongoDB PHP driver installation guide if you need help with the installation.

Next, we need to install the Laravel MongoDB package to configure Laravel to work with MongoDB. In the project directory, run the composer command below to install it.

作曲家 要求 mongodb/laravel-mongodb

Configure your .env File

We need to add your MongoDB Atlas connection details to the .env file. Log in to your MongoDB Atlas account and replace the MONGODB_URL below with your connection string.

MONGODB_URI = "mongodb+srv://USERNAME:PASSWORD@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority&appName=devrel-tutorial-customMiddlewareForQueryPerformance-laravelnews"
MONGODB_DATABASE = devrel-mongodb-drivers

Configure config/database.php

We also need to add the MongoDB configuration to the details in config/database.php . Open the file and add the code below to the connections 大批:

'mongodb' => [
'司机' => 'mongodb' ,
'dsn' => 环境 'MONGODB_URI' ),
'数据库' => 环境 'MONGODB_DATABASE' ),
],

Also, set the default database connection variable to MongoDB like so:

'默认' => 环境 'DB_CONNECTION' , 'mongodb' ),

Once configured, the Laravel application should be able to connect directly to your MongoDB Atlas cluster. Let's start building.

Creating a Post Model for Testing

We need a way to test the tracker. For this reason, we will create and seed a Post model to create some dummy data to test with. Let's proceed by creating the Post model, factory, and seeder with the command below:

php 工匠 品牌:型号 邮政 -mf

This generates the model, factory, and seeder files. Update app/Models/Post.php and replace the code with the code below:

命名空间 应用程序\模型 ;
使用 Illuminate\Database\Eloquent\Factories\HasFactory ;
使用 MongoDB\Laravel\Eloquent\模型 ;
班级 邮政 延伸 模型
{
使用 有工厂 ;
受保护 $连接 = 'mongodb' ;
受保护 $集合 = '帖子' ;
受保护 $可填充 = [
'标题' ,
'身体'
];
}

We simply modified the class to connect to MongoDB, use or create a posts collection, and declared the $fillable 项目。

Seeding Sample Data

Next, let's define the factory to generate sample records. Update database/factories/PostFactory.php with the code below

命名空间 Database\Factories ;
使用 应用程序\模型\帖子 ;
使用 Illuminate\Database\Eloquent\Factories\Factory ;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
*/
班级 PostFactory 延伸 工厂
{
受保护 $模型 = 邮政 ::班级 ;
/**
* 定义模型的默认状态。
*
* @返回 大批 < 细绳 , 混合>
*/
民众 功能 定义 () : 大批
{
返回 [
'标题' => 伪造的 () -> 句子 (),
'身体' => 伪造的 () -> paragraph 5 ),
];
}
}

Next, let's create the seeder file in database/seeders/PostSeeder.php . Update the content of the file with the code below

命名空间 数据库\种子用户 ;
使用 照亮\数据库\种子 ;
使用 应用程序\模型\帖子 ;
班级 PostSeeder 延伸 播种机
{
民众 功能 跑步 () : 空白
{
邮政 :: 工厂 ()
-> 数数 10
-> 创造 ();
}
}

Next, we need to register the Postseeder in DatabaseSeeder . Update database/seeders/DatabaseSeeder.php with the code below

命名空间 数据库\种子用户 ;
使用 应用程序\模型\用户 ;
使用 Illuminate\Database\Console\Seeds\WithoutModelEvents ;
使用 照亮\数据库\种子 ;
班级 DatabaseSeeder 延伸 播种机
{
使用 WithoutModelEvents ;
/**
* 为应用程序的数据库提供种子。
*/
民众 功能 跑步 () : 空白
{
// 用户::工厂(10)->创建();
$this -> 称呼 ([
PostSeeder ::班级 ,
]);
}
}

Then seed the database using the command below

php 工匠 数据库:种子

You should now have a posts collection populated with sample documents.

Performance Log

Query logs will be stored in the performance_log collection. We need to create a PerformanceLog model and update the code to save logs to the MongoDB collection. In your project directory, create the model using the command below

php 工匠 品牌:型号 PerformanceLog

Then update the content of the file with the code below

命名空间 应用程序\模型 ;
使用 MongoDB\Laravel\Eloquent\模型 ;
班级 PerformanceLog 延伸 模型
{
受保护 $连接 = 'mongodb' ;
受保护 $集合 = 'performance_logs' ;
受保护 $可填充 = [
'route' ,
'收藏' ,
'operation' ,
'duration_ms' ,
'request_duration' ,
'is_slow' ,
'created_at'
];
}

Creating the QueryMonitorService

Next, we need to create the service responsible for collecting query metrics. This service will be called later in the middleware file. For now, create app/Services/QueryMonitorService.php and update the code with the code below:

命名空间 应用/服务 ;
使用 App\Models\PerformanceLog ;
班级 QueryMonitorService
{
受保护 大批 查询 = [];
受保护 整数 $slowThreshold = 200 ; // ms
民众 功能 记录 细绳 $collection, 细绳 操作, 漂浮 $duration) : 空白
{
$this -> queries[] = [
'收藏' => $collection,
'operation' => 操作,
'duration_ms' => $duration,
'is_slow' => $duration > $this -> slowThreshold
];
}
民众 功能 坚持 细绳 $route, 漂浮 $requestDuration) : 空白
{
foreach $this -> 查询 作为 $query) {
PerformanceLog :: 创造 ([
'route' => $route,
'收藏' => $query[ '收藏' ],
'operation' => $query[ 'operation' ],
'duration_ms' => $query[ 'duration_ms' ],
'request_duration' => $requestDuration,
'is_slow' => $query[ 'is_slow' ],
'created_at' => 现在 ()
]);
}
}
}

QueryMonitorService is responsible for collecting and storing query performance metrics during a request. The record() method captures information about each MongoDB operation, including the collection name, operation type, and execution time. Queries that exceed the defined $slowThreshold are automatically flagged as slow.

At the end of the request, the persist() method saves the collected query data along with the route name and total request duration to the performance_logs collection. This allows us to later analyze which queries were executed and identify potential performance bottlenecks.

Listening to MongoDB Driver Events

MongoDB provides a powerful monitoring feature through its PHP driver. We can subscribe to database commands using a Command Subscriber to retrieve information about the request at runtime. Create the subscriber class in app/Monitoring/MongoCommandSubscriber.php and update it with the code below:

命名空间 App\Monitoring ;
使用 MongoDB\Driver\Monitoring\CommandSubscriber ;
使用 MongoDB\Driver\Monitoring\CommandStartedEvent ;
使用 MongoDB\Driver\Monitoring\CommandSucceededEvent ;
使用 MongoDB\Driver\Monitoring\CommandFailedEvent ;
使用 App\Services\QueryMonitorService ;
班级 MongoCommandSubscriber 实现 CommandSubscriber
{
受保护 大批 $startTimes = [];
受保护 大批 $operations = [];
受保护 大批 $collections = [];
民众 功能 命令开始 CommandStartedEvent $event) : 空白
{
$请求ID = $事件 -> 获取请求ID ();
$this -> startTimes[$requestId] = 微时间 真的 (英文):
$操作 = $事件 -> 获取命令名称 ();
命令 = 获取对象变量 (事件) -> 获取命令 ());
$集合 = $command[$operation] ?? 'unknown' ;
$this -> operations[$requestId] = $operation;
$this -> collections[$requestId] = 集合;
}
民众 功能 命令成功 CommandSucceededEvent $event) : 空白
{
$请求ID = $事件 -> 获取请求ID ();
如果 伊塞特 $this -> startTimes[$requestId])) {
返回 ;
}
$duration = 微时间 真的 - $this -> startTimes[$requestId]) * 1000 ;
$操作 = $this -> operations[$requestId] ?? $事件 -> 获取命令名称 ();
$集合 = $this -> collections[$requestId] ?? 'unknown' ;
$monitor = 应用程序 QueryMonitorService ::班级 (英文):
$monitor -> 记录
$collection,
操作,
$duration
(英文):
取消设置 $this -> startTimes[$requestId]);
取消设置 $this -> operations[$requestId]);
取消设置 $this -> collections[$requestId]);
}
民众 功能 命令失败 CommandFailedEvent $event) : 空白
{
$请求ID = $事件 -> 获取请求ID ();
// Clean up tracked state when a MongoDB command fails.
取消设置 $this -> startTimes[$requestId]);
取消设置 $this -> operations[$requestId]);
取消设置 $this -> collections[$requestId]);
}
}

MongoCommandSubscriber listens to database events emitted by the MongoDB PHP driver. When a MongoDB command starts, the commandStarted() method records the start time and stores details about the operation and collection being queried.

When the command finishes successfully, the commandSucceeded() method calculates the execution duration and forwards the query metrics to the QueryMonitorService . This allows the system to track how long each database operation takes.

If a command fails, the commandFailed() method simply clears any tracked data for that request to keep the monitoring state clean.

注册订阅者

Now we need to register the subscriber created above with the MongoDB driver. To do this, we addSubscriber() 在里面 boot() 方法 AppServiceProvider.php

打开 app/Providers/AppServiceProvider.php and update the page with the code below

命名空间 应用程序\提供商 ;
使用 App\Services\QueryMonitorService ;
使用 Illuminate\支持\服务提供商 ;
使用 功能 MongoDB\Driver\Monitoring\addSubscriber ;
使用 App\Monitoring\MongoCommandSubscriber ;
班级 应用服务提供商 延伸 服务提供者
{
/**
* 注册任何应用服务。
*/
民众 功能 登记 () : 空白
{
// Share one monitor instance per request lifecycle.
$this -> 应用程序 -> 单例 QueryMonitorService ::班级 , 功能 (){
返回 新的 QueryMonitorService ();
});
}
/**
* 引导任何应用服务。
*/
民众 功能 引导 () : 空白
{
addSubscriber 新的 MongoCommandSubscriber ());
}
}

Now every MongoDB command executed by the application will be monitored.

Creating Performance Middleware

Next, let's create the middleware to track request duration using the following command

php 工匠 make:middleware PerformanceMiddleware

Navigate to the newly created middleware file and update the content with the code below

命名空间 应用程序\HTTP\中间件 ;
使用 关闭 ;
使用 照亮\Http\请求 ;
使用 Symfony\Component\HttpFoundation\Response ;
使用 App\Services\QueryMonitorService ;
班级 PerformanceMiddleware
{
/**
* 处理传入的请求。
*
* @参数 \关闭 \Illuminate\Http\Request ): ( \Symfony\Component\HttpFoundation\Response )$ 下一个
*/
民众 功能 处理 要求 请求, 关闭 $next) : 回复
{
$开始 = 微时间 真的 (英文):
$响应 = $next($request);
$requestDuration = 微时间 真的 - $start) * 1000 ;
$monitor = 应用程序 QueryMonitorService ::班级 (英文):
$resolvedRoute = $请求 -> 路线 ();
路线 = $resolvedRoute ?-> 获取名称 () ?? $resolvedRoute ?-> 类型 () ?? $请求 -> 小路 ();
$monitor -> 坚持 ($route, $requestDuration);
返回 $response;
}
}

Next, we need to register the middleware. At the time of writing, this is a Laravel 12.x project. Middles are registered inside bootstrap/app.php . If you have a different Laravel version. Check the Laravel documentation on how to register middleware

With that said, navigate to bootstrap/app.php and update it with the code below.

使用 照亮\基础\应用 ;
使用 Illuminate\Foundation\Configuration\Exceptions ;
使用 Illuminate\Foundation\Configuration\Middleware ;
使用 App\Http\Middleware\PerformanceMiddleware ;
返回 应用 :: 配置 基本路径 : 目录名 __你__ ))
-> withRouting
网络 : __你__ '/../routes/web.php' ,
命令 : __你__ '/../routes/console.php' ,
健康 : '/向上' ,
-> withMiddleware 功能 中间件 $middleware) : 空白 {
$中间件 -> 网络 附加 :[
PerformanceMiddleware ::班级 ,
]);
})
-> 带有异常 功能 例外 $exceptions) : 空白 {
//
}) -> 创造 ();

Testing Query Monitoring

Let's test what we've built so far. To get started, we need a route. Create a simple web route to trigger database queries. In routes/web.php update the content with the code below

使用 照明\支持\立面\路线 ;
使用 应用程序\模型\帖子 ;
路线 :: 得到 ‘/’ , 功能 (){
返回 看法 '欢迎' (英文):
});
路线 :: 得到 '/posts' , 功能 (){
$posts = 邮政 :: 在哪里 '标题' , '喜欢' , '%API%'
-> 限制 10
-> 得到 ();
返回 回复 () -> json ($posts)
});

Now, we can start the server with the command below

php 工匠 服务

Then visit the /posts route. We expect to get a response like the following from the browser

[
{
“标题” : "Delectus atque odit sapiente ea maxime doloribus consequatur." ,
"body" : "Ut quis alias molestiae. Incidunt aut libero aut. Vel omnis et voluptatum doloremque ipsa. Illum quibusdam in rerum quam sunt voluptas ipsa. Sequi aliquid culpa quas. Ut iure quos corrupti consequatur commodi exercitationem ullam." ,
“updated_at” : "2026-03-11T15:53:31.954000Z" ,
“创建于” : "2026-03-11T15:53:31.954000Z" ,
“ID” : "69b18ffb449c5f787f0d08b9"
}
]

And our MongoDB performance_log collection should contain similar records

Automatically Cleaning Logs with TTL Indexes

Systems like this can generate a large number of log records. If these logs are never removed, the performance_logs collection will continue to grow and eventually consume unnecessary storage.

MongoDB provides a built-in feature called TTL (Time To Live) indexes that automatically delete documents after a specified time. This allows us to keep only recent monitoring data while old logs are removed automatically.

Since we are using MongoDB Atlas , we can create the TTL index directly from the Atlas dashboard following the simple steps below:

  1. Open your MongoDB Atlas dashboard
  2. Navigate to your cluster and click Browse Collections
  3. Select the performance_logs 收藏
  4. 打开 索引 tab and click Create Index

Configure the index with the following values:

Field: created_at
Type: Ascending (1)
TTL: 604800 seconds

If you're not using MongoDB Atlas, you can create the TTL index directly from the MongoDB shell using the command below:

db.performance_logs.createIndex( { created_at: 1 }, { expireAfterSeconds: 604800 }

This achieves the same result by automatically deleting logs after seven days.

604800 seconds is equal to 7天 . Once the index is created, MongoDB will automatically delete any performance log older than seven days.

Why This Approach Works Well

This architecture keeps monitoring concerns separate from application logic.

The middleware measures request performance while the MongoDB command subscriber captures database-level metrics. Because the subscriber hooks into the driver itself, it automatically detects queries without modifying application code.

The result is a simple but effective monitoring system that can help identify slow queries, diagnose performance bottlenecks, and improve application efficiency.

结论

Database performance issues are often difficult to diagnose without visibility into query execution. By combining Laravel middleware with MongoDB driver monitoring, we can build a lightweight system that tracks both request duration and query performance.

Once you understand how to capture database events at the driver level, it becomes much easier to build tools that detect slow queries, surface performance insights, and improve the reliability of your applications.

You can find the full source code on GitHub

As a bonus, I built a Laravel package for the implementation in this article . Feel free to install and use it in your already existing Laravel and MongoDB projects.

摩西·阿努马杜照片

软件工程师、技术撰稿人,精通 PHP、Laravel、Livewire、TailwindCSS 和 VueJS。Laravel 开发人员,codecontent.pro 创始人。

归档于:
立方体

Laravel 时事通讯

加入超过 4 万名开发者的行列,不错过任何新的技巧、教程等内容。

图像
Jump24 - 英国 Laravel 代理机构

Laravel 开发人员,精通技术,绝不外包,绝不离岸外包,始终卓越。

访问 Jump24 - 英国 Laravel 代理机构
Tinkerwell 徽标

廷克威尔

Laravel 开发者必备的代码运行器。可在本地和生产环境中体验 AI、自动补全和即时反馈功能。

廷克威尔
几天内即可获得 Laravel 代码审查徽标的专家指导

几天内即可获得 Laravel 代码审查方面的专家指导

专家级代码审查!两位拥有 10 年以上 Laravel 开发经验的开发者将为您提供清晰、实用的反馈,帮助团队构建更优质的应用程序。

几天内即可获得 Laravel 代码审查方面的专家指导
PhpStorm 标志

PhpStorm

首选的 PHP IDE,对 Laravel 及其生态系统提供广泛的开箱即用支持。

PhpStorm
Laravel Cloud 标志

Laravel 云

轻松创建和管理服务器,并在几秒钟内部署 Laravel 应用程序。

Laravel 云
了解 Softtech 的标志

了解软科技

Acquaint Softtech 提供 AI 就绪的 Laravel 开发人员,48 小时内即可上手,每月费用为 3000 美元,没有冗长的销售流程,并提供 100% 退款保证。

了解软科技
Kirschbaum 标志

樱桃树

提供创新和稳定性,确保您的Web应用程序取得成功。

樱桃树
Shift 标志

转移

还在运行旧版本的 Laravel?立即实现 Laravel 自动升级和代码现代化,让您的应用程序保持最新状态。

转移
鱼叉:新一代时间跟踪和发票标志

Harpoon:新一代时间跟踪和发票系统

新一代时间跟踪和计费软件,帮助您的机构规划和预测盈利的未来。

Harpoon:新一代时间跟踪和发票系统
Lucky Media 标志

幸运传媒

Get Lucky Now——拥有十余年经验的 Laravel 开发理想之选!

幸运传媒
SaaSykit:Laravel SaaS 入门套件徽标

SaaSykit:Laravel SaaS 入门套件

SaaSykit 是一个多租户 Laravel SaaS 入门套件,包含运行现代 SaaS 所需的所有功能,例如支付、美观的结账界面、管理面板、用户仪表盘、身份验证、现成组件、统计数据、博客、文档等等。

SaaSykit:Laravel SaaS 入门套件
MongoDB 徽标

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB
Laravel Mobile Pass: Generate Apple Wallet and Google Wallet Passes image

Laravel Mobile Pass: Generate Apple Wallet and Google Wallet Passes

阅读文章
PHPverse 2026 Returns June 9th image

PHPverse 2026 Returns June 9th

阅读文章
LLPhant: A PHP Generative AI Framework Inspired by LangChain image

LLPhant: A PHP Generative AI Framework Inspired by LangChain

阅读文章
Debounceable Queued Jobs in Laravel 13.6.0 image

Debounceable Queued Jobs in Laravel 13.6.0

阅读文章
Build Custom Middleware for Query Performance Monitoring and Optimization in Laravel with MongoDB image

Build Custom Middleware for Query Performance Monitoring and Optimization in Laravel with MongoDB

阅读文章
Laravel API Starter Kits Are Coming Soon! image

Laravel API Starter Kits Are Coming Soon!

阅读文章