Multi-tenant audit logging and AI-assisted monitoring - Python SDK
Project description
AuditFlow Python SDK
A lightweight Python SDK for sending logs to AuditFlow and streaming them back in real-time.
Features
- 🚀 Easy Integration - Drop-in sink for loguru, handler for stdlib logging
- 📤 Log Ingestion - Send logs from any Python application to AuditFlow
- 📡 Real-time Streaming - Stream logs back via WebSocket with flexible filtering
- ⚡ Async by Default - Non-blocking async transport with dedicated event loop
- 🔁 Automatic Retry - Built-in retry with exponential backoff
- 💾 Disk Fallback - Logs saved to disk when backend is unreachable
- 📦 Batching - Efficient batch sending (100 logs or 5 seconds)
- 🔌 Extensible - Designed to support multiple logging frameworks
Installation
# Basic installation (HTTP client only)
pip install auditflowlog
# With loguru integration
pip install auditflowlog[loguru]
# With WebSocket streaming
pip install auditflowlog[streaming]
# Everything
pip install auditflowlog[all]
Quick Start
1. Using with Loguru
from loguru import logger
from auditflowlog import AuditFlowSink
# Add AuditFlow sink to loguru (async by default for non-blocking log emission)
logger.add(
AuditFlowSink(
api_key="af_live_abc123...",
bucket="payment-service"
)
)
# Use loguru as normal - logs automatically go to AuditFlow
logger.bind(request_id="REQ-12345")
logger.info("Payment captured", order_id="ORD-789", amount=99.99)
logger.error("Payment failed", reason="insufficient_funds")
# Optional: Use sync client instead of async
logger.add(
AuditFlowSink(
api_key="af_live_abc123...",
bucket="payment-service",
use_async=False # Use sync client if event loop is undesirable
)
)
2. Direct Client Usage
from auditflowlog import AuditFlowClient, LogLevel
client = AuditFlowClient(
api_key="af_live_abc123...",
bucket="bucket_id_here" # Bucket ID, not name
)
# Send a single log
client.send_log(
level=LogLevel.INFO,
message="User logged in",
metadata={"user_id": "user_123", "ip": "192.168.1.1"}
)
# Send multiple logs in a batch
from auditflowlog import LogEntry
logs = [
LogEntry(bucket_id="bucket_id_here", level=LogLevel.INFO, message="Request started"),
LogEntry(bucket_id="bucket_id_here", level=LogLevel.INFO, message="Processing data"),
LogEntry(bucket_id="bucket_id_here", level=LogLevel.INFO, message="Request completed"),
]
client.send_logs(logs=logs)
3. Real-time Log Streaming
Note: WebSocket streaming requires a JWT token from user login, not an API key.
import asyncio
from auditflowlog import LogStream, LogLevel
async def main():
# Stream logs with JWT token (obtained from /api/v1/auth/login)
stream = LogStream(
jwt_token="your_jwt_token_here",
bucket_id="bucket_id_here",
level=LogLevel.ERROR, # Only errors and critical
)
# Option 1: Callback-based
def on_log(log):
print(f"[{log['level']}] {log['timestamp']}: {log['message']}")
await stream.connect(callback=on_log)
await stream.run() # Runs forever with auto-reconnect
# Option 2: Async iterator
async with stream:
async for log in stream:
print(f"[{log['level']}] {log['message']}")
# Stop on specific condition
if log['level'] == 'critical':
break
asyncio.run(main())
4. Using with Standard Library Logging
import logging
from auditflowlog.sink import StdlibHandler
logger = logging.getLogger(__name__)
# Add AuditFlow handler
handler = StdlibHandler(
api_key="af_live_abc123...",
bucket="my-service"
)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# Use stdlib logging as normal
logger.info("Application started", extra={"version": "1.0.0"})
logger.error("Database connection failed", extra={"host": "localhost"})
Advanced Usage
Filtering Streamed Logs
from datetime import datetime, timedelta
from auditflowlog import LogStream, LogLevel
# Filter by multiple criteria
stream = LogStream(
jwt_token="your_jwt_token_here",
bucket_id="bucket_id_here",
level=LogLevel.WARNING, # WARNING and above
start_time=datetime.now() - timedelta(hours=1), # Last hour
metadata_filters={
"user_id": "user_123", # Only logs for specific user
"payment_status": "failed"
}
)
async with stream:
async for log in stream:
print(log)
Async Client
import asyncio
from auditflowlog.client import AsyncAuditFlowClient, LogLevel
async def main():
async with AsyncAuditFlowClient(
api_key="af_live_abc123...",
bucket="bucket_id_here"
) as client:
await client.send_log(
level=LogLevel.INFO,
message="Async operation completed"
)
asyncio.run(main())
Context Manager for Cleanup
from auditflowlog import AuditFlowClient
# Automatically closes connection on exit
with AuditFlowClient(api_key="...", bucket="bucket_id_here") as client:
client.send_log(message="Processing batch")
# ... more operations
# Connection closed here
Reliability Features
The SDK is designed for production use with built-in resilience:
Non-Blocking Architecture
- Fire-and-forget: Logs are enqueued immediately and processed in the background
- Async by default: Loguru sink uses AsyncAuditFlowClient with dedicated event loop
- No blocking I/O: Your application never waits for log transmission
Automatic Retry with Backoff
- Network errors trigger automatic retry (3 attempts)
- Exponential backoff: 2s, 4s, 8s (max 10s)
- Authentication (401) and rate limit (429) errors raise immediately
Disk Fallback
- Failed logs are saved to
auditflow_fallback.logwhen backend is unreachable - Automatic retry every 60 seconds when connectivity is restored
- Successfully sent logs are removed from fallback file
Efficient Batching
- Logs are batched automatically (100 items or 5 seconds)
- Reduces HTTP requests and network overhead
- Configurable queue size (default: 10,000 items)
Configuration Options
from auditflowlog import AuditFlowSink
sink = AuditFlowSink(
api_key="af_live_abc123...",
bucket="my-service",
timeout=30.0, # Request timeout
enable_queue=True, # Enable fire-and-forget queue
queue_maxsize=10000, # Max queue size
use_async=True, # Use async client (default)
fallback_file="auditflow_fallback.log", # Disk fallback path
)
Configuration
Environment Variables
You can configure common settings via environment variables:
export AUDITFLOW_API_KEY="af_live_abc123..."
export AUDITFLOW_BUCKET="default-service"
Then use them in your code:
import os
from auditflowlog import AuditFlowClient
client = AuditFlowClient(
api_key=os.getenv("AUDITFLOW_API_KEY"),
bucket=os.getenv("AUDITFLOW_BUCKET"),
)
API Reference
AuditFlowClient
Main synchronous client for log ingestion.
Constructor:
api_key(str): Your AuditFlow API key (for log ingestion)bucket(str, optional): Default bucket IDtimeout(float): Request timeout in seconds (default: 30.0)
Methods:
send_log(message, level, bucket=None, source=None, timestamp=None, metadata=None)- Send a single logsend_logs(logs)- Send multiple logs in batch (each LogEntry must have bucket_id)close()- Close HTTP connection
AuditFlowSink
Loguru sink for automatic log forwarding.
Constructor:
api_key(str): Your AuditFlow API key (for log ingestion)bucket(str): Bucket ID for logstimeout(float): Request timeout (default: 30.0)include_extra(bool): Include loguru extra context (default: True)enable_queue(bool): Enable fire-and-forget queue (default: True, only applies to sync client)queue_maxsize(int): Maximum size of in-memory queue (default: 10000, only applies to sync client)use_async(bool): Use AsyncAuditFlowClient with dedicated event loop (default: True)auto_disable_on_auth_failure(bool): Auto-disable sink on authentication failures (default: True)
Methods:
enable()- Enable the sink to start sending logs againdisable()- Disable the sink to stop sending logsis_enabled()- Check if the sink is currently enabledclose()- Close the client and cleanup resources
LogStream
Async WebSocket client for real-time log streaming.
Note: Requires JWT token from user authentication, not API key.
Constructor:
jwt_token(str): JWT token from user login (for WebSocket streaming)bucket_id(str): Bucket ID to stream logs fromlevel(LogLevel, optional): Filter by minimum log levelstart_time(datetime, optional): Filter by start timeend_time(datetime, optional): Filter by end timemetadata_filters(dict, optional): Filter by metadata fieldsreconnect(bool): Auto-reconnect on disconnect (default: True)reconnect_interval(float): Seconds between reconnects (default: 5.0)
Methods:
connect(callback=None)- Connect to WebSocketdisconnect()- Disconnect from WebSocketrun()- Run stream with callback (blocking)- Supports async context manager and async iterator
LogLevel
Enum for log severity levels:
LogLevel.DEBUGLogLevel.INFOLogLevel.WARNINGLogLevel.ERRORLogLevel.CRITICAL
Error Handling
The SDK provides specific exception types:
from auditflowlog.exceptions import (
AuditFlowError, # Base exception
AuthenticationError, # Invalid API key
RateLimitError, # Rate limit exceeded
ConnectionError, # WebSocket connection failed
StreamError, # Streaming error
)
try:
client.send_log(message="test")
except AuthenticationError:
print("Invalid API key")
except RateLimitError:
print("Rate limit exceeded, retry later")
except AuditFlowError as e:
print(f"AuditFlow error: {e}")
Circuit Breaker Pattern
The SDK includes a circuit breaker to prevent wasting resources on invalid API keys. When authentication failures reach a threshold (default: 3), the circuit opens and stops sending requests for a cooldown period (default: 60 seconds).
Client Circuit Breaker:
from auditflowlog import AuditFlowClient
# Configure circuit breaker thresholds
client = AuditFlowClient(
api_key="your_api_key",
bucket_id="my-bucket",
circuit_breaker_threshold=3, # Failures before opening
circuit_breaker_timeout=60.0 # Seconds before retry
)
# Check circuit status
if client.is_circuit_open():
print("Circuit is open, requests paused")
# Manually reset circuit breaker
client.reset_circuit_breaker()
Sink Auto-Disable:
The AuditFlowSink can automatically disable itself when authentication failures occur:
from loguru import logger
from auditflowlog import AuditFlowSink
sink = AuditFlowSink(
api_key="your_api_key",
bucket_id="my-bucket",
auto_disable_on_auth_failure=True # Default: True
)
logger.add(sink)
# If API key is invalid, sink will auto-disable
# Re-enable after fixing API key:
sink.enable()
# Or manually disable:
sink.disable()
# Check status
if sink.is_enabled():
print("Sink is active")
Examples
See the examples/ directory for complete working examples:
examples/loguru_example.py- Loguru integrationexamples/stdlib_example.py- Standard library loggingexamples/streaming_example.py- Real-time log streamingexamples/async_example.py- Async client usage
Development
Setup
git clone https://github.com/jamesadewara/auditflowlog-py.git
cd auditflowlog-py
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -e ".[all]"
Building
pip install build
python -m build
This creates:
dist/auditflowlog-0.1.0-py3-none-any.whldist/auditflowlog-0.1.0.tar.gz
Publishing to PyPI
pip install twine
python -m twine upload dist/*
License
APACHE License - see LICENSE file for details.
Contributing
Contributions welcome! Please open an issue or submit a pull request.
Links
Project details
Release history Release notifications | RSS feed
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 auditflowlog-0.0.3.tar.gz.
File metadata
- Download URL: auditflowlog-0.0.3.tar.gz
- Upload date:
- Size: 38.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a902fb843f1a115f17ba4685ef38bdd8dc9ecc6b49eb34b7d85d8212d2495fe
|
|
| MD5 |
b1113970ed68abb96f40a334c3d495dc
|
|
| BLAKE2b-256 |
18f796835110ad418d25671973e659873f20ea5908f8dd40a3f3aececb339b2c
|
File details
Details for the file auditflowlog-0.0.3-py3-none-any.whl.
File metadata
- Download URL: auditflowlog-0.0.3-py3-none-any.whl
- Upload date:
- Size: 22.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b644f94fcc2ccc0326d0d26d045392ae8d98fe17232c66d7e34baad6e649ab6
|
|
| MD5 |
fd4ab47d3f893bf6a1994a7ba996854e
|
|
| BLAKE2b-256 |
25b760b9a4ab672a7758973a19cff4986f6cc3f732489aedfa802ca36499271e
|