diff --git a/onnxruntime/__init__.py b/onnxruntime/__init__.py index 31bf8082fa9cc..55864a263ccbf 100644 --- a/onnxruntime/__init__.py +++ b/onnxruntime/__init__.py @@ -56,6 +56,7 @@ get_all_providers, # noqa: F401 get_available_providers, # noqa: F401 get_build_info, # noqa: F401 + get_default_logger_severity, # noqa: F401 get_device, # noqa: F401 get_ep_devices, # noqa: F401 get_version_string, # noqa: F401 diff --git a/onnxruntime/python/onnxruntime_pybind_module.cc b/onnxruntime/python/onnxruntime_pybind_module.cc index e1c883f960dde..a9d3f100d9739 100644 --- a/onnxruntime/python/onnxruntime_pybind_module.cc +++ b/onnxruntime/python/onnxruntime_pybind_module.cc @@ -3,6 +3,7 @@ #include "onnxruntime_pybind_exceptions.h" #include "onnxruntime_pybind_module_functions.h" +#include #include #include "core/providers/get_execution_providers.h" #include "onnxruntime_config.h" @@ -33,7 +34,21 @@ OrtEnv* GetOrtEnv() { } static Status CreateOrtEnv() { Env::Default().GetTelemetryProvider().SetLanguageProjection(OrtLanguageProjection::ORT_PROJECTION_PYTHON); - OrtEnv::LoggingManagerConstructionInfo lm_info{nullptr, nullptr, ORT_LOGGING_LEVEL_WARNING, "Default"}; + + // Allow the default logging severity to be configured via ORT_LOGGING_LEVEL before the first import. + // Valid integer values: 0 (Verbose), 1 (Info), 2 (Warning), 3 (Error), 4 (Fatal). + OrtLoggingLevel logging_level = ORT_LOGGING_LEVEL_WARNING; + const std::string logging_env = Env::Default().GetEnvironmentVar("ORT_LOGGING_LEVEL"); + if (!logging_env.empty()) { + char* end = nullptr; + long level = std::strtol(logging_env.c_str(), &end, 10); + if (end != logging_env.c_str() && *end == '\0' && + level >= ORT_LOGGING_LEVEL_VERBOSE && level <= ORT_LOGGING_LEVEL_FATAL) { + logging_level = static_cast(level); + } + } + + OrtEnv::LoggingManagerConstructionInfo lm_info{nullptr, nullptr, logging_level, "Default"}; Status status; ort_env = OrtEnv::GetOrCreateInstance(lm_info, status, use_global_tp ? &global_tp_options : nullptr).release(); if (!status.IsOK()) return status; diff --git a/onnxruntime/python/onnxruntime_pybind_state.cc b/onnxruntime/python/onnxruntime_pybind_state.cc index 1a925a16a6a71..c7704d8292e4c 100644 --- a/onnxruntime/python/onnxruntime_pybind_state.cc +++ b/onnxruntime/python/onnxruntime_pybind_state.cc @@ -1467,6 +1467,11 @@ void addGlobalMethods(py::module& m) { default_logging_manager->SetDefaultLoggerSeverity(static_cast(severity)); }, "Sets the default logging severity. 0:Verbose, 1:Info, 2:Warning, 3:Error, 4:Fatal"); + m.def( + "get_default_logger_severity", []() -> int { + return static_cast(logging::LoggingManager::DefaultLogger().GetSeverity()); + }, + "Gets the default logging severity. 0:Verbose, 1:Info, 2:Warning, 3:Error, 4:Fatal"); m.def( "set_default_logger_verbosity", [](int vlog_level) { logging::LoggingManager* default_logging_manager = GetEnv().GetLoggingManager(); diff --git a/onnxruntime/test/python/onnxruntime_test_python.py b/onnxruntime/test/python/onnxruntime_test_python.py index 90e67e114808f..11ea6e5f30d5c 100644 --- a/onnxruntime/test/python/onnxruntime_test_python.py +++ b/onnxruntime/test/python/onnxruntime_test_python.py @@ -9,6 +9,7 @@ import pathlib import platform import queue +import subprocess import sys import threading import unittest @@ -104,6 +105,30 @@ def test_get_build_info(self): self.assertIsNot(onnxrt.get_build_info(), None) self.assertIn("Build Info", onnxrt.get_build_info()) + def test_get_default_logger_severity(self): + # Default severity should be WARNING (2) when ORT_LOGGING_LEVEL is not set. + severity = onnxrt.get_default_logger_severity() + self.assertIsInstance(severity, int) + self.assertGreaterEqual(severity, 0) + self.assertLessEqual(severity, 4) + + def test_ort_logging_level_env_var(self): + # Verify that ORT_LOGGING_LEVEL is honored on import by running a subprocess. + for level in [0, 1, 2, 3, 4]: + result = subprocess.run( + [ + sys.executable, + "-c", + "import onnxruntime as ort; print(ort.get_default_logger_severity())", + ], + capture_output=True, + check=False, + text=True, + env={**os.environ, "ORT_LOGGING_LEVEL": str(level)}, + ) + self.assertEqual(result.returncode, 0, msg=result.stderr) + self.assertEqual(result.stdout.strip(), str(level), msg=f"Expected severity {level}, got {result.stdout.strip()}") + def test_model_serialization(self): try: so = onnxrt.SessionOptions() diff --git a/orttraining/orttraining/python/orttraining_python_module.cc b/orttraining/orttraining/python/orttraining_python_module.cc index 67e6e90726d38..9634728d33419 100644 --- a/orttraining/orttraining/python/orttraining_python_module.cc +++ b/orttraining/orttraining/python/orttraining_python_module.cc @@ -4,6 +4,7 @@ #include "orttraining/python/orttraining_pybind_common.h" #include "python/onnxruntime_pybind_mlvalue.h" +#include #include "core/common/logging/logging.h" #include "core/common/logging/severity.h" #include "core/common/path_string.h" @@ -171,7 +172,21 @@ onnxruntime::Environment& GetEnv() { static Status CreateOrtEnv() { Env::Default().GetTelemetryProvider().SetLanguageProjection(OrtLanguageProjection::ORT_PROJECTION_PYTHON); - OrtEnv::LoggingManagerConstructionInfo lm_info{nullptr, nullptr, ORT_LOGGING_LEVEL_WARNING, "Default"}; + + // Allow the default logging severity to be configured via ORT_LOGGING_LEVEL before the first import. + // Valid integer values: 0 (Verbose), 1 (Info), 2 (Warning), 3 (Error), 4 (Fatal). + OrtLoggingLevel logging_level = ORT_LOGGING_LEVEL_WARNING; + const std::string logging_env = Env::Default().GetEnvironmentVar("ORT_LOGGING_LEVEL"); + if (!logging_env.empty()) { + char* end = nullptr; + long level = std::strtol(logging_env.c_str(), &end, 10); + if (end != logging_env.c_str() && *end == '\0' && + level >= ORT_LOGGING_LEVEL_VERBOSE && level <= ORT_LOGGING_LEVEL_FATAL) { + logging_level = static_cast(level); + } + } + + OrtEnv::LoggingManagerConstructionInfo lm_info{nullptr, nullptr, logging_level, "Default"}; Status status; OrtEnvPtr ort_env = OrtEnv::GetOrCreateInstance(lm_info, status, use_global_tp ? &global_tp_options : nullptr); if (!status.IsOK()) return status;