---
title: OpenTelemetry
description: Every createEnvironment validation pass surfaced as an env.validate span through @opentelemetry/api.
type: package
package: "@zap-studio/env"
---

`@opentelemetry/api` is a required peer dependency of `@zap-studio/env`. It's a tiny, side-effect-free package that's a no-op until an app registers a real SDK, so installing it costs nothing at runtime for consumers who never set one up.

<CodeGroup>

```bash npm
npm install @opentelemetry/api
```

```bash yarn
yarn add @opentelemetry/api
```

```bash pnpm
pnpm add @opentelemetry/api
```

```bash bun
bun add @opentelemetry/api
```

```bash deno
deno add npm:@opentelemetry/api
```

</CodeGroup>

There's no option to configure — instrumentation is always on, and does nothing observable until your app registers an OpenTelemetry SDK:

```ts
import { createEnvironment } from "@zap-studio/env";
import { z } from "zod";

// If your app has registered an OpenTelemetry SDK, this call now produces a
// span. If not, it does nothing. No extra setup is needed either way.
export const env = createEnvironment({
  server: { PORT: z.coerce.number() },
  runtimeEnv: process.env,
});
```

## The `env.validate` Span

Each `createEnvironment` validation pass gets an `INTERNAL` span named `env.validate`. On failure, the span carries the invalid key names — never their values — as the `env.invalid_keys` attribute, and is marked as an error, with the thrown error recorded via `recordException`.

Validation itself runs once at startup, not per-request, so the span cost is negligible.

## Skipped Validation, No Span

When [`skipValidation`](/env/advanced-options#skipvalidation) is `true`, `createEnvironment` returns early before the `env.validate` span is created — there's nothing to trace, since no validation actually runs.

## Log Correlation

Pair this with [`logger`](/logger/opentelemetry): once a span is active, `ConsoleLogger` automatically stamps `trace_id`/`span_id` onto every log line, so anything you log around startup lines up with the `env.validate` span.

## See Also

- [Errors](/env/errors) — `EnvironmentValidationError`, whose `invalidKeys` end up on the span
- [Advanced Options](/env/advanced-options) — `skipValidation` and when the span is skipped
