Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ resolved fresh on every call, so changes take effect immediately:

1. `LOG_LEVEL` env var (`debug` | `info` | `warn` | `error` | `silent`) — wins if set.
2. `setLogLevel(level)` or `setupOtel({ logLevel })`.
3. Default: `debug` in development (`DEPLOYMENT_ENV` unset or `development`), `info` otherwise.
3. Default: `debug` only when `DEPLOYMENT_ENV=development`; `info` otherwise (including when `DEPLOYMENT_ENV` is unset).

**Upgrading from v3.2.0:** an unset `DEPLOYMENT_ENV` previously selected `debug`.
New releases select `info`; set `LOG_LEVEL=debug` to preserve the old verbosity without
changing the deployment identity.

```ts
import { setLogLevel } from "@photon-ai/otel";
Expand All @@ -161,7 +165,7 @@ Standard OpenTelemetry env vars always take precedence over `SetupOtelOptions`:
| `OTEL_EXPORTER_OTLP_<SIGNAL>_HEADERS` | Trace-, log-, or metric-specific headers; override generic and code headers. |
| `OTEL_METRIC_EXPORT_INTERVAL` | Metric export interval in milliseconds. Defaults to `60000`. |
| `OTEL_METRIC_EXPORT_TIMEOUT` | Metric export timeout in milliseconds. Defaults to `30000`. |
| `DEPLOYMENT_ENV` | Attached as `deployment.environment` resource attribute. Defaults to `development`. Also drives the default log level. |
| `DEPLOYMENT_ENV` | Attached as `deployment.environment`; only that resource attribute falls back to `development` when unset. Logging falls back to `info` unless the value is explicitly `development`. |
| `LOG_LEVEL` | Minimum log level: `debug` \| `info` \| `warn` \| `error` \| `silent`. Overrides `setLogLevel()` / `setupOtel({ logLevel })`. |

## Automatic fetch instrumentation
Expand Down
11 changes: 6 additions & 5 deletions docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,13 @@ Avoid putting per-request values in resource attributes. Use span attributes or

`DEPLOYMENT_ENV` is attached as the resource attribute `deployment.environment`.

If unset, it defaults to `development`.
If unset, only that resource attribute defaults to `development`. Log-level
resolution still sees the variable as unset and defaults to `info`.

It also affects the default log level:
The raw environment value selects the default log level:

- `debug` when `DEPLOYMENT_ENV` is unset or `development`
- `info` in every other environment
- `debug` only when `DEPLOYMENT_ENV` is `development`
- `info` otherwise, including when `DEPLOYMENT_ENV` is unset

## Log level

Expand Down Expand Up @@ -249,7 +250,7 @@ The package always excludes its own OTLP exporter endpoints from fetch instrumen
| Metric endpoint | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | base endpoint + `/v1/metrics` |
| Signal headers | signal-specific `*_HEADERS` | generic headers, then `setupOtel({ headers })` |
| Log level | `LOG_LEVEL` | `setupOtel({ logLevel })` or `setLogLevel()` |
| Deployment environment | `DEPLOYMENT_ENV` | `development` |
| `deployment.environment` resource attribute | `DEPLOYMENT_ENV` | `development` |

## Best practices

Expand Down
8 changes: 6 additions & 2 deletions docs/guides/logging.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,12 @@ console.log(getLogLevel()); // "warn", unless LOG_LEVEL is set

Defaults:

- `debug` when `DEPLOYMENT_ENV` is unset or `development`
- `info` otherwise
- `debug` only when `DEPLOYMENT_ENV` is `development`
- `info` otherwise, including when `DEPLOYMENT_ENV` is unset

> **Upgrading from v3.2.0:** an unset `DEPLOYMENT_ENV` previously selected
> `debug`. Set `LOG_LEVEL=debug` to preserve the old verbosity without changing
> the deployment identity.

Allowed values:

Expand Down
4 changes: 3 additions & 1 deletion docs/reference/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ Returns the current effective log level.
function getLogLevel(): LogLevel;
```

The value is resolved from `LOG_LEVEL`, then programmatic override, then deployment default.
The value is resolved from `LOG_LEVEL`, then programmatic override, then the raw
`DEPLOYMENT_ENV` value (`debug` only for explicit `development`; `info`
otherwise, including when unset).

## `LogLevel`

Expand Down
12 changes: 7 additions & 5 deletions docs/reference/environment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Environment variables are deployment-owned configuration. They override code def
| `OTEL_EXPORTER_OTLP_<SIGNAL>_HEADERS` | Signal-specific exporter headers | generic and code headers |
| `OTEL_METRIC_EXPORT_INTERVAL` | Metric export interval in milliseconds | default `60000` |
| `OTEL_METRIC_EXPORT_TIMEOUT` | Metric export timeout in milliseconds | default `30000` |
| `DEPLOYMENT_ENV` | deployment resource attribute and default log-level input | default `development` |
| `DEPLOYMENT_ENV` | deployment resource attribute and default log-level selector | resource attribute uses `development` when unset; logger uses `info` |
| `LOG_LEVEL` | logger minimum severity | `setupOtel({ logLevel })` and `setLogLevel()` |

## `OTEL_EXPORTER_OTLP_ENDPOINT`
Expand Down Expand Up @@ -116,12 +116,14 @@ DEPLOYMENT_ENV=production

The value is attached to telemetry as `deployment.environment`.

If unset, the package uses `development`.
If unset, only the `deployment.environment` resource attribute uses
`development`. The logger reads the raw variable and therefore still sees it as
unset.

It also controls the default log level:
The raw value separately controls the default log level:

- `development` or unset: `debug`
- any other value: `info`
- `development`: `debug`
- any other value, including unset: `info`

## `LOG_LEVEL`

Expand Down
9 changes: 5 additions & 4 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ function envLevel(): LogLevel | undefined {
}

function defaultLevel(): LogLevel {
return (process.env.DEPLOYMENT_ENV ?? "development") === "development"
? "debug"
: "info";
// Read the raw env value: the resource attribute's separate `development`
// fallback must not opt an embedding SDK into debug logging.
return process.env.DEPLOYMENT_ENV === "development" ? "debug" : "info";
}

/**
Expand All @@ -41,7 +41,8 @@ function defaultLevel(): LogLevel {
* match the rest of the package's config story):
* 1. `LOG_LEVEL` env var
* 2. `setLogLevel()` / `setupOtel({ logLevel })`
* 3. environment-driven default (`debug` in development, `info` otherwise)
* 3. environment-driven default (`debug` only when `DEPLOYMENT_ENV=development`;
* `info` otherwise, including when `DEPLOYMENT_ENV` is unset)
*/
function resolveLevel(): LogLevel {
return envLevel() ?? levelOverride ?? defaultLevel();
Expand Down
5 changes: 3 additions & 2 deletions src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ export interface SetupOtelOptions {
instrumentFetch?: boolean | InstrumentFetchOptions;
/**
* Minimum log level emitted by `createLogger()` (to both OTLP and console).
* The `LOG_LEVEL` env var still takes precedence. Defaults to `debug` in
* development and `info` otherwise.
* The `LOG_LEVEL` env var still takes precedence. Defaults to `debug` only
* when `DEPLOYMENT_ENV=development`; `info` otherwise, including when the
* variable is unset.
*/
logLevel?: LogLevel;
/**
Expand Down
20 changes: 14 additions & 6 deletions tests/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,23 +190,31 @@ describe("getLogLevel resolution", () => {
vi.resetModules();
delete process.env.LOG_LEVEL;
process.env.DEPLOYMENT_ENV = "development";
const fresh = await import("../src/logger");
expect(fresh.getLogLevel()).toBe("debug");
const loggerModule = await import("../src/logger");
expect(loggerModule.getLogLevel()).toBe("debug");
});

it("env-driven default is info when DEPLOYMENT_ENV is unset", async () => {
vi.resetModules();
delete process.env.LOG_LEVEL;
delete process.env.DEPLOYMENT_ENV;
const loggerModule = await import("../src/logger");
expect(loggerModule.getLogLevel()).toBe("info");
});

it("env-driven default is info outside development", async () => {
vi.resetModules();
delete process.env.LOG_LEVEL;
process.env.DEPLOYMENT_ENV = "production";
const fresh = await import("../src/logger");
expect(fresh.getLogLevel()).toBe("info");
const loggerModule = await import("../src/logger");
expect(loggerModule.getLogLevel()).toBe("info");
});

it("ignores an invalid LOG_LEVEL value", async () => {
vi.resetModules();
process.env.LOG_LEVEL = "loud";
process.env.DEPLOYMENT_ENV = "production";
const fresh = await import("../src/logger");
expect(fresh.getLogLevel()).toBe("info");
const loggerModule = await import("../src/logger");
expect(loggerModule.getLogLevel()).toBe("info");
});
});
21 changes: 20 additions & 1 deletion tests/setup.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import dc from "node:diagnostics_channel";
import { metrics } from "@opentelemetry/api";
import { MeterProvider as SdkMeterProvider } from "@opentelemetry/sdk-metrics";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { isOtelActive, setupOtel } from "../src/setup";

const ENV_KEYS = [
Expand All @@ -16,6 +16,7 @@ const ENV_KEYS = [
"OTEL_METRIC_EXPORT_INTERVAL",
"OTEL_METRIC_EXPORT_TIMEOUT",
"DEPLOYMENT_ENV",
"LOG_LEVEL",
] as const;

// Fetch instrumentation has two strategies and only one touches
Expand Down Expand Up @@ -69,6 +70,24 @@ describe("setupOtel", () => {
expect(second).toBe(first);
});

it("lets logLevel override the deployment default but not LOG_LEVEL", async () => {
vi.resetModules();
process.env.DEPLOYMENT_ENV = "development";
const otelModule = await import("../src/index");
const handle = otelModule.setupOtel({
serviceName: "log-level-precedence",
logLevel: "warn",
});
try {
expect(otelModule.getLogLevel()).toBe("warn");

process.env.LOG_LEVEL = "error";
expect(otelModule.getLogLevel()).toBe("error");
} finally {
await handle.shutdown();
}
});

it("works with no endpoint configured (graceful no-op exporters)", () => {
const handle = setupOtel({ serviceName: "no-endpoint" });
expect(handle).toBeDefined();
Expand Down
Loading