diff --git a/example-django/.env.example b/example-django/.env.example new file mode 100644 index 0000000..b2370fa --- /dev/null +++ b/example-django/.env.example @@ -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 \ No newline at end of file diff --git a/example-django/.gitignore b/example-django/.gitignore new file mode 100644 index 0000000..64ff402 --- /dev/null +++ b/example-django/.gitignore @@ -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 \ No newline at end of file diff --git a/example-django/Dockerfile b/example-django/Dockerfile new file mode 100644 index 0000000..efc2ad7 --- /dev/null +++ b/example-django/Dockerfile @@ -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"] \ No newline at end of file diff --git a/example-django/README.md b/example-django/README.md new file mode 100644 index 0000000..64d0a1f --- /dev/null +++ b/example-django/README.md @@ -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. diff --git a/example-django/demo/__init__.py b/example-django/demo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example-django/demo/settings.py b/example-django/demo/settings.py new file mode 100644 index 0000000..23a2fbe --- /dev/null +++ b/example-django/demo/settings.py @@ -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, + }, + }, +} \ No newline at end of file diff --git a/example-django/demo/templates/index.html b/example-django/demo/templates/index.html new file mode 100644 index 0000000..0b00886 --- /dev/null +++ b/example-django/demo/templates/index.html @@ -0,0 +1,85 @@ + + +
+ + +This demo shows how to integrate Logtail with Django for structured logging. Each button above triggers a different type of log:
+Check your Logtail dashboard to see the logged events!
+