|
Boost : |
From: Christian Holmquist (c.holmquist_at_[hidden])
Date: 2008-04-08 18:47:56
On 08/04/2008, Andrey Semashev <andysem_at_[hidden]> wrote:
> Although I'm not the author of the library in the review queue, I'm
> working on my own library and would like to share some thoughts on this.
>
>
> Christian Holmquist wrote:
> >
> > First of all, I think that a boost logging library should come up with
> > one, and one only, way of expressing a log line.
> >
> > BOOST_LOG(some, kind, of, args);
>
>
> Agreed, the simpler the interface is, the easier to learn the lib.
>
>
> > Now, how should the interface of BOOST_LOG(...) look like? To find
> > that out, I'll start by explaining how I would like boost log to
> > behave from a users point of view, for instance using boost.Asio.
> >
> > #define BOOST_ASIO_LOG_ENABLE
> > #define BOOST_ASIO_LOG_MIN_LEVEL boost::log::level::debug
> > #include <boost/asio/asio.hpp>
> >
> > // Insert lots of broken client code here... =)
>
>
> [snip]
>
>
> > So, I've only informed the libraries that I want log to be generated,
> > not anything about where it'll end up, or what kind of information
> > it'll contain. The author of Asio also doesn't know what kind of
> > information that will be generated either, but can, and should, only
> > provide what is available from the library's point of view.
>
>
> Although it looks very tempting, I have two concerns about it:
> 1. Such configuration macros tend to introduce ODR violations. This
> problem probably won't get in the way in scope of a single executable,
> but what if the application consists of a dozen of them? It gets worse
> in case if the Boost library is compiled into binary itself.
I don't think there should be any ODR violations with header only libraries?
Each executable (we're talking shared objects/libraries here, right?)
may use boost libraries independently with different configs, as long
as additional data members are not introduced to allow logging. The
logging facility should IMO be a shared global entry for the
executable to avoid such problems. I think it'd be a reasonable
requirement that library A-with-log.dll/so is binary compatible with
A-without-log.dll/so.
As I wrote in my review a logging mechanism becomes a part of a
library's public interface one way or another, so totally ignoring how
that interaction behaves across library/executable borders seems like
giving most of the burden to the end user.
> 2. Recursive calls can become a real issue. Assume that I want to send
> logs by network using a sink based on Asio. Trying to log from Asio
> would be impossible. The only solution I imagine right now is to detect
> such recursive calls in the logging library and filter away logging
> records from inside processing of other logging records.
>
Agreed.
> IMHO, the first issue is not very special for logging. There are quite a
> lot of various configuration macros of different Boost libraries and yet
> there are no common means to define them _both_ when building the Boost
> itself and user-side applications except to manually add them to every
> compiler command line. I think a common user configuration header file
> is needed to address this problem.
>
> [snip]
>
>
> > Is this along the line what boost.log has in mind, or will it be a
> > logging library not to be used by the boost libraries themselves?
>
>
> I think, making Boost libraries able to write logs is definitely worth
> thinking of, although it may not be the top priority feature. IMO, we
> should first provide users with ability to easily write logs. We can add
> logging to boost libraries at a later stage.
If boost doesn't use its own logging library because that library
doesn't address the more difficult tasks, why should it match the any
other usage? IMO one could build sufficiently with boost signals,
function and possibly bind... (simple code just tested with msvc 8..)
#include <boost/signal.hpp>
#include <boost/function/function0.hpp>
#include <boost/function/function1.hpp>
#include <sstream>
#include <string>
namespace mlog
{
struct message
{
const char* system;
const long level;
const char* source;
const char* function;
const long line;
const boost::function0<const char*> msg;
};
typedef boost::function1<bool, const message&> filter;
struct level_filter
{
long level;
level_filter(long lev)
{
level = lev;
}
bool operator()(const message& msg) const
{
return level <= msg.level;
}
};
boost::signal<void(const message&)> log;
volatile bool enable = false;
}
#define LOG(sys, lev, user_msg) \
if(mlog::enable) \
{ \
struct \
{ \
const char* operator()() \
{ \
if(msg_.empty()) \
{ \
std::ostringstream os; \
os << user_msg; \
msg_ = os.str(); \
} \
return msg_.c_str(); \
} \
std::string msg_; \
} writer; \
mlog::message m = {sys ? sys : "", lev, __FILE__, __FUNCTION__,
__LINE__, writer}; \
mlog::log(m); \
}
#include <iostream>
void log_to_cout(const mlog::filter& f, const mlog::message& msg)
{
if(f(msg))
{
std::cout
<< msg.system <<"::" << msg.function << " "
<< msg.source << " : " << msg.line <<" -> "
<< msg.msg()
<< std::endl;
}
}
#define MY_LOG(lev, msg) LOG("mysys", lev, msg)
#include <boost/bind.hpp>
int main()
{
mlog::log.connect(boost::bind(&log_to_cout, mlog::level_filter(2), _1));
mlog::enable = true;
MY_LOG(3, "hello " << "world");
MY_LOG(1, "no hello " << "world");
return 0;
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk