Async Python client for Zen Logs ingestion endpoints
Project description
zen-logs-client
Python (>=3.10) async client for sending logs to Zen Logs ingestion endpoints.
Installation
pip install zen-logs-client
Or for development:
pip install -e ".[dev]"
Integration test (optional)
Tests are skipped unless ZEN_LOGS_URL is set.
# In one terminal:
# start Zen Logs (e.g. go run ./cmd/logservice or docker-compose up)
# In another terminal:
ZEN_LOGS_URL=http://localhost:7777 pytest
Usage
Create a client
import asyncio
from zen_logs_client import ZenLogsClient
client = ZenLogsClient({
"base_url": "http://localhost:7777",
"token": os.environ.get("LOG_AUTH_TOKEN"), # optional
"default_service_name": "billing-service",
})
Standard logging conventions (internal)
Use these conventions across all services to make Zen Logs queries and stats consistent.
Required/expected metadata (recommendation)
Add these keys to metadata wherever available:
env:dev|staging|prodservice_version: semantic version or build numberbuild_sha: git SHAregion: e.g.us-east-1request_id: stable per-request identifiertrace_id: distributed tracing trace id (if you have one)span_id: distributed tracing span id (optional)
Avoid secrets (tokens/passwords).
Endpoint cardinality rule (usage logs)
For usage.endpoint, prefer route templates (low cardinality):
/api/users/{id}/api/users/123
This keeps the stats endpoint usable (top endpoints) and reduces noise.
Examples (copy/paste standards)
1) Events: error with correlation + typed metadata
await client.event({
"level": "ERROR",
"message": "db connect failed",
"stack_trace": traceback.format_exc(),
"metadata": {
"env": os.environ.get("ENV", "dev"),
"service_version": os.environ.get("SERVICE_VERSION", "dev"),
"build_sha": os.environ.get("BUILD_SHA", "local"),
"region": os.environ.get("AWS_REGION", "local"),
"request_id": request.id,
"trace_id": request.trace_id,
"error_code": "DB_CONN_FAILED",
"component": "db",
"retry_count": 3,
},
})
2) Events: startup / lifecycle message
await client.event({
"level": "INFO",
"message": "service started",
"metadata": {
"env": os.environ.get("ENV", "dev"),
"service_version": os.environ.get("SERVICE_VERSION", "dev"),
"build_sha": os.environ.get("BUILD_SHA", "local"),
},
})
3) Audit: user action (who did what, on what)
await client.audit({
"action": "UPDATE_PROFILE",
"result": "SUCCESS",
"user_id": user.id,
"resource": f"users/{user.id}",
"ip_address": request.client.host,
"user_agent": request.headers.get("user-agent"),
"metadata": {
"env": os.environ.get("ENV", "dev"),
"request_id": request.id,
"trace_id": request.trace_id,
"actor_type": "user",
"fields_changed": ["email", "phone"],
},
})
4) Audit: failure example (important for stats)
await client.audit({
"action": "DELETE_API_KEY",
"result": "FAILURE",
"user_id": user.id,
"resource": f"apikeys/{api_key_id}",
"metadata": {
"request_id": request.id,
"trace_id": request.trace_id,
"reason": "insufficient_permissions",
},
})
5) Usage: API request latency (recommended fields)
await client.usage({
"endpoint": "/api/users/{id}",
"method": "GET",
"duration_ms": int(timing_ms),
"status_code": response.status_code,
"user_id": user.id if user else None,
"metadata": {
"env": os.environ.get("ENV", "dev"),
"request_id": request.id,
"trace_id": request.trace_id,
"response_size_bytes": len(response.body),
"cache": cache_status, # e.g. "hit"/"miss"
},
})
6) Adding consistent metadata automatically
Use enrich_metadata in ZenLogsClientConfig to ensure every log includes the same baseline keys.
import os
from zen_logs_client import ZenLogsClient
def enrich_metadata(metadata):
base = {
"env": os.environ.get("ENV", "dev"),
"service_version": os.environ.get("SERVICE_VERSION", "dev"),
"build_sha": os.environ.get("BUILD_SHA", "local"),
"region": os.environ.get("AWS_REGION", "local"),
}
if metadata:
base.update(metadata)
return base
client = ZenLogsClient({
"base_url": os.environ.get("ZEN_LOGS_URL", "http://localhost:7777"),
"token": os.environ.get("LOG_AUTH_TOKEN"),
"default_service_name": "billing-service",
"enrich_metadata": enrich_metadata,
})
7) Graceful shutdown (recommended)
Call shutdown() during process termination so the in-memory queue is drained best-effort.
import signal
import asyncio
async def shutdown_handler():
await client.shutdown(timeout_ms=5000)
def handle_signal(sig, frame):
asyncio.create_task(shutdown_handler())
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
Or use async context manager:
async with ZenLogsClient({
"base_url": "http://localhost:7777",
"default_service_name": "my-service",
}) as client:
await client.event({"level": "INFO", "message": "Hello"})
# Client automatically shuts down on exit
Send an event
await client.event({
"level": "ERROR",
"message": "Failed to connect to database",
"stack_trace": "...",
"metadata": {
"error_code": "DB_CONN_FAILED",
"request_id": "req_123",
},
})
Send an audit log
await client.audit({
"action": "UPDATE_PROFILE",
"result": "SUCCESS",
"user_id": "user-123",
"resource": "users/123",
"metadata": {
"fields_changed": ["email", "phone"],
},
})
Send a usage log
await client.usage({
"endpoint": "/api/products",
"method": "GET",
"duration_ms": 45,
"status_code": 200,
"metadata": {
"cache": "hit",
},
})
Flush / shutdown
await client.flush()
await client.shutdown(timeout_ms=5000)
Using with type hints
The client also accepts typed dataclass inputs:
from zen_logs_client import (
ZenLogsClient,
EventLogInputWithDefaults,
LogLevel,
AuditResult,
)
await client.event(EventLogInputWithDefaults(
level=LogLevel.ERROR,
message="Database error",
metadata={"component": "db"},
))
await client.audit(AuditLogInputWithDefaults(
action="LOGIN",
result=AuditResult.SUCCESS,
user_id="user-123",
))
Defaults
Low-latency defaults (configurable in ZenLogsClientConfig):
flush_interval_ms=250max_batch_size=50max_queue_size=2000max_retries=2base_backoff_ms=200max_backoff_ms=2000retry_on=[network, 5xx]- Backpressure:
blockwithenqueue_timeout_ms=5000, then drop-newest
Configuration Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
base_url |
str | required | Zen Logs server URL |
token |
str | None | Optional X-Log-Token for auth |
default_service_name |
str | None | Default service name for logs |
flush_interval_ms |
int | 250 | Background flush interval |
max_batch_size |
int | 50 | Max logs per batch request |
max_queue_size |
int | 2000 | Max in-memory queue size |
backpressure |
BackpressureMode | BLOCK | Queue full behavior |
enqueue_timeout_ms |
int | 5000 | Block timeout (if backpressure=block) |
max_retries |
int | 2 | Max retry attempts |
base_backoff_ms |
int | 200 | Initial retry backoff |
max_backoff_ms |
int | 2000 | Max retry backoff |
retry_on |
list[RetryMode] | [network, 5xx] | Retry conditions |
enrich_metadata |
Callable | None | Metadata enrichment hook |
Notes
- The client uses
POST /api/v1/batchfor flushes - Timestamps are assigned by the server
- Uses
httpxfor async HTTP requests
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zen_logs_client-0.1.0.tar.gz.
File metadata
- Download URL: zen_logs_client-0.1.0.tar.gz
- Upload date:
- Size: 17.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f83acd57b148df421636707361b7e09c8a232217c5a4dc2cf3f66aa9588e1961
|
|
| MD5 |
e1c541974979878e0d9ec26a6db077d7
|
|
| BLAKE2b-256 |
aa9dac127768b354e5e9dd19ae67ce11f9546ff311055bb34cc016077b9e620a
|
File details
Details for the file zen_logs_client-0.1.0-py3-none-any.whl.
File metadata
- Download URL: zen_logs_client-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f182393237b0220248e7ed713aae4fa3192829f5c3adab4219d649c40fbc9d6
|
|
| MD5 |
ca3d3a995d92e286166a4d7965ca69a0
|
|
| BLAKE2b-256 |
70cd399a6042986bb93281f220a5629ad389ee07b5d2412576cfc73c139d1ae9
|