Skip to main content

Async analytics tracking library with PostgreSQL/TimescaleDB support

Project description

Analytics Tracker

A robust, async analytics tracking library for Python applications with PostgreSQL and TimescaleDB support.

Features

  • Event Tracking: Track user events with rich properties and automatic timestamps
  • Async Batch Processing: Efficient background worker for database persistence
  • Thread-Safe Buffer: Event buffer with configurable size and flush intervals
  • Plugin System: Transform events with custom plugins
  • Connection Pooling: Efficient PostgreSQL/TimescaleDB connection management
  • Graceful Shutdown: Automatic flush on shutdown with error aggregation
  • Retry Logic: Automatic retry for temporary database connection errors
  • Metrics Collection: Track performance and event statistics

Installation

pip install analytics-tracker

Quick Start

import asyncio
from analytics_tracker import EventTracker, DBConfig, BufferConfig

async def main():
    db_config = DBConfig(
        host="localhost",
        port=5432,
        database="analytics",
        user="postgres",
        password="secret",
        min_size=1,
        max_size=10,
    )
    buffer_config = BufferConfig(
        max_size=1000,
        flush_interval=5.0,
        batch_size=100,
    )

    tracker = EventTracker(
        buffer_config=buffer_config,
        table_name="business_events",
    )

    await tracker.initialize(db_config)
    await tracker.run_migrations()
    await tracker.start()

    tracker.track("user123", "page_view", {"page": "/home"})
    tracker.track("user123", "button_click", {"button": "submit"})

    with tracker.user("user456") as user:
        user.track("purchase", {"amount": 99.99, "currency": "USD"})

    await tracker.close()

asyncio.run(main())

Configuration

DBConfig

Database connection configuration with validation:

from analytics_tracker import DBConfig

config = DBConfig(
    host="localhost",       # Database host
    port=5432,              # Port (1-65535)
    database="analytics",   # Database name
    user="postgres",        # Username
    password="secret",      # Password (hidden in repr)
    min_size=1,             # Min pool size (>=1)
    max_size=10,            # Max pool size (>= min_size)
)

config = DBConfig.from_env()

BufferConfig

Event buffer configuration:

from analytics_tracker import BufferConfig

config = BufferConfig(
    max_size=1000,      # Max events in buffer
    flush_interval=5.0, # Seconds between flushes
    batch_size=100,     # Events per batch insert
)

Plugins

Transform events using plugins. Plugins receive an event dict and return a transformed dict:

from analytics_tracker import EventTracker

def add_timestamp_plugin(event: dict) -> dict:
    from datetime import datetime, timezone
    event["processed_at"] = datetime.now(timezone.utc).isoformat()
    return event

def add_utm_params(event: dict) -> dict:
    if "properties" not in event:
        event["properties"] = {}
    event["properties"]["utm_source"] = "newsletter"
    return event

tracker = EventTracker()
tracker.add_plugin(add_timestamp_plugin)
tracker.add_plugin(add_utm_params)

Environment Variables

All configuration can be set via environment variables:

Variable Description Default
DB_HOST Database hostname localhost
DB_PORT Database port 5432
DB_NAME Database name analytics
DB_USER Database user postgres
DB_PASSWORD Database password postgres
DB_MIN_SIZE Min pool size 1
DB_MAX_SIZE Max pool size 10

API Reference

EventTracker

Main class for event tracking:

class EventTracker:
    def __init__(
        self,
        buffer_config: BufferConfig | None = None,
        table_name: str = "business_events",
        db_config: DBConfig | None = None,
    )

    async def initialize(db_config: DBConfig | None = None) -> None
    async def run_migrations() -> None
    async def start() -> None
    async def stop() -> None
    async def close() -> None

    def track(user_id: str, event_type: str, properties: dict | None = None) -> SendResult
    def add_plugin(plugin: Plugin) -> None
    def get_metrics() -> dict

    @contextmanager
    def user(user_id: str) -> UserTracker

UserTracker

Context manager for tracking events for a specific user:

class UserTracker:
    def track(event_type: str, properties: dict | None = None) -> SendResult

SendResult

Return type for track operations:

@dataclass
class SendResult:
    success: bool           # Whether event was queued
    event_id: UUID | None   # Event ID if successful
    error: str | None       # Error message if failed

EventModel

Event data model:

class EventModel:
    event_id: UUID          # Unique event identifier
    user_id: str            # User identifier
    event_type: str         # Type of event
    properties: dict        # Event properties
    timestamp: datetime     # Event timestamp (UTC)

BufferConfig

Configuration for event buffer:

@dataclass
class BufferConfig:
    max_size: int           # Max events in buffer (default: 1000)
    flush_interval: float   # Seconds between flushes (default: 5.0)
    batch_size: int         # Events per batch insert (default: 100)

DBConfig

Database connection configuration with validation:

@dataclass
class DBConfig:
    host: str               # Database host
    port: int               # Port (1-65535)
    database: str           # Database name
    user: str               # Username
    password: str           # Password
    min_size: int           # Min pool size (>=1)
    max_size: int           # Max pool size (>= min_size)

    @property
    def connection_url(self) -> str

MetricsCollector

Track performance metrics:

class MetricsCollector:
    def record_event_queued() -> None
    def record_dropped() -> None
    def record_batch_sent(batch_size: int, duration_ms: float) -> None
    def get_metrics() -> dict

MigrationManager

Handle database migrations:

class MigrationManager:
    table_name: str

    async def initialize(db_config: DBConfig) -> None
    async def run_migrations() -> None
    async def close() -> None

BackgroundWorker

Background worker for batch processing:

class BackgroundWorker:
    def __init__(
        self,
        buffer: EventBuffer,
        dsn: str,
        batch_size: int,
        flush_interval: float,
        metrics: MetricsCollector,
        connection_manager: DBConnectionManager,
        event_saver: EventSaver,
    )

    async def start() -> None
    async def stop() -> None
    async def async_flush() -> None

Metrics

Track performance metrics:

metrics = tracker.get_metrics()
# {
#     "events_queued_total": 500,
#     "events_dropped": 0,
#     "events_sent_total": 400,
#     "batches_sent_total": 4,
#     "avg_flush_time_ms": 45.2,
#     "buffer_size": 100,
#     "migrations_run": True,
# }

Exceptions

Custom exceptions for error handling:

from analytics_tracker import (
    TrackerError,      # Base exception
    ValidationError,   # Configuration validation errors
    BufferError,       # Buffer operation errors
    PluginError,       # Plugin execution errors
)

try:
    tracker.track("user123", "event", {})
except ValidationError as e:
    print(f"Invalid configuration: {e}")
except BufferError as e:
    print(f"Buffer error: {e}")

Aggregate error for graceful shutdown:

from analytics_tracker.tracker import AggregateError

try:
    await tracker.close()
except AggregateError as e:
    print(f"Multiple errors occurred: {e.errors}")

Architecture

User Code
    |
    v
EventTracker.track() --> EventBuffer --> BackgroundWorker --> EventSaver --> PostgreSQL
    |                           |                                          |
    +--> PluginManager ---------+                                          |
    |                                                                          |
    +--> MetricsCollector ----------------------------------------------+

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

analytics_tracker-0.1.0.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

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

analytics_tracker-0.1.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file analytics_tracker-0.1.0.tar.gz.

File metadata

  • Download URL: analytics_tracker-0.1.0.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for analytics_tracker-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d80bf0ff0cb48d0a787cf7f7a61063ab6c0619340cd37a79e838c2cf6908de4a
MD5 1d7668cd5620f1dff02f030de5311f02
BLAKE2b-256 45e50f971c489f9e98e49b7cdd87d71fcc7ba420357cf33b058961ac19a3e98e

See more details on using hashes here.

Provenance

The following attestation bundles were made for analytics_tracker-0.1.0.tar.gz:

Publisher: python-publish.yml on YOxan/analytics-tracker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file analytics_tracker-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for analytics_tracker-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a717824dce17e8204ec9a36610313e800aeaf3af9264bf4727c0fbd86f9af5a5
MD5 d2c0ffe185d85cf2e9a406c70f5ec6e7
BLAKE2b-256 156bde5989f450e041e9efcd8306947134cd7df0f5f135f0a68119eb3c68e1af

See more details on using hashes here.

Provenance

The following attestation bundles were made for analytics_tracker-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on YOxan/analytics-tracker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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