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

# Send OpenTelemetry data from a Python app to Axiom

> This guide explains how to send OpenTelemetry data from a Python app to Axiom using the Python OpenTelemetry SDK.

This guide explains how to send OpenTelemetry data from a Python app to Axiom using the [Python OpenTelemetry SDK](https://opentelemetry.io/docs/languages/python/instrumentation/).

## Prerequisites

## Prerequisites

* [Create an Axiom account](https://app.axiom.co/register).
* [Create a dataset in Axiom](/reference/datasets#create-dataset) where you send your data.
* [Create an API token in Axiom](/reference/tokens) with permissions to ingest data to the dataset you have created.

- Install Python version 3.7 or higher.

## Install required dependencies

To install the required Python dependencies, run the following code in your terminal:

```bash theme={null}
pip install opentelemetry-api opentelemetry-sdk opentelemetry-instrumentation-flask opentelemetry-exporter-otlp Flask
```

### Install dependencies with requirements file

Alternatively, if you use a `requirements.txt` file in your Python project, add these lines:

```txt theme={null}
opentelemetry-api
opentelemetry-sdk
opentelemetry-instrumentation-flask
opentelemetry-exporter-otlp
Flask
```

Then run the following code in your terminal to install dependencies:

```bash theme={null}
pip install -r requirements.txt
```

## Create an app.py file

Create an `app.py` file with the following content. This file creates a basic HTTP server using Flask. It also demonstrates the usage of span links to establish relationships between spans across different traces.

```python theme={null}
# app.py

from flask import Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry import trace
from random import randint
import exporter

# Creating a Flask app instance
app = Flask(__name__)

# Automatically instruments Flask app to enable tracing
FlaskInstrumentor().instrument_app(app)

# Retrieving a tracer from the custom exporter
tracer = exporter.service1_tracer

@app.route("/rolldice")
def roll_dice(parent_span=None):
    # Starting a new span for the dice roll. If a parent span is provided, link to its span context.
    with tracer.start_as_current_span("roll_dice_span",
          links=[trace.Link(parent_span.get_span_context())] if parent_span else None) as span:
        # Spans can be created with zero or more Links to other Spans that are related.
        # Links allow creating connections between different traces
        return str(roll())

@app.route("/roll_with_link")
def roll_with_link():
    # Starting a new 'parent_span' which may later link to other spans
    with tracer.start_as_current_span("parent_span") as parent_span:
        # A common scenario is to correlate one or more traces with the current span.
        # This can help in tracing and debugging complex interactions across different parts of the app.
        result = roll_dice(parent_span)
        return f"Dice roll result (with link): {result}"

def roll():
    # Function to generate a random number between 1 and 6
    return randint(1, 6)

if __name__ == "__main__":
    # Starting the Flask server on the specified PORT and enabling debug mode
    app.run(port=8080, debug=True)
```

## Create an exporter.py file

Create an `exporter.py` file with the following content. This file establishes an OpenTelemetry configuration and sets up an exporter that sends trace data to Axiom.

```python theme={null}
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

# Define the service name resource for the tracer.
resource = Resource(attributes={
    SERVICE_NAME: "NAME_OF_SERVICE" # Replace `NAME_OF_SERVICE` with the name of the service you want to trace.
})

# Create a TracerProvider with the defined resource for creating tracers.
provider = TracerProvider(resource=resource)

# Configure the OTLP/HTTP Span Exporter with Axiom headers and endpoint.
otlp_exporter = OTLPSpanExporter(
    endpoint="https://AXIOM_DOMAIN/v1/traces",
    headers={
        "Authorization": "Bearer API_TOKEN",
        "X-Axiom-Dataset": "DATASET_NAME"
    }
)

# Create a BatchSpanProcessor with the OTLP exporter to batch and send trace spans.
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)

# Set the TracerProvider as the global tracer provider.
trace.set_tracer_provider(provider)

# Define a tracer for external use in different parts of the app.
service1_tracer = trace.get_tracer("service1")
```

<Info>
  Replace `NAME_OF_SERVICE` with the name of the service you want to trace. This is important for identifying and categorizing trace data, particularly in systems with multiple services.

  Replace `API_TOKEN` with the Axiom API token you have generated. For added security, store the API token in an environment variable.

  Replace `DATASET_NAME` with the name of the Axiom dataset where you send your data.

  Replace `AXIOM_DOMAIN` with the base domain of your edge deployment. For more information, see [Edge deployments](/reference/edge-deployments).
</Info>

For more information on the libraries imported by the `exporter.py` file, see the [Reference](#reference) below.

## Run the app

Run the following code in your terminal to run the Python project:

macOS/Linux

```bash theme={null}
python3 app.py
```

Windows

```
py -3 app.py
```

In your browser, go to `http://127.0.0.1:8080/rolldice` to interact with your Python app. Each time you load the page, the app displays a random number and sends the collected traces to Axiom.

## Observe the telemetry data in Axiom

In Axiom, go the **Stream** tab and click your dataset. This page displays the traces sent to Axiom and enables you to monitor and analyze your app’s performance and behavior.

<Frame caption="Observing the Telemetry data in Axiom image">
  <img src="https://mintcdn.com/axiom/6UXjyyx-ZiWeEcux/doc-assets/shots/opentelemetry-data-python-axiom.png?fit=max&auto=format&n=6UXjyyx-ZiWeEcux&q=85&s=22f0fdd1b016b061eb74670375604de5" alt="Observing the Telemetry data in Axiom image" width="1428" height="1060" data-path="doc-assets/shots/opentelemetry-data-python-axiom.png" />
</Frame>

## Dynamic OpenTelemetry traces dashboard

In Axiom, go the **Dashboards** tab and click **OpenTelemetry Traces (python)**. This pre-built traces dashboard provides further insights into the performance and behavior of your app.

<Frame caption="Dynamic OpenTelemetry Traces dashboard">
  <img src="https://mintcdn.com/axiom/6UXjyyx-ZiWeEcux/doc-assets/shots/opentelemetry-dashboard-python-traces.png?fit=max&auto=format&n=6UXjyyx-ZiWeEcux&q=85&s=90ea9e6a4f6bff7a23b9f2fdab4dab97" alt="Dynamic OpenTelemetry Traces dashboard" width="1555" height="965" data-path="doc-assets/shots/opentelemetry-dashboard-python-traces.png" />
</Frame>

## Send data from an existing Python project

### Manual instrumentation

Manual instrumentation in Python with OpenTelemetry involves adding code to create and manage spans around the blocks of code you want to trace. This approach allows for precise control over the trace data.

1. Import and configure a tracer at the start of your main Python file. For example, use the tracer from the `exporter.py` configuration.

   ```python theme={null}
   import exporter
   tracer = exporter.service1_tracer
   ```

2. Enclose the code blocks in your app that you want to trace within spans. Start and end these spans in your code.

   ```python theme={null}
   with tracer.start_as_current_span("operation_name"):
   ```

3. Add relevant metadata and logs to your spans to enrich the trace data, providing more context for your data.

   ```python theme={null}
   with tracer.start_as_current_span("operation_name") as span:
       span.set_attribute("key", "value")
   ```

### Automatic instrumentation

Automatic instrumentation in Python with OpenTelemetry simplifies the process of adding telemetry data to your app. It uses pre-built libraries that automatically instrument the frameworks and libraries.

1. Install the OpenTelemetry packages designed for specific frameworks like Flask or Django.

   ```bash theme={null}
   pip install opentelemetry-instrumentation-flask
   ```

2. Configure your app to use these libraries that automatically generate spans for standard operations.

   ```python theme={null}
   from opentelemetry.instrumentation.flask import FlaskInstrumentor
   # This assumes `app` is your Flask app.
   FlaskInstrumentor().instrument_app(app)
   ```

After you set them up, these libraries automatically trace relevant operations without additional code changes in your app.

## Reference

### List of OpenTelemetry trace fields

| Field Category      | Field Name                              | Description                                            |
| ------------------- | --------------------------------------- | ------------------------------------------------------ |
| Unique Identifiers  |                                         |                                                        |
|                     | \_rowid                                 | Unique identifier for each row in the trace data.      |
|                     | span\_id                                | Unique identifier for the span within the trace.       |
|                     | trace\_id                               | Unique identifier for the entire trace.                |
| Timestamps          |                                         |                                                        |
|                     | \_systime                               | System timestamp when the trace data was recorded.     |
|                     | \_time                                  | Timestamp when the actual event being traced occurred. |
| HTTP Attributes     |                                         |                                                        |
|                     | attributes.custom\["http.host"]         | Host information where the HTTP request was sent.      |
|                     | attributes.custom\["http.server\_name"] | Server name for the HTTP request.                      |
|                     | attributes.http.flavor                  | HTTP protocol version used.                            |
|                     | attributes.http.method                  | HTTP method used for the request.                      |
|                     | attributes.http.route                   | Route accessed during the HTTP request.                |
|                     | attributes.http.scheme                  | Protocol scheme (HTTP/HTTPS).                          |
|                     | attributes.http.status\_code            | HTTP response status code.                             |
|                     | attributes.http.target                  | Specific target of the HTTP request.                   |
|                     | attributes.http.user\_agent             | User agent string of the client.                       |
| Network Attributes  |                                         |                                                        |
|                     | attributes.net.host.port                | Port number on the host receiving the request.         |
|                     | attributes.net.peer.port                | Port number on the peer (client) side.                 |
|                     | attributes.custom\["net.peer.ip"]       | IP address of the peer in the network interaction.     |
| Operational Details |                                         |                                                        |
|                     | duration                                | Time taken for the operation.                          |
|                     | kind                                    | Type of span (for example,, server, client).           |
|                     | name                                    | Name of the span.                                      |
|                     | scope                                   | Instrumentation scope.                                 |
|                     | service.name                            | Name of the service generating the trace.              |

### List of imported libraries

The `exporter.py` file imports the following libraries:

from opentelemetry import trace

This module creates and manages trace data in your app. It creates spans and tracers which track the execution flow and performance of your app.

from opentelemetry.sdk.trace import TracerProvider

`TracerProvider` acts as a container for the configuration of your app’s tracing behavior. It allows you to define how spans are generated and processed, essentially serving as the central point for managing trace creation and propagation in your app.

from opentelemetry.sdk.trace.export import BatchSpanProcessor

`BatchSpanProcessor` is responsible for batching spans before they are exported. This is an important aspect of efficient trace data management as it aggregates multiple spans into fewer network requests, reducing the overhead on your app’s performance and the tracing backend.

from opentelemetry.sdk.resources import Resource, SERVICE\_NAME

The `Resource` class is used to describe your app’s service attributes, such as its name, version, and environment. This contextual information is attached to the traces and helps in identifying and categorizing trace data, making it easier to filter and analyze in your monitoring setup.

from opentelemetry.exporter.otlp.proto.http.trace\_exporter import OTLPSpanExporter

The `OTLPSpanExporter` is responsible for sending your app’s trace data to a backend that supports the OTLP such as Axiom. It formats the trace data according to the OTLP standards and transmits it over HTTP, ensuring compatibility and standardization in how telemetry data is sent across different systems and services.
