dock-ray
Python client for DockRay. Reports exceptions, messages and HTTP transactions from asyncio applications.
Installation
pip install dock-ray
Usage
Every reporting method is a coroutine, so calls are awaited:
from dock_ray import DockRayClient
client = DockRayClient(
token="project-token",
private_key="project-private-key",
environment="production",
)
try:
settle(order)
except Exception as error:
await client.capture_exception(error)
await client.capture_message("Nightly import finished late", level="warning")
Constructed without credentials the client is inert: client.enabled is
False and every call returns False without touching the network. Wire it up
unconditionally and leave the credentials out of development.
To report without waiting for the request, hand the coroutine to
send_later(). It keeps a reference to the task until it finishes — a task
held only by the event loop can be collected mid-flight, which drops the event
silently — and logs failures instead of raising them somewhere unrelated:
client.send_later(client.capture_exception(error))
Call await client.close() on shutdown; it flushes pending sends before
closing the connection pool.
When events are sent
Never during the part of the request the visitor is waiting on. send_later()
schedules the send without awaiting it, and the middleware reports a
transaction only after the application has finished writing the response. A
slow or unreachable panel costs the visitor nothing.
ASGI and FastAPI
The middleware opens one transaction per request and reports unhandled exceptions. It is plain ASGI, so it works under FastAPI, Starlette and anything else that speaks the protocol, and it does not buffer streaming responses.
from fastapi import FastAPI
from dock_ray import DockRayClient, DockRayFastAPIMiddleware
client = DockRayClient(token="...", private_key="...")
app = FastAPI()
app.add_middleware(
DockRayFastAPIMiddleware,
client=client,
exclude_paths=["/health", "/metrics"],
)
@app.on_event("shutdown")
async def shutdown() -> None:
await client.close()
Each request is reported with its method, path, duration and response status.
HTTPException is handled by FastAPI itself and never reaches the middleware,
so expected 404 and 422 responses do not turn into errors — they are
recorded as transactions with their status.
The client IP is read from X-Forwarded-For or X-Real-IP before falling back
to the socket address, and Authorization, Cookie and X-Api-Key are
stripped from the reported headers.
Package inventory and vulnerability alerts
Version 1.5 adds opt-in reporting of installed production Python packages. Upgrade the DockRay API with Python inventory support before enabling it. The inventory uses the same project token, private key and environment as errors; no inventory site URL is configured or sent.
import os
from dock_ray import DockRayClient
client = DockRayClient(
token=os.environ["RAY_TOKEN"],
private_key=os.environ["RAY_PRIVATE_KEY"],
environment="production",
inventory_enabled=True,
inventory_exclude_packages=["company-*"],
# Optional: directory with the deployed production manifest.
# inventory_project_directory="/srv/my-app",
# Optional: private persistent state, outside public assets.
# inventory_state_directory="/var/lib/my-app/dockray-inventory",
# Optional: override manifest selection.
# inventory_requirements_file="requirements/production.txt",
# Optional: production feature groups from project.optional-dependencies.
# inventory_optional_groups=["postgres"],
)
Or set RAY_INVENTORY=true and RAY_ENVIRONMENT=production alongside the
existing credentials. Explicit inventory_enabled=False overrides the flag.
The client also reads RAY_INVENTORY_PROJECT_DIR, RAY_INVENTORY_STATE_DIR,
RAY_INVENTORY_REQUIREMENTS_FILE, comma-separated
RAY_INVENTORY_EXCLUDE_PACKAGES and RAY_INVENTORY_OPTIONAL_GROUPS when the
corresponding option is unset. Environment defaults to RAY_ENVIRONMENT or
production. prod/production map to prod; dev/development/staging/preview/test/local
map to dev. Unknown values fail without sending. Development requires a
configured development environment in the DockRay project.
Scheduling
When created in a running asyncio loop, the client starts inventory once in
the background and then checks hourly. When created before the loop starts,
call client.start_inventory() in application startup. The ASGI middleware
also calls it once during lifespan startup; it never collects inside HTTP
request handling. A FastAPI lifespan is a clear place to start and stop it:
from contextlib import asynccontextmanager
from fastapi import FastAPI
from dock_ray import DockRayFastAPIMiddleware
@asynccontextmanager
async def lifespan(app):
client.start_inventory()
try:
yield
finally:
await client.close()
app = FastAPI(lifespan=lifespan)
app.add_middleware(DockRayFastAPIMiddleware, client=client)
Changed inventory is sent at the next check; unchanged inventory is refreshed
approximately every 12 hours. await client.report_inventory(force=True)
requests a deployment/manual report. await client.close() stops the schedule,
waits for pending inventory and event sends, and closes transport resources.
Manual force never bypasses retry backoff or backend rate limits.
For short-lived processes, serverless or a separate scheduler, keep the client's automatic inventory disabled and run the installed command hourly using the same Python interpreter/virtual environment as the application:
RAY_TOKEN=project-token
RAY_PRIVATE_KEY=project-private-key
RAY_INVENTORY=true
RAY_ENVIRONMENT=production
RAY_INVENTORY_PROJECT_DIR=/srv/my-app
RAY_INVENTORY_STATE_DIR=/var/lib/my-app/dockray-inventory
dockray-inventory --force # first/deployment report
dockray-inventory # schedule hourly in cron
# Equivalent: python -m dock_ray.inventory_cli
The command does not read .env automatically and never accepts credentials in
command arguments. Provide them securely through the process environment.
Run one schedule per application or share private state on the same machine;
separate machines need scheduler coordination. A standalone InventoryReporter
supports await reporter.report(), reporter.start() and await reporter.stop().
Collection boundaries and privacy
The collector reads bounded METADATA files from installed .dist-info
distributions in this interpreter's site-packages. It does not import package
code, execute setup.py or pip, or read application source, .env, user data,
repository URLs or direct_url.json contents. Only canonical package names and
PEP 440 versions, the normalized environment and Python/SDK versions are sent.
DockRay matches these reports against its existing PyPI advisory sources,
displays findings in the Software tab and uses existing project notifications.
Production roots come from static [project].dependencies in pyproject.toml,
plus explicitly selected optional groups. Otherwise use requirements.txt, or
set inventory_requirements_file explicitly. Dependencies are followed from
installed Requires-Dist metadata with version checks, markers and extras.
Development groups, unrelated installed packages and the application itself
are excluded. Excluded/private/direct-URL packages are omitted from the report,
but their public dependencies are still followed. Use name/glob exclusions for
private packages installed from an index: their names alone do not reveal
whether a distribution is public. An isolated production virtual environment
is recommended. Requirements containing development dependencies will report
them too; choose an actual production manifest.
Requirements files support plain PEP 508 requirements, relative -r includes
inside the project, continuation lines and SHA-256 hashes. Index/trusted-host
options are ignored and never used for network access. Editable requirements,
constraints, dynamic setup.py/Poetry dependencies, legacy egg metadata and
standalone/bundled environments without installed metadata are unsupported.
Select a static production requirements file for these deployments. Missing,
ambiguous, mismatched or malformed metadata fails without sending a partial or
empty snapshot. Explicitly declaring no production dependencies can send an
empty snapshot. Unsupported collected data never silently clears existing findings.
Delivery requires verified HTTPS, uses a Bearer header and refuses redirects. It has an eight-second total HTTP deadline, payload/response bounds, generic failure logs and Retry-After/backoff handling. Inventory currently supports Linux and macOS/POSIX; Windows returns invalid_configuration without sending. Cross-process OS file locks are released on exit/crash. State directories must be private (0700); files are created as 0600 and contain only hashes/timings. Use persistent private state for containers; do not put it under public assets.
Manual transactions
Useful for background jobs:
import time, uuid
from dock_ray import Span
started = time.time()
await run_daily_cleanup()
ended = time.time()
span = Span(
span_id=uuid.uuid4().hex[:16],
trace_id=uuid.uuid4().hex,
start_timestamp=started,
end_timestamp=ended,
status="200",
description="Daily cleanup",
op="worker.task",
data={"url": "job:daily_cleanup", "method": "CLI"},
)
await client.capture_transaction(name="job:daily_cleanup", spans=[span])
The root span — the one without a parent_span_id — carries the URL, the
method and the status the panel indexes the transaction by.
Failure behaviour
Nothing in this client raises into the application it monitors. Network errors,
rejected events and malformed responses are logged to the dock_ray logger
and reported as False. A 200 response carrying {"success": false} means
the account reached its monthly error quota.
Tests
pip install -e '.[dev]'
pytest tests
Contributing
Issues and pull requests: https://github.com/dockcodes/dock-ray/issues
Metadata
Release files for dock-ray 1.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| dock_ray-1.5.0.tar.gz | 25.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dock_ray-1.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.1 kB
Release files / dock_ray-1.5.0.tar.gz
| Download URL | dock_ray-1.5.0.tar.gz |
|---|---|
| Size | 25.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1ec382a1192779f5cd0c562778db8e46321b002a3f5b6ed31a90b3307ceacd79
|
|
BLAKE2b-256 checksum How to use checksums |
720bcac8523a892abd6788a25884e0a4870446bc53f0ea4b4939c60024c96064
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|
Release files / dock_ray-1.5.0-py3-none-any.whl
| Download URL | dock_ray-1.5.0-py3-none-any.whl |
|---|---|
| Size | 21.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
70e28ac46a7d083a0e67cfc5788ead3c1de9f381a11f6470fa3509e27c35d0b3
|
|
BLAKE2b-256 checksum How to use checksums |
10a470540bec830e5070a0dae44e826b8128d8949891c2f972f6632bfbb6671f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|
Security for dock-ray 1.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.