logging: Logger optimizations
As implemented, string formatting occurred as part of the creation of
LogMessages, which were then passed to a logging method that would
accept or reject them based on the configured level. This was OK,
because no DEBUG logging had yet been added, but that will soon
This changeset alters Logger.log(...) to accept a function that
returns a LogMessage struct, instead of the LogMessage itself.
This allows construction of the message (including any string
formatting) to occur only after it has been accepted for delivery.
Tests have also been included.