PHPackages                             ahmedmashhour/laravel-query-debugger - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. ahmedmashhour/laravel-query-debugger

ActiveLibrary[Database &amp; ORM](/categories/database)

ahmedmashhour/laravel-query-debugger
====================================

Automatic query tracking, analysis, and debugging for Laravel applications with N+1 detection, slow query monitoring, and performance insights

v1.0.2(5mo ago)0101MITPHPPHP ^8.1

Since Jan 1Pushed 5mo agoCompare

[ Source](https://github.com/AhmedMashhour/laravel-query-debugger)[ Packagist](https://packagist.org/packages/ahmedmashhour/laravel-query-debugger)[ RSS](/packages/ahmedmashhour-laravel-query-debugger/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (6)Versions (4)Used By (0)

Laravel Query Debugger
======================

[](#laravel-query-debugger)

Automatic query tracking, analysis, and debugging for Laravel applications with N+1 detection, slow query monitoring, and performance insights.

Features
--------

[](#features)

- ✅ **Auto-Tracking**: Automatically track all database queries with zero code changes
- ✅ **Daily JSON Logs**: Store queries in JSON format with daily rotation
- ✅ **N+1 Detection**: Automatically detect and alert on N+1 query patterns
- ✅ **Slow Query Monitoring**: Flag queries exceeding configurable thresholds
- ✅ **EXPLAIN ANALYZE**: Auto-execute EXPLAIN for slow queries
- ✅ **Backtrace Collection**: Track where queries originate in your code
- ✅ **API Response Injection**: Optionally include query debug data in responses
- ✅ **Multi-Tenant Support**: Optional tenant context capture
- ✅ **Sampling**: Reduce overhead by sampling percentage of queries
- ✅ **Alert System**: Log, Slack notifications for query issues
- ✅ **Per-Route Configuration**: Different settings per endpoint
- ✅ **Artisan Commands**: Analyze and clean logs via CLI

Installation
------------

[](#installation)

### Option 1: Install from GitHub (Recommended)

[](#option-1-install-from-github-recommended)

Add the package repository to your `composer.json`:

```
{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/AhmedMashhour/laravel-query-debugger.git"
        }
    ]
}
```

Then install via Composer:

```
composer require ahmedmashhour/laravel-query-debugger:dev-main
```

Or specify a specific version/tag:

```
composer require ahmedmashhour/laravel-query-debugger:^1.0
```

### Option 2: Install from Local Path

[](#option-2-install-from-local-path)

Add the package to your `composer.json`:

```
{
    "repositories": [
        {
            "type": "path",
            "url": "./packages/query-debugger"
        }
    ],
    "require": {
        "ahmedmashhour/laravel-query-debugger": "dev-main"
    }
}
```

Run composer install:

```
composer require ahmedmashhour/laravel-query-debugger
```

### Step 2: Publish Configuration (Optional)

[](#step-2-publish-configuration-optional)

```
php artisan vendor:publish --tag=query-debugger-config
```

### Step 3: Configure

[](#step-3-configure)

Add to your `.env`:

```
QUERY_DEBUG_ENABLED=true
QUERY_DEBUG_SLOW_THRESHOLD=100
QUERY_DEBUG_CONNECTIONS=mysql,tenant_db
QUERY_DEBUG_BACKTRACE=true
QUERY_DEBUG_N_PLUS_ONE=true
QUERY_DEBUG_INJECT_RESPONSE=false
QUERY_DEBUG_SAMPLING=100
```

Configuration
-------------

[](#configuration)

### Quick Start - Key Environment Variables

[](#quick-start---key-environment-variables)

```
# Enable/Disable
QUERY_DEBUG_ENABLED=true

# API Response Injection
QUERY_DEBUG_INJECT_RESPONSE=true              # Show summary in responses
QUERY_DEBUG_FULL_QUERIES_IN_RESPONSE=true     # Show ALL queries (default: false)

# Analysis Features
QUERY_DEBUG_ANALYZE=true                      # Enable EXPLAIN for slow queries (default: false)
QUERY_DEBUG_ANALYZE_ALL=true                  # Enable EXPLAIN for ALL queries (default: false)
QUERY_DEBUG_EXPLAIN_ANALYZE=true              # Enable EXPLAIN ANALYZE for slow queries (default: false)
QUERY_DEBUG_EXPLAIN_ANALYZE_ALL=true          # Enable EXPLAIN ANALYZE for ALL queries (default: false)
QUERY_DEBUG_SLOW_THRESHOLD=100                # Slow query threshold in ms
QUERY_DEBUG_N_PLUS_ONE=true                   # Enable N+1 detection

# Performance
QUERY_DEBUG_BACKTRACE=true                    # Collect stack traces
QUERY_DEBUG_SAMPLING=100                      # Log X% of queries (100 = all)

# Connections
QUERY_DEBUG_CONNECTIONS=mysql,tenant_db       # Which connections to track
```

### Basic Settings

[](#basic-settings)

```
'enabled' => env('QUERY_DEBUG_ENABLED', false),
'slow_query_threshold' => 100, // milliseconds
'connections' => ['mysql', 'tenant_db'], // or ['*'] for all
```

### Storage

[](#storage)

```
'storage' => [
    'path' => storage_path('logs/queries'),
    'rotation' => 'daily',
    'retention_days' => 7,
    'max_file_size_mb' => 50,
],
```

### N+1 Detection

[](#n1-detection)

```
'n_plus_one_detection' => [
    'enabled' => true,
    'threshold' => 3, // min occurrences
    'time_window_ms' => 100,
],
```

### API Response Injection

[](#api-response-injection)

```
'inject_in_response' => false, // always inject
// Or use header: X-Query-Debug: true
```

### Alerts

[](#alerts)

```
'alerts' => [
    'enabled' => true,
    'channels' => ['log', 'slack'],
    'slack' => [
        'webhook_url' => env('QUERY_DEBUG_SLACK_WEBHOOK'),
    ],
],
```

Usage
-----

[](#usage)

### Enable for Development

[](#enable-for-development)

```
# Basic - Enable query debugging
QUERY_DEBUG_ENABLED=true

# Show summary in API responses
QUERY_DEBUG_INJECT_RESPONSE=true

# Show ALL queries in API responses (not just summary)
QUERY_DEBUG_FULL_QUERIES_IN_RESPONSE=true

# Enable EXPLAIN for slow queries (query execution plan)
QUERY_DEBUG_ANALYZE=true

# Enable EXPLAIN for ALL queries (not just slow ones)
QUERY_DEBUG_ANALYZE_ALL=true

# Enable EXPLAIN ANALYZE for slow queries (detailed execution stats with actual timing)
QUERY_DEBUG_EXPLAIN_ANALYZE=true

# Enable EXPLAIN ANALYZE for ALL queries (very high overhead!)
QUERY_DEBUG_EXPLAIN_ANALYZE_ALL=true

# Slow query threshold (milliseconds)
QUERY_DEBUG_SLOW_THRESHOLD=100

# Enable N+1 detection
QUERY_DEBUG_N_PLUS_ONE=true

# Enable backtrace
QUERY_DEBUG_BACKTRACE=true

# Sampling rate (100 = log all queries)
QUERY_DEBUG_SAMPLING=100
```

### Enable for Specific Request

[](#enable-for-specific-request)

```
curl -H "X-Query-Debug: true" https://api.example.com/orders
```

**Summary Response** (default - `QUERY_DEBUG_FULL_QUERIES_IN_RESPONSE=false`):

```
{
    "data": { ... },
    "_query_debug": {
        "total_queries": 15,
        "total_time_ms": 45.67,
        "slow_queries_count": 2,
        "n_plus_one_count": 1,
        "slow_queries": [
          {
            "sql": "SELECT * FROM orders WHERE ...",
            "time_ms": 150.5,
            "formatted_sql": "SELECT * FROM orders WHERE id = 123",
            "explain": { ... }
          }
        ],
        "n_plus_one_patterns": [
          {
            "query_pattern": "SELECT * FROM items WHERE order_id = ?",
            "count": 10,
            "suggestion": "Use Order::with('items')->get()"
          }
        ]
    }
}
```

**Full Response** (with `QUERY_DEBUG_FULL_QUERIES_IN_RESPONSE=true`):

```
{
    "data": { ... },
    "_query_debug": {
        "total_queries": 15,
        "total_time_ms": 45.67,
        "slow_queries_count": 2,
        "n_plus_one_count": 1,
        "slow_queries": [...],
        "n_plus_one_patterns": [...],
        "queries": [
          {
            "timestamp": "2026-01-01T15:30:45.123456Z",
            "sql": "SELECT * FROM users WHERE id = ?",
            "bindings": [123],
            "time_ms": 2.5,
            "connection": "mysql",
            "route": "GET /api/orders",
            "backtrace": [...],
            ...
          },
          ...all 15 queries...
        ]
    }
}
```

### Query Analysis: EXPLAIN vs EXPLAIN ANALYZE

[](#query-analysis-explain-vs-explain-analyze)

The package supports two levels of query analysis for slow queries:

#### EXPLAIN (Execution Plan)

[](#explain-execution-plan)

- **What it does**: Shows the query execution plan without running the query
- **Enable**: `QUERY_DEBUG_ANALYZE=true`
- **Output**: Tabular data showing indexes, join types, estimated rows
- **Overhead**: Low (~5-10ms per slow query)
- **Use when**: You want to understand the query plan and index usage

**Example output:**

```
"explain": [
  {
    "id": 1,
    "select_type": "SIMPLE",
    "table": "orders",
    "type": "index_merge",
    "possible_keys": "orders_restaurant_id_foreign,orders_branch_id_foreign",
    "key": "orders_restaurant_id_foreign,orders_branch_id_foreign",
    "rows": 6463,
    "filtered": 99.99,
    "Extra": "Using intersect(...); Using where"
  }
]
```

#### EXPLAIN ANALYZE (Detailed Execution Stats)

[](#explain-analyze-detailed-execution-stats)

- **What it does**: Actually executes the query and provides real timing/cost data
- **Enable**: `QUERY_DEBUG_EXPLAIN_ANALYZE=true`
- **Output**: JSON tree with actual timing, row counts, and costs for each operation
- **Overhead**: Higher (~20-50ms per slow query as it executes the query)
- **Use when**: You need detailed execution statistics and actual vs estimated comparisons

**Example output:**

```
"explain_analyze": {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "1234.56"
    },
    "table": {
      "table_name": "orders",
      "access_type": "index_merge",
      "actual_rows": 6500,
      "filtered": 100.0,
      "cost_info": {
        "read_cost": "1000.00",
        "eval_cost": "234.56",
        "prefix_cost": "1234.56",
        "data_read_per_join": "5M"
      },
      "used_columns": [...],
      "index_merge": {
        "sort_union": [...]
      }
    }
  }
}
```

**Configuration Options:**

1. **Slow Queries Only** (Recommended)

    ```
    # Run EXPLAIN ANALYZE only on slow queries (>= 100ms)
    QUERY_DEBUG_ANALYZE=false                    # Disable regular EXPLAIN
    QUERY_DEBUG_EXPLAIN_ANALYZE=true             # Enable EXPLAIN ANALYZE for slow queries
    QUERY_DEBUG_SLOW_THRESHOLD=100               # Define what "slow" means
    ```

    - **Use when**: You want to analyze only problematic queries
    - **Overhead**: Low-Medium
    - **Best for**: Production/staging environments
2. **Both EXPLAIN and EXPLAIN ANALYZE for Slow Queries**

    ```
    QUERY_DEBUG_ANALYZE=true                     # EXPLAIN for slow queries
    QUERY_DEBUG_EXPLAIN_ANALYZE=true             # EXPLAIN ANALYZE for slow queries
    ```

    - **Use when**: You need both execution plans and detailed stats
    - **Overhead**: Medium
3. **All Queries** (Deep Debugging)

    ```
    QUERY_DEBUG_ANALYZE_ALL=true                 # EXPLAIN for ALL queries
    QUERY_DEBUG_EXPLAIN_ANALYZE_ALL=true         # EXPLAIN ANALYZE for ALL queries
    ```

    - **Use when**: You need to analyze every single query in a request
    - **Overhead**: High-Very High (use sparingly!)
    - **Best for**: Local debugging only

**Important Notes:**

- The `_ALL` variants will override slow query filtering
- Make sure to use boolean values (`true`/`false`), not strings
- After changing `.env`, clear config cache: `php artisan config:clear`

**Recommendation:**

- **Local Development**: Use `EXPLAIN_ANALYZE_ALL=true` for targeted debugging of specific endpoints
- **Staging**: Use `EXPLAIN_ANALYZE=true` (slow queries only) to identify bottlenecks
- **Production**: Disable all analysis unless debugging critical issues

### View Logs

[](#view-logs)

```
# View today's log
cat storage/logs/queries/queries-2026-01-01.json | jq

# Filter slow queries
cat storage/logs/queries/queries-2026-01-01.json | jq '.[] | select(.slow_query == true)'

# Count total queries
cat storage/logs/queries/queries-2026-01-01.json | jq 'length'

# Get queries from specific route
cat storage/logs/queries/queries-2026-01-01.json | jq '.[] | select(.route | contains("/api/users"))'
```

### Analyze Queries

[](#analyze-queries)

```
# Analyze today's queries
php artisan query-debugger:analyze

# Analyze specific date
php artisan query-debugger:analyze --date=2026-01-01

# Show only slow queries
php artisan query-debugger:analyze --slow

# Show only N+1 patterns
php artisan query-debugger:analyze --n-plus-one
```

### Clean Old Logs

[](#clean-old-logs)

```
# Clean logs older than retention period
php artisan query-debugger:clear

# Keep last 3 days
php artisan query-debugger:clear --days=3
```

JSON Log Format
---------------

[](#json-log-format)

Queries are stored as a JSON array with pretty formatting for easy reading:

```
[
  {
    "timestamp": "2026-01-01T15:30:45.123456Z",
    "request_id": "req_abc123",
    "connection": "mysql",
    "sql": "select * from users where id = ?",
    "bindings": [123],
    "time_ms": 2.45,
    "slow_query": false,
    "route": "GET /api/users/123",
    "method": "GET",
    "user_id": "uuid",
    "tenant_id": "tenant-uuid",
    "ip": "192.168.1.1",
    "backtrace": [...],
    "explain": null,
    "query_hash": "md5_hash",
    "memory_mb": 45.2,
    "source": "App\\Repositories\\UserRepository::find",
    "n_plus_one": {...}
  },
  {
    "timestamp": "2026-01-01T15:30:46.789012Z",
    "request_id": "req_abc123",
    "connection": "mysql",
    "sql": "select * from posts where user_id = ?",
    "bindings": [123],
    "time_ms": 5.67,
    "slow_query": false,
    "route": "GET /api/users/123",
    "method": "GET",
    "user_id": "uuid",
    "ip": "192.168.1.1",
    ...
  }
]
```

Per-Route Configuration
-----------------------

[](#per-route-configuration)

Override settings for specific routes:

```
'route_config' => [
    'api/report/*' => [
        'slow_query_threshold' => 500, // higher for reports
        'n_plus_one_detection' => ['enabled' => false],
    ],
],
```

Performance
-----------

[](#performance)

### Overhead

[](#overhead)

- **Minimal mode** (sampling=10, backtrace=false): &lt;1% overhead
- **Standard mode** (sampling=100, backtrace=true): &lt;5% overhead
- **Full analysis** (explain=true): &lt;20% overhead (dev only)

### Optimization Tips

[](#optimization-tips)

1. Use sampling in production: `QUERY_DEBUG_SAMPLING=10`
2. Disable backtrace: `QUERY_DEBUG_BACKTRACE=false`
3. Disable EXPLAIN: `QUERY_DEBUG_ANALYZE=false`
4. Use exclude patterns for known queries
5. Enable only in local/staging environments

Multi-Tenant Support
--------------------

[](#multi-tenant-support)

Optional tenant context capture for multi-tenant applications:

- Set `tenant_id` via `config('app.tenant_id')` in your application
- Or the package will try to read from `config('globals.tenant')->id` (if available)
- Enable in config: `'metadata' => ['tenant_id' => true]`

The package works with any Laravel application - multi-tenant or single-tenant!

Advanced Features
-----------------

[](#advanced-features)

### Exclude Patterns

[](#exclude-patterns)

Skip tracking specific queries:

```
'exclude_patterns' => [
    '/^SELECT \* FROM `sessions`/i',
    '/^SELECT \* FROM `cache`/i',
    '/information_schema/i',
],
```

### Metadata Collection

[](#metadata-collection)

Configure what metadata to collect:

```
'metadata' => [
    'user_id' => true,
    'tenant_id' => true,
    'branch_id' => true,
    'restaurant_id' => true,
    'ip' => true,
    'user_agent' => false,
    'request_id' => true,
    'memory_usage' => true,
],
```

Troubleshooting
---------------

[](#troubleshooting)

### Queries Not Being Logged

[](#queries-not-being-logged)

1. Check `QUERY_DEBUG_ENABLED=true` in `.env`
2. Verify connection is in `connections` array
3. Check sampling rate: `QUERY_DEBUG_SAMPLING=100`
4. Look for exclude patterns matching your queries

### High Overhead

[](#high-overhead)

1. Reduce sampling: `QUERY_DEBUG_SAMPLING=10`
2. Disable backtrace: `QUERY_DEBUG_BACKTRACE=false`
3. Disable EXPLAIN: `QUERY_DEBUG_ANALYZE=false`
4. Add exclude patterns for frequent queries

### Logs Not Rotating

[](#logs-not-rotating)

1. Check file permissions on `storage/logs/queries/`
2. Verify `max_file_size_mb` setting
3. Run `php artisan query-debugger:clear` manually

License
-------

[](#license)

MIT

Support
-------

[](#support)

For issues and questions, please open an issue on the GitHub repository.

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance71

Regular maintenance activity

Popularity12

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

176d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6678c0210d878988220dc6313ef6b57e3d3b30e881a4905ce0b821bac3f636de?d=identicon)[AhmedMashhour](/maintainers/AhmedMashhour)

---

Top Contributors

[![AhmedMashhour](https://avatars.githubusercontent.com/u/33825921?v=4)](https://github.com/AhmedMashhour "AhmedMashhour (7 commits)")

---

Tags

laravelmonitoringdebugdatabaseperformancequeryn-plus-one

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ahmedmashhour-laravel-query-debugger/health.svg)

```
[![Health](https://phpackages.com/badges/ahmedmashhour-laravel-query-debugger/health.svg)](https://phpackages.com/packages/ahmedmashhour-laravel-query-debugger)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.6k](/packages/larastan-larastan)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[itpathsolutions/dbstan

Database Standardization and Analysis Tool for Laravel

442.1k](/packages/itpathsolutions-dbstan)[calebdw/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

15104.9k4](/packages/calebdw-larastan)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
