Skip to main content

Error tracking and monitoring SDK for Python applications

Project description

Statly Observe SDK for Python

PyPI version Python versions License: MIT

Error tracking and monitoring for Python applications. Capture exceptions, track releases, and debug issues faster.

📚 Full Documentation | 🚀 Get Started | 💬 Support

This SDK requires a Statly account. Sign up free at statly.live to get your DSN and start tracking errors in minutes.

Features

  • Automatic exception capturing with full stack traces
  • Structured Logging: Production-grade logging with automatic scrubbing
  • Distributed Tracing: Track function execution and latency across your app
  • Performance Monitoring: Measure execution time and success rates
  • Breadcrumbs for debugging context
  • User context tracking
  • Release tracking
  • Framework integrations (Flask, Django, FastAPI)
  • Async support
  • Minimal overhead

Installation

pip install statly-observe

With framework integrations:

pip install statly-observe[flask]     # Flask support
pip install statly-observe[django]    # Django support
pip install statly-observe[fastapi]   # FastAPI/Starlette support
pip install statly-observe[all]       # All integrations

Getting Your DSN

  1. Go to statly.live/dashboard/observe/setup
  2. Create an API key for Observe
  3. Copy your DSN (format: https://<api-key-prefix>@statly.live/<org-slug>)
    • Notice: The DSN uses a public 16-character prefix (e.g., sk_live_a1b2c3d4) and is safe for client-side use.
  4. Add to your .env file: STATLY_DSN=https://...

Quick Start

The SDK automatically loads DSN from environment variables, so you can simply:

from statly_observe import Statly

# Auto-loads STATLY_DSN from environment
Statly.init()

Or pass it explicitly:

from statly_observe import Statly

# Initialize the SDK
Statly.init(
    dsn="https://sk_live_a1b2c3d4@statly.live/your-org",
    environment="production",
    release="1.0.0",
)

# Errors are captured automatically via sys.excepthook

# Manual capture
try:
    risky_operation()
except Exception as e:
    Statly.capture_exception(e)

# Capture a message
Statly.capture_message("User completed checkout", level="info")

# Set user context
Statly.set_user(
    id="user-123",
    email="user@example.com",
)

# Add breadcrumb for debugging
Statly.add_breadcrumb(
    message="User logged in",
    category="auth",
    level="info",
)

# Always close before exit
Statly.close()

Tracing & Performance

Statly Observe supports distributed tracing to help you visualize function execution and measure performance.

Automatic Tracing (Decorators)

Use the @Statly.trace() decorator to automatically time functions and capture their results:

from statly_observe import Statly

@Statly.trace("process_order", tags={"priority": "high"})
async def handle_order(order_id):
    # This function is now automatically timed
    # Any exceptions will be linked to this span
    result = await database.save(order_id)
    return result

Manual Spans

For more granular control, you can start and finish spans manually:

span = Statly.start_span("heavy_computation")
try:
    # Perform operation
    do_work()
    span.set_tag("items_processed", "100")
finally:
    span.finish() # Sends data to Statly

Structured Logging

The Logger class provides production-grade structured logging with automatic secret scrubbing, session management, and batching.

Quick Start

from statly_observe import Logger

logger = Logger(
    dsn='https://sk_live_xxx@statly.live/your-org',
    environment='production',
    logger_name='api-server',
)

# Log at different levels
logger.trace('Entering function', args=[1, 2, 3])
logger.debug('Processing request', request_id='req_123')
logger.info('User logged in', user_id='user_123')
logger.warn('Rate limit approaching', current=95, limit=100)
logger.error('Payment failed', order_id='ord_456', error='Card declined')
logger.fatal('Database connection lost', host='db.example.com')
logger.audit('User role changed', user_id='user_123', new_role='admin')

# Always close before exit
logger.close()

Context Manager

Use the logger as a context manager for automatic cleanup:

with Logger(dsn='...') as logger:
    logger.info('Processing...')
# Automatically flushed and closed

Child Loggers

Create child loggers with inherited context:

request_logger = logger.child(
    context={'request_id': 'req_123'},
    logger_name='request-handler',
)

request_logger.info('Processing request')  # Includes request_id automatically

User Context

Associate logs with users:

logger.set_user(
    id='user_123',
    email='jane@example.com',
    name='Jane Doe',
)

Secret Scrubbing

The logger automatically scrubs sensitive data (API keys, passwords, credit cards, etc.). Add custom patterns:

logger = Logger(
    dsn='...',
    scrub_patterns=[
        r'my-custom-secret-[a-z0-9]+',
        r'internal-token-\d+',
    ],
)

Sample Rates

Control log volume with per-level sampling:

logger = Logger(
    dsn='...',
    sample_rates={
        'trace': 0.01,   # 1% of trace logs
        'debug': 0.1,    # 10% of debug logs
        'info': 0.5,     # 50% of info logs
        'warn': 1.0,     # 100% of warnings
        'error': 1.0,    # 100% of errors
        'fatal': 1.0,    # 100% of fatal
    },
)

Logger Options

Option Type Default Description
dsn str required Your project's Data Source Name
environment str None Environment name
release str None Release/version identifier
logger_name str None Logger name for filtering
session_id str auto-generated Session ID for grouping logs
user dict None User context
default_context dict {} Default context for all logs
min_level str 'trace' Minimum level to log
sample_rates dict all 1.0 Per-level sample rates
scrub_patterns list[str] [] Additional regex patterns to scrub
batch_size int 50 Batch size before flush
flush_interval float 5.0 Flush interval in seconds
max_queue_size int 1000 Max queue size

Framework Integrations

Flask

from flask import Flask
from statly_observe import Statly
from statly_observe.integrations.flask import init_flask

app = Flask(__name__)

# Initialize Statly
Statly.init(
    dsn="https://sk_live_a1b2c3d4@statly.live/your-org",
    environment="production",
)

# Attach to Flask app
init_flask(app)

@app.route("/")
def index():
    return "Hello World"

@app.route("/error")
def error():
    raise ValueError("Test error")  # Automatically captured

Django

settings.py:

INSTALLED_APPS = [
    # ...
    'statly_observe.integrations.django',
]

MIDDLEWARE = [
    'statly_observe.integrations.django.StatlyMiddleware',
    # ... other middleware (Statly should be first)
]

# Statly configuration
STATLY_DSN = "https://sk_live_xxx@statly.live/your-org"
STATLY_ENVIRONMENT = "production"
STATLY_RELEASE = "1.0.0"

wsgi.py or manage.py:

from statly_observe import Statly
from django.conf import settings

Statly.init(
    dsn=settings.STATLY_DSN,
    environment=settings.STATLY_ENVIRONMENT,
    release=settings.STATLY_RELEASE,
)

FastAPI

from fastapi import FastAPI
from statly_observe import Statly
from statly_observe.integrations.fastapi import init_fastapi

app = FastAPI()

# Initialize Statly
Statly.init(
    dsn="https://sk_live_a1b2c3d4@statly.live/your-org",
    environment="production",
)

# Attach to FastAPI app
init_fastapi(app)

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/error")
async def error():
    raise ValueError("Test error")  # Automatically captured

Generic WSGI/ASGI

from statly_observe import Statly
from statly_observe.integrations.wsgi import StatlyWSGIMiddleware
from statly_observe.integrations.asgi import StatlyASGIMiddleware

Statly.init(dsn="https://sk_live_xxx@statly.live/your-org")

# WSGI
app = StatlyWSGIMiddleware(your_wsgi_app)

# ASGI
app = StatlyASGIMiddleware(your_asgi_app)

Environment Variables

The SDK automatically loads configuration from environment variables:

Variable Description
STATLY_DSN Your project's DSN (primary)
STATLY_OBSERVE_DSN Alternative DSN variable
STATLY_ENVIRONMENT Environment name
PYTHON_ENV or ENV Fallback for environment

Configuration Options

Option Type Default Description
dsn str os.environ["STATLY_DSN"] Your project's Data Source Name
environment str None Environment name (production, staging, development)
release str None Release/version identifier for tracking
debug bool False Enable debug logging to stderr
sample_rate float 1.0 Sample rate for events (0.0 to 1.0)
max_breadcrumbs int 100 Maximum breadcrumbs to store
before_send callable None Callback to modify/filter events before sending

before_send Example

def before_send(event):
    # Filter out specific errors
    if "KeyboardInterrupt" in event.get("message", ""):
        return None  # Drop the event

    # Scrub sensitive data
    if "extra" in event and "password" in event["extra"]:
        del event["extra"]["password"]

    return event

Statly.init(
    dsn="...",
    before_send=before_send,
)

API Reference

Statly.capture_exception(exception, **context)

Capture an exception with optional additional context:

try:
    process_payment(order)
except PaymentError as e:
    Statly.capture_exception(
        e,
        extra={
            "order_id": order.id,
            "amount": order.total,
        },
        tags={
            "payment_provider": "stripe",
        },
    )

Statly.capture_message(message, level="info")

Capture a message event:

Statly.capture_message("User signed up", level="info")
Statly.capture_message("Payment failed after 3 retries", level="warning")
Statly.capture_message("Database connection lost", level="error")

Levels: "debug" | "info" | "warning" | "error" | "fatal"

Statly.set_user(**kwargs)

Set user context for all subsequent events:

Statly.set_user(
    id="user-123",
    email="user@example.com",
    username="johndoe",
    # Custom fields
    subscription="premium",
)

# Clear user on logout
Statly.set_user(None)

Statly.set_tag(key, value) / Statly.set_tags(tags)

Set tags for filtering and searching:

Statly.set_tag("version", "1.0.0")

Statly.set_tags({
    "environment": "production",
    "server": "web-1",
    "region": "us-east-1",
})

Statly.add_breadcrumb(**kwargs)

Add a breadcrumb for debugging context:

Statly.add_breadcrumb(
    message="User clicked checkout button",
    category="ui.click",
    level="info",
    data={
        "button_id": "checkout-btn",
        "cart_items": 3,
    },
)

Statly.flush() / Statly.close()

# Flush pending events (keeps SDK running)
Statly.flush()

# Flush and close (use before process exit)
Statly.close()

Context Manager

Use context manager for automatic cleanup:

from statly_observe import Statly

with Statly.init(dsn="...") as client:
    # Your code here
    pass
# Automatically flushed and closed

Async Support

The SDK automatically detects async contexts:

import asyncio
from statly_observe import Statly

async def main():
    Statly.init(dsn="...")

    try:
        await risky_async_operation()
    except Exception as e:
        Statly.capture_exception(e)

    await Statly.flush_async()

asyncio.run(main())

Logging Integration

Capture Python logging as breadcrumbs or events:

import logging
from statly_observe import Statly
from statly_observe.integrations.logging import StatlyHandler

Statly.init(dsn="...")

# Add handler to capture logs as breadcrumbs
handler = StatlyHandler(level=logging.INFO)
logging.getLogger().addHandler(handler)

# Now logs are captured
logging.info("User logged in")  # Becomes a breadcrumb
logging.error("Database error")  # Captured as error event

Requirements

  • Python 3.8+
  • Works with sync and async code

Resources

Why Statly?

Statly is more than error tracking. Get:

  • Status Pages - Beautiful public status pages for your users
  • Uptime Monitoring - Multi-region HTTP/DNS checks every minute
  • Error Tracking - SDKs for JavaScript, Python, and Go
  • Incident Management - Track and communicate outages

All on Cloudflare's global edge network. Start free →

License

MIT

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

statly_observe-0.2.0.tar.gz (36.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

statly_observe-0.2.0-py3-none-any.whl (50.9 kB view details)

Uploaded Python 3

File details

Details for the file statly_observe-0.2.0.tar.gz.

File metadata

  • Download URL: statly_observe-0.2.0.tar.gz
  • Upload date:
  • Size: 36.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for statly_observe-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ff2cfe106e9205416a0618b68b048d5f0c3ac1b6ec751139ae329cbe2f1e512a
MD5 c8831bc9534a0e8d245e95b8f98c23f6
BLAKE2b-256 6ba969607ec63ee3afa9ea3ce9a6cbd6af174e1386d30e89b23d71b18d00fbeb

See more details on using hashes here.

File details

Details for the file statly_observe-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: statly_observe-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 50.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for statly_observe-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 07ade433bb15ebe39f634ce767bd51c038eea61cc4053860f92e4692652c3401
MD5 bf7fe69922d8ec2da7749fa1847208ba
BLAKE2b-256 1f9d9af1d57b5305bf01b08a044ab6ea137b1fa61db679dcc28648228587d11f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page