I want to avoid the eager evaluation of f-strings in logging, therefore I use %-formatting.
The precision is defined by a variable precision.
import logging
value = 3.14159
logging.error("%.2f", value)
precision = 2
logging.error(f"{value:.{precision}f}") # OK but eager evaluation
logging.error("%.*f", value, precision) # fails
logging.error("%.%if", value, precision) # fails
Any suggestion?
>Solution :
logging.error("%.*f", value, precision) # fails
is almost correct, but the precision has to come first:
logging.error("%.*f", precision, value) # works
Mnemonic: The placeholder * for precision comes before the placeholder f for the float value, so the precision is provided before the float.
Sadly, while the mnemonic applies in other formatting tools, it’s applied to the beginning of each placeholder, not the end. For example, in str.format, the auto-numbering of placeholders is based on the order of the open brace { for each placeholder, so for that case, you’d do:
'{:.{}f}'.format(value, precision)
since the open brace for the placeholder for value comes before the open brace placeholder for precision. Of course, in that case, you can avoid ambiguity by being explicit and numbering them:
'{0:.{1}f}'.format(value, precision)
or naming them:
'{value:.{precision}f}'.format(precision=precision, value=value) # Order of kwargs irrelevant
With f-strings of course, you inline the names being used in each place so no confusion exists:
f'{value:.{precision}f}'
and it’s faster than any other means of string formatting in modern Python, so for any logs likely to actually get emitted in normal use, I’d just stick with the f-strings. Even for ones that won’t get emitted, the overhead of logging stuff that’s below the logging level (and doesn’t get emitted) is high enough that the savings from avoiding the temporary str are pretty minimal in context, and the risk of a mistake that isn’t noticed unless logging is turned up is high enough, that I’d typically use eager formatting, especially for more complicated cases like this. I’ve definitely been bitten by code of the form:
logging.debug('%s %s', a, b, c)
where the code was executed unconditionally, but never with DEBUG level logging enabled; the first time I needed it to figure out a problem, the code broke due to the mismatched placeholder counts. A similar problem is impossible with eager formatting, whether it is modern f-strings or less modern str.format or even eager printf-style formatting like logging.debug('%s %s' % (a, b, c)); you’d see the error even if logging was turned off entirely.