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

# atoti_observability

Plugin enhancing observability of Atoti sessions.

Projects using Atoti Python SDK will have this kind of architecture:

```mermaid theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
flowchart LR
    upstream["Upstream service"]
    style upstream stroke-dasharray: 5 5

    subgraph python["Python application"]
        atoti-python-sdk["Atoti Python SDK"]
        other-libs["Other third-party libraries"]
        style other-libs stroke-dasharray: 5 5
    end

    atoti-server["Atoti Server"]
    other-services["Other services"]
    style other-services stroke-dasharray: 5 5

    upstream -.-> python
    atoti-python-sdk --> atoti-server
    other-libs -.-> other-services
```

OpenTelemetry distributed tracing allows these different services to contribute their own spans to the same trace.
This makes it possible to follow a request across process boundaries, identify bottlenecks, and understand the end-to-end latency of operations.

With this plugin enabled, Atoti Server will also expose [some metrics](https://docs.activeviam.com/products/atoti/server/6.2/docs/monitoring/metrics/#metrics-available) through OpenTelemetry.

A quick way to get started with observability is to:

1. Run a [Jaeger all-in-one Docker container](https://www.jaegertracing.io/docs/2.16/getting-started/#all-in-one) (or any other OpenTelemetry collector).
2. Install [one of the Python OpenTelemetry exporters](https://opentelemetry.io/docs/languages/python/exporters/#otlp-dependencies).
3. Install [`atoti-observability`](#module-atoti_observability) (this plugin).
4. Call `opentelemetry.trace.set_tracer_provider()` at the start of the Python application.
5. Create spans in the application code for each major step.

See the [project template](https://github.com/activeviam/atoti-python-sdk-project-template) for a working example.

<Note>
  When using different ports than the OpenTelemetry’s default ones, do not forget to set `os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"]` before calling [`atoti.Session.start()`](./atoti.Session.start#atoti.Session.start) so that the Atoti Server launched in a subprocess inherits it and exports its telemetry data to the right place.
</Note>

<Tip>
  This plugin automatically adds the [OpenTelemetry Java agent](https://opentelemetry.io/docs/zero-code/java/agent/) to the server process but this can be changed by passing another `-javaagent:*` to the [`java_options`](./atoti.config.session_config#atoti.SessionConfig.java_options).
</Tip>

### Example

Here, the OpenTelemetry environment is already configured:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> import os  
>>> from opentelemetry.sdk.environment_variables import (  
...     OTEL_EXPORTER_OTLP_ENDPOINT,
... )
>>> OTEL_EXPORTER_OTLP_ENDPOINT in os.environ  
True
```

Creating a simple trace with multiple spans:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> from opentelemetry.trace import get_tracer  
>>> TRACER = get_tracer("example")  
```

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> with TRACER.start_as_current_span("root") as span:  
...     foo = 1
...     with TRACER.start_as_current_span("intermediate"):
...         bar = 2
...         with TRACER.start_as_current_span("leaf"):
...             baz = 3
>>> _print(span)  
⏺ root           [atoti.python_sdk]
└── intermediate [atoti.python_sdk]
    └── leaf     [atoti.python_sdk]
```

Defining a function to avoid duplicating logic below:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> def query(session: tt.Session, /):  
...     cities_df = pd.DataFrame(
...         columns=["City", "Price"],
...         data=[
...             ("Berlin", 150.0),
...             ("London", 240.0),
...             ("New York", 270.0),
...             ("Paris", 200.0),
...         ],
...     )
...     table = session.read_pandas(cities_df, keys={"City"}, table_name="Example")
...     cube = session.create_cube(table)
...     level = cube.levels["City"]
...     measure = cube.measures["Price.SUM"]
...     with TRACER.start_as_current_span("example query") as span:
...         _ = cube.query(measure, levels=[level])
...     return span
```

Calling [`atoti.Cube.query()`](./atoti.Cube.query#atoti.Cube.query) generates some Python spans:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> span = query(session)  
>>> _print(span)  
⏺ example query                     [atoti.python_sdk]
└── Cube.query                      [atoti.python_sdk]
    ├── generate_mdx                [atoti.python_sdk]
    └── cellset_to_mdx_query_result [atoti.python_sdk]
        ├── Level.data_type         [atoti.python_sdk]
        └── Measure.data_type       [atoti.python_sdk]
```

Using a session with the observability plugin enabled will also export the server spans:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> span = query(session_with_observability_plugin)  
>>> _print(span)  
⏺ example query                                        [atoti.python_sdk]
└── Cube.query                                         [atoti.python_sdk]
    ├── GET /activeviam/pivot/rest/v10/cube/discovery  [atoti.server]
    │   └── unsecured request                          [atoti.server]
    ├── generate_mdx                                   [atoti.python_sdk]
    ├── POST /activeviam/pivot/rest/v10/cube/query/mdx [atoti.server]
    │   └── unsecured request                          [atoti.server]
    │       └── Json query service                     [atoti.server]
    │           └── select query                       [atoti.server]
    │               └── Get-aggregates execution       [atoti.server]
    │                   └── Async query executor       [atoti.server]
    │                       ├── Plan graph build       [atoti.server]
    │                       └── just-in-time query     [atoti.server]
    │                           └── Database query     [atoti.server]
    └── cellset_to_mdx_query_result                    [atoti.python_sdk]
        ├── Level.data_type                            [atoti.python_sdk]
        └── Measure.data_type                          [atoti.python_sdk]
```

| [`ObservabilityConfig`](./atoti_observability.observability_config#atoti_observability.ObservabilityConfig) |   |
| ----------------------------------------------------------------------------------------------------------- | - |
