0

I use qInstallMessageHandler to register a custom logging function. This function formats message (for example it adds a time stamp) and prints it to the console and to the log file.

My concern is, if the Qt library components (like QDateTime, QDir, QFile...) can call qDebug(), qWarning(), etc. ?

If yes, this may lead to the infinite recursion...

user2449761
  • 1,169
  • 13
  • 25
  • 3
    Yes, Qt itself uses those debug/warning facilities, so you'll need to protect yourself. – peppe Jun 24 '16 at 11:30

1 Answers1

1

The simple answer is: avoid reentering your message handler, then:

// C++11, Qt 5.4+
void myMessageHandler(…) {
  thread_local bool entered = false;
  if (entered) return; // oops
  QScopedValueRollback set{entered, true};
  …
}

// C++11, Qt 4.8+
void myMessageHandler(…) {
  thread_local bool entered = false;
  if (entered) return; // oops
  QScopedValueRollback back{entered};
  entered = true;
  …
}

// C++98, Qt 4
QThreadStorage<bool> entered;
void myMessageHandler(…) {
  if (entered.localData()) return;
  QScopedValueRollback back(entered.localData());
  entered.localData() = true;
  …
}

This was the old and silly answer, for posterity:

It will lead to infinite recursion only if your logging is synchronous. As soon as you make the logging asynchronous, you don't get the problem anymore: the message handler won't ever be re-entered, since you exit it immediately after emitting a signal, and signal emission over a queued connection posts zero or more QMetaCallEvent instances to relevant threads' event queues and that's that.

Asynchronous logging is achieved by emitting a signal in the message handler, and handling the log writing from a slot/functor connected to the signal via an explicitly queued connection. You'll probably want your logger to reside in its own thread, so this is a very natural approach then and works great.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • nice idea! thank you. What object should emit this signal? Can you please give an example? – user2449761 Jun 24 '16 at 15:48
  • @user2449761 An object specially made for the purpose, of course. A helper object, if you will. It can be a global singleton, allocated in `main()` before the application instance is created. – Kuba hasn't forgotten Monica Jun 24 '16 at 16:12
  • asynchronous logging object will not create a recursion, but infinitely growing events queue. This is even more difficult to detect – user2449761 Jun 28 '16 at 11:03
  • @user2449761 Why would it create an infinitely growing events queue? You're supposed to service these events :) – Kuba hasn't forgotten Monica Jun 28 '16 at 14:21
  • Similarly to the recursion. When I handle "log events" in asynchronous task (thread), some Q* object might use QDebug() to print some warning. This will cause another log event, and the same object might want to use QDebug() again, and again... and queue might be growing infinitely. I know it is very unlikely to happen (Qt objects are not very verbose), but my finding is that one should not use Qt objects inside of message handler (sync or async). – user2449761 Jun 28 '16 at 14:53
  • @user2449761 You have full control over what Qt code runs in the message handler. You can audit it to ensure that it doesn't add more messages. You can even modify your copy of Qt to have per-thread control of qDebug's message handler. – Kuba hasn't forgotten Monica Jun 28 '16 at 15:05
  • Yes. Anyway I prefer not to use `qInstallMessageHandler`/QDebug :) – user2449761 Jun 28 '16 at 15:07