This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 9f87af96fd9 Add uvicorn_logging_level config option to control API
server access logs (#55405) (#56062)
9f87af96fd9 is described below
commit 9f87af96fd9df92faea1e38f33aba48a219ba65b
Author: SeongJun Shin <[email protected]>
AuthorDate: Thu Dec 11 09:59:25 2025 +0900
Add uvicorn_logging_level config option to control API server access logs
(#55405) (#56062)
* Fix uvicorn INFO logs ignoring logging_level=ERROR (#55405)
* Fix unit tests for uvicorn log level and fix trailing whitespace
* Fix code formatting with prek
* Add uvicorn_logging_level to config.yml
---------
Co-authored-by: Andrew <[email protected]>
---
airflow-core/src/airflow/cli/commands/api_server_command.py | 9 ++++++++-
airflow-core/src/airflow/config_templates/config.yml | 9 +++++++++
airflow-core/src/airflow/configuration.py | 3 +++
airflow-core/src/airflow/utils/serve_logs/core.py | 7 ++++++-
airflow-core/tests/unit/cli/commands/test_api_server_command.py | 2 ++
5 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/airflow-core/src/airflow/cli/commands/api_server_command.py
b/airflow-core/src/airflow/cli/commands/api_server_command.py
index cdecbea9b8e..1753499916a 100644
--- a/airflow-core/src/airflow/cli/commands/api_server_command.py
+++ b/airflow-core/src/airflow/cli/commands/api_server_command.py
@@ -30,6 +30,7 @@ import uvicorn
from airflow import settings
from airflow.cli.commands.daemon_utils import run_command_with_daemon_option
+from airflow.configuration import conf
from airflow.exceptions import AirflowConfigException
from airflow.typing_compat import ParamSpec
from airflow.utils import cli as cli_utils
@@ -76,6 +77,11 @@ def _run_api_server(args, apps: str, num_workers: int,
worker_timeout: int, prox
setproctitle(f"airflow api_server -- host:{args.host}
port:{args.port}")
+ # Get uvicorn logging configuration from Airflow settings
+ uvicorn_log_level = conf.get("logging", "uvicorn_logging_level",
fallback="info").lower()
+ # Control access log based on uvicorn log level - disable for ERROR and
above
+ access_log_enabled = uvicorn_log_level not in ("error", "critical",
"fatal")
+
uvicorn_kwargs = {
"host": args.host,
"port": args.port,
@@ -85,7 +91,8 @@ def _run_api_server(args, apps: str, num_workers: int,
worker_timeout: int, prox
"timeout_worker_healthcheck": worker_timeout,
"ssl_keyfile": ssl_key,
"ssl_certfile": ssl_cert,
- "access_log": True,
+ "access_log": access_log_enabled,
+ "log_level": uvicorn_log_level,
"proxy_headers": proxy_headers,
}
# Only set the log_config if it is provided, otherwise use the default
uvicorn logging configuration.
diff --git a/airflow-core/src/airflow/config_templates/config.yml
b/airflow-core/src/airflow/config_templates/config.yml
index 285f02c84a6..a8412155042 100644
--- a/airflow-core/src/airflow/config_templates/config.yml
+++ b/airflow-core/src/airflow/config_templates/config.yml
@@ -842,6 +842,15 @@ logging:
type: string
example: ~
default: "WARNING"
+ uvicorn_logging_level:
+ description: |
+ Logging level for uvicorn (API server and serve-logs).
+
+ Supported values: ``CRITICAL``, ``ERROR``, ``WARNING``, ``INFO``,
``DEBUG``.
+ version_added: 3.2.0
+ type: string
+ example: ~
+ default: "INFO"
logging_config_class:
description: |
Logging class
diff --git a/airflow-core/src/airflow/configuration.py
b/airflow-core/src/airflow/configuration.py
index 793718669a6..780913f2eea 100644
--- a/airflow-core/src/airflow/configuration.py
+++ b/airflow-core/src/airflow/configuration.py
@@ -296,6 +296,9 @@ class AirflowConfigParser(_SharedAirflowConfigParser):
("logging", "fab_logging_level"): _available_logging_levels,
# celery_logging_level can be empty, which uses logging_level as
fallback
("logging", "celery_logging_level"): [*_available_logging_levels, ""],
+ # uvicorn and gunicorn logging levels for web servers
+ ("logging", "uvicorn_logging_level"): _available_logging_levels,
+ ("logging", "gunicorn_logging_level"): _available_logging_levels,
("webserver", "analytical_tool"): ["google_analytics", "metarouter",
"segment", "matomo", ""],
("api", "grid_view_sorting_order"): ["topological",
"hierarchical_alphabetical"],
}
diff --git a/airflow-core/src/airflow/utils/serve_logs/core.py
b/airflow-core/src/airflow/utils/serve_logs/core.py
index ddd44c5bc8f..c09a9f603f3 100644
--- a/airflow-core/src/airflow/utils/serve_logs/core.py
+++ b/airflow-core/src/airflow/utils/serve_logs/core.py
@@ -49,8 +49,13 @@ def serve_logs(port=None):
logger.info("Starting log server on %s", serve_log_uri)
+ # Get uvicorn logging configuration from Airflow settings
+ uvicorn_log_level = conf.get("logging", "uvicorn_logging_level",
fallback="info").lower()
+
# Use uvicorn directly for ASGI applications
- uvicorn.run("airflow.utils.serve_logs.log_server:get_app", host="",
port=port, log_level="info")
+ uvicorn.run(
+ "airflow.utils.serve_logs.log_server:get_app", host="", port=port,
log_level=uvicorn_log_level
+ )
# Log serving is I/O bound and has low concurrency, so single process is
sufficient
diff --git a/airflow-core/tests/unit/cli/commands/test_api_server_command.py
b/airflow-core/tests/unit/cli/commands/test_api_server_command.py
index d633a6791c0..58a0923a2f9 100644
--- a/airflow-core/tests/unit/cli/commands/test_api_server_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_api_server_command.py
@@ -194,6 +194,7 @@ class TestCliApiServer(_CommonCLIUvicornTestClass):
"timeout_graceful_shutdown": args.worker_timeout,
"timeout_worker_healthcheck": args.worker_timeout,
"access_log": True,
+ "log_level": "info",
"proxy_headers": args.proxy_headers,
**expected_additional_kwargs,
},
@@ -246,6 +247,7 @@ class TestCliApiServer(_CommonCLIUvicornTestClass):
ssl_keyfile=None,
ssl_certfile=None,
access_log=True,
+ log_level="info",
proxy_headers=False,
)