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

# Manual Tracing

> Create custom spans in Netra with fine-grained control using SpanWrapper. Set prompts, model names, costs, and attributes on any operation in your app.

Manual tracing gives you complete control over span creation, attributes, and lifecycle. Use it when you need to trace custom operations, add detailed metadata, or track usage and costs.

## Getting Started

To start manual tracing, you'll need to:

1. Import the required classes from Netra
2. Create a new span using `start_span()`
3. Track your operations within the span
4. Add relevant attributes and events

## Creating Spans

Use `start_span()` to create a span that wraps a block of code. In Python, use it as a context manager. In TypeScript, explicitly call `end()` when done.

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra

  # Use as context manager (recommended)
  with Netra.start_span("process-document") as span:
      result = process_document(doc)
      span.set_attribute("document.pages", result.page_count)
      # Span automatically ends when exiting the context
  ```

  ```typescript TypeScript theme={null}
  const parent = Netra.startSpan("parent").start();

  await parent.withActiveAsync(async () => {
    const child = Netra.startSpan("child").start();

    await child.withActiveAsync(async () => {
      Netra.addConversation(...);
    });

    child.end();
  });
  ```
</CodeGroup>

### Span Parameters

| Parameter                    | Type        | Description                                 |
| ---------------------------- | ----------- | ------------------------------------------- |
| `name`                       | string      | Name of the span (required)                 |
| `attributes`                 | dict/object | Initial attributes to set on the span       |
| `module_name` / `moduleName` | string      | Module or component name for organization   |
| `as_type` / `asType`         | SpanType    | Type of span (SPAN, GENERATION, TOOL, etc.) |

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra, SpanType

  with Netra.start_span(
      "generate-summary",
      attributes={
          "input.length": len(document),
          "model": "gpt-4",
      },
      module_name="summarization",
      as_type=SpanType.GENERATION,
  ) as span:
      # Your code here
      pass
  ```

  ```typescript TypeScript theme={null}
  import { Netra, SpanType } from "netra-sdk";

  async function generateSummary(document: string) {
    const span = Netra.startSpan(
      "generate-summary",
      {
        "input.length": document.length,
        model: "gpt-4",
      },
      "summarization",
      SpanType.GENERATION
    ).start();

    await span.withActiveAsync(async () => {
      // Your code here
    });

    span.end();
  }
  ```
</CodeGroup>

## Span Types

Use the `as_type` parameter to categorize spans. This helps Netra display them correctly and enables type-specific features.

| Type                  | Use For                                  |
| --------------------- | ---------------------------------------- |
| `SpanType.GENERATION` | LLM completions, image generation        |
| `SpanType.EMBEDDING`  | Vector embedding operations              |
| `SpanType.TOOL`       | Function calls, API requests, DB queries |
| `SpanType.AGENT`      | AI agent reasoning and decisions         |
| `SpanType.SPAN`       | General operations (default)             |

See [Spans](/Observability/Traces/spans#span-types) for detailed guidance on when to use each type.

## Local Span Blocking

You can block specific spans locally within a particular span scope. This is useful when you want to filter out noisy child spans (like HTTP requests) within a specific operation.

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra

  # Block POST spans within this scope
  with Netra.start_span("image-generation", attributes={"blocked_spans": ["POST", "GET"]}) as span:
      # HTTP spans named "POST" or "GET" created within this scope will be filtered
      generate_image(prompt)
  ```

  ```typescript TypeScript theme={null}
  import { Netra } from "netra-sdk";

  // Block POST spans within this scope
  const span = Netra.startSpan("image-generation", {
    "blocked_spans": ["POST", "GET"],
  });

  // HTTP spans named "POST" or "GET" created within this scope will be filtered
  await generateImage(prompt);

  span.end();
  ```
</CodeGroup>

This is different from global `blocked_spans` in `Netra.init()` which blocks spans across the entire application. Local blocking only affects spans created within the specific parent span's scope.

## SpanWrapper Methods

The `start_span()` function returns a `SpanWrapper` object with methods for adding context to your spans.

### Setting Span Attributes

Add custom key-value pairs to provide context about the operation:

<CodeGroup>
  ```python Python theme={null}
  with Netra.start_span("search-products") as span:
      span.set_attribute("query", user_query)
      span.set_attribute("filters.category", category)
      span.set_attribute("filters.price_range", [min_price, max_price])
      span.set_attribute("results.count", len(results))
  ```

  ```typescript TypeScript theme={null}
  const span = Netra.startSpan("search-products");

  span.setAttribute("query", userQuery);
  span.setAttribute("filters.category", category);
  span.setAttribute("filters.priceRange", [minPrice, maxPrice]);
  span.setAttribute("results.count", results.length);

  span.end();
  ```
</CodeGroup>

### LLM-Specific Attributes

For LLM operations, use dedicated methods to set prompts, models, and system information:

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra, SpanType

  with Netra.start_span("generate-response", as_type=SpanType.GENERATION) as span:
      span.set_prompt(user_message)
      span.set_negative_prompt("blurry, low quality")  # For image generation
      span.set_model("gpt-4-turbo")
      span.set_llm_system("openai")

      response = generate_response(user_message)

      span.set_attribute("completion", response.content)
  ```

  ```typescript TypeScript theme={null}
  import { Netra, SpanType } from "netra-sdk";

  const span = Netra.startSpan("generate-response", {}, undefined, SpanType.GENERATION);

  span.setPrompt(userMessage);
  span.setNegativePrompt("blurry, low quality"); // For image generation
  span.setModel("gpt-4-turbo");
  span.setLlmSystem("openai");

  const response = await generateResponse(userMessage);

  span.setAttribute("completion", response.content);
  span.end();
  ```
</CodeGroup>

### Recording Events

Track significant moments within a span's lifecycle:

<CodeGroup>
  ```python Python theme={null}
  with Netra.start_span("order-processing") as span:
      span.add_event("validation-started")
      validate_order(order)
      span.add_event("validation-completed", {"valid": True})

      span.add_event("payment-started")
      payment = process_payment(order)
      span.add_event("payment-completed", {
          "transaction_id": payment.id,
          "amount": payment.amount,
      })
  ```

  ```typescript TypeScript theme={null}
  const span = Netra.startSpan("order-processing");

  span.addEvent("validation-started");
  await validateOrder(order);
  span.addEvent("validation-completed", { valid: true });

  span.addEvent("payment-started");
  const payment = await processPayment(order);
  span.addEvent("payment-completed", {
    transactionId: payment.id,
    amount: payment.amount,
  });

  span.end();
  ```
</CodeGroup>

## Tracking Usage Data

Use `UsageModel` to track token usage and costs for LLM operations:

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra, SpanType, UsageModel

  with Netra.start_span("llm-call", as_type=SpanType.GENERATION) as span:
      response = openai.chat.completions.create(
          model="gpt-4",
          messages=[{"role": "user", "content": prompt}],
      )

      # Track usage
      span.set_usage([
          UsageModel(
              model="gpt-4",
              cost_in_usd=calculate_cost(response.usage),
              usage_type="chat",
              units_used=1,
          )
      ])
  ```

  ```typescript TypeScript theme={null}
  import { Netra, SpanType, UsageModel } from "netra-sdk";

  const span = Netra.startSpan("llm-call", {}, undefined, SpanType.GENERATION);

  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: [{ role: "user", content: prompt }],
  });

  // Track usage
  span.setUsage([
    {
      model: "gpt-4",
      costInUsd: calculateCost(response.usage),
      usageType: "chat",
      unitsUsed: 1,
    },
  ]);

  span.end();
  ```
</CodeGroup>

### UsageModel Fields

| Field                       | Type   | Description                                       |
| --------------------------- | ------ | ------------------------------------------------- |
| `model`                     | string | Model name used                                   |
| `cost_in_usd` / `costInUsd` | float  | Calculated cost in USD                            |
| `usage_type` / `usageType`  | string | Type of usage (e.g., "chat", "image\_generation") |
| `units_used` / `unitsUsed`  | int    | Number of units consumed                          |

## Adding Action Tracking

Use `ActionModel` to track discrete actions, tool calls, or database operations within a span:

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra, ActionModel

  with Netra.start_span("agent-execution") as span:
      # Record actions taken by the agent
      span.set_action([
          ActionModel(
              action="DB",
              action_type="INSERT",
              affected_records=[
                  {"record_id": "user_123", "record_type": "user"},
                  {"record_id": "profile_456", "record_type": "profile"},
              ],
              metadata={
                  "table": "users",
                  "operation_id": "tx_789",
                  "duration_ms": "45",
              },
              success=True,
          ),
          ActionModel(
              action="API",
              action_type="CALL",
              metadata={
                  "endpoint": "/api/v1/process",
                  "method": "POST",
                  "status_code": "200",
              },
              success=True,
          ),
      ])
  ```

  ```typescript TypeScript theme={null}
  import { Netra, ActionModel } from "netra-sdk";

  const span = Netra.startSpan("agent-execution");

  // Record actions taken by the agent
  span.setAction([
    {
      action: "DB",
      actionType: "INSERT",
      affectedRecords: [
        { recordId: "user_123", recordType: "user" },
        { recordId: "profile_456", recordType: "profile" },
      ],
      metadata: {
        table: "users",
        operationId: "tx_789",
        durationMs: "45",
      },
      success: true,
    },
    {
      action: "API",
      actionType: "CALL",
      metadata: {
        endpoint: "/api/v1/process",
        method: "POST",
        statusCode: "200",
      },
      success: true,
    },
  ]);

  span.end();
  ```
</CodeGroup>

### ActionModel Fields

| Field                                  | Type        | Description                                                 |
| -------------------------------------- | ----------- | ----------------------------------------------------------- |
| `action`                               | string      | Action category (e.g., "DB", "API", "CACHE")                |
| `action_type` / `actionType`           | string      | Action subtype (e.g., "INSERT", "SELECT", "CALL")           |
| `affected_records` / `affectedRecords` | array       | List of affected records with `record_id` and `record_type` |
| `metadata`                             | dict/object | Additional metadata as key-value pairs                      |
| `success`                              | boolean     | Whether the action succeeded                                |

## Error Handling

Mark spans as errors when operations fail:

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra

  with Netra.start_span("risky-operation") as span:
      try:
          result = risky_operation()
          span.set_success()
      except Exception as e:
          span.set_error(str(e))
          raise
  ```

  ```typescript TypeScript theme={null}
  import { Netra } from "netra-sdk";

  const span = Netra.startSpan("risky-operation");

  try {
    const result = await riskyOperation();
    span.setSuccess();
    span.end();
    return result;
  } catch (error) {
    span.setError(error.message);
    span.end();
    throw error;
  }
  ```
</CodeGroup>

When using Python's context manager, exceptions are automatically recorded and the span is marked as an error. You can still explicitly call `set_error()` for custom error messages.

## Nested Spans

Create hierarchical traces by nesting spans. Child spans automatically inherit the parent context:

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra

  def process_order(order: dict):
      with Netra.start_span("process-order") as parent_span:
          parent_span.set_attribute("order.id", order["id"])

          # Child span for validation
          with Netra.start_span("validate-order"):
              validate_order(order)

          # Child span for payment
          with Netra.start_span("process-payment") as payment_span:
              payment = process_payment(order)
              payment_span.set_attribute("payment.id", payment.id)

          # Child span for fulfillment
          with Netra.start_span("fulfill-order"):
              fulfill_order(order)
  ```

  ```typescript TypeScript theme={null}
  import { Netra } from "netra-sdk";

  async function processOrder(order: Order) {
    const parentSpan = Netra.startSpan("process-order");
    parentSpan.setAttribute("order.id", order.id);

    try {
      // Child span for validation
      const validateSpan = Netra.startSpan("validate-order");
      await validateOrder(order);
      validateSpan.end();

      // Child span for payment
      const paymentSpan = Netra.startSpan("process-payment");
      const payment = await processPayment(order);
      paymentSpan.setAttribute("payment.id", payment.id);
      paymentSpan.end();

      // Child span for fulfillment
      const fulfillSpan = Netra.startSpan("fulfill-order");
      await fulfillOrder(order);
      fulfillSpan.end();

      parentSpan.end();
    } catch (error) {
      parentSpan.setError(error.message);
      parentSpan.end();
      throw error;
    }
  }
  ```
</CodeGroup>

## Accessing the Current Span

Get the currently active span to add attributes from anywhere in your code:

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra

  def log_user_action(action: str):
      current_span = Netra.get_current_span()
      if current_span:
          current_span.add_event("user-action", {"action": action})

  # Usage within a traced operation
  with Netra.start_span("user-session"):
      # ... somewhere deep in the call stack ...
      log_user_action("clicked-submit")
  ```

  ```typescript TypeScript theme={null}
  import { Netra } from "netra-sdk";

  function logUserAction(action: string) {
    const currentSpan = Netra.getCurrentSpan();
    if (currentSpan) {
      currentSpan.addEvent("user-action", { action });
    }
  }

  // Usage within a traced operation
  const span = Netra.startSpan("user-session");
  // ... somewhere deep in the call stack ...
  logUserAction("clicked-submit");
  span.end();
  ```
</CodeGroup>

## Example: RAG Pipeline

This example demonstrates nested spans with multiple span types - a common pattern for AI pipelines.

<CodeGroup>
  ```python Python theme={null}
  from netra import Netra, SpanType, UsageModel

  def rag_pipeline(query: str):
      with Netra.start_span("rag-pipeline") as pipeline_span:
          pipeline_span.set_attribute("query", query)

          # Step 1: Generate embedding
          with Netra.start_span(
              "generate-embedding", as_type=SpanType.EMBEDDING
          ) as embed_span:
              embedding = embed_model.embed(query)
              embed_span.set_usage([
                  UsageModel(
                      model="text-embedding-3-small",
                      usage_type="embedding",
                      units_used=1
                  )
              ])

          # Step 2: Retrieve documents
          with Netra.start_span(
              "retrieve-documents", as_type=SpanType.TOOL
          ) as retrieve_span:
              documents = vector_store.search(embedding, top_k=5)
              retrieve_span.set_attribute("documents.count", len(documents))

          # Step 3: Generate response
          with Netra.start_span(
              "generate-response", as_type=SpanType.GENERATION
          ) as generate_span:
              generate_span.set_prompt(query)
              generate_span.set_model("gpt-4")
              generate_span.set_llm_system("openai")

              response = openai.chat.completions.create(
                  model="gpt-4",
                  messages=[
                      {"role": "system", "content": build_context(documents)},
                      {"role": "user", "content": query},
                  ],
              )

              generate_span.set_usage([
                  UsageModel(
                      model="gpt-4",
                      cost_in_usd=calculate_cost(response.usage),
                      usage_type="chat",
                      units_used=1,
                  )
              ])

          pipeline_span.set_success()
          return response.choices[0].message.content
  ```

  ```typescript TypeScript theme={null}
  import { Netra, SpanType, UsageModel } from "netra-sdk";

  async function ragPipeline(query: string) {
    const pipelineSpan = Netra.startSpan("rag-pipeline");
    pipelineSpan.setAttribute("query", query);

    try {
      // Step 1: Generate embedding
      const embedSpan = Netra.startSpan("generate-embedding", {}, undefined, SpanType.EMBEDDING);
      const embedding = await embedModel.embed(query);
      embedSpan.setUsage([
        { model: "text-embedding-3-small", usageType: "embedding", unitsUsed: 1 },
      ]);
      embedSpan.end();

      // Step 2: Retrieve documents
      const retrieveSpan = Netra.startSpan("retrieve-documents", {}, undefined, SpanType.TOOL);
      const documents = await vectorStore.search(embedding, { topK: 5 });
      retrieveSpan.setAttribute("documents.count", documents.length);
      retrieveSpan.end();

      // Step 3: Generate response
      const generateSpan = Netra.startSpan("generate-response", {}, undefined, SpanType.GENERATION);
      generateSpan.setPrompt(query);
      generateSpan.setModel("gpt-4");
      generateSpan.setLlmSystem("openai");

      const response = await openai.chat.completions.create({
        model: "gpt-4",
        messages: [
          { role: "system", content: buildContext(documents) },
          { role: "user", content: query },
        ],
      });

      generateSpan.setUsage([
        {
          model: "gpt-4",
          costInUsd: calculateCost(response.usage),
          usageType: "chat",
          unitsUsed: 1,
        },
      ]);
      generateSpan.end();

      pipelineSpan.setSuccess();
      pipelineSpan.end();

      return response.choices[0].message.content;
    } catch (error) {
      pipelineSpan.setError(error.message);
      pipelineSpan.end();
      throw error;
    }
  }
  ```
</CodeGroup>

## Best Practices

1. **Use context managers in Python** - They ensure spans are properly closed even when exceptions occur.
2. **End spans in TypeScript** - Always call `span.end()` in both success and error paths, preferably in a `finally` block.
3. **Add meaningful attributes** - Include information that will help you debug and analyze traces later.
4. **Track usage for LLM calls** - Use `setUsage()` to monitor token consumption and costs.
5. **Use appropriate span types** - Set `as_type` to categorize spans correctly (GENERATION for LLM calls, TOOL for function calls, etc.).
6. **Handle errors explicitly** - Call `setError()` with descriptive messages to make debugging easier.
7. **Use local span blocking** - Filter noisy child spans when you only care about the parent operation.
8. **Add events for milestones** - Use `addEvent()` to mark important points in long-running operations.

## Learn More

* [Decorators](/Observability/Traces/decorators) - Simpler instrumentation with decorators
* [Auto Instrumentation](/Observability/Traces/auto-instrumentation) - Zero-code tracing
* [Initialization](/Observability/Traces/configuration/initialization) - Configure tracing behavior
