Skip to content

Decouple logger levels from deployment environment defaults - #15

Merged
Ryan Zhu (underthestars-zhy) merged 1 commit into
mainfrom
ryan/eng-2063-decouple-deployment-environment-from-logger-level-defaults
Jul 21, 2026
Merged

Decouple logger levels from deployment environment defaults#15
Ryan Zhu (underthestars-zhy) merged 1 commit into
mainfrom
ryan/eng-2063-decouple-deployment-environment-from-logger-level-defaults

Conversation

@underthestars-zhy

@underthestars-zhy Ryan Zhu (underthestars-zhy) commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Make programmatic logger levels take precedence over LOG_LEVEL
  • Default logger levels to info independently of DEPLOYMENT_ENV
  • Validate and normalize environment and programmatic log levels
  • Update documentation and integration coverage for the new precedence rules

Testing

  • Added and updated unit tests for precedence, defaults, normalization, invalid values, and setup validation

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
This is a breaking behavior change for deployments that relied on LOG_LEVEL overriding code or on debug-by-default in development; logging volume and incident toggles may shift until callers adjust.

Overview
Logger configuration is SDK-first and no longer tied to deployment metadata. setLogLevel() and setupOtel({ logLevel }) now override LOG_LEVEL; when nothing is set programmatically, the default is always info (not debug in development). DEPLOYMENT_ENV only sets deployment.environment on the OTel resource and does not affect verbosity.

Validation and edge cases: LOG_LEVEL is trimmed and case-insensitive; empty values are ignored; invalid env values warn once per distinct normalized value and fall back to info (and are not evaluated when a programmatic override is active). Invalid values passed to setLogLevel / setupOtel({ logLevel }) throw TypeError before changing state; setupOtel applies logLevel when it is explicitly provided (including levels that are falsy in other senses).

Docs, README, and tests (unit + collector integration) are updated to match the new precedence and to assert programmatic levels win even when LOG_LEVEL=silent.

Reviewed by Cursor Bugbot for commit 8741ba7. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Enhancements

    • Programmatic log-level settings now take precedence over LOG_LEVEL.
    • LOG_LEVEL values are trimmed and case-insensitive; empty values use the default info level.
    • Invalid environment values fall back to info with a warning, while invalid programmatic values are rejected immediately.
    • DEPLOYMENT_ENV now only sets telemetry metadata and no longer affects logging.
  • Documentation

    • Updated configuration, logging, API, environment, and architecture guidance to reflect the new precedence and validation behavior.

Copilot AI review requested due to automatic review settings July 21, 2026 22:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The logger now resolves levels in the order of programmatic configuration, valid LOG_LEVEL, and info. Environment values are normalized and invalid values warn once; invalid programmatic values throw TypeError. Setup behavior, tests, and documentation reflect these rules.

Changes

Log-Level Resolution

Layer / File(s) Summary
Logger resolution and validation
src/logger.ts, tests/logger.test.ts
Programmatic levels override LOG_LEVEL; environment values are normalized and validated; invalid values warn once, while invalid programmatic values throw TypeError.
Setup wiring and integration coverage
src/setup.ts, tests/setup.test.ts, tests/integration/otel-collector.test.ts
Defined setup values are applied, invalid setup values preserve state, and integration coverage verifies programmatic precedence and deployment metadata.
Documentation alignment
README.md, docs/**/*.mdx
Documentation describes precedence, normalization, fallback behavior, and DEPLOYMENT_ENV as telemetry metadata only.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant Logger
  participant LOG_LEVEL as process.env.LOG_LEVEL
  Application->>Logger: log(message)
  Logger->>Logger: resolveLevel()
  Logger->>LOG_LEVEL: read level when no override exists
  LOG_LEVEL-->>Logger: normalized or invalid value
  Logger-->>Application: emit or suppress record
Loading

Possibly related PRs

  • photon-hq/otel#14: Both changes modify logger default-level resolution based on deployment environment.

Poem

A rabbit hops through logs so bright,
Programmed levels lead the night.
Env values trim and softly warn,
Invalid spells are quickly torn.
info awaits when paths are still—
Thump, thump, clarity by will!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: logger levels no longer depend on deployment environment defaults.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ryan/eng-2063-decouple-deployment-environment-from-logger-level-defaults

Comment @coderabbitai help to get the list of available commands.

@underthestars-zhy
Ryan Zhu (underthestars-zhy) merged commit c5d5d6a into main Jul 21, 2026
8 of 9 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/reference/environment.mdx`:
- Around line 144-147: Clarify the logging environment behavior in
docs/reference/environment.mdx lines 144-147: each log call rereads the running
process’s process.env.LOG_LEVEL, so only in-process mutations are observed live;
deployment-side changes require a restart or redeploy. Also qualify the
“environment changes” wording in docs/concepts/architecture.mdx lines 121-122 to
explicitly mean changes to the running process’s process.env.

In `@tests/logger.test.ts`:
- Around line 264-268: Update the invalid log-level assertions around
fresh.setLogLevel in tests/logger.test.ts (lines 264-268) and the corresponding
assertion in tests/setup.test.ts (lines 87-95) to require a TypeError while
preserving the existing error message validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b09dfd4b-0e21-4b7b-84cc-1bc9c1b82d7e

📥 Commits

Reviewing files that changed from the base of the PR and between 82c15a5 and 8741ba7.

📒 Files selected for processing (11)
  • README.md
  • docs/concepts/architecture.mdx
  • docs/configuration.mdx
  • docs/guides/logging.mdx
  • docs/reference/api.mdx
  • docs/reference/environment.mdx
  • src/logger.ts
  • src/setup.ts
  • tests/integration/otel-collector.test.ts
  • tests/logger.test.ts
  • tests/setup.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Cursor Bugbot
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Prefer interface for defining object shapes in TypeScript rather than type aliases

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use as const const assertions for immutable values and literal types
Leverage TypeScript type narrowing instead of type assertions
Use meaningful variable names instead of magic numbers; extract descriptive constants
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, and never use var
Always await promises in async functions and use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors

Files:

  • tests/setup.test.ts
  • src/setup.ts
  • tests/integration/otel-collector.test.ts
  • tests/logger.test.ts
  • src/logger.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{js,jsx,ts,tsx}: Use camelCase for variable and function names in JavaScript/TypeScript
Use PascalCase for class and component names in JavaScript/TypeScript
Always use async/await for promise handling instead of .then() chains
Include JSDoc comments for exported functions and classes
Use meaningful variable names that clearly describe their purpose
Avoid deeply nested conditionals; use early returns or guard clauses instead
Use const by default, let when reassignment is needed, avoid var

**/*.{js,jsx,ts,tsx}: Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully; don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Group related code together and separate concerns
Add rel="noopener" when using target="_blank" on links
Avoid dangerouslySetInnerHTML unless absolutely necessary
Don't use eval() or assign directly to document.cookie
Validate and sanitize user input
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Avoid barrel files (index files that re-export everything)
Use proper image components (for example, Next.js <Image>) over <img> tags
Use next/head or the App Router metadata API for head elements
Use Server Components for async data fetching instead of async Client Components

Files:

  • tests/setup.test.ts
  • src/setup.ts
  • tests/integration/otel-collector.test.ts
  • tests/logger.test.ts
  • src/logger.ts
**/*.test.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Write unit tests for all public functions and components

Files:

  • tests/setup.test.ts
  • tests/integration/otel-collector.test.ts
  • tests/logger.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,jsx,ts,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests; use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat; avoid excessive describe nesting

Files:

  • tests/setup.test.ts
  • tests/integration/otel-collector.test.ts
  • tests/logger.test.ts
🔇 Additional comments (11)
src/logger.ts (1)

14-15: LGTM!

Also applies to: 25-29, 31-52, 54-63, 65-80

tests/logger.test.ts (1)

170-257: LGTM!

src/setup.ts (1)

78-81: LGTM!

Also applies to: 239-239

tests/setup.test.ts (1)

5-20: LGTM!

tests/integration/otel-collector.test.ts (1)

309-312: LGTM!

Also applies to: 402-402

README.md (1)

9-9: LGTM!

Also applies to: 101-102, 137-172

docs/concepts/architecture.mdx (1)

93-111: LGTM!

docs/configuration.mdx (1)

6-13: LGTM!

Also applies to: 147-186, 260-269

docs/guides/logging.mdx (1)

100-118: LGTM!

Also applies to: 194-194

docs/reference/api.mdx (1)

55-55: LGTM!

Also applies to: 145-158

docs/reference/environment.mdx (1)

3-25: LGTM!

Also applies to: 123-123, 141-143

Comment on lines +144 to +147
When no programmatic level is active, `LOG_LEVEL` is checked on every log call
so deployment changes take effect immediately. Both `setupOtel({ logLevel })`
and `setLogLevel()` take precedence over it. If neither source supplies a valid
level, the logger defaults to `info`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify that only in-process environment mutations are observed live.

A deployment-side environment update does not mutate an already-running Node/Bun process. State that each call rereads process.env.LOG_LEVEL; deployment changes require restart/redeploy unless application code updates process.env.

  • docs/reference/environment.mdx#L144-L147: replace “deployment changes take effect immediately” with the process-local behavior.
  • docs/concepts/architecture.mdx#L121-L122: qualify “environment changes” as changes to the running process’s process.env.
📍 Affects 2 files
  • docs/reference/environment.mdx#L144-L147 (this comment)
  • docs/concepts/architecture.mdx#L121-L122
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/environment.mdx` around lines 144 - 147, Clarify the logging
environment behavior in docs/reference/environment.mdx lines 144-147: each log
call rereads the running process’s process.env.LOG_LEVEL, so only in-process
mutations are observed live; deployment-side changes require a restart or
redeploy. Also qualify the “environment changes” wording in
docs/concepts/architecture.mdx lines 121-122 to explicitly mean changes to the
running process’s process.env.

Comment thread tests/logger.test.ts
Comment on lines +264 to +268
expect(() =>
Reflect.apply(fresh.setLogLevel, undefined, ["verbose"])
).toThrowError(
"Invalid log level; expected one of: debug, info, warn, error, silent."
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files tests/logger.test.ts tests/setup.test.ts docs/concepts/architecture.mdx docs/reference/environment.mdx src || true

printf '\n== outlines ==\n'
for f in tests/logger.test.ts tests/setup.test.ts docs/concepts/architecture.mdx docs/reference/environment.mdx; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    wc -l "$f"
  fi
done

printf '\n== relevant excerpts ==\n'
sed -n '230,290p' tests/logger.test.ts
printf '\n---\n'
sed -n '60,120p' tests/setup.test.ts
printf '\n---\n'
sed -n '100,160p' docs/concepts/architecture.mdx
printf '\n---\n'
sed -n '130,170p' docs/reference/environment.mdx

Repository: photon-hq/otel

Length of output: 7970


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== grep for log level validation ==\n'
rg -n "Invalid log level|setLogLevel|setupOtel|getLogLevel|isOtelActive|TypeError" tests src docs -S

Repository: photon-hq/otel

Length of output: 17949


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# show the exact implementation and any typed error assertions nearby
for f in $(git ls-files | rg '(^|/)(logger|setup)\.(test|ts)$|(^|/)logger\.ts|(^|/)setup\.ts|(^|/)otel'); do
  echo "### $f"
  rg -n "throw new|TypeError|Invalid log level|setLogLevel|setupOtel|getLogLevel|isOtelActive" "$f" -n -S || true
done

Repository: photon-hq/otel

Length of output: 1204


Assert the thrown error type too. toThrowError(...) still passes if these paths start throwing a plain Error, so add a TypeError assertion in both tests/logger.test.ts and tests/setup.test.ts.

📍 Affects 2 files
  • tests/logger.test.ts#L264-L268 (this comment)
  • tests/setup.test.ts#L87-L95
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/logger.test.ts` around lines 264 - 268, Update the invalid log-level
assertions around fresh.setLogLevel in tests/logger.test.ts (lines 264-268) and
the corresponding assertion in tests/setup.test.ts (lines 87-95) to require a
TypeError while preserving the existing error message validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Fight on!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants