Skip to main content

Async-native, persistent, inter-process performance logger.

Project description

Ferret

Ferret is an async-native, persistent, and thread-safe performance logger for Python. Built on top of BeaverDB, it provides high-performance structured logging without the overhead of heavy APM solutions.

Ferret can profile your code in two ways:

  1. Automagically: Via the CLI, injecting profiling logic at runtime without changing a single line of your code.
  2. Explicitly: As a library, giving you granular control over semantic tags, dynamic naming, and span context.

Installation

pip install ferret-io

🚀 Mode 1: Zero-Code Profiling (Automagic)

The easiest way to use Ferret is to let it run your script. It uses AST transformation to inject decorators into your functions on the fly.

1. Write your code (unaware of Ferret)

Take my_script.py. Notice it has no imports from ferret.

# my_script.py
import asyncio
import time

def heavy_computation(n: int):
    # Simulates CPU-bound synchronous work
    count = 0
    for i in range(n):
        count += i * i
    return count

async def fetch_database_item(item_id: int):
    # Simulates IO-bound asynchronous work
    await asyncio.sleep(0.02)
    return f"item_{item_id}"

async def main():
    print("Starting Workload...")
    # Mixes async IO and sync CPU work
    await fetch_database_item(101)
    heavy_computation(50_000)
    print("Done!")

if __name__ == "__main__":
    asyncio.run(main())

2. Run it with Ferret

ferret run my_script.py --db traces.db

Ferret will execute the script, instrumenting every function call automatically, and save the performance data to traces.db.

🛠️ Mode 2: Power User Profiling (Explicit)

For deep observability, import the Profiler. This allows for dynamic span naming, conditional tagging, and manual context control.

Semantic Instrumentation

Based on examples/simple.py:

import asyncio
import random
from ferret import Profiler

# Initialize with a specific Run ID for easy lookup later
profiler = Profiler(db_path="ferret_explicit.db", run_id="semantic_demo_v1")

# 1. Dynamic Naming: Name spans based on arguments (e.g., user_id)
@profiler.measure(lambda args: f"process_user:{args[0]}")
async def process_user_data(user_id: int):

    # 2. Context Manager with Tags: Add metadata immediately
    with profiler.measure("db_fetch") as span:
        # 3. Semantic Annotation: Add business logic context
        span.annotate(table="users", shard_id=user_id % 5)

        delay = random.uniform(0.01, 0.05)
        await asyncio.sleep(delay)

        # 4. Conditional Tagging: Flag spans based on runtime performance
        if delay > 0.04:
            span.annotate(performance_flag="slow_query", alert=True)
            span.model.status = "warning"

    return True

async def main():
    await process_user_data(42)
    profiler.close()

if __name__ == "__main__":
    asyncio.run(main())

📊 Analysis & Reporting

Once you have generated a database (e.g., ferret.db), you can analyze it directly from the CLI.

Table Summary

View a statistical aggregation of function performance (Min, Max, Avg, Error Rate):

ferret analyze ferret.db

Hierarchical Tree View

View the actual execution call stack, identifying exactly where time was spent in nested calls.

ferret analyze ferret.db --tree

Output Example:

Trace Root
├── process_batch - 120.50ms
│   ├── fetch_database_item - 20.10ms (async)
│   └── heavy_computation - 95.00ms (cpu)
└── Finalizing...

Features

  • Async & Sync Support: Handles asyncio context propagation and standard synchronous execution seamlessly.
  • Low Overhead: Writes are buffered and flushed to a local, append-only BeaverDB instance.
  • Process Safe: Can be used in multi-process environments safely.
  • Semantic Tagging: Attach arbitrary dictionaries (tags) to spans to query specific business cases later.
  • Dynamic Naming: Name your trace spans based on the runtime arguments of the function.
  • Rich CLI: Built with Typer and Rich for beautiful terminal output.

License

Distributed under the MIT License. See LICENSE for more information.

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

ferret_io-0.3.1.tar.gz (41.4 kB view details)

Uploaded Source

Built Distribution

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

ferret_io-0.3.1-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

File details

Details for the file ferret_io-0.3.1.tar.gz.

File metadata

  • Download URL: ferret_io-0.3.1.tar.gz
  • Upload date:
  • Size: 41.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ferret_io-0.3.1.tar.gz
Algorithm Hash digest
SHA256 844388ab0d3ea3d62032fae9272e9169de61e75990889b6157045e33cbf6212c
MD5 92387b79bccaccc7102224bd78cbecae
BLAKE2b-256 65d3753b2d68fe26b92e00070df8c36eccb71d3e2a0970c10a5b81796e90d1c4

See more details on using hashes here.

File details

Details for the file ferret_io-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: ferret_io-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 12.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ferret_io-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 396acfee8eb39ec22501285e890a2a08e2fb0151c111ad6e30fd99aeac8814d7
MD5 0cd4b27b10eeb357f7160ad1fee69edb
BLAKE2b-256 13e3ff9248ca97145b9dd9c007db87469c70ad65f0ce8cb0e0ea069ed4583d21

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