Skip to content

Commit

Permalink
Add testing, new Profyle options, port and host
Browse files Browse the repository at this point in the history
  • Loading branch information
vpcarlos committed Oct 17, 2023
1 parent 9529173 commit 957ec8f
Show file tree
Hide file tree
Showing 29 changed files with 503 additions and 159 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Tests
on:
push:
pull_request:

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: 3.9

- name: Install poetry
run: pip install poetry

- name: Install dependencies
run: |
poetry config virtualenvs.create false
poetry install
- name: Run tests
run: |
poetry run pytest
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,13 @@ $ pip install profyle
### 1. Implement
In order to track all your API requests you must implement the <code>ProfyleMiddleware</code>
#### ProfyleMiddleware
* enabled : Default true. You can use an env variable to decide if profyle is enabled.
* pattern: Profyle only will trace those paths that match with pattern (<a href="https://en.wikipedia.org/wiki/Glob_(programming)" class="external-link" target="_blank">glob pattern</a>)
| Attribute | Required | Default | Description |
| --- | --- | --- | --- |
| `enabled` | No | `True` | Enable or disable Profyle |
| `pattern` | No | `None` | 0nly trace those paths that match with pattern (<a href="https://en.wikipedia.org/wiki/Glob_(programming)" class="external-link" target="_blank">glob pattern</a>) |
| `max_stack_depth` | No | `-1` | Limit maximum stack trace depth |
| `min_duration` | No | `0` (milisecons) | Only record traces with a greather duration than the limit. |


<details markdown="1" open>
<summary>FastAPI</summary>
Expand All @@ -51,7 +56,27 @@ from fastapi import FastAPI
from profyle.fastapi import ProfyleMiddleware

app = FastAPI()
app.add_middleware(ProfyleMiddleware, pattern='*/api/v2/*')
# Trace all requests
app.add_middleware(ProfyleMiddleware)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
```

```Python
from fastapi import FastAPI
from profyle.fastapi import ProfyleMiddleware

app = FastAPI()
# Trace all requests that match that start with /api/products
# with a minimum duration of 100ms and a maximum stack depth of 20
app.add_middleware(
ProfyleMiddleware,
pattern="/api/products*",
max_stack_depth=20,
min_duration=100
)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
Expand All @@ -68,7 +93,7 @@ from profyle.flask import ProfyleMiddleware

app = Flask(__name__)

app.wsgi_app = ProfyleMiddleware(app.wsgi_app, pattern='*/api/products*')
app.wsgi_app = ProfyleMiddleware(app.wsgi_app, pattern="*/api/products*")

@app.route("/")
def hello_world():
Expand Down Expand Up @@ -120,12 +145,19 @@ INFO: Application startup complete.
## CLI Commands
### start
* Start the web server and view profile traces

| Options | Type | Default | Description |
| --- | --- | --- | --- |
| --port | INTEGER | 0 | web server port |
| --host | TEXT | 127.0.0.1 | web server host |

<div class="termy">

```console
$ profyle start
$ profyle start --port 5432

INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Uvicorn running on http://127.0.0.1:5432 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
Expand Down
49 changes: 31 additions & 18 deletions profyle/application/profyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import re
from typing import Optional
from dataclasses import dataclass
from tempfile import NamedTemporaryFile

from viztracer import VizTracer

Expand All @@ -15,38 +16,50 @@
class profyle:
name: str
repo: TraceRepository
max_stack_depth: int = -1
min_duration: float = 0
pattern: Optional[str] = None
tracer: VizTracer = VizTracer(
verbose=0,
log_async=True
)
tracer: Optional[VizTracer] = None

def __enter__(self):
def __enter__(self) -> None:

if self.should_trace():
self.tracer = VizTracer(
log_func_args=True,
log_print=True,
log_func_retval=True,
log_async=True,
file_info=True,
min_duration=self.min_duration,
max_stack_depth=self.max_stack_depth
)
self.tracer.start()

def __exit__(
self,
*args,
):
if not self.tracer.enable:
return
self.tracer.stop()
self.tracer.parse()
new_trace = TraceCreate(
data=json.dumps(self.tracer.data),
name=self.name
)
store_trace(
new_trace=new_trace,
repo=self.repo
)
) -> None:
if self.tracer and self.tracer.enable:
self.tracer.stop()
temp_file = NamedTemporaryFile(suffix=".json")
self.tracer.save(temp_file.name)
temp_file.close()
new_trace = TraceCreate(
data=json.dumps(self.tracer.data),
name=self.name
)
store_trace(
new_trace=new_trace,
repo=self.repo
)

def should_trace(self) -> bool:
if not self.pattern:
return True

regex = fnmatch.translate(self.pattern)
reobj = re.compile(regex)
method_and_name = self.name.split(' ')
if len(method_and_name) > 1:
return bool(reobj.match(method_and_name[1]))
return bool(reobj.match(self.name))
4 changes: 2 additions & 2 deletions profyle/application/trace/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
def delete_all_traces(repo: TraceRepository) -> int:
return repo.delete_all_traces()

def delete_trace_by_id(repo: TraceRepository, trace_id: int) -> int:
return repo.delete_trace_by_id(trace_id=trace_id)
def delete_trace_by_id(repo: TraceRepository, trace_id: int):
repo.delete_trace_by_id(trace_id=trace_id)
4 changes: 2 additions & 2 deletions profyle/application/trace/get.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import List, Optional
from typing import Optional
from profyle.domain.trace import Trace
from profyle.domain.trace_repository import TraceRepository


def get_all_traces(repo: TraceRepository) -> List[Trace]:
def get_all_traces(repo: TraceRepository) -> list[Trace]:
return repo.get_all_traces()


Expand Down
20 changes: 14 additions & 6 deletions profyle/domain/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Trace(BaseModel):
data: Json[Any] = {}
name: str
duration: float = 0
timestamp: str = ''
timestamp: str = ""
id: int


Expand All @@ -17,13 +17,21 @@ class TraceCreate(BaseModel):
@computed_field
@property
def duration(self) -> float:
any_trace_to_analize = any(
True
for trace in self.data.get("traceEvents", [])
if trace.get("ts")
)
if not any_trace_to_analize:
return 0

start = min(
trace.get('ts')
for trace in self.data.get('traceEvents', [])
if trace.get('ts')
trace.get("ts",0)
for trace in self.data.get("traceEvents", [])
if trace.get("ts")
)
end = max(
trace.get('ts', 0)
for trace in self.data.get('traceEvents', [])
trace.get("ts", 0)
for trace in self.data.get("traceEvents", [])
)
return end-start
6 changes: 3 additions & 3 deletions profyle/domain/trace_repository.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import List, Optional
from typing import Optional

from profyle.domain.trace import Trace, TraceCreate

Expand Down Expand Up @@ -31,7 +31,7 @@ def store_trace(self, new_trace: TraceCreate) -> None:
...

@abstractmethod
def get_all_traces(self) -> List[Trace]:
def get_all_traces(self) -> list[Trace]:
...

@abstractmethod
Expand All @@ -43,5 +43,5 @@ def get_trace_selected(self) -> Optional[int]:
...

@abstractmethod
def delete_trace_by_id(self, trace_id: int) -> int:
def delete_trace_by_id(self, trace_id: int):
...
2 changes: 1 addition & 1 deletion profyle/fastapi.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .infrastructure.middleware.fastapi import ProfyleMiddleware
from profyle.infrastructure.middleware.fastapi import ProfyleMiddleware
2 changes: 1 addition & 1 deletion profyle/flask.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .infrastructure.middleware.flask import ProfyleMiddleware
from profyle.infrastructure.middleware.flask import ProfyleMiddleware
Loading

0 comments on commit 957ec8f

Please sign in to comment.