urllib) so it installs with zero additional dependencies. The async client adds httpx for non-blocking HTTP and is installed via an optional extra. Both clients are thread-safe, buffer events automatically, and flush on process exit so you never lose an audit record.
Installation
- Sync (zero dependencies)
- Async (httpx)
atexit. Requires Python 3.9+.Initialization
- Sync
- Async
Create an Instantiate the client once at module level (or in your application factory) and import it wherever you need it.
AuditRails instance with a Config object. The only required field is api_key. The client starts its background flush thread immediately on construction.Configuration options
Logging events
Buffered logging (recommended)
audit.log() adds an AuditEvent to an in-memory buffer and returns immediately. The buffer is flushed in the background on the flush_interval schedule. This method never raises an exception.
Direct (immediate) logging
audit.log_direct() sends the event immediately and returns the API response. It raises AuditRailsError on failure.
- Sync
- Async
Direct batch logging
Send multiple events in a single HTTP request, bypassing the buffer. Raises on error.Manual flush
Framework integration
Django
AddAuditMiddleware to your MIDDLEWARE list in settings.py. The middleware logs every request after the response has been generated, capturing the final HTTP status code.
FastAPI
Use a lifespan context manager to start and close the async client alongside your application.Error handling
log() never raises. For log_direct() and log_batch_direct(), catch AuditRailsError to inspect the failure details. AuditRailsTimeoutError is a subclass specifically for request timeouts.
Graceful shutdown
Sync: The background daemon thread is registered with Python’satexit module. When your process exits normally, it automatically flushes any buffered events. You can also flush and shut down manually:
atexit hook is not available for async clients. You must call await audit.close() explicitly — for example, in your framework’s shutdown lifecycle hook (as shown in the FastAPI lifespan example above).
For async applications, always wire
await audit.close() into your framework’s shutdown hook. If the event loop is torn down before close() is called, buffered events that have not yet been flushed will be lost.