# Send data with OpenTelemetry

> Get your telemetry flowing into Fixter in three steps, whatever language or platform you run.

Fixter ingests telemetry over standard **OpenTelemetry (OTLP/HTTP)**. If your services
already emit OpenTelemetry, through the Collector, an SDK, or an auto-instrumentation
agent, connecting them to Fixter is three steps and no code changes. (Just getting
started? [Onboarding](/getting-started/onboarding/) is the guided path; this page is the
depth behind its manual tier.) On Claude Code you do not wire any of this by hand: the
[plugin](/setup/claude-code-plugin/) provisions the key and configures export for you.

## Step 1: Create an API key

1. Open [Settings > API keys](https://app.fixter.dev/settings/api-keys) in the Fixter
   app.
2. Click **Create key** and give it a label you'll recognize later, like
   `production-collector`.
3. **Copy the key immediately.** For security it is shown only this once; if you lose
   it, revoke it and create a new one.

{/* fx-image: Settings > API keys with the create-key modal and the one-time key reveal */}

Keys look like `fixter_sk_...`. Store it wherever your infrastructure keeps secrets,
and export it in the shell you'll use below:

```sh
export FIXTER_API_KEY="fixter_sk_..."
```

No workspace yet? Email [info@fixter.dev](mailto:info@fixter.dev) and one of our
founders will set you up.

## Step 2: Point your OpenTelemetry setup at Fixter

Pick the path that matches your setup:

- **On Kubernetes:** run the Fixter collector (Path A).
- **You already run an OpenTelemetry Collector:** add a Fixter exporter to it (Path B).
- **Anything else** (a plain process, a VM, Docker): export straight from your
  application (Path C).

### Path A: run the Fixter collector (Kubernetes)

The Fixter collector is a standard OpenTelemetry Collector that Fixter pre-configures
and publishes, so there is no proprietary agent involved. On Kubernetes it is the
recommended path, installed as a Helm chart.

Create the `fixter` namespace and, inside it, a Secret named `fixter-credentials`
holding the key from Step 1. Then install the chart pointing at that Secret:

```sh
helm install fixter-collector \
  oci://ghcr.io/fixter-dev/charts/fixter-collector \
  --namespace fixter \
  --set fixter.existingSecret=fixter-credentials \
  --set fixter.clusterName=<your-cluster>
```

Pass the key by Secret reference, never `--set fixter.apiKey=...`, which would leave it
in your shell history and the pod's process list.

Installed on its own, the chart brings **pod logs and host and cluster metrics** into
Fixter with no application changes at all. It parses JSON pod logs for severity and
trace correlation, and auto-routes common database logs (Postgres, MySQL, Kafka and
others). To add each app's own traces and logs on top, point its workload at the
collector's in-cluster Service instead of at `ingest.fixter.dev`:

```sh
OTEL_EXPORTER_OTLP_ENDPOINT=http://fixter-collector-agent.fixter.svc.cluster.local:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=checkout-service
```

The [Claude Code plugin](/setup/claude-code-plugin/) installs and configures this chart
for you, including the Secret and the case where a GitOps or Terraform system already
manages your Helm releases.

### Path B: add Fixter to a Collector you already run

If you already run [an OpenTelemetry Collector](https://opentelemetry.io/docs/collector/),
one exporter block sends its telemetry to Fixter as well, covering every service that
exports to it whatever the language. Add the exporter to your Collector's configuration
file (the YAML you start it with via `--config`; on package installs typically
`/etc/otelcol/config.yaml`) and reference it from each pipeline:

```yaml
# otel-collector-config.yaml
exporters:
  otlphttp/fixter:
    endpoint: https://ingest.fixter.dev
    headers:
      Authorization: "Bearer ${env:FIXTER_API_KEY}"
    compression: gzip

service:
  pipelines:
    logs:
      exporters: [otlphttp/fixter]   # append to your existing exporters list
    traces:
      exporters: [otlphttp/fixter]
    metrics:
      exporters: [otlphttp/fixter]
```

Then restart the Collector (for example `systemctl restart otelcol`, or redeploy the
container) with `FIXTER_API_KEY` present in its environment.

### Path C: export straight from your application

No Collector needed: every OpenTelemetry SDK and auto-instrumentation agent can send
directly to Fixter. **The universal contract is four environment variables**, the
same in every language:

```sh
OTEL_SERVICE_NAME=checkout-service            # how Fixter names your service
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.fixter.dev
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf     # Fixter speaks OTLP over HTTP, not gRPC
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $FIXTER_API_KEY"
```

Set those wherever your process gets its environment, add your language's
OpenTelemetry instrumentation, and you're exporting. Concrete recipes:

**Logs are configured separately from traces, and how much is automatic depends on your
stack.** Instrumentation gives you traces and metrics; logs need a bridge from your logging
library, and it is not free everywhere. The Java agent auto-bridges logback and log4j2, so
logs come with no extra work. Node exports logs only for Winston, Pino, or Bunyan (plain
`console` sends none). Python needs `opentelemetry-instrumentation-logging` to bridge stdlib
`logging`. Go, PHP, and Ruby have no stable log bridge yet and ship logs as JSON through a
collector instead. Each tab below notes what its stack needs; do not assume logs arrive just
because traces do.

<Tabs>
  <TabItem label="Node.js">

```sh
# install the auto-instrumentation bundle
npm install @opentelemetry/auto-instrumentations-node
```

How you load it depends on whether your app is CommonJS or ES modules, and getting it wrong
fails silently. With the CommonJS loader on an ESM app, traces still appear (CommonJS deps
pulled in through interop are patched) while ESM-loaded libraries like `pino` are not, so no
logs and no `trace_id` reach Fixter and nothing errors or warns.

**CommonJS** (a `require`-based app, no `"type": "module"` in `package.json`):

```sh
# the four OTEL_* vars set as above
node --require @opentelemetry/auto-instrumentations-node/register app.js
```

**ES modules** (`"type": "module"`, or `.mjs` files): `--require` does not hook ESM imports.
Register the ESM loader hook from a small file and start the app with `--import` (Node also
deprecates `--require` in favour of `--import`):

```js
// instrumentation.mjs

register('import-in-the-middle/hook.mjs', import.meta.url);
await import('@opentelemetry/auto-instrumentations-node/register');
```

```sh
# the four OTEL_* vars set as above
node --import ./instrumentation.mjs app.js
```

**Logs need an instrumented log library.** Auto-instrumentation exports logs only for
Winston, Pino, or Bunyan; a service that logs through plain `console.log` sends traces and
metrics but no logs at all. If you use `console`, move to one of those to get queryable logs
and the `trace_id` on each line that ties a log to its trace.

  </TabItem>
  <TabItem label="Python">

```sh
# install and let it detect your frameworks
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

# run your app wrapped (the four OTEL_* vars set as above)
opentelemetry-instrument python app.py
```

**Logs:** `opentelemetry-bootstrap` adds the logging instrumentation that bridges Python's
stdlib `logging`; with `OTEL_LOGS_EXPORTER=otlp` set, log records export alongside traces.

  </TabItem>
  <TabItem label="Java">

```sh
# download the agent once, next to your service's jar
curl -LO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

# attach it at startup (the four OTEL_* vars set as above)
java -javaagent:./opentelemetry-javaagent.jar -jar your-service.jar
```

**Logs:** the agent auto-bridges logback and log4j2, so your existing logs export with no
extra work.

  </TabItem>
  <TabItem label="Other languages">

Go, Ruby, .NET, Rust, PHP, Erlang: every official OpenTelemetry SDK honors the four
environment variables above. Follow your language's instrumentation guide at
[opentelemetry.io/docs/languages](https://opentelemetry.io/docs/languages/), and
where it asks for an OTLP endpoint or headers, the values are already in your
environment. (Some SDKs without auto-instrumentation need the OTLP **http** exporter
package chosen explicitly; the `http/protobuf` protocol line is the setting that
matters.)

**Logs:** log-bridge maturity varies by language. Go, PHP, and Ruby have no stable OTLP log
bridge yet, so emit JSON logs to stdout and let the Fixter collector pick them up rather than
exporting logs from the SDK.

  </TabItem>
</Tabs>

**Where do the env vars go?** Wherever your deploy defines its environment:

| You run on | Set them in |
| --- | --- |
| A plain process / VM | the shell, a `.env` loader, or the systemd unit (`Environment=`) |
| Docker | `ENV`/`-e`, or `environment:` in compose |
| Kubernetes (any flavor: EKS, GKE, self-managed) | the Deployment's `env:` block, with the key from a `Secret` |
| AWS ECS | the task definition's `environment`/`secrets` |

Store the API key as a secret in all of these; it's a credential, not config.

## Step 3: See your data

1. Open the [Logs page](https://app.fixter.dev/logs) in the Fixter app (it's also
   where you land after login).
2. The default view shows the last hour. If your service logged anything since Step
   2, it's here; use the filter bar to narrow to your service name.
3. Click the **tail** button next to the query bar to stream new logs live while you
   exercise your service.

{/* fx-image: Logs page filtered to the new service with live tail enabled */}

Sent metrics or traces instead? Metrics appear on the
[Metrics page](https://app.fixter.dev/metrics); traces attach to your logs (any log
with a trace opens its full trace view). From here,
[connect your agent over MCP](/setup/mcp/) and ask it about your
data instead.

What each signal is for, what you get without writing instrumentation yourself, and what
happens to it all once it lands: [Telemetry](/using-fixter/telemetry/).

## If nothing shows up

- `401` in your exporter or Collector logs: the `Authorization` header isn't reaching
  Fixter or the key was revoked. Re-check Step 1 and that the env var is actually set
  in the process's environment.
- Exporter errors mentioning gRPC: set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`
  (see Step 2).
- Traces arrive but no logs (Node): the app is ESM and was started with `--require`, which
  does not instrument ESM imports, or it logs through plain `console.log`. Use the `--import`
  loader and a Winston/Pino/Bunyan logger (see the Node.js recipe in Step 2).
- Data arrives but under the wrong name: set `OTEL_SERVICE_NAME`.
- Batches partially rejected: the exporter logs show Fixter's `partialSuccess`
  message naming the first rejected record and why.

Still stuck? Email [info@fixter.dev](mailto:info@fixter.dev) and one of our founders will help you fix your problem asap.