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

# Axiom

> Export Tilebox workflow logs and traces to Axiom datasets in addition to Tilebox Console.

Tilebox can export workflow telemetry to [Axiom](https://axiom.co/) through OTLP. Use this when your team already analyzes logs and traces in Axiom or wants long-term telemetry in Axiom datasets.

Built-in Tilebox Console observability does not require Axiom. Axiom export is optional and additive.

<Frame>
  <img src="https://mintcdn.com/tilebox/9yPiIuCV-2WPK6fa/assets/workflows/observability/tilebox-axiom.png?fit=max&auto=format&n=9yPiIuCV-2WPK6fa&q=85&s=f65b3e36a5103203a97ef64ae45f6fa6" alt="Tilebox is pre-integrated with Axiom" width="2000" height="300" data-path="assets/workflows/observability/tilebox-axiom.png" />
</Frame>

## Configure Axiom export

Create Axiom datasets for logs and traces and an API key with ingest permissions. Then configure export when the runner process starts.

<CodeGroup>
  ```python Python theme={"system"}
  from tilebox.workflows import Client
  from tilebox.workflows.observability.logging import configure_otel_logging_axiom
  from tilebox.workflows.observability.tracing import configure_otel_tracing_axiom

  from my_workflow import ProcessScene

  configure_otel_tracing_axiom(
      service="sentinel-2-runner",
      dataset="workflow-traces",
      api_key="<axiom-api-key>",
  )
  configure_otel_logging_axiom(
      service="sentinel-2-runner",
      dataset="workflow-logs",
      api_key="<axiom-api-key>",
  )

  client = Client(name="sentinel-2-runner")
  runner = client.runner(tasks=[ProcessScene])
  runner.run_forever()
  ```

  ```go Go theme={"system"}
  package main

  import (
  	"context"
  	"log/slog"

  	"github.com/tilebox/tilebox-go/observability"
  	"github.com/tilebox/tilebox-go/observability/logger"
  	"github.com/tilebox/tilebox-go/observability/tracer"
  	"github.com/tilebox/tilebox-go/workflows/v1"
  	"go.opentelemetry.io/otel"
  )

  type ProcessScene struct{}

  func (t *ProcessScene) Execute(ctx context.Context) error {
  	slog.InfoContext(ctx, "processing scene")
  	return nil
  }

  func main() {
  	ctx := context.Background()
  	service := &observability.Service{Name: "sentinel-2-runner"}
  	apiKey := "<axiom-api-key>"

  	traceProvider, shutdownTracer, err := tracer.NewAxiomProvider(ctx, service, "workflow-traces", apiKey)
  	if err != nil {
  		slog.ErrorContext(ctx, "failed to configure Axiom tracing", slog.Any("error", err))
  		return
  	}
  	defer shutdownTracer(ctx)
  	otel.SetTracerProvider(traceProvider)

  	logHandler, shutdownLogger, err := logger.NewAxiomHandler(ctx, service, "workflow-logs", apiKey,
  		logger.WithLevel(slog.LevelInfo),
  	)
  	if err != nil {
  		slog.ErrorContext(ctx, "failed to configure Axiom logging", slog.Any("error", err))
  		return
  	}
  	defer shutdownLogger(ctx)
  	slog.SetDefault(logger.New(logHandler))

  	client := workflows.NewClient()
  	runner, err := client.NewTaskRunner(ctx)
  	if err != nil {
  		slog.ErrorContext(ctx, "failed to create task runner", slog.Any("error", err))
  		return
  	}

  	if err := runner.RegisterTasks(&ProcessScene{}); err != nil {
  		slog.ErrorContext(ctx, "failed to register tasks", slog.Any("error", err))
  		return
  	}

  	runner.Run(ctx)
  }
  ```
</CodeGroup>

## Environment variables

You can omit credentials from code by setting environment variables:

| Variable               | Used by                          |
| ---------------------- | -------------------------------- |
| `AXIOM_API_KEY`        | log and trace export             |
| `AXIOM_LOGS_DATASET`   | `configure_otel_logging_axiom()` |
| `AXIOM_TRACES_DATASET` | `configure_otel_tracing_axiom()` |

<CodeGroup>
  ```python Python theme={"system"}
  configure_otel_tracing_axiom(service="sentinel-2-runner")
  configure_otel_logging_axiom(service="sentinel-2-runner")
  ```

  ```go Go theme={"system"}
  traceProvider, shutdownTracer, err := tracer.NewAxiomProviderFromEnv(ctx, service)
  if err != nil {
  	slog.ErrorContext(ctx, "failed to configure Axiom tracing", slog.Any("error", err))
  	return
  }
  defer shutdownTracer(ctx)
  otel.SetTracerProvider(traceProvider)

  logHandler, shutdownLogger, err := logger.NewAxiomHandlerFromEnv(ctx, service,
  	logger.WithLevel(slog.LevelInfo),
  )
  if err != nil {
  	slog.ErrorContext(ctx, "failed to configure Axiom logging", slog.Any("error", err))
  	return
  }
  defer shutdownLogger(ctx)
  slog.SetDefault(logger.New(logHandler))
  ```
</CodeGroup>

## Existing Axiom screenshots

<Frame>
  <img src="https://mintcdn.com/tilebox/9yPiIuCV-2WPK6fa/assets/workflows/observability/logs-light.png?fit=max&auto=format&n=9yPiIuCV-2WPK6fa&q=85&s=068fa329d5ba74113d4505d20d8a58b0" alt="Tilebox workflow logs in Axiom" className="dark:hidden" width="841" height="684" data-path="assets/workflows/observability/logs-light.png" />

  <img src="https://mintcdn.com/tilebox/9yPiIuCV-2WPK6fa/assets/workflows/observability/logs-dark.png?fit=max&auto=format&n=9yPiIuCV-2WPK6fa&q=85&s=71b48623b4832591e536cd48d1c84830" alt="Tilebox workflow logs in Axiom" className="hidden dark:block" width="849" height="683" data-path="assets/workflows/observability/logs-dark.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/tilebox/9yPiIuCV-2WPK6fa/assets/workflows/observability/traces-light.png?fit=max&auto=format&n=9yPiIuCV-2WPK6fa&q=85&s=434762a673ed02a64e7c633025fcf7b2" alt="Tilebox workflow traces in Axiom" className="dark:hidden" width="916" height="716" data-path="assets/workflows/observability/traces-light.png" />

  <img src="https://mintcdn.com/tilebox/9yPiIuCV-2WPK6fa/assets/workflows/observability/traces-dark.png?fit=max&auto=format&n=9yPiIuCV-2WPK6fa&q=85&s=72b52e97d58311372fb33b26f0287bd7" alt="Tilebox workflow traces in Axiom" className="hidden dark:block" width="913" height="713" data-path="assets/workflows/observability/traces-dark.png" />
</Frame>
