Metadata-Version: 2.5
Name: profiler-flask
Version: 0.2.0
Summary: Monitor and analyze flask endpoint and request performance.
Project-URL: Homepage, https://pypi.org/project/profiler-flask
Project-URL: Source Code, https://github.com/Dumbliidore/profiler-flask
Author-email: Dumbliidore <2361018131@qq.com>
License: MIT
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Requires-Dist: flask-httpauth>=4.8.1
Requires-Dist: flask>=3.1.3
Provides-Extra: all
Requires-Dist: pymongo>=4.12; extra == 'all'
Requires-Dist: pytest>=8.4.1; extra == 'all'
Requires-Dist: sqlalchemy>=2.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: pytest>=8.4.1; extra == 'dev'
Provides-Extra: mongodb
Requires-Dist: pymongo>=4.12; extra == 'mongodb'
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy>=2.0; extra == 'sqlalchemy'
Description-Content-Type: text/markdown

[English](README.md) | [简体中文](README.zh-CN.md)

## Introduction
**Profiler-Flask comes from the refactoring of flask-profiler, and I would like to express my heartfelt thanks to its author [@muatik](https://github.com/muatik) for his open source spirit and excellent code.**

The project beautifies the front-end interface on the basis of flask-profiler, and is basically consistent with its function.

With the web interface, You can monitor all your endpoints' performance you want to monitor and check the performance of endpoints and requests through filters.

## Screenshots

Dashboard view

![image](https://github.com/Dumbliidore/profiler-flask/blob/main/assets/dashboard.png)


You can create filters to investigate certain type requests.

![image](https://github.com/Dumbliidore/profiler-flask/blob/main/assets/filtering.png)


You can see all the details of a request.

![image](https://github.com/Dumbliidore/profiler-flask/blob/main/assets/details.png)



## Installation
use `uv`
```powershell
uv add profiler-flask
```
use `pip`
```
pip install profiler-flask
```

To use optional storage backends, install with extras:
```powershell
uv add "profiler-flask[mongodb]"   # MongoDB
uv add "profiler-flask[sqlalchemy]" # SQLAlchemy
```


## Example
This is an example. Let's dive in.

```python
from flask import Flask
import flask_profiler


app = Flask(__name__)
app.config["DEBUG"] = True
app.config["profiler"] = {
    "storage": {"engine": "sqlite"},
    "basicAuth": {"enabled": True, "username": "admin", "password": "admin"},
    "ignore": ["^/static/.*"],
    "redact": ["password", "token", "authorization"],
}

@app.route("/product/<id>", methods=["GET"])
def getProduct(id):
    return f"product id is {id}"


@app.route("/product/<id>", methods=["PUT"])
def updateProduct(id):
    return f"product {id} is being updated"


@app.route("/products", methods=["GET"])
def listProducts():
    return "suppose I send you product list..."

flask_profiler.init_app(app)

# 也可以使用装饰器的方法使用
@app.route("/doSomethingImportant/<id>", methods=["GET"])
@flask_profiler.profile()
def doSomethingImportant(id):
    return "profiler will measure this request."
```

The `redact` option is optional. When set, any captured form/query-string key (or
JSON body key) whose lowercase name contains one of the listed tokens is dropped
from the stored context (sensitive values in JSON bodies are replaced with
`[REDACTED]`). Non-JSON request bodies are not stored while `redact` is enabled.

The `DELETE /profiler/db/data/delete` endpoint only accepts requests that carry
the `HX-Request: true` header (sent automatically by htmx), which limits
accidental deletion from plain cross-site form posts.

Now, run your flask app.

### Generate sample data

To fill the profiler with a few measurements so the dashboard and filtering pages
have something to show, run the included seed script:

```bash
uv run python example/seed.py
```

It clears any existing measurements and replays a handful of requests through the
example routes with Flask's test client.

## Using with different database system
You can use profiler-flask with Sqlite database systems.

#### SQLite
In order to use SQLite, just specify it as the value of storage.engine directive as follows.

```python
app.config["profiler"] = {
    "storage": {
        "engine": "sqlite",
    }
}
```

Below the other options are listed.
|  Filter key   |                  Description                  |      Default       |
| :-----------: | :-------------------------------------------: | :----------------: |
| storage.FILE  |           SQLite database file name           | flask_profiler.sql |
| storage.TABLE | table name in which profiler data will reside |    measurements    |

## Sampling
Control the number of samples taken by profiler-flask

You would want control over how many times should the profiler-flask take samples while running in production mode. You can supply a function and control the sampling according to your business logic.

Example 1: Sample 1 in 100 times with random numbers

```python
app.config["profiler"] = {
    "sampling_function": lambda: True if random.sample(list(range(1, 101)), 1) == [42] else False
}
```

Example 2: Sample for specific users

```python
app.config["profiler"] = {
    "sampling_function": lambda: True if user is 'Dumblidore' else False
}
```

If sampling function is not present, all requests will be sampled.

### Changing profiler-flask endpoint root
By default, we can access profiler-flask at /profiler, but you can change the endpoint root.

Example:
```python
app.config["profiler"] = {
        "endpointRoot": "profiler-flask"
}
```
the endpoint root will be changed to /profiler-flask.

### Ignored endpoints

Profiler-Flask will try to track every endpoint defined so far when init_app() is invoked. If you want to exclude some of the endpoints, you can define matching regex for them as follows:

```python
app.config["profiler"] = {
        "ignore": [
	        "^/static/.*",
	        "/api/users/\w+/password"
        ]
}
```

> **Note:** `init_app()` only wraps the routes registered up to that call. Any route registered *afterwards* (e.g. in a blueprint loaded later) won't be measured automatically — decorate it explicitly with `@flask_profiler.profile()` to opt it in.

## Updates

- **UI overhaul** — the Bootstrap/DataTables stack was replaced by a custom CSS design system (light/dark themes) with Grid.js tables, htmx and Alpine.js. ECharts keeps a single chart instance per container, so re-rendering the dashboard never leaks listeners or orphaned canvases.
- **Dashboard** — summary aggregation is computed inside the storage backends (SQLite / MongoDB / SQLAlchemy) and returns an identical schema (`id`, `method`, `name`, `count`, `minElapsed`, `maxElapsed`, `avgElapsed`, `timestamp`). Time ranges (1 / 7 / 30 days) are unified across backends and capped at 90 days. A Columns menu lets you toggle the statistic columns.
- **Filtering page** — server-side pagination and search (across method, name, elapsed and date) with proper `%`/`_` escaping, a debounced search box, per-row detail modal, and race-guarded fetches so stale responses never overwrite newer ones.
- **Storage consistency** — `filter()` and `paginate()` return the same shape on every backend, including a real epoch `startedAt` and a stable row `id`. SQLite uses WAL + busy timeout for safe concurrent use, and `init_app()` is idempotent and never measures its own admin routes.
- **Request context safety** — request bodies are capped at 64 KB and, while `redact` is enabled, only JSON bodies are stored. Binary or non-UTF-8 payloads no longer break the wrapper, and `args`/`kwargs`/`context` are normalized to JSON-safe values so a non-serializable object never silently drops a measurement.
- **Security hardening** — all admin pages set `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy` and `Cache-Control` (public pages are `no-store`, static assets are cached); deleting data requires the `HX-Request: true` header (sent automatically by htmx).
- **Custom storage engines** — set `storage.engine` to any `module.ClassName` as long as it subclasses `flask_profiler.storage.BaseStorage`.
- **Quality** — the suite is covered by 20 pytest cases spanning the wrapper, redaction, pagination/search, backend consistency and idempotent initialization.

## License
This project is licensed under the MIT License (see the `LICENSE` file for details). Some macros were part of Flask-Profiler and were modified under the terms of its MIT License.
