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

# Custom Spans

> Create OpenTelemetry spans enriched with Galileo-specific attributes using the start_galileo_span context manager.

{/*<!-- markdownlint-enable MD044 -->*/}

The `start_galileo_span` function provides a context manager for creating OpenTelemetry spans that are automatically enriched with Galileo-specific attributes. This enables seamless observability for custom spans within your application while maintaining compatibility with the broader OpenTelemetry ecosystem.

## Prerequisites

Before using `start_galileo_span`, set up the tracer provider with the Galileo span processor:

```python Python theme={null}
from opentelemetry.sdk.trace import TracerProvider
from galileo.otel import GalileoSpanProcessor, add_galileo_span_processor

tracer_provider = TracerProvider()
processor = GalileoSpanProcessor(
    project="your-project-name",      # Optional: falls back on env var
    logstream="your-logstream-name"   # Optional: falls back on env var
)
add_galileo_span_processor(tracer_provider, processor)
```

<Note>
  Requires the OpenTelemetry extra: `pip install galileo[otel]`
</Note>

### Environment Variables

| Variable             | Description                               |
| -------------------- | ----------------------------------------- |
| `GALILEO_PROJECT`    | Default project name for trace export     |
| `GALILEO_LOG_STREAM` | Default Log stream for trace organization |

## Basic Usage

```python Python theme={null}
from galileo.otel import start_galileo_span
from galileo_core.schemas.logging.span import WorkflowSpan

galileo_span = WorkflowSpan(name="my-custom-operation")
with start_galileo_span(galileo_span) as span:
    # Your custom logic here
    span.set_attribute("custom.attribute", "value")
    result = perform_operation()
```

## Tool Spans

Use `ToolSpan` for tool/function execution operations. This captures tool invocation details including the tool name, arguments, and results.

```python Python theme={null}
from galileo.otel import start_galileo_span
from galileo_core.schemas.logging.span import ToolSpan

tool_span = ToolSpan(
    name="get_weather",
    input='{"location": "San Francisco", "unit": "celsius"}',
    tool_call_id="call_abc123"
)
with start_galileo_span(tool_span) as span:
    result = get_weather(location="San Francisco", unit="celsius")
    tool_span.output = '{"temperature": 18, "condition": "sunny"}'
```

When a `ToolSpan` is provided, the following attributes are set automatically:

| Attribute                    | Value            | Description                                       |
| ---------------------------- | ---------------- | ------------------------------------------------- |
| `gen_ai.operation.name`      | `"execute_tool"` | Indicates a tool execution operation              |
| `gen_ai.tool.name`           | Tool name        | Name of the tool being executed                   |
| `gen_ai.tool.call.arguments` | String           | Tool input arguments                              |
| `gen_ai.tool.call.result`    | String           | Tool output result                                |
| `gen_ai.tool.call.id`        | String           | Unique identifier for the tool call (if provided) |
| `gen_ai.input.messages`      | JSON array       | Tool input formatted as message                   |
| `gen_ai.output.messages`     | JSON array       | Tool output formatted as message                  |

## Retriever Spans

Use `RetrieverSpan` for search and retrieval operations. This automatically captures query input and document output with the correct semantic attributes.

```python Python theme={null}
from galileo.otel import start_galileo_span
from galileo_core.schemas.logging.span import RetrieverSpan
from galileo_core.schemas.shared.document import Document

retriever_span = RetrieverSpan(name="document-search", input="What is Galileo?")
with start_galileo_span(retriever_span) as span:
    documents = search_documents("What is Galileo?")
    retriever_span.output = [Document(content=doc) for doc in documents]
```

When a `RetrieverSpan` is provided, the following attributes are set automatically:

| Attribute                | Value      | Description                             |
| ------------------------ | ---------- | --------------------------------------- |
| `db.operation`           | `"search"` | Indicates a search/retrieval operation  |
| `gen_ai.input.messages`  | JSON array | User query formatted as input message   |
| `gen_ai.output.messages` | JSON array | Retrieved documents formatted as output |

## Workflow Spans

Use `WorkflowSpan` for higher-level orchestration units that coordinate multiple sub-operations, such as chains, pipelines, or multi-step processes.

```python Python theme={null}
from galileo.otel import start_galileo_span
from galileo_core.schemas.logging.span import WorkflowSpan

workflow_span = WorkflowSpan(
    name="document-processing-pipeline",
    input="Summarize the quarterly report"
)
with start_galileo_span(workflow_span) as span:
    result = process_document("Summarize the quarterly report")
    workflow_span.output = "The quarterly report shows 15% revenue growth..."
```

When a `WorkflowSpan` is provided, the following attributes are set automatically:

| Attribute                | Value      | Description                                                     |
| ------------------------ | ---------- | --------------------------------------------------------------- |
| `gen_ai.input.messages`  | JSON array | Workflow input formatted as user message                        |
| `gen_ai.output.messages` | JSON array | Workflow output formatted as assistant message or document list |

## Supported Span Types

| Span Type       | Attributes Set                                                                                                                        | Description                                                       |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `WorkflowSpan`  | `gen_ai.system`, `gen_ai.input.messages`, `gen_ai.output.messages`                                                                    | Orchestration span for pipelines and multi-step processes         |
| `ToolSpan`      | `gen_ai.system`, `gen_ai.operation.name`, `gen_ai.tool.name`, `gen_ai.tool.call.*`, `gen_ai.input.messages`, `gen_ai.output.messages` | Tool execution span with invocation details                       |
| `RetrieverSpan` | `gen_ai.system`, `db.operation`, `gen_ai.input.messages`, `gen_ai.output.messages`                                                    | Retriever span with search operation details and document results |

## Function Signature

```python Python theme={null}
@contextmanager
def start_galileo_span(galileo_span: Span) -> Generator[trace.Span, Any, None]
```

| Parameter      | Type   | Required | Description                                                          |
| -------------- | ------ | -------- | -------------------------------------------------------------------- |
| `galileo_span` | `Span` | Yes      | A Galileo span object containing the span name and optional metadata |

**Returns:** A context manager yielding an OpenTelemetry `Span` object.

## Execution Flow

When `start_galileo_span` is called, the following steps occur:

1. The `TracerProvider` is retrieved (from the context variable or via `trace.get_tracer_provider()`)
2. A tracer named `"galileo-tracer"` is created
3. An OpenTelemetry span is started using the `galileo_span.name`
4. The span is yielded for your code to execute
5. On exit, Galileo-specific attributes are set (e.g., `gen_ai.system = "galileo-otel"` and any span-type-specific attributes)

## Complete Example

```python Python theme={null}
from galileo.otel import (
    start_galileo_span,
    GalileoSpanProcessor,
    add_galileo_span_processor,
)
from galileo_core.schemas.logging.span import WorkflowSpan, RetrieverSpan
from galileo_core.schemas.shared.document import Document
from opentelemetry.sdk.trace import TracerProvider


def setup_tracing():
    """Initialize Galileo OpenTelemetry tracing."""
    tracer_provider = TracerProvider()
    processor = GalileoSpanProcessor(
        project="my-ai-application",
        logstream="production",
    )
    add_galileo_span_processor(tracer_provider, processor)
    return processor


def search_knowledge_base(query: str) -> list[str]:
    """Search with automatic span creation."""
    retriever_span = RetrieverSpan(name="knowledge-base-search", input=query)

    with start_galileo_span(retriever_span) as span:
        results = ["Result 1", "Result 2", "Result 3"]
        retriever_span.output = [Document(content=r) for r in results]

        # Add custom metrics
        span.set_attribute("search.result_count", len(results))
        return results


def main():
    processor = setup_tracing()

    # Create a parent span for the workflow
    workflow_span = WorkflowSpan(name="user-query-workflow")

    with start_galileo_span(workflow_span) as span:
        span.set_attribute("user.query", "What is machine learning?")

        # Nested retriever span
        results = search_knowledge_base("What is machine learning?")
        print(f"Found {len(results)} results")


if __name__ == "__main__":
    main()
```

## Best Practices

* **Initialize `TracerProvider` early** — Set up the `GalileoSpanProcessor` at application startup.
* **Use descriptive span names** — Provide meaningful names in `Span` for better observability.
* **Choose the right span type** — Use `ToolSpan` for function calls, `RetrieverSpan` for search operations, and `WorkflowSpan` for orchestration.
* **Add custom attributes** — Use the yielded span to add operation-specific attributes.
* **Set span output before exiting** — The output must be set before the context manager exits for proper attribute capture.
* **Nested spans are supported** — Child spans inherit the parent context automatically through OpenTelemetry's context propagation.
