# Axiom

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.

![Tilebox is pre-integrated with Axiom](/docs/assets/workflows/observability/tilebox-axiom.png)

## 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.

**Python**

```python title="Python"
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 title="Go"
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 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)
}
```

## 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()` |

**Python**

```python title="Python"
configure_otel_tracing_axiom(service="sentinel-2-runner")
configure_otel_logging_axiom(service="sentinel-2-runner")
```

**Go**

```go title="Go"
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))
```

## Existing Axiom screenshots

![Tilebox workflow logs in Axiom](/docs/assets/workflows/observability/logs-light.png)

![Tilebox workflow logs in Axiom](/docs/assets/workflows/observability/logs-dark.png)

![Tilebox workflow traces in Axiom](/docs/assets/workflows/observability/traces-light.png)

![Tilebox workflow traces in Axiom](/docs/assets/workflows/observability/traces-dark.png)
