[Log] Pre-review questions

Hi, I'm currently attempting to convert parts of our production log system to using Boost.Log as part of my review. At first glance this looked like a trivial thing, since Boost.Log supports all features (and more!) than I would need to do this task. However, I can't find what I'm looking for in the docs and I wish to not poke around anymore in the Boost.Log code. Simple task 1 I've two source level attributes, System and Severity. I wish to create a log macro that takes these two arguments. enum System { SYSTEM_A, SYSTEM_B, }; enum SeverityLevel // I don't want to use the built-in severity enum, since need flexibility to add/remove levels. { FREQUENT, DEBUG, TEMP, INFO, WARNING, ERROR, }; void foo() { LOG(SYSTEM_A, INFO, "Hello cruel world" << "!"); } The above is not a complicated scenario and I should be able to quickly find an answer. Maybe I've just missed it..:) Simple task 2 Create a custom filter without using any Lambda magic. struct Filter { System sys; SeverityLevel sev; bool operator()(???) { // pseudo-code, since I don't know how to write this return Attributes["System"] == sys && Attributes["SeverityLevel"] >= sev; } }; Help appreciated, I would like to get to the more complicated parts.. / Christian

On 03/14/2010 06:29 PM, Christian Holmquist wrote:
Simple task 1 I've two source level attributes, System and Severity. I wish to create a log macro that takes these two arguments.
[snip]
The above is not a complicated scenario and I should be able to quickly find an answer. Maybe I've just missed it..:)
Well, depending on how you use the "System" attribute, you can take either of the following approaches: 1. Use the severity_channel_logger. The severity attribute will handle your severity levels, and the "System" attribute will be represented as a channel. This means that you would have to have a logger per "System". typedef src::severity_channel_logger< SeverityLevel, System
my_logger_t;
my_logger_t lg_SYSTEM_A(keywords::channel = SYSTEM_A); my_logger_t lg_SYSTEM_B(keywords::channel = SYSTEM_B); BOOST_LOG_SEV(lg_SYSTEM_A, INFO) << "Hello"; If you want to, you can define your macro like this: #define LOG(x, y, z) BOOST_LOG_SEV(lg_ ## x, y) << z LOG(SYSTEM_A, INFO, "Hello"); 2. If having several loggers is not an option, but the system attribute is needed occasionally, you can add the attribute as a tag. The technique is described here: http://tinyurl.com/y8a2gfv 3. If the system attribute is needed all the time, then I might suggest to develop a logger feature, pretty much like it is described in the docs by the link I posted, but with one minor difference. The attribute should be added to the logger on its constructor, and only modified on the open_record call. Altering the example at the link, it should look something like that: template< typename BaseT > class system_feature : public BaseT { public: typedef typename BaseT::string_type string_type; typedef typename BaseT::attribute_set_type attribute_set_type; typedef typename BaseT::threading_model threading_model; typedef typename BaseT::record_type record_type; shared_ptr< attrs::mutable_constant< System > > m_pSystem; public: system_feature() : m_pSystem(new attrs::mutable_constant< System >(SYSTEM_A)) { this->add_attribute_unlocked("System", m_pSystem); } system_feature(record_tagger_feature const& that) : BaseT(static_cast< BaseT const& >(that)), m_pSystem(new attrs::mutable_constant< System >(SYSTEM_A)) { this->add_attribute_unlocked("System", m_pSystem); } template< typename ArgsT > system_feature(ArgsT const& args) : BaseT(args), m_pSystem(new attrs::mutable_constant< System >(args[keywords::system])) { this->add_attribute_unlocked("System", m_pSystem); } typedef typename src::strictest_lock< boost::lock_guard< threading_model >, typename BaseT::open_record_lock >::type open_record_lock; protected: template< typename ArgsT > record_type open_record_unlocked(ArgsT const& args) { m_pSystem->set_value(args[keywords::system]); return BaseT::open_record_unlocked(args); } };
Simple task 2 Create a custom filter without using any Lambda magic.
struct Filter { System sys; SeverityLevel sev;
bool operator()(???) { // pseudo-code, since I don't know how to write this return Attributes["System"] == sys&& Attributes["SeverityLevel"]>= sev; } };
Well, if you want to develop a filter from ground, here you go: struct Filter { typedef bool result_type; System m_sys; SeverityLevel m_sev; result_type operator() (attribute_values_view const& attrs) const { shared_ptr< logging::attribute > attr = attrs["System"]; if (attr) { optional< System > sys = attr->get< System >(); if (!sys) throw runtime_error("The System attribute has invalid type"); if (sys.get() != m_sys) return false; } else throw runtime_error("The System attribute not found"); attr = attrs["Severity"]; if (attr) { optional< SeverityLevel > sev = attr->get< SeverityLevel >(); if (!sev) throw runtime_error( "The Severity attribute has invalid type"); if (sev.get() != m_sev) return false; } else throw runtime_error("The Severity attribute not found"); return true; } }; But honestly, it would be much simpler if you used the tools provided by the library.

On 14 March 2010 13:00, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On 03/14/2010 06:29 PM, Christian Holmquist wrote:
Simple task 1 I've two source level attributes, System and Severity. I wish to create a log macro that takes these two arguments.
[snip]
The above is not a complicated scenario and I should be able to quickly
find an answer. Maybe I've just missed it..:)
Well, depending on how you use the "System" attribute, you can take either of the following approaches:
<snip> Ok. We probably about have 200 hundred named systems. Which approach would be most optimal in terms of compilation speed and impact on the size of the final executable? System and SeverityLevel must always be present, our current macro looks something like LOG(System, SeverityLevel, Messsage) \ if(Log::IsEnabled(System, SeverityLevel) \ { Log::Record r(System, SeverityLevel, __LINE__, __FUNCTION__, timestamp, thread-id, etc, etc..) r.msg << Message; Log::Add(r); }
Simple task 2
Create a custom filter without using any Lambda magic.
Well, if you want to develop a filter from ground, here you go:
<snip> This code looks kind of expensive to execute. Hard to tell the impact in reality, but it must be much heavier than using a plain old struct for the attributes. Is it out of the question to change the attributes from being a pure runtime configured entity, to something like a fusion::map? But honestly, it would be much simpler if you used the tools provided by the
library. _______________________________________________
Ok, let me explain my usecase further. At runtime I want to modify the filter which controls the log output to the console sink. UpdateConsoleFilter(System, SeverityLevel) The filter will have something like array<SeverityLevel, N>, where N is number of 'System's. This function is exposed in the runtime command line, allowing me to write MyApp> log "SystemA" "DEBUG" to change the behaviour of the filter during execution of the program. struct filter { const array<SeverityLevel, NumberOfSystems>& levels; bool operator()(Attributes const& attrs) { return levels[attrs["System"]] >= attrs["SeverityLevel"]; } }; Since I can't retrieve the filter from a sink, the levels array is a const ref so that I can modify it elsewhere. (it doesn't have to be thread safe, since it's just assignment of integers, btw..) I find my filter code really simple, and I don't see why I would need to figure out how to express this using lambda style. / christian

On 03/15/2010 12:11 AM, Christian Holmquist wrote:
On 14 March 2010 13:00, Andrey Semashev<andrey.semashev@gmail.com> wrote:
Well, depending on how you use the "System" attribute, you can take either of the following approaches:
Ok. We probably about have 200 hundred named systems. Which approach would be most optimal in terms of compilation speed and impact on the size of the final executable? System and SeverityLevel must always be present
Then my third variant suits you best.
This code looks kind of expensive to execute. Hard to tell the impact in reality, but it must be much heavier than using a plain old struct for the attributes. Is it out of the question to change the attributes from being a pure runtime configured entity, to something like a fusion::map?
It has been discussed in another thread of this review. No, fusion::map is not possible to be used now. And if something like it gets incorporated into the library, it won't be a compile-time lookup anyway.
Ok, let me explain my usecase further. At runtime I want to modify the filter which controls the log output to the console sink. UpdateConsoleFilter(System, SeverityLevel)
The filter will have something like array<SeverityLevel, N>, where N is number of 'System's.
This function is exposed in the runtime command line, allowing me to write MyApp> log "SystemA" "DEBUG" to change the behaviour of the filter during execution of the program.
struct filter { const array<SeverityLevel, NumberOfSystems>& levels; bool operator()(Attributes const& attrs) { return levels[attrs["System"]]>= attrs["SeverityLevel"]; } };
Since I can't retrieve the filter from a sink, the levels array is a const ref so that I can modify it elsewhere. (it doesn't have to be thread safe, since it's just assignment of integers, btw..)
Generally, that doesn't mean that it doesn't have to be thread-protected.
I find my filter code really simple, and I don't see why I would need to figure out how to express this using lambda style.
To write a shorter code? Lambda functions can really help in this regard. For instance, my previous version could be rewritten as follows: result_type operator() (attribute_values_view const& attrs) const { using namespace boost::lambda; System sys; if (!logging::extract< System >("System", attrs, var(sys) = _1)) throw runtime_error("System not found"); SeverityLevel sev; if (!logging::extract< SeverityLevel >( "Severity", attrs, var(sev) = _1)) throw runtime_error("Severity not found"); return levels[sys] >= sev; }

Andrey Semashev <andrey.semashev <at> gmail.com> writes:
Tinyurl is not a permalink or at least is not a component we can control, which breaks the backtracking of discussions in a couple of years. I think the exact link could always be provided: <http://boost-log.sourceforge.net/libs/log/doc/html/log/extension/sources.html> I provide this here, because I got lost several times digging the archives ... OT: I vote for banning short URLs besides permalink.gmane.org Markus

On 03/15/2010 07:45 PM, Markus Werle wrote:
Andrey Semashev<andrey.semashev<at> gmail.com> writes:
Tinyurl is not a permalink or at least is not a component we can control, which breaks the backtracking of discussions in a couple of years. I think the exact link could always be provided:
<http://boost-log.sourceforge.net/libs/log/doc/html/log/extension/sources.html>
I provide this here, because I got lost several times digging the archives ...
OT: I vote for banning short URLs besides permalink.gmane.org
Long links are sometimes wrapped by mail clients (or servers - I don't know), which complicates their usage.

Andrey Semashev wrote:
On 03/15/2010 07:45 PM, Markus Werle wrote: [snip]
link could always be provided:
<http://boost-log.sourceforge.net/libs/log/doc/html/log/extension/sources.html>
[snip]
Long links are sometimes wrapped by mail clients (or servers - I don't know), which complicates their usage.
which is why you should add < > around the link
participants (4)
-
Andrey Semashev
-
Christian Holmquist
-
Jamie Allsop
-
Markus Werle