Poco log lines from different streams overwrite each other

I have two different streams that register to split channels Poco::WindowsColorConsoleChannel

.

However, sometimes the messages appear interleaved, for example. if one thread also logs aaaaaaa

other logs bbbbbbbb

then the screen might look like abbbbbaaaaaabbb

.

The streams use completely different objects Logger

and different channel objects.

The same happens when using the default channel. The problem is especially pronounced in the cantilever color channel, as the colors appear as if the wrong lines are being painted.

Is there a way to make each log line "atomic"?

Note. I haven't tested yet if the same issue occurs when logging in SimpleFileChannel

, but if it does, I'll need a solution for that as well :)

+3


source to share


2 answers


ConsoleChannel logging is protected by static FastMutex . However, to ensure correct Unicode logging in Windows, the ConsoleChannel defaults to WindowsConsoleChannel , which converts UTF-8 to UTF-16.

Thus, Application :: logger () will most likely have a default WindowsConsoleChannel, whereas there are ConsoleChannel (or Color *) versions in the thread; in any case, you must have a mix of different channels to see the effect you are describing - although the assignment is the same, you are registering through different channels that are protected by different mutexes. I think it makes sense for Poco to protect all console pipes with a single static mutex to avoid such problems.

With all this in mind, the above code example should work fine even without a special timing scheme.



Note also that whenever you log into the same type of console channel, your threads will wait for each other during each log operation. To avoid bottlenecks, you can use AsyncChannel .

The (simple) FileChannel is protected by a non-static FastMutex , so it won't suffer the same problem as long as each thread enters its own file, or they are all logged to the same file through the same channel instance. I've never tried the latter, but intuitively it doesn't sound like it should.

+1


source


I made a "decision". It has some drawbacks: it probably reinvents the wheel, it might be counterintuitive to Poco (I'm pretty new to Poco), and I'm worried there might be a dead end if exceptions are thrown when a thread or application is disconnected.

But now it works.

In the header file:

    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;
    } ;

      



Using:

// 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_AutoPtr

is the same as std::make_unique

, but with Poco::AutoPtr

instead I use it to avoid duplicating the type name.

0


source







All Articles