Skip to content
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
5 changes: 5 additions & 0 deletions example-django/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DEBUG=True
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=localhost,127.0.0.1
BETTER_STACK_SOURCE_TOKEN=your-source-token-here
BETTER_STACK_INGESTING_HOST=your-source-ingesting-host-here
46 changes: 46 additions & 0 deletions example-django/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
media/
staticfiles/

# Environment
.env
.venv
env/
venv/
ENV/

# IDE
.idea/
.vscode/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db
22 changes: 22 additions & 0 deletions example-django/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.12-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy project files
COPY . .

# Expose port
EXPOSE 8000

# Run the application
CMD ["gunicorn", "demo.wsgi:application", "--bind", "0.0.0.0:8000"]
74 changes: 74 additions & 0 deletions example-django/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Logtail Django Demo

This is a sample Django application that demonstrates how to integrate Logtail for structured logging in a Django project.

## Features

- Structured logging with Logtail
- Different log levels (INFO, WARNING, ERROR)
- Custom context and extra data in logs
- Exception handling and logging
- Docker support
- Django 5.1.3 with Python 3.12

## Prerequisites

- Docker
- A Better Stack account

## Setup

1. Go to Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/260195/sources/new?platform=python), and create new Python source
2. Clone this repository
3. Copy `.env.example` to `.env` and update the values:
```
BETTER_STACK_SOURCE_TOKEN=your-source-token-here
BETTER_STACK_INGESTING_HOST=your-source-ingesting-host-here
```

## Running with Docker

1. Build the Docker image:
```bash
docker build -t logtail-django-demo .
```

2. Run the container:
```bash
docker run -p 8000:8000 --env-file .env logtail-django-demo
```

3. Visit http://localhost:8000 in your browser

## Testing the Logging

The demo includes three endpoints that trigger different types of logs:

1. **Info Log** (Homepage): Logs basic request information
2. **Warning Log** (/trigger-warning/): Logs a warning with custom data
3. **Error Log** (/trigger-error/): Triggers and logs an exception

Check your Logtail dashboard to see the logged events.

## Project Structure

```
example-django/
├── demo/
│ ├── templates/
│ │ └── index.html
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
├── .env
├── Dockerfile
├── README.md
├── manage.py
└── requirements.txt
```

## Logging Configuration

The logging configuration can be found in `settings.py`. It sets up both console and Logtail handlers with a verbose formatter.
Empty file added example-django/demo/__init__.py
Empty file.
135 changes: 135 additions & 0 deletions example-django/demo/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import os
from pathlib import Path
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Build paths inside the project
BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('SECRET_KEY', 'your-secret-key-here')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'

ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '').split(',')

# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'demo.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'demo', 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

WSGI_APPLICATION = 'demo.wsgi.application'

# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}

# Password validation
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]

# Internationalization
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True

# Static files (CSS, JavaScript, Images)
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'demo', 'static'),
]

# Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# Default timeout for database operations (new in Django 5.1)
DATABASE_TIMEOUT = 30

# Logging configuration with Logtail
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'logtail': {
'class': 'logtail.LogtailHandler',
'source_token': os.getenv('BETTER_STACK_SOURCE_TOKEN'),
'host': 'https://' + os.getenv('BETTER_STACK_INGESTING_HOST'),
'formatter': 'verbose',
},
},
'root': {
'handlers': ['console', 'logtail'],
'level': 'INFO',
},
'loggers': {
'django': {
'handlers': ['console', 'logtail'],
'level': 'INFO',
'propagate': False,
},
},
}
85 changes: 85 additions & 0 deletions example-django/demo/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Logtail Django Demo</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
margin-bottom: 30px;
}
.button-container {
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #3498db;
color: white;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.3s;
}
.button:hover {
background-color: #2980b9;
}
.button.error {
background-color: #e74c3c;
}
.button.error:hover {
background-color: #c0392b;
}
.button.warning {
background-color: #f1c40f;
}
.button.warning:hover {
background-color: #f39c12;
}
.description {
margin-top: 30px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>Logtail Django Demo</h1>

<div class="button-container">
<a href="/" class="button">Trigger Info Log</a>
<a href="/trigger-warning/" class="button warning">Trigger Warning Log</a>
<a href="/trigger-error/" class="button error">Trigger Error Log</a>
</div>

<div class="description">
<h2>About this Demo</h2>
<p>This demo shows how to integrate Logtail with Django for structured logging. Each button above triggers a different type of log:</p>
<ul>
<li><strong>Info Log:</strong> Logs basic information about the request including the user agent</li>
<li><strong>Warning Log:</strong> Demonstrates logging with custom data structures</li>
<li><strong>Error Log:</strong> Shows how exceptions are logged with full stack traces</li>
</ul>
<p>Check your Logtail dashboard to see the logged events!</p>
</div>
</div>
</body>
</html>
11 changes: 11 additions & 0 deletions example-django/demo/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
from . import views

urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index, name='index'),
path('trigger-error/', views.trigger_error, name='trigger-error'),
path('trigger-warning/', views.trigger_warning, name='trigger-warning'),
]
Loading