Skip to content

feat(logging): honour LOG_LEVEL env var on startup#3030

Merged
enoch85 merged 1 commit into
Maintainerr:developmentfrom
Arvuno:feat/log-level-env-override
Jun 5, 2026
Merged

feat(logging): honour LOG_LEVEL env var on startup#3030
enoch85 merged 1 commit into
Maintainerr:developmentfrom
Arvuno:feat/log-level-env-override

Conversation

@Arvuno

@Arvuno Arvuno commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

What

The winston factory now reads an optional LOG_LEVEL env var. When set to one of fatal|error|warn|info|verbose|debug it overrides the value persisted in the log_settings row for the lifetime of the process. Unknown values produce a single warning at startup and fall back to the persisted level. When the env var is unset, behaviour is identical to before.

The factory also tolerates a missing log_settings row during the first-boot migration window.

Why

Today the only way to change verbosity is to flip it in the UI, which writes the database. That is awkward in two common ops situations: triage a single container with extra debug logs, and keep an unattended container on a quieter level without baking it into the DB. The env override is the standard knob for both.

How

  • Read process.env.LOG_LEVEL, trim, lower-case.
  • If it matches the allowed set, use it; otherwise warn once and fall through.
  • Default to the persisted row's level, or 'info' if the row hasn't been created yet.
  • Apply the same null-safety to max_size/max_files so the factory does not throw on a fresh DB.

Notes

  • Pre-existing TypeScript strict-mode warnings in the logging module are untouched (decorators, logSettings narrowing, etc.) — they are not introduced by this change.

@Arvuno Arvuno requested a review from enoch85 as a code owner June 3, 2026 21:08
Operators can override the persisted log level for a single container by
setting LOG_LEVEL=debug|verbose|info|warn|error|fatal in the environment.
The persisted setting (the value the UI shows) is left untouched, so the
override is never baked into the database. An unrecognised value logs a
single warning at startup and falls back to the persisted level; with the
env var unset, behaviour is unchanged.

The winston factory now also tolerates a missing log_settings row during
first boot, falling back to the shared DEFAULT_LOG_* constants for level,
max size, and max files instead of dereferencing a null row.

Co-Authored-By: Arvuno <hi@arvuno.xyz>
@enoch85 enoch85 force-pushed the feat/log-level-env-override branch from 923b656 to d32c397 Compare June 5, 2026 11:05
@enoch85

enoch85 commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Took this over and pushed a cleaned-up version. Summary of what changed:

  • Wired the resolved level into createLogger. The env value was being computed but never applied, so LOG_LEVEL had no effect before. It now actually overrides the persisted level.
  • Made the factory null-safe for a missing log_settings row on first boot, so it no longer throws before the row exists.
  • Replaced the hardcoded fallbacks with the shared DEFAULT_LOG_* constants, and derived the allowed-level list from LOG_LEVELS in contracts instead of a local set.
  • Extracted the resolution into a small resolveLogLevel helper with unit tests.

The branch is now logging-only - the health endpoint changes were dropped since they already landed via #3029. Full suite passes. Thanks for the original work, kept you credited on the commit.

@enoch85 enoch85 merged commit 8788609 into Maintainerr:development Jun 5, 2026
11 checks passed
maintainerr-automation Bot added a commit that referenced this pull request Jun 5, 2026
* fix(rules): save rule groups from incomplete payloads and clarify YAML import errors (#3045)

- Default keepLogsForMonths to 6 when omitted instead of binding NaN (create and update)
- Return a structured {code:0} error instead of a silent empty 201 when a save fails
- Guard updateRules against a missing collection block (no throw, no spurious media wipe)
- Clearer message for invalid YAML; log real server-side faults instead of mislabeling them as syntax errors

Refs #3044

* feat(server): add /api/health endpoints with DB liveness check (#3029)

* feat(server): add /api/health endpoints with DB liveness check

The application has no /health endpoint today, which makes it
awkward to wire up Kubernetes livenessProbe / readinessProbe or
Docker HEALTHCHECK directives. /api/app/status is not a substitute
because it also exercises the GitHub releases API.

Add three endpoints under /api/health:

  - GET /api/health       — combined: 200 with database=ok, or 503
                            with database=unreachable.
  - GET /api/health/live  — liveness: 200 as long as the process is
                            up, no DB call. Use for Kubernetes
                            livenessProbe (no restarts on transient
                            DB blips).
  - GET /api/health/ready — readiness: SELECT 1 against the
                            configured TypeORM datasource; returns
                            503 if the query fails. Use for
                            Kubernetes readinessProbe (stop
                            sending traffic until the DB is back).

The DB check uses the same TypeORM datasource the rest of the app
uses, so a misconfigured connection string or a full disk surfaces
as a clear 503 rather than a silent 200.

* refactor(server): move health DB check to a service; add tests and Docker HEALTHCHECK

- Extract the SELECT 1 ping into HealthService so the controller only shapes the
  response, matching the controllers-delegate-to-services pattern.
- Log the caught DB error via MaintainerrLogger (warn + debug) instead of
  swallowing it.
- Use process.uptime(); drop the instantiation-time uptime helper.
- Move the response shapes to @maintainerr/contracts (LivenessResponse,
  HealthResponse); live() returns the shared envelope; root route uses @get().
- Add HealthService and HealthController specs.
- Wire the Dockerfile HEALTHCHECK to /api/health/ready via a BASE_PATH-aware
  probe script.

---------

Co-authored-by: enoch85 <mailto@danielhansson.nu>

* docs: clarify yarn command-not-found means a stale node_modules

* fix(rules): preserve collection link and visibility on partial updates (#3046)

When the collection block is omitted from PUT /api/rules, fall back to the
saved values for manualCollection, manualCollectionName, visibleOnHome and
visibleOnRecommended instead of forwarding undefined. This stops
updateCollection from silently unlinking a manual collection (mediaServerId
cleared) or switching off Plex Home/Recommended visibility.

Follow-up to #3045.

* feat(logging): honour LOG_LEVEL env var on startup (#3030)

Operators can override the persisted log level for a single container by
setting LOG_LEVEL=debug|verbose|info|warn|error|fatal in the environment.
The persisted setting (the value the UI shows) is left untouched, so the
override is never baked into the database. An unrecognised value logs a
single warning at startup and falls back to the persisted level; with the
env var unset, behaviour is unchanged.

The winston factory now also tolerates a missing log_settings row during
first boot, falling back to the shared DEFAULT_LOG_* constants for level,
max size, and max files instead of dereferencing a null row.

Co-authored-by: enoch85 <mailto@danielhansson.nu>

* fix(notifications): validate webhook URL scheme before posting (#3031)

User-configured notification URLs were passed straight to axios.post, so a
misconfigured value pointing at file://, gopher://, or any other non-http(s)
scheme would be turned into an outbound request. These URLs have no validation
at the settings layer, so this is the only guard.

Add a shared validateWebhookUrl helper and apply it in every agent that posts
to an operator-supplied URL: the webhookUrl agents (webhook, slack, lunasea,
discord) and ntfy's server url. Unparseable URLs and non-http(s) schemes are
rejected before sending and the normalised URL is posted; the agent returns a
clear Failure result.

Also replace ntfy's regex slash-trimming with the codebase's char-based idiom,
per the no-regex convention.

Co-authored-by: enoch85 <mailto@danielhansson.nu>

* docs: refresh README - features, health endpoints, deploy examples, credits (#3048)

---------

Co-authored-by: enoch85 <mailto@danielhansson.nu>
Co-authored-by: Arvuno <hi@arvuno.xyz>
@maintainerr-automation

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.14.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Jun 6, 2026
… ➔ 3.14.0) (#207)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/maintainerr/maintainerr](https://github.com/Maintainerr/Maintainerr) | minor | `3.13.0` → `3.14.0` |

---

### Release Notes

<details>
<summary>Maintainerr/Maintainerr (ghcr.io/maintainerr/maintainerr)</summary>

### [`v3.14.0`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3140-2026-06-05)

[Compare Source](Maintainerr/Maintainerr@v3.13.0...v3.14.0)

#### Highlights

- Added `/api/health` endpoints with liveness and readiness checks for monitoring and integration with tools like Kubernetes and Docker Compose ([#&#8203;3029](Maintainerr/Maintainerr#3029)).
- Collection handler now skips media currently being streamed to avoid disrupting active viewers ([#&#8203;3027](Maintainerr/Maintainerr#3027)).
- Fixed issue where saving log settings would overwrite an active `LOG_LEVEL` environment variable override ([#&#8203;3053](Maintainerr/Maintainerr#3053)).

#### Features

- Added `/api/health` endpoints with liveness and readiness checks ([#&#8203;3029](Maintainerr/Maintainerr#3029)).
- Collection handler now skips media currently being streamed ([#&#8203;3027](Maintainerr/Maintainerr#3027)).
- Logging system now honors the `LOG_LEVEL` environment variable on startup ([#&#8203;3030](Maintainerr/Maintainerr#3030)).

#### Fixes

- Fixed issue where saving log settings would overwrite an active `LOG_LEVEL` environment variable override ([#&#8203;3053](Maintainerr/Maintainerr#3053)).
- Validated webhook URL schemes to prevent invalid or potentially harmful requests ([#&#8203;3031](Maintainerr/Maintainerr#3031)).
- Fixed issue where rule groups lost collection links and visibility on partial updates ([#&#8203;3045](Maintainerr/Maintainerr#3045), [#&#8203;3046](Maintainerr/Maintainerr#3046)).
- Fixed issue with manual collections not being found across libraries on Jellyfin/Emby ([#&#8203;3026](Maintainerr/Maintainerr#3026), [#&#8203;3042](Maintainerr/Maintainerr#3042)).
- Resolved issue where deleted media remained stuck in Jellyfin/Emby collections and caused repeated processing errors ([#&#8203;3023](Maintainerr/Maintainerr#3023), [#&#8203;3024](Maintainerr/Maintainerr#3024), [#&#8203;3040](Maintainerr/Maintainerr#3040)).
- Fixed issue where Seerr requests for episode rules incorrectly deleted entire season requests ([#&#8203;3015](Maintainerr/Maintainerr#3015)).
- Improved error notifications for collection handling failures to include the name of the failing collection ([#&#8203;3013](Maintainerr/Maintainerr#3013)).
- Used Radarr bulk exclusions endpoint to avoid duplicate 400 errors when adding exclusions ([#&#8203;3012](Maintainerr/Maintainerr#3012)).

#### Performance

- Pruned media that no longer exists on the media server to improve collection handling efficiency ([#&#8203;3023](Maintainerr/Maintainerr#3023), [#&#8203;3040](Maintainerr/Maintainerr#3040)).

#### Internal

- Refreshed README with updated features, deployment examples, and credits ([#&#8203;3048](Maintainerr/Maintainerr#3048)).
- Clarified that a missing `yarn` command indicates a stale `node_modules` directory.

#### Dependencies

- Updated 20 dependencies, including `@typescript-eslint/parser`, `react-router-dom`, `axios`, and `vite`.

#### New Contributors

- [@&#8203;Arvuno](https://github.com/Arvuno) made their first contribution in [#&#8203;3029](Maintainerr/Maintainerr#3029)

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/New_York)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDYuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIwNi4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/207
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants