Skip to content
Merged
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
11 changes: 9 additions & 2 deletions Foundation/include/Poco/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,15 @@ class Foundation_API Logger: public Channel
/// become invalid.

static void shutdown();
/// Shuts down the logging framework and releases all
/// Loggers.
/// Shuts down the logging framework: detaches and releases the
/// channels of all existing Loggers, so that channel resources
/// (open files, sockets, ...) are freed. The Logger instances
/// themselves remain alive, so any cached Logger references held
/// by singletons stay valid; logging through them becomes a
/// silent no-op.
///
/// Intended to be called once, as the last logging-related action
/// before the process exits.

static void names(std::vector<std::string>& names);
/// Fills the given vector with the names
Expand Down
13 changes: 12 additions & 1 deletion Foundation/src/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,18 @@ void Logger::shutdown()
{
Mutex::ScopedLock lock(_mapMtx);

_pLoggerMap.reset();
// Detach channels from all loggers instead of destroying the logger
// objects. Singletons and other components may still hold Logger
// references obtained via Logger::get() during their lifetime.
// Destroying the loggers would leave those references dangling,
// causing use-after-free during static destruction.
if (_pLoggerMap)
{
for (auto& [name, pLogger] : *_pLoggerMap)
{
if (pLogger) pLogger->setChannel(nullptr);
}
}
}


Expand Down
27 changes: 27 additions & 0 deletions Foundation/testsuite/src/LoggerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,32 @@ void LoggerTest::testFormatStdThreadName()
#endif
}

void LoggerTest::testLoggerRefSurvivesShutdown()
{
// Simulate a singleton caching a Logger& obtained before shutdown.
Logger& cached = Logger::get("TestLogger.Cached");
AutoPtr<TestChannel> pChannel = new TestChannel;
cached.setChannel(pChannel);
cached.setLevel(Message::PRIO_INFORMATION);
cached.information("before shutdown");
assertTrue (pChannel->list().size() == 1);

Logger::shutdown();

// The cached reference must still be valid; logging through it
// must be a safe no-op (channel detached).
cached.information("post-shutdown message");
cached.log(Message("x", "y", Message::PRIO_ERROR));
assertTrue (cached.getChannel().isNull());
assertTrue (pChannel->list().size() == 1);

// get() with the same name returns the same (muted) instance.
Logger& again = Logger::get("TestLogger.Cached");
assertTrue (&again == &cached);
assertTrue (again.getChannel().isNull());
}


void LoggerTest::setUp()
{
Logger::shutdown();
Expand All @@ -406,6 +432,7 @@ CppUnit::Test* LoggerTest::suite()
CppUnit_addTest(pSuite, LoggerTest, testDump);
CppUnit_addTest(pSuite, LoggerTest, testFormatThreadName);
CppUnit_addTest(pSuite, LoggerTest, testFormatStdThreadName);
CppUnit_addTest(pSuite, LoggerTest, testLoggerRefSurvivesShutdown);

return pSuite;
}
1 change: 1 addition & 0 deletions Foundation/testsuite/src/LoggerTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class LoggerTest: public CppUnit::TestCase
void testDump();
void testFormatThreadName();
void testFormatStdThreadName();
void testLoggerRefSurvivesShutdown();

void setUp();
void tearDown();
Expand Down
Loading