Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions onnxruntime/core/session/inference_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,14 @@ class InferenceSession {
/// convenience pointer to logger. should always be the same as session_state_.Logger();
const logging::Logger* session_logger_;

/// Logging manager if provided.
/// When user_logging_function is set, user_logging_manager_ owns the LoggingManager and
/// logging_manager_ points to it. Both MUST be declared before owned_session_logger_ and
/// execution_providers_ so they outlive the logger and EPs during destruction (C++ destroys
/// members in reverse declaration order).
logging::LoggingManager* logging_manager_;
std::unique_ptr<logging::LoggingManager> user_logging_manager_;

/// Logger for this session. WARNING: Will contain nullptr if logging_manager_ is nullptr.
/// This MUST be declared before execution_providers_ so the logger outlives EPs during destruction
/// (C++ destroys members in reverse declaration order), allowing EP teardown callbacks to safely
Expand Down Expand Up @@ -901,12 +909,6 @@ class InferenceSession {
return session_options_.IsLoadCancellationFlagSet();
};

/// Logging manager if provided.
logging::LoggingManager* logging_manager_;

/// User specified logging mgr; logging_manager_ is simply the ptr in this unique_ptr when available
std::unique_ptr<logging::LoggingManager> user_logging_manager_;

// Profiler for this session.
profiling::Profiler session_profiler_;

Expand Down
42 changes: 42 additions & 0 deletions onnxruntime/test/framework/inference_session_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3159,5 +3159,47 @@ TEST(InferenceSessionTests, SessionLoggerOutlivesEPsWithMultipleEPs) {
ASSERT_TRUE(logger_valid_ep2);
}

TEST(InferenceSessionTests, SessionLoggerOutlivesEPsWithUserLoggingFunction) {
// Test the user_logging_function path, where the session owns a per-session LoggingManager
// (user_logging_manager_). Both the LoggingManager and its Logger must outlive EPs during
// session destruction.
std::vector<std::string> log_msgs;
bool logger_was_valid_in_dtor = false;

{
SessionOptions so;
so.session_logid = "SessionLoggerUserLoggingFn";
so.session_log_severity_level = static_cast<int>(logging::Severity::kVERBOSE);
so.user_logging_function = [](void* param, OrtLoggingLevel severity, const char* category,
const char* logid, const char* code_location, const char* message) {
ORT_UNUSED_PARAMETER(severity);
ORT_UNUSED_PARAMETER(category);
ORT_UNUSED_PARAMETER(logid);
ORT_UNUSED_PARAMETER(code_location);
auto* msgs = reinterpret_cast<std::vector<std::string>*>(param);
msgs->push_back(std::string(message));
};
so.user_logging_param = &log_msgs;

InferenceSession session{so, GetEnvironment()};
ASSERT_STATUS_OK(session.RegisterExecutionProvider(
std::make_unique<LoggingOnDestroyExecutionProvider>(&logger_was_valid_in_dtor)));
ASSERT_STATUS_OK(session.Load(MODEL_URI));
ASSERT_STATUS_OK(session.Initialize());

// Session goes out of scope here. user_logging_manager_ and owned_session_logger_ must
// outlive execution_providers_ so EP teardown logging is safe.
}

ASSERT_TRUE(logger_was_valid_in_dtor);

// Verify the EP's teardown log message was captured by the user logging function.
bool found_teardown_msg = std::any_of(log_msgs.begin(), log_msgs.end(), [](const std::string& msg) {
return msg.find("LoggingOnDestroyExecutionProvider teardown") != std::string::npos;
});
ASSERT_TRUE(found_teardown_msg)
<< "Expected EP teardown log message not found via user_logging_function.";
}

} // namespace test
} // namespace onnxruntime
Loading