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,
         )
 

Reply via email to