Boost logo

Boost :

Subject: Re: [boost] [boost.log] Using stream manipulators to influence record formatting per sink
From: Jamie Allsop (ja11sop_at_[hidden])
Date: 2010-11-09 17:39:54


Andrey Semashev wrote:
> On 11/09/2010 12:26 AM, Jamie Allsop wrote:
>> So I've been using the previously reviewed log library by Andrey
>> Semashev with some degree of success. One thing however has caused me a
>> great deal of trouble and I'm sure with a couple of changes would become
>> a lot easier.
>>
>> Basically I have an application that outputs tabular data in the logs. I
>> have objects that know how to stream themselves either as an ascii table
>> or as an html table. I have a custom defined manipulator that can be set
>> for a stream that the objects can check the value of, and stream
>> themselves either as text or html as appropriate.
>>
>> In using the log library I face two major challenges. The first is how
>> can I set the manipulator to the desired value so that the log record
>> will be aware of the manipulator while building its message? It is not
>> possible to do this by calling set_formatter() on the sink since the
>> this receives *an already composed* record.message() string.
>
> That's right, record message is composed only once, for all sinks. It is
> intended to be used for informational purposes, if you need a per-sink
> formatting logic then you should use sink formatters and attributes.
> This is a fundamental limitation, I don't think I'll be able to change
> it without severely compromising usability.

I guess that's my point. There is no ability to format the actual record
message itself and I'm suggesting there should be. From the current
design this is made impossible but I'm suggesting that perhaps it can be
accommodated. Perhaps if I don't overload the meaning of attributes and
focus only on manipulators I might make myself clearer. What I am
suggesting is really two things:

1 - Allow for the possibility of passing manipulators down to the
temporary stream object that is used to create the record message.
Perhaps this might mean you need to add some sort of manipulator
facility much like your attribute facility. This would then allow you to
  write your formatters in such a way so as the record message could be
composed using a stream with the appropriate manipulators present.

In my example that would mean 'output_format' would be set appropriately
so streamed objects would know how to stream themselves as the record
message is composed.

Initially with the current design only global manipulators would be
supported since, as you say, the record message is composed only once.

2 - The second thing I am suggesting is that manipulators also be
supported per-sink. This seems to be a natural extension of what sinks
do, they decide if a record should be written or not and what format the
overall message should be written in. In order to support that you would
of course have to compose the log record message lazily once for each
sink with different manipulators, but only once, as we do now, if all
sinks had either no manipulators or the same manipulators. Not trivial
but not too complex either.

>
> I'd suggest attaching your objects as attributes to the log record and
> then put them into the stream either way in the sink formatter. I'd even
> decouple the object you have from the formatting logic but that's not
> strictly necessary. You'll have
>
> text_sink->set_formatter(fmt::stream << as_text("MyAttr"));
>
> and
>
> xml_sink->set_formatter(fmt::stream << as_xml("MyAttr"));
>
> where as_text and as_xml will both extract your objects from the
> "MyAttr" attribute in the log record and then apply the appropriate
> formatting. No need to imbue the stream with anything.

Hmm.. this is an interesting idea. Can you give me an example of what an
actual log statement might look? I'm not visualising this.

Currently I have a log statement something like this:

LOG_DEBUG(logger) << table_object;

If I set a the global_attribute to "OutputFormat" to html then the table
outputs itself as html, otherwise it outputs itself as text. How would I
do that using attributes as you suggest?

>
>> So starting at the beginning I have objects that implement streaming
>> something like this:
>
> [snip]
>
>> Given that it is
>> generally desirable to treat different sinks differently (and often the
>> reason they exist) it would be much nice if there was the concept of
>> sink-level attributes. These could be added to the record's attribute
>> set along with the source and global attributes.
>>
>> Currently there is no way (it seems) to associate any state with a sink
>> or make any decision during logging that relates to a sink. We can set a
>> custom formatter that applies to the log message created but that has no
>> effect on the record message itself. (Decorators only seem to be for
>> post-processing of the record message). We can also set a custom filter
>> to determine whether a log record gets logged by a given sink. It seems
>> odd then that we have no way to to be able to say (for example) that a
>> given sink will have an attribute that indicates that all messages
>> through that sink should be in a certain format (for those objects that
>> support the formatting). From looking at the code this does not appear
>> to be a major issue so hopefully it could be supported.
>
> I think you misunderstood the relations between sinks, filters and
> attributes. There can not be a sink-related attribute because it is
> attributes (or, more precisely, their values attached to the given
> record) decide which sinks will get the record. This decision is made by
> filters. Therefore each sink gets the same set of attribute values and
> at that point the set is fixed. It is the sink's nature, formatter
> included, what makes the same log record look different in files from
> different sinks. So you could maintain some kind of attributes in sinks
> but they wouldn't be able to participate in filtering and be injected
> into the record somehow, so they would be functionally equivalent to
> different sink formatters.

I guess my point was that the sink has no way to make the
record.message() look any differently and that is where the real
information is. Yes you can provide different formatting around the
record message but not the message itself. Everything else is mostly
window dressing, like timestamps etc. It certainly all has value of
course. I'm just looking for a way to control formatting of the log
record message itself.

>
>> It would also be highly desirable to be able to specify manipulators
>> directly that could then be applied to the record message stream itself.
>> Supporting this at the global and sink level should be possible. If we
>> had sink-level attributes the technique I described could be used to
>> simulate this, even though it is somewhat kludgey.
>
> One of the key ideas behind the library was to isolate the level of
> emitting log records and the library configuration level. Any given log
> record may potentially be processed by any set of sinks and the code
> that emits the record has no idea about which sinks and how will process
> it. So any processing nuances should be described in the library
> configuration code (in your case, I believe, sink formatters is the
> right place). I'd like not to expose any sink-related interaction to the
> user that wants to simply write something into log.

I can see where you are coming from here. Bear in mind this all started
from trying something very simple. I have objects that can represent
their state as either plain ascii or as html. You *cannot* post-process
that information. Originally I was directly streaming this information
out and simply set the manipulator state to either text or html to get
the appropriate output.

I then decided to use your logging library as it seemed to support this.
My first naive attempt to do this was to simply add the call to
set_output_format( log::output_format::html ) to the custom formatter
for a sink. Of course I found that it had no effect on the record
message itself. After a little thought it was clear why it did not work,
but that did not mean it was not a reasonable expectation. There should
be a simple way to achieve this.

Thanks for your considered reply.


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