Re: How to add thread-safety in a logging library?
On 2007-08-03 12:27, ZHENG Zhong wrote:
Hi,
I implemented a small logging library with the API like this:
[snip]
logger& log = log_manager::instance().get_logger("my_logger");
log.stream(DEBUG) << "this is a debug message" << std::endl;
log.stream(INFO) << "this is an info message" << std::endl;
[/snip]
Every logger has a unique name, and manages an output stream
(std::ostream). The 'stream(log_level_t)' member function returns an
ostream_proxy object, which is implemented as the following:
[snip]
class ostream_proxy {
public:
// other member functions...
template<typename T>
ostream_proxy& operator<<(T const& t) {
(*os_) << t;
return *this;
}
private:
std::ostream* os_; // ostream_proxy objects from the same logger
object share
// the same output stream.
};
[/snip]
Thus it is possible that two threads retrieve the same logger object
and write messages to the logging stream at the same time.
Now i want to add thread-safety to my logging library, and i realize
that in ostream_proxy, the statement "(*os_) << t;" is not thread-safe
(std::ostream is not thread-safe, right?). So i need to re-implement
the operator << in ostream_proxy like this:
[snip]
class ostream_proxy {
public:
// other member functions...
template<typename T>
ostream_proxy& operator<<(T const& t) {
{
boost::mutex::scoped_lock lock(*os_mutex_);
(*os_) << t;
}
return *this;
}
private:
std::ostream* os_; // ostream_proxy objects from the same
logger object
boost::mutex* os_mutex_; // share the same output stream and the
same mutex.
};
[/snip]
In this way, i can guarantee that at any moment, there is at most one
thread that calls "(*os_) << t;".
I have not given this issue a lot of though but with the code above you
protect each << operation with a lock, however if you have two threads
running and both have a statement like this:
log.stream(DEBUG) << "Foo: " << foo << ", Bar: " << bar << std::endl;
(i.e. multiple << on one line) you only lock each << and there can be
interleaving from multiple threads giving you a result like this in the log:
Foo: Foo: 4 Bar: 32 Bar: 5
But since logging may be an action that is frequently performed, the
code above may be too expensive to bear... Surely i can use a policy
to allow user to choose if s/he want thread-safety or not. But in a
multi-threaded application, user still has to pay for logging...
So i would like to know if such implementation is proper, or if there
is a way to make that better.
I would appreciate your advice. Thanks!
Locks are usually more expensive if they are heavily contended, so if
you have many threads that perform logging (or a few threads which logs
a lot) then you might want to consider giving each thread its own,
thread local, log.
--
Erik Wikstr?m