A Python logging handler for Google Cloud Logging with request tracing support
Project description
Cloud Logging Handler
A Python logging handler for Google Cloud Logging with request tracing support.
Features
- Structured JSON Logging: Outputs logs in Google Cloud Logging's structured format
- Request Tracing: Automatic trace context propagation via
X-Cloud-Trace-Contextheader - Log Aggregation: Aggregates all logs within a single request into one log entry
- Severity Tracking: Automatically tracks the highest severity level per request
- Multi-Framework Support: FastAPI, Flask, Django, aiohttp, Sanic
- Auto Framework Detection: Automatically detects framework from app instance
- Custom JSON Encoder: Support for high-performance JSON libraries (e.g.,
ujson) - Zero Dependencies: Core handler has no external dependencies
Installation
# Using uv (recommended)
uv add cloud-logging-handler
# Using pip
pip install cloud-logging-handler
Quick Start
FastAPI / Starlette
import logging
from fastapi import FastAPI, Request
from cloud_logging_handler import CloudLoggingHandler, RequestLogs
app = FastAPI()
# Initialize handler with app for auto framework detection
handler = CloudLoggingHandler(
app=app,
project="your-gcp-project-id"
)
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
@app.middleware("http")
async def logging_middleware(request: Request, call_next):
handler.set_request(RequestLogs(request))
response = await call_next(request)
handler.flush()
return response
@app.get("/")
async def root():
logging.info("Processing request")
return {"message": "Hello World"}
Flask
import logging
from flask import Flask, g, request
from cloud_logging_handler import CloudLoggingHandler, RequestLogs
app = Flask(__name__)
handler = CloudLoggingHandler(
app=app,
project="your-gcp-project-id"
)
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
@app.before_request
def before_request():
handler.set_request(RequestLogs(request))
@app.after_request
def after_request(response):
handler.flush()
return response
@app.route("/")
def hello():
logging.info("Processing request")
return {"message": "Hello World"}
Django
# settings.py
LOGGING = {
'version': 1,
'handlers': {
'cloud': {
'class': 'cloud_logging_handler.CloudLoggingHandler',
'framework': 'django',
'project': 'your-gcp-project-id',
},
},
'root': {
'handlers': ['cloud'],
'level': 'DEBUG',
},
}
# middleware.py
from cloud_logging_handler import CloudLoggingHandler, RequestLogs
import logging
class CloudLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.handler = None
for h in logging.getLogger().handlers:
if isinstance(h, CloudLoggingHandler):
self.handler = h
break
def __call__(self, request):
if self.handler:
self.handler.set_request(RequestLogs(request))
response = self.get_response(request)
if self.handler:
self.handler.flush()
return response
aiohttp
import logging
from aiohttp import web
from cloud_logging_handler import CloudLoggingHandler, RequestLogs
app = web.Application()
handler = CloudLoggingHandler(
app=app,
project="your-gcp-project-id"
)
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
@web.middleware
async def logging_middleware(request, handler_func):
handler.set_request(RequestLogs(request))
response = await handler_func(request)
handler.flush()
return response
app.middlewares.append(logging_middleware)
async def hello(request):
logging.info("Processing request")
return web.json_response({"message": "Hello World"})
app.router.add_get("/", hello)
Sanic
import logging
from sanic import Sanic, json
from cloud_logging_handler import CloudLoggingHandler, RequestLogs
app = Sanic("MyApp")
handler = CloudLoggingHandler(
app=app,
project="your-gcp-project-id"
)
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
@app.middleware("request")
async def before_request(request):
handler.set_request(RequestLogs(request))
@app.middleware("response")
async def after_request(request, response):
handler.flush()
@app.get("/")
async def hello(request):
logging.info("Processing request")
return json({"message": "Hello World"})
Using with ujson
For better JSON serialization performance:
import ujson
from cloud_logging_handler import CloudLoggingHandler
handler = CloudLoggingHandler(
app=app,
json_impl=ujson,
project="your-gcp-project-id"
)
Configuration
CloudLoggingHandler Parameters
| Parameter | Type | Description |
|---|---|---|
app |
object |
Web application instance for auto framework detection |
framework |
str |
Explicit framework name (starlette, flask, django, aiohttp, sanic) |
trace_header_name |
str |
HTTP header name for trace context (default: X-Cloud-Trace-Context) |
json_impl |
module |
Custom JSON encoder module (must have dumps method) |
project |
str |
GCP project ID for trace URL construction |
Framework Detection
The handler automatically detects the framework from the app instance:
| App Module | Detected Framework |
|---|---|
starlette.*, fastapi.* |
starlette |
flask.* |
flask |
django.* |
django |
aiohttp.* |
aiohttp |
sanic.* |
sanic |
You can also explicitly specify the framework:
handler = CloudLoggingHandler(
framework="flask",
project="your-gcp-project-id"
)
Log Output Format
With Request Context
When logging within a request context, logs are aggregated and output as structured JSON:
{
"severity": "INFO",
"name": "root",
"process": 12345,
"url": "https://example.com/api/endpoint",
"logging.googleapis.com/trace": "projects/your-project/traces/abc123",
"logging.googleapis.com/spanId": "def456",
"message": "\n2025-12-01T12:00:00.000000+00:00\tINFO\tProcessing request\n2025-12-01T12:00:00.001000+00:00\tINFO\tRequest completed"
}
Without Request Context
When logging outside a request context, logs are output as plain text:
Processing request
How It Works
- Handler Initialization: Framework-specific wrapper is selected based on
apporframeworkparameter - Request Start: Middleware creates a
RequestLogscontext - Log Accumulation: All log calls within the request are accumulated in
messagefield - Severity Tracking: The highest severity level is tracked
- Trace Extraction: Trace context is extracted from request headers using framework-specific methods
- Request End:
flush()emits all accumulated logs as a single structured entry
This approach provides several benefits:
- Correlate all logs from a single request
- View logs grouped by trace in Cloud Console
- Reduce log volume while maintaining detail
- Optimized header/URL extraction per framework
Development
Setup
# Clone the repository
git clone https://github.com/loplat/gcp-cloud-logging-handler.git
cd cloud-logging-handler
# Install with dev dependencies using uv
uv sync --all-extras
# Run tests
uv run pytest
# Run linting
uv run ruff check .
uv run ruff format .
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Inspired by Google Cloud Logging documentation
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cloud_logging_handler-0.2.3.tar.gz.
File metadata
- Download URL: cloud_logging_handler-0.2.3.tar.gz
- Upload date:
- Size: 14.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a12828bcbec7e0bd70fc2dc7b8429ed3ee57effd3860d35072677c762d2c59e
|
|
| MD5 |
acd18e77e5cb8d0da8c6e49e89b4c80f
|
|
| BLAKE2b-256 |
42641eb54d9d058871019da434092b91c2b557613196ec14c683ab785a62a807
|
File details
Details for the file cloud_logging_handler-0.2.3-py3-none-any.whl.
File metadata
- Download URL: cloud_logging_handler-0.2.3-py3-none-any.whl
- Upload date:
- Size: 9.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1591862bbc6a87dd14766d2fce36bbea79f543ae81440b95e5be179d5da94a0
|
|
| MD5 |
3ed3c1fdb10726106b8793cfe1983e17
|
|
| BLAKE2b-256 |
f7bf5422b086c44bc575ad0ce7edaa15b968b0f98bd5aa3ded6ce983a6fccd4b
|