来自不同线程的Poco日志行相互覆盖

Poco log lines from different thread overwrite each other

本文关键字:日志 覆盖 Poco 线程      更新时间:2023-10-16

我有两个不同的线程,它们都记录到单独的Poco::WindowsColorConsoleChannel通道。

然而,有时消息看起来是交错的,例如,如果一个线程记录aaaaaaa,而另一个线程则记录bbbbbbbb,则屏幕可能看起来像abbbbbaaaaaabbb

线程使用完全不同的Logger对象和不同的通道对象。

如果使用默认通道,也会发生同样的情况。颜色控制台通道的问题尤其明显,因为它使颜色看起来像是错误的线条被着色了。

有没有一种方法可以使每条日志行成为"原子"?

注意:我还没有检查登录到SimpleFileChannel时是否会出现同样的问题,但如果是这样,那么我也需要一个解决方案:)

ConsoleChannel日志记录操作由静态FastMutex保护。但是,为了提供正确的Unicode文本日志记录,在Windows上,控制台通道默认为WindowsConsoleChannel,它将UTF-8转换为UTF-16。

因此,Application::logger()可能具有默认的WindowsConsoleChannel,而线程具有ConsoleChannnel(或其Color*版本);在任何情况下,您都必须混合使用不同的通道才能看到您所描述的效果——尽管目的地是相同的,但您是通过不同的通道进行日志记录的,这些通道由不同的互斥体保护。我认为Poco用一个静态互斥来保护所有控制台通道以避免类似的问题是有意义的。

考虑到所有这些,即使没有自定义同步方案,您在上面发布的代码示例也应该可以正常工作。

还要注意,每当您登录到相同的控制台通道类型时,您的线程都会在每次日志操作期间相互等待。为了避免瓶颈,您可以考虑使用AsyncChannel。

(简单)FileChannel由非静态FastMutex保护,因此只要每个线程都登录到自己的文件中,或者它们都通过同一个通道实例登录到同一个文件,它就不会遇到同样的问题。我从未尝试过后者,但凭直觉,这听起来不是正确的做法

我已经制定了一个"解决方案"。它有一些缺陷:它可能是在重新发明轮子,它可能不适合Poco(我对Poco还很陌生),我担心如果在线程或应用程序关闭期间抛出异常,可能会出现死锁。

但目前看来是可行的。

在头文件中:

struct RC_Semaphore: Poco::Semaphore, Poco::RefCountedObject
{
using Poco::Semaphore::Semaphore;
};
struct SemaphoreLock
{
SemaphoreLock(Poco::Semaphore &sem): sem(sem) { sem.wait(); }
~SemaphoreLock() { sem.set(); }
Poco::Semaphore &sem;
};
struct SynchronizingChannel: Poco::Channel, noncopyable
{
SynchronizingChannel(Poco::AutoPtr<RC_Semaphore> sem, Poco::AutoPtr<Poco::Channel> dest)
: sem(sem), dest(dest) {}
virtual void log(const Poco::Message& msg)
{
SemaphoreLock lock(*sem);
dest->log(msg);
}
private:
Poco::AutoPtr<RC_Semaphore> sem;
Poco::AutoPtr<Poco::Channel> dest;
} ;

用法:

// Synchronization for log channels outputting to console
auto semConsole = make_AutoPtr<RC_Semaphore>(1);
// Logging channel - main
auto chanMainRaw = make_AutoPtr<Poco::WindowsColorConsoleChannel>();
chanMainRaw->setProperty("debugColor", "green");
auto chanMain = make_AutoPtr<SynchronizingChannel>(semConsole, chanMainRaw);
// Channel that will be used by thread
auto chanThreadRaw = make_AutoPtr<Poco::WindowsColorConsoleChannel>();
chanThreadRaw->setProperty("debugColor", "magenta");
auto chanThread = make_AutoPtr<SynchronizingChannel>(semConsole, chanThreadRaw);
// (other code to set up filters can go here)
logger().setChannel(chanMain);
OtherThread::logger().setChannel(chanThread);

注:。make_AutoPtrstd::make_unique相同,但使用了Poco::AutoPtr,我使用它来避免重复类型名称。