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.0.tar.gz (41.5 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.0-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ferret_io-0.3.0.tar.gz
  • Upload date:
  • Size: 41.5 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.0.tar.gz
Algorithm Hash digest
SHA256 c966f90670967c6295ef13501f41e82f288ac93d37ef2802623dd0c5bcadf273
MD5 66b48940d840ca2bb93fb7692c135c3e
BLAKE2b-256 2d01198c40dfd17350a8469c23299e872a704e6740035d8b49ded8e982a9b78c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: ferret_io-0.3.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 93e18afa5edacfb846dc2041c8ae64ad5e923744a76a07c7e04e1df0a5f08a99
MD5 accdeacbec11b1ca161f9c152377a5e0
BLAKE2b-256 b008e610dc9129b4c797bc187063497e7ecb94cdd3f27b1bc10db0eed1cd3180

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