4

I'm trying to creating a logging class where the call to write a log is static. Now, due to performance requirements I'm want to perform the actual logging in a separate thread. Since the function to write to a log is static, I think the thread also needs to be static, which is also tied to another static member function that performs the actual writing of the log. I tried coding it but somehow it hangs during the initialization of the static thread. The code sample that duplicates the behavior is below:

"Logger.h"

#ifndef LOGGER_H
#define LOGGER_H

#include <condition_variable>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <vector>

#define LIBRARY_EXPORTS

#ifdef LIBRARY_EXPORTS // inside DLL
#define LIBRARY_API __declspec(dllexport) 
#else // outside DLL
#define LIBRARY_API __declspec(dllimport)
#endif 

using namespace std;

namespace Company { namespace Logging {

class LIBRARY_API Logger
{
public:
    ~Logger();

    void static Write(string message, vector<string> categories = vector<string>());

private:
    Logger();
    Logger(Logger const&) {}
    void operator=(Logger const&) {}

    static thread processLogEntriesThread;

    static void ProcessLogEntries();
};

}}

#endif

"Logger.cpp"

#include "Logger.h"

#include <iostream>

using namespace std;

namespace Company { namespace Logging {

thread Logger::processLogEntriesThread = thread(&Logger::ProcessLogEntries);

Logger::Logger()
{
}

Logger::~Logger()
{
    Logger::processLogEntriesThread.join();
}

void Logger::Write(string message, vector<string> categories)
{
    cout << message << endl;
}

void Logger::ProcessLogEntries()
{
}

}}

One odd behavior that I found is that the hanging part only happens when the class packaged in a DLL. If I use the class files directly into the console EXE project it seems to be working.

So basically my problem is the hanging part and if I'm doing things correctly.

Thanks in advance...

Boggs
  • 133
  • 9
  • avoid `using namespace std;` in header file. `std::cout` is not "threadsafe". Your thread does nothing... – Jarod42 Sep 13 '13 at 19:24
  • 1
    I don't see any `Logger` instance, so the `destructor` is not called, and no `join` – Jarod42 Sep 13 '13 at 19:38
  • Thanks for the comment about the using namespace std; in the header file. – Boggs Sep 13 '13 at 19:47
  • You need a mutex in the `Write` call. Also, you're essentially creating a singleton...a static class. When you do so, you have to be careful about initialization and the object never really gets destroyed. – Gort the Robot Sep 13 '13 at 22:24
  • @Steven. I've been considering a singleton if I run out of choices. I'm not sure if I will be able to keep a calling convention of "Logger::Write(...)" when I use a singleton. It might become something like "Logger::GetInstance().Write(...)", which of course I prefer the former. I will probably have to try to implement it to be sure. – Boggs Sep 13 '13 at 22:55
  • "the hanging part only happens when the class packaged in a DLL" It is possible that this is the reason. Initialization of global variables happens in the [CRT DLL entry point routine](http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx) (see bottom), which means that the restrictions imposed on calls inside a DLL entry routine also apply to the initializations of those globals. [Threads can be created during DLL start-up via `CreateThread`](http://msdn.microsoft.com/en-us/library/ms682453(VS.85).aspx) (see bottom) but no guarantee about using `std::thread`. – dyp Sep 14 '13 at 00:06
  • 1
    Are you using MSVC? Might be related to this: http://stackoverflow.com/q/10915233/231299 – beerboy Sep 14 '13 at 01:49
  • Why not just take g2log? – Dmitry Ledentsov Sep 14 '13 at 10:18
  • Thank you for the comments. I'm not sure if I have the time to investigate things deeper. For now, I'll look first into just using Boost.Log 2.0. But if I ever have to go this direction again I'll look into the suggestions and update things accordingly. – Boggs Sep 19 '13 at 22:44

3 Answers3

1

you can use my logger library => https://github.com/PraGitHub/Prapository/tree/master/C_Cpp/Logger

If this post is found irrelevant, please pardon me.

1

the hanging part only happens when the class packaged in a DLL

See Dynamic-Link Library Best Practices for full details why it hangs:

You should never perform the following tasks from within DllMain:

  • Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.

The solution is provide an initialization function/object for your logger library that the user must call explicitly in main, rather than having a global thread object initialized before main is entered. This function should create the thread.

Or create the thread on the first logging call using std::call_once. However, this involves an extra conditional check on each logging call. This check may be cheap but it is not free.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
0

I can not see any usage of the logger thread. Having a thread as member in a class did not mean that all member functions will run in the created thread. The destructor of logger will never called, while you have no logger instance. iostream is not thread safe!

What you have to do:

Create some kind of storage to collect the logging infos. This instance must be thread safe! Push messages from the outside world into this instance. The instance itself must have a own thread which reads from the storage and put the data to the output. This must be done also in a thread safe manner because reading and writing comes from different threads!

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • I admit that the code is not really complete and does not make complete sense, because I was more focus on demonstrating the hanging problem. My initial plan was to use a static declared queue that will contain the logs (and probably put locks around the queue). The "Write()" function will just push the logs to the queue. The "processLogEntriesThread" thread will do the actual logging and empty out the queue. The "cout" was just to test whether the hanging happens before the call to "Write()". Anyway, right now I'm just looking into using the Boost.Log 2.0. – Boggs Sep 19 '13 at 22:39