Skip to main content

Lightweight Django middleware for profiling SQL queries, detecting performance issues, and analyzing request execution.

Project description

🚀 ami-djAPI-analyzing

A lightweight Django performance analysis tool for monitoring SQL queries, request performance, and detecting common database issues such as:

  • N+1 queries
  • Duplicate queries
  • Slow queries
  • Missing indexes

It provides detailed profiling information directly via response headers and API responses.


✨ Features

  • 📊 Capture all SQL queries executed during a request
  • 🔁 Automatic N+1 query detection
  • 🔍 Detect duplicate queries
  • ⚡ Identify slow SQL queries
  • 📌 Detect potential missing indexes
  • ⏱ Analyze DB time vs total response time
  • 🧠 Performance scoring system (A–F grading)
  • 🗂 Track request history
  • 💾 Monitor memory usage
  • 📈 Generate EXPLAIN plans for slow queries
  • 🔎 Optional step-by-step view execution profiling
  • 🔐 Encrypted performance payload in response headers

📦 Installation

pip install ami-djAPI-analyzing

⚙️ Setup

Add the middleware to your Django project:

MIDDLEWARE = [
    ...
    'ami_djapi_analyzing.middleware.PerformanceMiddleware',
]

🔥 Usage Flow

1️⃣ Make an API Request

Call any API endpoint in your Django project.

You will receive additional headers like:

{
    "agent_encrypted": "...",
    "X-Encryption-Key": "your-encryption-key"
}

🧪 How to Use (Decrypt & Analyze Performance Data)

1️⃣ Install Required Dependencies

pip install requests cryptography

2️⃣ Decrypt the Performance Payload

import json
import base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

def decrypt_agent_payload(agent_encrypted: str, key_b64: str) -> dict:
    key = base64.b64decode(key_b64)
    raw = base64.b64decode(agent_encrypted)

    nonce, ciphertext = raw[:12], raw[12:]
    aesgcm = AESGCM(key)

    plaintext = aesgcm.decrypt(nonce, ciphertext, None)
    return json.loads(plaintext)

3️⃣ Make API Requests & Extract Data

import requests
import json

session = requests.Session()

def make_request(req):
    try:
        response = session.request(
            method=req.get("method", "GET"),
            url=req.get("url"),
            headers=req.get("headers"),
            params=req.get("params"),
            data=req.get("data"),
            json=req.get("json")
        )

        output = {
            "status_code": response.status_code,
            "agent_data": None
        }

        try:
            output["json"] = response.json()
        except:
            pass

        if "agent_encrypted" in response.headers and "X-Encryption-Key" in response.headers:
            try:
                output["agent_data"] = decrypt_agent_payload(
                    response.headers["agent_encrypted"],
                    response.headers["X-Encryption-Key"]
                )
            except Exception as e:
                output["agent_data"] = f"Decryption failed: {str(e)}"

        return output

    except Exception as e:
        return {"error": str(e)}

4️⃣ Example Requests

🔹 JSON Request

REQUESTS = [
    {
        "method": "POST",
        "url": "http://127.0.0.1:8000/auth/login/",
        "headers": {
            "Content-Type": "application/json",
        },
        "json": {
            "emp_id": "112",
            "password": "Amit@123"
        }
    }
]

🔹 Bearer Token + Form Data Request

TOKEN = "your-access-token"

REQUESTS = [
    {
        "method": "POST",
        "url": "http://localhost:8000/auth/register/",
        "headers": {
            "Accept": "application/json",
            "Authorization": f"Bearer {TOKEN}"
        },
        "data": [
            ("user_status", "active"),
            ("first_name", "ac"),
            ("last_name", "r"),
            ("emp_id", "1019"),
            ("email", "ac@gmailc.com"),
            ("workcenter", "8"),
            ("workcenter", "9"),
            ("plant", "37"),
            ("role_id", "7"),
            ("password", "Qwe2@1234"),
        ]
    }
]

5️⃣ Run Requests

if __name__ == "__main__":
    for i, req in enumerate(REQUESTS, start=1):
        print(f"\n========== REQUEST {i} ==========")
        result = make_request(req)
        print(json.dumps(result, indent=4))

📊 Output Structure

After decryption, you will get detailed performance insights, not just summary.

🔍 Example Output

{
  "summary": {
    "query_count": 3,
    "db_time": 0.003,
    "response_time": 0.0167,
    "score": 100,
    "grade": "A"
  },
  "sql_queries": [
    {
      "sql": "SELECT ...",
      "time": 0.0012,
      "model": "auth_user"
    }
  ],
  "top_slow_queries": [
    {
      "sql": "SELECT ...",
      "time": 0.0012
    }
  ],
  "sql_analysis": {
    "duplicate_queries": [],
    "n_plus_one_queries": [],
    "slow_query_count": 0
  }
}

---

### 📌 Key Sections Explained

- **summary**  Overall performance score (A–F)
- **sql_queries**  All executed SQL queries
- **top_slow_queries**  Slowest queries
- **sql_analysis**
  - duplicate_queries
  - n_plus_one_queries
  - slow_query_count
- **memory_usage**  Memory consumption
- **request_history**  Past request performance

---

## 💡 Best Practices

- Use in **development or staging environments**
- Avoid exposing headers in production without proper security
- Great for:
  - Debugging Django ORM issues
  - Optimizing API performance
  - Identifying database bottlenecks

---

## 🚀 Future Improvements

- CLI tool support  
- Web dashboard for visualization  
- Postman integration  

---

## 🧑‍💻 Author

Made with ❤️ by Ami

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ami_djapi_analyzing-0.1.1.tar.gz (8.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ami_djapi_analyzing-0.1.1-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

Details for the file ami_djapi_analyzing-0.1.1.tar.gz.

File metadata

  • Download URL: ami_djapi_analyzing-0.1.1.tar.gz
  • Upload date:
  • Size: 8.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for ami_djapi_analyzing-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a82589aad50289efbfea0ea9cbdc6eb365cb000c01765467ba89e5e8ec08e884
MD5 fdee979446e7fe25a26794ec97e31b2c
BLAKE2b-256 71e42a8436ebcbbf03ff6fd2f34cf1d6c3a5cbfdd8f6437f54a2b909a9102c97

See more details on using hashes here.

File details

Details for the file ami_djapi_analyzing-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for ami_djapi_analyzing-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bd727eabf2a87fda935e4019c4d3b128e85cda16cb5f80c581f16f99d4db687a
MD5 e23ba290bb0f99f2c0cefe9052427f59
BLAKE2b-256 4f97be6503e053e8e5269814371cf54bd8b14f8831daabb419a219c311c12702

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page