Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Django & FastAPI examples #19

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Flask with middleware-run",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/flask/flaskenv/bin/flask",
"args": [
"run",
"--no-reload"
],
"env": {
"FLASK_APP": "${workspaceFolder}/flask/app.py", // adjust if your app entry point is different
"FLASK_ENV": "development",
"MW_SERVICE_NAME": "flask-demo-apps",
"PATH": "${workspaceFolder}/flask/flaskenv/bin",
"DEBUG_MODE": "1"
},
"cwd": "${workspaceFolder}/flask",
"console": "integratedTerminal",
"justMyCode": false,
"python": "${workspaceFolder}/flask/flaskenv/bin/python3",
"subProcess": true
}
]
}

1 change: 1 addition & 0 deletions Flask-O-shop
Submodule Flask-O-shop added at d439c4
7 changes: 7 additions & 0 deletions django/django_uwsgi_nginx/django_uwsgi_nginx_wsgi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import os
from django.core.wsgi import get_wsgi_application
from uwsgidecorators import postfork
from middleware import mw_tracker, MWOptions, record_exception, DETECT_AWS_EC2

@postfork
def tracing():
mw_tracker()

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_uwsgi_nginx_settings')
application = get_wsgi_application()

24 changes: 24 additions & 0 deletions django/django_uwsgi_nginx_otel/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3.10
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
ENV DJANGO_SETTINGS_MODULE=django_uwsgi_nginx_settings
RUN ls -l /app && python manage.py collectstatic --noinput

# replace with your Middleware API key
ENV MW_API_KEY="whkvkobudfitutobptgonaezuxpjjypnejbb"

# replace with your Middleware App name
ENV MW_SERVICE_NAME="MyPythonApp"

# replace with your Middleware App url for serverless
ENV MW_TARGET="https://myapp.middleware.io:443"

# enable Middleware Console Exporter for logs and debug
ENV MW_CONSOLE_EXPORTER=True
ENV MW_DEBUG_LOG_FILE=True
ENV MW_LOG_LEVEL=DEBUG

# attach middleware-run to the entrypoint for instrumentation
CMD ["middleware-run","uwsgi", "--http", ":8000", "--module", "django_uwsgi_nginx_wsgi", "--static-map", "/static=/static"]
56 changes: 56 additions & 0 deletions django/django_uwsgi_nginx_otel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Django Application with uWSGI, Nginx, and Middleware Integration Guide

This project demonstrates how to set up a **Django** application with **uWSGI** and **Nginx**, along with **Middleware** integration for Application Performance Monitoring (APM).

Follow the [Middleware APM setup documentation](https://docs.middleware.io/docs/apm-configuration/python/python-apm-setup) to integrate APM with your Django application.

[![PyPI - Version](https://img.shields.io/pypi/v/middleware-io)](https://pypi.org/project/middleware-io/)

| Traces | Metrics | Profiling | Logs (App/Custom) |
|:------:|:-------:|:---------:|:-----------------:|
| Yes | Yes | Yes | Yes/Yes |

## Prerequisites

Before running the project, ensure you have the **Middleware Host Agent** installed to view Python demo data on your dashboard.

---

## Steps to Run the Project

Create and Activate Virtual Environment:

```python
python3 -m venv env
source env/bin/activate
```

Install Dependencies

```
pip install -r requirements.txt
```

Install OpenTelemetry instrument libraries

```python
middleware-bootstrap -a install
```

Run the Application

```
docker compose up --build
```

Command for App Instrumentation

```python
DJANGO_SETTINGS_MODULE="django_uwsgi_nginx_settings" middleware-run uwsgi --http :8000 --module django_uwsgi_nginx_wsgi --static-map /static=/static
```

If you are running this application in a serverless setup consider adding `MW_API_KEY` and `MW_TARGET` as mentioned in the command below :

```python
MW_API_KEY=********** MW_TARGET=https://*****.middleware.io:443 DJANGO_SETTINGS_MODULE="django_uwsgi_nginx_settings" middleware-run uwsgi --http :8000 --module django_uwsgi_nginx_wsgi --static-map /static=/static
```
36 changes: 36 additions & 0 deletions django/django_uwsgi_nginx_otel/django_uwsgi_nginx_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
SECRET_KEY = 'replace-with-a-secure-key'
DEBUG = True
ALLOWED_HOSTS = ['*']
ROOT_URLCONF = 'django_uwsgi_nginx_urls'
WSGI_APPLICATION = 'django_uwsgi_nginx_wsgi.application'

INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.staticfiles',
'sample_app',
]

MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
]

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [],
},
},
]

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'db.sqlite3',
}
}

STATIC_URL = '/static/'
STATIC_ROOT = '/static/'
5 changes: 5 additions & 0 deletions django/django_uwsgi_nginx_otel/django_uwsgi_nginx_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.urls import path, include

urlpatterns = [
path('', include('sample_app.urls')),
]
26 changes: 26 additions & 0 deletions django/django_uwsgi_nginx_otel/django_uwsgi_nginx_wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
from django.core.wsgi import get_wsgi_application

from uwsgidecorators import postfork

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

@postfork
def init_tracing():
resource = Resource.create(attributes={
"service.name": "django-uwsgi-otel"
})

trace.set_tracer_provider(TracerProvider(resource=resource))
span_processor = BatchSpanProcessor(
OTLPSpanExporter(endpoint="http://localhost:9319")
)
trace.get_tracer_provider().add_span_processor(span_processor)

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_uwsgi_nginx_settings')
application = get_wsgi_application()

19 changes: 19 additions & 0 deletions django/django_uwsgi_nginx_otel/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: '3.8'

services:
django:
build: .
volumes:
- ./static:/static
ports:
- "8000:8000"

nginx:
image: nginx:latest
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./static:/static
ports:
- "8080:80"
depends_on:
- django
15 changes: 15 additions & 0 deletions django/django_uwsgi_nginx_otel/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_uwsgi_nginx_settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
21 changes: 21 additions & 0 deletions django/django_uwsgi_nginx_otel/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
worker_processes 1;

events {
worker_connections 1024;
}

http {
server {
listen 80;

location /static/ {
alias /static/;
}

location / {
proxy_pass http://django:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
4 changes: 4 additions & 0 deletions django/django_uwsgi_nginx_otel/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
django
uwsgi
opentelemetry-distro
opentelemetry-exporter-otlp
Empty file.
Empty file.
19 changes: 19 additions & 0 deletions django/django_uwsgi_nginx_otel/sample_app/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<h1>Welcome to the Django Sample App</h1>
<button onclick="fetchAPI()">Call API</button>
<p id="api-response"></p>

<script>
async function fetchAPI() {
const response = await fetch('/api/');
const data = await response.json();
document.getElementById('api-response').textContent = data.message;
}
</script>
</body>
</html>
8 changes: 8 additions & 0 deletions django/django_uwsgi_nginx_otel/sample_app/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import path
from .views import api_endpoint, html_page, raise_exception

urlpatterns = [
path('api/', api_endpoint, name='api'),
path('page/', html_page, name='html_page'),
path('error/', raise_exception, name='raise_exception'),
]
11 changes: 11 additions & 0 deletions django/django_uwsgi_nginx_otel/sample_app/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.http import JsonResponse
from django.shortcuts import render

def api_endpoint(request):
return JsonResponse({"message": "Hello from the API!"})

def html_page(request):
return render(request, "index.html")

def raise_exception(request):
raise ValueError("This is a sample exception!")
51 changes: 31 additions & 20 deletions fastapi/uvicorn/main.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
import logging
import os
from typing import Union
from fastapi import FastAPI, HTTPException
from dotenv import load_dotenv

from middleware import mw_tracker, MWOptions
mw_tracker(
MWOptions(
access_token="whkvkobudfitutobptgonaezuxpjjypnejbb",
target="https://myapp.middleware.io:443",
service_name="MyPythonApp",
)
)


from dotenv import load_dotenv
# Load environment variables
load_dotenv()
import os

from typing import Union

from fastapi import FastAPI

# Initialize FastAPI app
app = FastAPI()

# Set logging level
logging.getLogger().setLevel(logging.INFO)

# Sample APIs
@app.get("/")
def read_root():
logging.info("Hello World")
return {"Hello": "World"}

logging.info("Root API accessed")
return {"message": "Welcome to the API"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
logging.debug("Item ID: %s", item_id)
return {"item_id": item_id, "q": q}
return {"item_id": item_id, "query": q}

@app.post("/create")
def create_item(name: str, price: float):
logging.info("Item created: %s", name)
return {"name": name, "price": price}

@app.put("/update/{item_id}")
def update_item(item_id: int, name: str):
logging.info("Item updated: %d", item_id)
return {"item_id": item_id, "updated_name": name}

@app.delete("/delete/{item_id}")
def delete_item(item_id: int):
logging.info("Item deleted: %d", item_id)
return {"message": f"Item {item_id} deleted"}

@app.get("/error")
def generate_error():
logging.error("Error generated")
raise HTTPException(status_code=500, detail="Simulated error")

if __name__ == "__main__":
import uvicorn

uvicorn.run('main:app', reload=True)
uvicorn.run(app, host="127.0.0.1", port=5002, reload=True)
5 changes: 3 additions & 2 deletions fastapi/uvicorn/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
middleware-io>=2.0.0
middleware-io==2.1.0
fastapi==0.115.5
uvicorn==0.32.0
python-dotenv=1.0.1
python-dotenv==1.0.1
httpx==0.20.0
Loading