Add LOG_FORMAT environment variable — set to json to switch all stdout logging to single-line JSON objects suitable for log aggregators (Loki, Fluentd, CloudWatch, Datadog)
Added
LOG_FORMAT environment variable (json to enable, unset or any other value for default plain-text)
JSONFormatter class in env.py for early stdlib logging (covers module imports, alembic migrations)
_json_sink() Loguru sink in logger.py for runtime logging
Changed
start_logger() conditionally uses JSON sink or plain-text format based on LOG_FORMAT
migrations/env.py re-applies JSONFormatter after alembic's fileConfig() to preserve JSON logging during migrations
ASCII banner is suppressed when LOG_FORMAT=json to keep log stream parseable
JSON log fields
Field
Description
ts
ISO 8601 timestamp with milliseconds (UTC)
level
debug, info, warn, error, fatal
msg
Log message
caller
Logger name (early) or module:function:line (Loguru)
Default behavior (no LOG_FORMAT set) is completely unchanged
Two-phase approach: stdlib JSONFormatter handles early startup logs before Loguru is initialized, then _json_sink takes over after start_logger() runs
Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/open-webui/open-webui/pull/21747
**Author:** [@goto1134](https://github.com/goto1134)
**Created:** 2/22/2026
**Status:** ✅ Merged
**Merged:** 2/22/2026
**Merged by:** [@tjbck](https://github.com/tjbck)
**Base:** `dev` ← **Head:** `feat/json-logging`
---
### 📝 Commits (3)
- [`39b8c94`](https://github.com/open-webui/open-webui/commit/39b8c945f41d586df7e83d78e2c10843e064b746) feat: add LOG_FORMAT env var with JSON formatter for early logging
- [`eea1130`](https://github.com/open-webui/open-webui/commit/eea11303d44c62f7683c49b9b6e4f48c9ad36e85) feat: add JSON sink for Loguru when LOG_FORMAT=json
- [`922f854`](https://github.com/open-webui/open-webui/commit/922f8547bd1c3c00754c9571526b5e6adfe4a7a8) feat: suppress ASCII banner and fix alembic logging in JSON mode
### 📊 Changes
**4 files changed** (+101 additions, -11 deletions)
<details>
<summary>View changed files</summary>
📝 `backend/open_webui/env.py` (+46 -1)
📝 `backend/open_webui/main.py` (+3 -1)
📝 `backend/open_webui/migrations/env.py` (+9 -1)
📝 `backend/open_webui/utils/logger.py` (+43 -8)
</details>
### 📄 Description
# Changelog Entry
### Description
- Add `LOG_FORMAT` environment variable — set to `json` to switch all stdout logging to single-line JSON objects suitable for log aggregators (Loki, Fluentd, CloudWatch, Datadog)
### Added
- `LOG_FORMAT` environment variable (`json` to enable, unset or any other value for default plain-text)
- `JSONFormatter` class in `env.py` for early stdlib logging (covers module imports, alembic migrations)
- `_json_sink()` Loguru sink in `logger.py` for runtime logging
### Changed
- `start_logger()` conditionally uses JSON sink or plain-text format based on `LOG_FORMAT`
- `migrations/env.py` re-applies `JSONFormatter` after alembic's `fileConfig()` to preserve JSON logging during migrations
- ASCII banner is suppressed when `LOG_FORMAT=json` to keep log stream parseable
### JSON log fields
| Field | Description |
|---|---|
| `ts` | ISO 8601 timestamp with milliseconds (UTC) |
| `level` | `debug`, `info`, `warn`, `error`, `fatal` |
| `msg` | Log message |
| `caller` | Logger name (early) or `module:function:line` (Loguru) |
| `extra` | Loguru extra context (when present) |
| `error` | Exception traceback (when present) |
| `stacktrace` | Stack info (when present) |
### Example output
```json
{"ts": "2026-02-22T20:14:53.386+00:00", "level": "info", "msg": "GLOBAL_LOG_LEVEL: INFO", "caller": "open_webui.env"}
{"ts": "2026-02-22T20:15:02.245+00:00", "level": "info", "msg": "Context impl SQLiteImpl.", "caller": "alembic.runtime.migration"}
```
---
### Additional Information
- Default behavior (no `LOG_FORMAT` set) is completely unchanged
- Two-phase approach: stdlib `JSONFormatter` handles early startup logs before Loguru is initialized, then `_json_sink` takes over after `start_logger()` runs
### Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/21747
Author: @goto1134
Created: 2/22/2026
Status: ✅ Merged
Merged: 2/22/2026
Merged by: @tjbck
Base:
dev← Head:feat/json-logging📝 Commits (3)
39b8c94feat: add LOG_FORMAT env var with JSON formatter for early loggingeea1130feat: add JSON sink for Loguru when LOG_FORMAT=json922f854feat: suppress ASCII banner and fix alembic logging in JSON mode📊 Changes
4 files changed (+101 additions, -11 deletions)
View changed files
📝
backend/open_webui/env.py(+46 -1)📝
backend/open_webui/main.py(+3 -1)📝
backend/open_webui/migrations/env.py(+9 -1)📝
backend/open_webui/utils/logger.py(+43 -8)📄 Description
Changelog Entry
Description
LOG_FORMATenvironment variable — set tojsonto switch all stdout logging to single-line JSON objects suitable for log aggregators (Loki, Fluentd, CloudWatch, Datadog)Added
LOG_FORMATenvironment variable (jsonto enable, unset or any other value for default plain-text)JSONFormatterclass inenv.pyfor early stdlib logging (covers module imports, alembic migrations)_json_sink()Loguru sink inlogger.pyfor runtime loggingChanged
start_logger()conditionally uses JSON sink or plain-text format based onLOG_FORMATmigrations/env.pyre-appliesJSONFormatterafter alembic'sfileConfig()to preserve JSON logging during migrationsLOG_FORMAT=jsonto keep log stream parseableJSON log fields
tsleveldebug,info,warn,error,fatalmsgcallermodule:function:line(Loguru)extraerrorstacktraceExample output
Additional Information
LOG_FORMATset) is completely unchangedJSONFormatterhandles early startup logs before Loguru is initialized, then_json_sinktakes over afterstart_logger()runsContributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.