Python Custom Instrumentation
This guide explains how to implement Custom Instrumentation in Python applications using the Motadata Custom Instrumentation package.
Custom instrumentation enables you to inject business-specific attributes into the currently active span using OTel-compatible APIs, without affecting the existing auto-instrumentation flow. The package ensures attribute validation, consistent namespacing, and safe runtime behavior.
Prerequisites
- The application must already be instrumented using Motadata Auto Instrumentation so that span context is available.
- Python 3.9 or higher
- pip 20 or higher
- Motadata Agent 8.1.3 or higher
- The application must already be generating traces in APM
Configuration Steps
Step 1: Install the Python Custom Instrumentation Package
If you are using a virtual environment (such as venv), ensure it is activated before running the installation command.
Install directly from the repository:
pip install "git+https://github.com/motadata2025/motadata-apm-custom-instrumentation-python.git"
Add to your requirements.txt:
motadata-apm-custom-instrumentation @ git+https://github.com/motadata2025/motadata-apm-custom-instrumentation-python.git
Step 2: Import the Package
from motadata.apm import CustomInstrumentation
Step 3: Add Custom Attributes to the Active Span
Use the package APIs to inject custom attributes into the currently active span.
Basic Example
from motadata.apm import CustomInstrumentation
CustomInstrumentation.set("apm.user.id", 12345)
CustomInstrumentation.set("apm.user.name", "john.doe")
CustomInstrumentation.set("apm.request.success", True)
CustomInstrumentation.set_str_list("apm.tags", ["api", "production", "critical"])
Keys are automatically prefixed with apm. if the prefix is not provided. However, it is recommended to explicitly use the prefix for consistency across services.
Since the package raises runtime exceptions for invalid input or span-context failures, wrap calls in try/except in production code.
Recommended Production Pattern
from motadata.apm import CustomInstrumentation
try:
CustomInstrumentation.set("apm.order.id", order_id)
except Exception as error:
logger.warning("Failed to set apm.order.id: %s", error)
Supported Methods
Scalar Attribute Setter
| Method | Parameter |
|---|---|
set(key, value) | bool, int, float, or str |
Collection Attribute Setters
| Method | Parameter |
|---|---|
set_str_list(key, values) | Sequence[str] |
set_int_list(key, values) | Sequence[int] |
set_float_list(key, values) | Sequence[float] |
set_bool_list(key, values) | Sequence[bool] |
Attribute Behavior and Validation Rules
The Python Custom Instrumentation package validates both keys and values before attaching attributes to the span.
Key Handling
- Keys are trimmed and normalized to lowercase.
- Keys are automatically prefixed with
apm.when missing. - Keys allow only alphanumeric characters and dots.
- Keys must not be null, empty, or whitespace-only.
Value Handling
- Scalar values must not be
None. - Empty string values are ignored and are not added to the trace.
- Float values must be finite.
NaNandInfinityare not allowed. - List methods reject null or empty lists.
set_str_list,set_int_list, andset_bool_listremoveNonevalues.set_float_listremovesNone,NaN, andInfinityvalues.- Each list must retain at least one valid value after filtering.
Runtime Behavior
- Raises
Exceptionfor invalid input. - Raises
Exceptionwhen span context is not available. - Designed to work safely alongside existing auto-instrumentation.
Example Scenarios
Add Business Context to a Request
try:
CustomInstrumentation.set("apm.customer.id", 1001)
CustomInstrumentation.set("apm.customer.name", "john.doe")
CustomInstrumentation.set("apm.customer.tier", "gold")
except Exception as error:
logger.warning("Failed to set customer attributes: %s", error)
Add Transaction Details
try:
CustomInstrumentation.set("apm.order.id", 987654)
CustomInstrumentation.set("apm.order.amount", 2599.75)
CustomInstrumentation.set("apm.order.currency", "USD")
CustomInstrumentation.set("apm.order.success", True)
except Exception as error:
logger.warning("Failed to set order attributes: %s", error)
Add Collection Attributes
try:
CustomInstrumentation.set_str_list(
"apm.order.items",
["router", "switch", "firewall"]
)
except Exception as error:
logger.warning("Failed to set order items: %s", error)
Add Failure Context
try:
payment_service.process(payment_request)
except Exception as error:
try:
CustomInstrumentation.set("apm.payment.status", "failed")
CustomInstrumentation.set("apm.payment.error_message", str(error))
except Exception as instrumentation_error:
logger.warning("Failed to set payment error attributes: %s", instrumentation_error)
raise
Advanced Example
The following example demonstrates enterprise-style usage where custom attributes are added across the lifecycle of a request.
from motadata.apm import CustomInstrumentation
def process_order(order):
try:
CustomInstrumentation.set("apm.order.id", order.id)
CustomInstrumentation.set("apm.order.customer_id", order.customer_id)
CustomInstrumentation.set("apm.order.amount", order.amount)
CustomInstrumentation.set("apm.order.currency", order.currency)
CustomInstrumentation.set_str_list(
"apm.order.tags",
["sales", "priority", "online"]
)
if order.amount > 10000:
CustomInstrumentation.set("apm.order.high_value", True)
CustomInstrumentation.set("apm.order.stage", "validation")
validate_order(order)
CustomInstrumentation.set("apm.order.stage", "inventory_check")
check_inventory(order)
CustomInstrumentation.set("apm.order.stage", "payment_processing")
process_payment(order)
CustomInstrumentation.set("apm.order.stage", "dispatch")
dispatch_order(order)
CustomInstrumentation.set("apm.order.status", "completed")
except Exception as error:
try:
CustomInstrumentation.set("apm.order.status", "failed")
CustomInstrumentation.set("apm.order.error_message", str(error))
except Exception as instrumentation_error:
logger.warning("Failed to set failure attributes: %s", instrumentation_error)
raise
def validate_order(order):
pass
def check_inventory(order):
pass
def process_payment(order):
pass
def dispatch_order(order):
pass
This approach enables correlation of business metadata such as order ID, customer, stage, and failure reason directly with traces.
Best Practices
- Use descriptive and hierarchical keys such as
apm.order.id. - Prefer explicitly using the
apm.prefix. - Use correct data types (int for IDs, float for metrics, bool for flags).
- Use list setters for collections and allow filtering within the package.
- Keep naming consistent across services for easier querying.
- Treat instrumentation as non-blocking and always handle exceptions.
What to Avoid
- Do not inject sensitive data such as passwords, tokens, or secrets.
- Do not use inconsistent key naming across services.
- Do not send empty string values.
- Do not pass invalid keys with unsupported characters.
- Do not assume attributes will be added when span context is missing.
Verify the Attributes
After implementing custom instrumentation:
- Go to APM Explorer.
- Open the required trace.
- Select the relevant span.
- Confirm custom attributes are visible under span attributes.
Summary
The Motadata Python Custom Instrumentation package provides a lightweight and enterprise-ready mechanism to inject validated, namespaced, business-specific attributes into the active span. It enhances trace visibility while maintaining compatibility with existing auto-instrumentation and OTel-based span lifecycle management.