> ## Documentation Index
> Fetch the complete documentation index at: https://docs.honeyhive.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Create Custom Spans in HoneyHive

> Use the @trace decorator and context managers to trace business logic, workflows, and async functions beyond automatic LLM instrumentation.

Use `@trace` decorators to trace business logic, workflow steps, and application components that automatic LLM instrumentation does not cover. Initialize your tracer first (see [tracer initialization](/v2/tracing/tracer-initialization)), then add [trace enrichments](/v2/tracing/enrich-traces) for metadata on each span.

<Tip>
  **Decorator-First:** Use `@trace` decorators as your primary pattern. Context managers are for special cases like loops or conditional tracing.
</Tip>

## When should you use custom spans?

Custom spans let you trace specific business logic, workflow steps, and application components beyond just LLM calls. For the tracing data model behind spans, see [tracing concepts](/v2/tracing/concepts).

**Use Cases:**

* Business process tracking
* Performance bottleneck identification
* Complex workflow visualization
* Custom error tracking

***

## How do you trace functions with @trace?

The recommended approach for function-level tracing:

```python theme={null}
import os
from honeyhive import HoneyHiveTracer, trace, enrich_span

tracer = HoneyHiveTracer.init(api_key=os.getenv("HH_API_KEY"))

@trace
def process_request(user_id: str, data: dict) -> dict:
    """Automatically traced with inputs/outputs captured."""
    
    # Add custom context (see Enriching Traces for patterns)
    enrich_span({"user_id": user_id})
    
    result = do_processing(data)
    return {"status": "success", "data": result}

@trace
def nested_workflow(request: dict) -> dict:
    """Nested calls create trace hierarchy automatically."""
    validated = validate(request)      # Child span
    processed = process(validated)     # Child span
    return save(processed)             # Child span
```

**Benefits:**

* ✅ Automatic inputs/outputs capture
* ✅ Nested calls create proper trace hierarchy
* ✅ Clean code without span management clutter

<Note>
  For details on adding metadata with `enrich_span()`, see [Enriching Traces](/v2/tracing/enrich-traces).
</Note>

***

## How do you trace async functions?

The `@trace` decorator works with both sync and async functions automatically:

```python theme={null}
@trace
async def fetch_data(url: str) -> dict:
    """Async function - @trace works automatically."""
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()

# Call with await
result = await fetch_data("https://api.example.com/data")
```

<Note>
  **No separate `@atrace` needed.** The decorator detects async functions automatically.
</Note>

***

## When should you use context managers?

Use context managers for scenarios where decorators don't fit:

### When to use context managers

* ✅ **Loop iterations** - Tracing individual items in batch processing
* ✅ **Conditional spans** - Dynamic span creation based on runtime conditions
* ✅ **Non-function blocks** - Setup, cleanup, or configuration phases
* ❌ **Regular functions** - Use `@trace` instead

### `enrich_span_context()` (Recommended)

Creates spans with automatic HoneyHive namespacing:

```python theme={null}
from honeyhive.tracer.processing.context import enrich_span_context

@trace
def process_batch(items: list) -> list:
    results = []
    
    for i, item in enumerate(items):
        with enrich_span_context(
            event_name=f"process_item_{i}",
            inputs={"item": item},
            metadata={"batch_size": len(items)}
        ):
            result = transform_item(item)
            tracer.enrich_span(outputs={"result": result})
            results.append(result)
    
    return results
```

### `tracer.start_span()` (Low-Level)

For raw OpenTelemetry-style control:

```python theme={null}
with tracer.start_span("process_item") as span:
    span.set_attribute("item.index", i)
    result = do_processing()
    span.set_attribute("success", True)
```

### Comparison

| Feature              | `enrich_span_context()` | `tracer.start_span()` |
| -------------------- | ----------------------- | --------------------- |
| Auto namespacing     | ✅ Automatic             | ❌ Manual              |
| HoneyHive enrichment | ✅ Built-in              | ❌ Manual attributes   |
| Best for             | Business logic          | Low-level control     |

***

## How do you create conditional spans?

Create spans only when conditions are met:

```python theme={null}
import os

DEBUG_MODE = os.getenv("DEBUG", "false").lower() == "true"

@trace
def operation_with_debug(data: dict):
    if DEBUG_MODE:
        with enrich_span_context(
            event_name="debug_inspection",
            inputs={"data": data}
        ):
            inspect_data(data)
    
    return process(data)
```

***

## What are custom span best practices?

### Span naming

```python theme={null}
# ✅ Good: Descriptive, hierarchical
@trace(event_name="user_authentication")
@trace(event_name="payment_processing_stripe")

# ❌ Bad: Generic
@trace(event_name="process")
@trace(event_name="api_call")
```

### Avoid Over-Instrumentation

```python theme={null}
# ❌ Bad: Span per item in hot path
for item in million_items:
    with enrich_span_context(event_name="process_item"):
        process(item)

# ✅ Good: Batch-level span only
@trace
def process_batch(items: list):
    for item in items:
        process(item)
```

***

## Where should you go next?

<CardGroup cols={2}>
  <Card title="Enriching Traces" icon="sparkles" href="/v2/tracing/enrich-traces">
    Add metadata with enrich\_span and enrich\_session
  </Card>

  <Card title="Enrichment Schema" icon="book" href="/v2/tracing/enrichment-schema">
    Namespaces, data types, backend attributes
  </Card>

  <Card title="Distributed Tracing" icon="network-wired" href="/v2/tracing/distributed-tracing">
    Cross-service tracing
  </Card>

  <Card title="Python SDK Reference" icon="code" href="https://honeyhiveai.github.io/python-sdk/">
    Full API documentation
  </Card>
</CardGroup>
