How to Add Metadata Attributes to a Span in Python
Create a lightweight dataclass-based Span mock that stores key-value metadata attributes for tracing or event logging.
Python code
25 linesfrom dataclasses import dataclass, field
from typing import Dict, Any
@dataclass
class Span:
name: str
attributes: Dict[str, Any] = field(default_factory=dict)
def set_attribute(self, key: str, value: Any) -> None:
self.attributes[key] = value
def get_attribute(self, key: str) -> Any:
return self.attributes.get(key)
if __name__ == "__main__":
span = Span("http_request")
span.set_attribute("http.method", "GET")
span.set_attribute("http.status_code", 200)
span.set_attribute("http.url", "/api/users")
span.set_attribute("user.id", 42)
print(f"Span: {span.name}")
print(f"Attributes: {span.attributes}")
print(f"Method: {span.get_attribute('http.method')}")
print(f"Has user.id: {'user.id' in span.attributes}")
Output
Span: http_request
Attributes: {'http.method': 'GET', 'http.status_code': 200, 'http.url': '/api/users', 'user.id': 42}
Method: GET
Has user.id: True
How it works
The @dataclass decorator automatically generates __init__, __repr__, and __eq__ methods. Using field(default_factory=dict) ensures each Span instance gets a fresh dictionary, avoiding shared mutable state. The set_attribute and get_attribute methods provide a clean API for adding and retrieving metadata. The dictionary comprehension in __main__ demonstrates the typical usage pattern of attaching tracing-like attributes to a span.
Common mistakes
- Using a mutable default argument like `attributes={}` in the dataclass, which is shared across all instances.
- Forgetting to import `field` from `dataclasses`; using `default_factory` without `field` causes a TypeError.
- Assuming `get_attribute` returns a default value; it returns `None` if the key doesn't exist, which may need explicit handling.
Variations
- Use `__post_init__` to add validation logic, e.g., ensuring attribute keys follow a naming convention like `namespace.key`.
- Inherit from an abstract base class or protocol to match a specific OpenTelemetry `BaseSpan` interface.
Real-world use cases
- Unit-testing telemetry modules by mocking spans and verifying that expected attributes are set.
- Building a lightweight structured logger that tags each log event with request-scoped metadata.
- Creating a simple counter or metrics collector that stores labels as in-memory span attributes.
Sponsored
More from Observability & SRE
- Adding a Correlation ID to Log Context in Python medium
- Calculate Error Rate from Log Stream in Python easy
- Check if a Timestamp Falls in a Daily Maintenance Window in Python easy
- Export Metrics with OTLP Mock in Python medium
- Generate Mock CPU and Memory Metrics in Python easy
- Generate Prometheus Text Exposition Format in Python easy
Keep learning
Related tutorials and quizzes for this topic.