Boost logo

Boost :

Subject: Re: [boost] [logging] Interest check on logging library.
From: Jason Wagner (jason_at_[hidden])
Date: 2008-12-27 15:18:27


> -------Original Message-------
> From: Emil Dotchevski <emildotchevski_at_[hidden]>
> Subject: Re: [boost] [logging] Interest check on logging library.
> Sent: 27 Dec '08 15:25
>
> On Fri, Dec 26, 2008 at 9:48 AM, Jason Wagner <jason_at_[hidden]> wrote:
> > Hello all,
> >
> > I know there are two proposed logging libraries out there for Boost,
> > already, but I had a different itch to scratch on a project.  I think I saw
> > that Andrey Semashev was going to submit his far more mature version soon,
> > but I thought I'd throw my technique out there for an interest check and
> > hopefully commentary.  This is very much a prototype-- there's a lot of work
> > yet to be done but the basic outline and functionality is implemented.
> >
> > Getting the code
> > ----------------
> > http://www.nialscorva.net/boost_logging-0_1.tar.bz2
>
> What is the motivation for using boost::mpl in the logging library? I
> didn't look too deeply into your code, but wouldn't something like
> this work just as well:
>
> struct logging_debug;
> struct logging_release;
>
> template <class>
> struct logging_traits;
>
> template <>
> logging_traits<logging_debug>
> {
>   ....
> };
>
> In other words, let the user provide an explicit specialization to
> configure the logger; once that's done, they just do:
>
> logger<logging_debug> log;

This handles severity, but not necessarily other conditions. The library handles a more general case of this. In the use cases I'm working on, there are multiple independant tags on a record, severity being only one:

severity := trace | debug | info | notice | warn | error | fatal // syslog levels, may be missing one
build_type := release | debug

The logger defines what, if any, conditions of these attributes a record must meet before being logged:

logger<
  mpl::vector<
    condition::greater_equal<severity::info>,
    condition::in<build_target::debug>
>
> log;

The

logger<logging_debug> log;

is equivalent to

logger<mpl::vector<condition::greater_equal<severity::debug> > > log;

A simple use case would be when you want to put more information into a debug build target than a release build target for the same level of severity:

log << severity::info() << "An event that you should be informed of occurred"
     << build_target::debug() << " debugging information: " << dump_lots_of_info()
     << end_record();

User defined tag types are extremely easy to make and integrate seamlessly. In the application I'll be using this for, I'll have tags for build_target (client, server, possibly one of several types of server). This could be done via severity levels, but it would be fitting orthogonal concerns into a single enumeration.

> My reasoning is that a logging library should be lightweight in terms
> of source code; when I need logging, I'd rather not get boost::mpl
> too.

Understandable. Everything's a matter of tradeoffs. The project I'm working on has a slightly more involved logging use case. One concern I have is how much compile times will be affected by MPL and the templates I'm using and whether the flexibility is worth the cost.
  
> A few more comments:
>
> In general I don't like using macros or #ifdefs, but one place where I
> think they are OK is in a logging library interface, partially because
> they make it much lighter, and partially because automatically logging
> __FILE__, __LINE__, etc. is desirable IMO.

I'm differentiating macros from simple defines.

#define LOG_LINE filename(__FILE__) << line(__LINE__) << function(__FUNCTION__)

will be in the library in some form in the future.

> Finally, I think that all the machinery a typical logging lib uses to
> kill logging expressions when they're not needed is an overkill. I've
> found that using something as simple as the code below works just
> fine:
>
> namespace logging
> {
>   static bool const warnings=false;
>   static bool const errors=true;
> }
>
> and then:
>
> if( logging::warnings )
>   log << "Warning";
>
> where log simply implements std::ostream.
>
> You could put the if in a macro but frankly, I personally don't mind
> typing it every time; compared to the rest of the junk I have to type
> to format a meaningful logging message, the if is not an issue. As a
> bonus, you don't have to worry about functions referenced by a logging
> expression being executed when logging is not enabled.

I'm not a fan of the macros, either. What's really the difference between what you wrote and:

LOG_DEBUG(log) << "Warning";

other than the latter being ugly.

One of the needs I have is sending output to multiple sinks and reconfiguring filter conditions on the fly (runtime conditions, nt compile time conditions). The if() becomes more complicated and there's visual overhead in having code to change the condition and the potential for typos in non-trivial checks. Hiding that complexity makes the real code more readable and maintainable.

That being said, my approach may be overkill for situations of writing to a single file based upon simple variables. I don't think the implementation complexity for the end-user is too high, but it's all about what your needs are.

> --
> Emil Dotchevski
> Reverge Studios, Inc.
> http://www.revergestudios.com/reblog/index.php?n=ReCode
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
>

---
Jason Wagner
jason_at_[hidden]

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk