diff --git a/airflow-core/src/airflow/plugins_manager.py b/airflow-core/src/airflow/plugins_manager.py index ff3c620b9341f..ccc29fa828882 100644 --- a/airflow-core/src/airflow/plugins_manager.py +++ b/airflow-core/src/airflow/plugins_manager.py @@ -133,7 +133,16 @@ def __register_plugins(plugin_instances: list[AirflowPlugin], errors: dict[str, if not settings.LAZY_LOAD_PROVIDERS: __register_plugins(*_load_providers_plugins()) - log.debug("Loading %d plugin(s) took %.2f ms", len(plugins), timer.duration) + if import_errors: + log.warning( + "Failed to load %d plugin file(s): %s", + len(import_errors), + sorted(import_errors.keys()), + ) + elif not plugins: + log.debug("No plugins loaded (plugins folder is empty or contains no valid plugins)") + else: + log.debug("Loading %d plugin(s) took %.2f ms", len(plugins), timer.duration) return plugins, import_errors diff --git a/airflow-core/tests/unit/plugins/test_plugins_manager.py b/airflow-core/tests/unit/plugins/test_plugins_manager.py index 7f458ef4d906b..bef30bcd253b0 100644 --- a/airflow-core/tests/unit/plugins/test_plugins_manager.py +++ b/airflow-core/tests/unit/plugins/test_plugins_manager.py @@ -91,6 +91,28 @@ def test_loads_filesystem_plugins(self, caplog): assert [r for r in caplog.record_tuples if not r[0].startswith("opentelemetry.")] == [] + def test_empty_plugins_folder_logs_no_failure(self, caplog, tmp_path): + from airflow import plugins_manager + + with ( + caplog.at_level(logging.DEBUG, logger="airflow.plugins_manager"), + conf_vars( + { + ("core", "plugins_folder"): os.fspath(tmp_path), + ("core", "load_examples"): "False", + } + ), + mock.patch("airflow.plugins_manager._load_entrypoint_plugins", return_value=([], [])), + mock.patch("airflow.plugins_manager._load_providers_plugins", return_value=([], [])), + ): + plugins, import_errors = plugins_manager._get_plugins() + + assert plugins == [] + assert import_errors == {} + received_logs = caplog.text + assert "Failed to load" not in received_logs + assert "No plugins loaded" in received_logs + def test_loads_filesystem_plugins_exception(self, caplog, tmp_path): from airflow import plugins_manager @@ -108,6 +130,7 @@ def test_loads_filesystem_plugins_exception(self, caplog, tmp_path): received_logs = caplog.text assert "Failed to load plugin" in received_logs + assert "Failed to load 1 plugin file(s)" in received_logs assert "testplugin.py" in received_logs def test_duplicate_plugin_name_does_not_prevent_loading_subsequent_plugins(self):