Thanks for the review.
----- Original Message -----
On Thu, Sep 20, 2012 at 08:24:08PM +0200, Miloslav Trmač wrote:
> ... and update all users. No change in functionality, the parameter
> will be used in later patches.
>
> diff --git a/src/util/logging.h b/src/util/logging.h
> index a66f5dc..14b7b7c 100644
> --- a/src/util/logging.h
> +++ b/src/util/logging.h
> @@ -141,13 +142,13 @@ extern int virLogParseFilters(const char
> *filters);
> extern int virLogParseOutputs(const char *output);
> extern void virLogMessage(const char *category, int priority,
> const char *funcname, long long linenr,
> - unsigned int flags,
> - const char *fmt, ...)
> ATTRIBUTE_FMT_PRINTF(6, 7);
> + virJSONObjectPtr properties, unsigned
> int flags,
> + const char *fmt, ...)
Definite NACK to this change, since it is exposing the impl details
of the Lumberjack log output function to all users of the libvirt
logging API, not to mention the general unpleasant usability aspects
of having to build up JSON objects simply to pass a few extra metadata
fields.
I didn't think adding an abstraction for a single user was worth it, but you're
right, the result did end up rather uglier than I expected.
I'm all for allowing more metadata properties to be passed into
the
logging functions, but we need a simpler API along the lines of the
systemd journal sd_journal_send() style which allows for a set of
key=value pairs to be passed in. I'd not try to shoe-horn it into
the existing virLogMessage() APIs. In the same way that there is
sd_journal_print() for simple string messages, vis sd_journal_send()
for arbitrary key=value pair log messages, I'd create a new API
for this called virLogMetadata and virLogVMetadata. eg to allow
sending a message with an error no
virLogMetadata(__FILE__, VIR_LOG_WARN,
__FUNC__, __LINE__,
0,
"MESSAGE=Unable to open file %s: %s",
"/some/file",
strerror(errno),
"ERRNO=%d", errno,
NULL);
I'm afraid this is not possible to implement portably and reliably without
reimplementing 50% of sprintf(), especially in the presence of translations and the
associated %5$s formats.[1][2]
One problem with the above is that it is possible to send a log event without the MESSAGE
field, which would leave non-structured formats with nothing to log. I can see three
options:
1) as proposed above, silently failing if the MESSAGE field is missing (or if there is a
typo in the field name); with type fields added to represent JSON integers as integers.
2) MESSAGE is mandatory, everything pre-formatted
virAsprintf(&msg, "Unable to open file %s: %s", "/some/file",
strerror(errno));
virLogMetadata(... __LINE__, msg,
"ERRNO", VIR_LOG_INT, errno,
NULL)
3) MESSAGE is printf-like, everything else in an array.
struct virLogMetadata meta[2] = { { "ERRNO", VIR_LOG_INT, .i = errno }, {
NULL, } };
virLogMetadata(... __LINE__, meta,
"Unable to open file %s: %s", "/some/file",
strerror(errno));
or equivalently:
virLogMetadata(... __LINE__,
&(struct virLogMetadata []) {
{ "ERRNO", VIR_LOG_INT, .i = errno },
{ NULL, }
},
"Unable to open file %s: %s", "/some/file",
strerror(errno));
which could be hidden by macros:
virLogMetadata(VIR_LOG_INT("ERRNO", errno"),
VIR_LOG_END,
"Unable to open file %s: %s", "/some/file",
strerror(errno));
Do you have any preference? I'm leaning towards the first variant of 3) for now, we
can add fancy macros later when/if more callers of virLogMetadata are added.
Thanks again,
Mirek
[1] The unportable way to do this is to use parse_printf_format() from glibc.
[2] The logging API could implement only a subset of the printf formats, but it would be
rather difficult to notice if a newly added caller used an unsupported format a few years
later, especially when translators can use different formats than the original string.