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

时间:2015-04-30 02:44:01

标签: c++ multithreading logging poco-libraries

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

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

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

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

有没有办法让每个日志行都“原子化”?

注意:我还没有检查过登录SimpleFileChannel时是否出现同样的问题,但是如果是这样的话我也需要一个解决方案:)< / p>

2 个答案:

答案 0 :(得分:1)

ConsoleChannel日志记录操作受static FastMutex保护。但是,要在Windows上提供正确的Unicode文本日志记录,ConsoleChannel defaults to WindowsConsoleChannel会将UTF-8转换为UTF-16。

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

鉴于这一切,即使没有自定义同步方案,上面发布的代码示例也应该可以正常工作。

另请注意,每当您登录到相同的控制台通道类型时,您的线程将在每个日志操作期间相互等待。为避免出现瓶颈,您可以考虑使用AsyncChannel

(简单)FileChannel受non-static FastMutex保护,因此只要每个线程都登录到自己的文件或者它们都通过同一个通道实例登录到同一个文件,它就不会遇到同样的问题。我从未尝试过后者,但直觉上听起来不是正确的事情。

答案 1 :(得分:0)

我做了一个“解决方案”。它有一些缺陷:它可能重新发明轮子,它可能不是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);

NB。 make_AutoPtrstd::make_unique相同,但我使用Poco::AutoPtr来避免重复输入类型名称。