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.

Easy Python 3.9+ Aug 9, 2026 Observability & SRE 14 views 0 copies

Python code

25 lines
Python 3.9+
from 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

stdout
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

  1. Use `__post_init__` to add validation logic, e.g., ensuring attribute keys follow a naming convention like `namespace.key`.
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Observability & SRE

Related tutorials and quizzes for this topic.