I like this proposal a lot, actually I though the lazy evaluation applied to logging was one of the main benefits of the t-strings, I’m surprised to see people thinking otherwise.
f-strings may be fast enough as mentioned here, but they are often used with expensive expressions inside that totally undermine their performance. The official python logging and also many linterns recommendation is to use “%” formatting and completely avoid “f-strings” for this precise reason.
From pylint:
logging-format-interpolation (W1202):
Use % formatting in logging functions and pass the % parameters as arguments Used when a logging statement has a call form of “logging.(format_string.format(format_args…))”. Such calls should use % formatting instead, but leave interpolation to the logging function by passing the parameters as arguments.
Python logging docs :
Optimization
Formatting of message arguments is deferred until it cannot be avoided. However, computing the arguments passed to the logging method can also be expensive, and you may want to avoid doing it if the logger will just throw away your event. To decide what to do, you can call the isEnabledFor()
method which takes a level argument and returns true if the event would be created by the Logger for that level of call. You can write code like this:
if logger.isEnabledFor(logging.DEBUG):
logger.debug('Message with %s, %s', expensive_func1(),
expensive_func2())
Following this recommendations results in very ugly code in my opinion, and I would much rather write this instead:
logger.debug(t'Message with {expensive_func1()}, {expensive_func2()}')
But not this:
logger.debug(f'Message with {expensive_func1()}, {expensive_func2()}')
As it will evaluate both functions.
Being able to deactivate them by simply changing the logger level makes them usable in performance critical code (that is set to debug only). For now the only way of lazy evaluating is using it with the if statement or other methods mentioned that won’t look as nice as just swapping the f-string by a t-string.
As an additional benefit, using t-strings may fix the recurring issue of the programs that fail precisely evaluating the f-string during a log that is precisely there to catch a error. So the real error is masked by a f-string evaluation error and you don’t have the log of the real one.
For example this may fail:
logger.debug(f'Message with {expensive_func1()}, {func_that_may_fail()}')
While this may handle the issue gracefully:
logger.debug(t'Message with {expensive_func1()}, {func_that_may_fail()}')
Am I missing something about t-strings?