Skip to main content

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.

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

MethodParameter
set(key, value)bool, int, float, or str

Collection Attribute Setters

MethodParameter
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. NaN and Infinity are not allowed.
  • List methods reject null or empty lists.
  • set_str_list, set_int_list, and set_bool_list remove None values.
  • set_float_list removes None, NaN, and Infinity values.
  • Each list must retain at least one valid value after filtering.

Runtime Behavior

  • Raises Exception for invalid input.
  • Raises Exception when 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:

  1. Go to APM Explorer.
  2. Open the required trace.
  3. Select the relevant span.
  4. 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.