A production-grade Python SDK for HTTP tracing and export to PingOps. Built on OpenTelemetry for maximum compatibility and reliability.
- Automatic HTTP Instrumentation - Automatically captures outgoing HTTP requests from
requests,urllib3,httpx, andaiohttp - Manual Tracing - Start traces with custom attributes like
userId,sessionId,tags, andmetadata - Flexible Configuration - Configure via code, YAML/JSON files, or environment variables
- Domain Filtering - Allow/deny lists to control which requests are traced
- Header Redaction - Automatically redact sensitive headers (Authorization, API keys, etc.)
- Body Capture - Optionally capture request/response bodies with size limits
- Batched Export - Efficient batch export with configurable size and timeout
pip install pingops-sdkfrom pingops import initialize_pingops, start_trace, StartTraceOptions, PingopsTraceAttributes
# Initialize the SDK
initialize_pingops({
"baseUrl": "https://api.pingops.com",
"serviceName": "my-service",
"apiKey": "your-api-key",
})
# Create a trace with custom attributes
options = StartTraceOptions(
attributes=PingopsTraceAttributes(
user_id="user-123",
session_id="session-456",
tags=["api-call", "important"],
)
)
# All HTTP calls within this trace will be captured
result = start_trace(options, lambda: make_api_calls())from pingops import start_trace_async, StartTraceOptions
async def main():
options = StartTraceOptions(
attributes=PingopsTraceAttributes(user_id="user-123")
)
result = await start_trace_async(options, async_api_call)from pingops import initialize_pingops
# Load from YAML or JSON file
initialize_pingops("config/pingops.yaml")Example pingops.yaml:
apiKey: your-api-key
baseUrl: https://api.pingops.com
serviceName: my-service
debug: false
# Only trace these domains
domainAllowList:
- domain: api.example.com
paths: ["/v1/", "/v2/"]
- domain: ".internal.company.com"
captureRequestBody: true
captureResponseBody: true
# Block these domains from tracing
domainDenyList:
- domain: analytics.example.com
# Header handling
headersAllowList:
- content-type
- accept
- x-request-id
headerRedaction:
strategy: partial
visibleChars: 4
sensitivePatterns:
- authorization
- x-api-keyThe SDK reads these environment variables:
| Variable | Description |
|---|---|
PINGOPS_API_KEY |
API key for authentication |
PINGOPS_BASE_URL |
PingOps backend URL |
PINGOPS_SERVICE_NAME |
Service name for spans |
PINGOPS_DEBUG |
Enable debug logging ("true" to enable) |
PINGOPS_BATCH_SIZE |
Batch size for export (default: 50) |
PINGOPS_BATCH_TIMEOUT |
Batch timeout in ms (default: 5000) |
PINGOPS_EXPORT_MODE |
"batched" or "immediate" |
PINGOPS_CONFIG_FILE |
Path to config file |
Environment variables override config file values.
Initialize the SDK. Idempotent - calling multiple times is safe.
# From config object
initialize_pingops(PingopsProcessorConfig(
base_url="https://api.pingops.com",
service_name="my-service",
))
# From file path
initialize_pingops("config/pingops.yaml")
# From dict
initialize_pingops({
"baseUrl": "https://api.pingops.com",
"serviceName": "my-service",
})
# From environment variables
initialize_pingops()Gracefully shut down the SDK, flushing pending spans.
# Async
await shutdown_pingops()
# Sync
shutdown_pingops_sync()Start a new trace and run a callback within it.
from pingops import start_trace, StartTraceOptions, PingopsTraceAttributes
options = StartTraceOptions(
seed="deterministic-seed", # For deterministic trace IDs
attributes=PingopsTraceAttributes(
trace_id="custom-trace-id", # Override trace ID
user_id="user-123",
session_id="session-456",
tags=["tag1", "tag2"],
metadata={"key": "value"},
capture_request_body=True, # Override body capture
capture_response_body=True,
),
)
result = start_trace(options, lambda: do_work())Async version of start_trace.
result = await start_trace_async(options, async_function)Get the current trace or span ID.
from pingops import get_active_trace_id, get_active_span_id
def handler():
trace_id = get_active_trace_id() # "abc123..."
span_id = get_active_span_id() # "def456..."| Field | Type | Required | Default | Description |
|---|---|---|---|---|
api_key |
str |
No | - | API key for backend |
base_url |
str |
Yes | - | PingOps backend URL |
service_name |
str |
Yes | - | Service name for resource |
debug |
bool |
No | False |
Enable debug logging |
headers_allow_list |
list[str] |
No | - | Only include these headers |
headers_deny_list |
list[str] |
No | - | Exclude these headers |
capture_request_body |
bool |
No | False |
Capture request bodies |
capture_response_body |
bool |
No | False |
Capture response bodies |
max_request_body_size |
int |
No | 4096 |
Max request body bytes |
max_response_body_size |
int |
No | 4096 |
Max response body bytes |
domain_allow_list |
list[DomainRule] |
No | - | Only trace these domains |
domain_deny_list |
list[DomainRule] |
No | - | Never trace these domains |
header_redaction |
HeaderRedactionConfig |
No | - | Header redaction settings |
batch_size |
int |
No | 50 |
Spans per batch |
batch_timeout |
int |
No | 5000 |
Batch timeout (ms) |
export_mode |
str |
No | "batched" |
"batched" or "immediate" |
DomainRule(
domain="api.example.com", # Exact match
# or domain=".example.com" # Suffix match
paths=["/api/", "/v2/"], # Optional path prefixes
headers_allow_list=["x-custom"], # Override headers
capture_request_body=True, # Override capture
)HeaderRedactionConfig(
sensitive_patterns=["x-custom-secret"],
strategy=HeaderRedactionStrategy.PARTIAL, # REPLACE, PARTIAL, PARTIAL_END, REMOVE
redaction_string="[REDACTED]",
visible_chars=4,
enabled=True,
)This SDK is built on OpenTelemetry and is fully compatible with the OpenTelemetry ecosystem. The SDK:
- Uses OpenTelemetry API for context propagation
- Creates CLIENT spans with standard HTTP semantic conventions
- Exports via OTLP HTTP protocol
- Works alongside other OpenTelemetry instrumentations
MIT License - see LICENSE for details.