Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

logging-format-interpolation for f-strings #1788

Closed
eddie-dunn opened this issue Dec 21, 2017 · 22 comments
Closed

logging-format-interpolation for f-strings #1788

eddie-dunn opened this issue Dec 21, 2017 · 22 comments
Labels
Enhancement ✨ Improvement to a component

Comments

@eddie-dunn
Copy link

Pylint will warn against using .format and % when logging. As the example below shows, I think it would be prudent to also output warnings when using f-strings.

Steps to reproduce

"""Docstring"""
import logging


def main():
    """Main"""
    url = 'http://example.com/%fe'
    # This works:
    logging.error(
        'url is %s | log method: %s', url, 'logging with parameters only')

    # This will crash the logger, but pylint will output
    # logging-format-interpolation:
    logging.error(
        'url is {} | log method: %s'.format(url), 'logging with .format')

    # This will also crash the logger, but pylint accepts it:
    logging.error(f'url is {url} | log method: %s', 'logging with f-string')


main()

Current behavior

Pylint does not warn against f-string interpolation in loggers.

Expected behavior

Pylint should warn against f-string interpolation in loggers, just as it warns against .format-interpolation.

pylint --version output

pylint 1.8.1, 
astroid 1.6.0
Python 3.6.2 (default, Sep 22 2017, 12:45:49) 
[GCC 6.3.0 20170516]
@PCManticore PCManticore added Bug 🪲 Enhancement ✨ Improvement to a component and removed Bug 🪲 labels Dec 22, 2017
@PCManticore
Copy link
Contributor

Thanks!

@sscherfke
Copy link

Please not! f-Strings are more readable and fast. We don't want to mix old-style and new-style formatting in our code (this is IMHO worse than not using logger.info('hello %s', name).

@sscherfke
Copy link

sscherfke commented Apr 6, 2018

@PCManticore Or give it at least a different error message (e.g., logging-fstring-interpolation) so that this can be ignored (but not .format()) and users can keep using f-Strings.

@PCManticore
Copy link
Contributor

A different message sounds good to me, if you can please create an issue with it, that would be great! Nevertheless, f-strings might be fast, but there's no need to compute them if there is no need to emit the logging message if its under the expected logging level.

@eddie-dunn
Copy link
Author

@sscherfke as @PCManticore hinted at, the reason that pylint emits the logging-format-interpolation warning is because it is inefficient to compute the string for a log message that won't be logged. This also applies to f-strings, since even if they are faster than .format it is still slower to interpolate than not interpolate at all.

I share your opinion on the greatness of f-strings, but if you are using them with the logging module, you are using the logging module incorrectly/inefficiently.

@sscherfke
Copy link

Sometimes I just don care. Practicality beats purity. 😉

@zachliu
Copy link

zachliu commented Jul 19, 2018

I agree with @sscherfke. Code readability out weights the tiny efficiency increase.

@leahein
Copy link

leahein commented Aug 3, 2018

Seconded. IMO mixing old-style formatting and f-strings throughout the application is less desirable than the cost of computing the string interpolation.

@PCManticore
Copy link
Contributor

Commenting on this issue won't change anything, if you don't agree with the warning, feel free to disable it, but this is going to be the default in pylint.

@Steffan-Ennis
Copy link

Steffan-Ennis commented Aug 16, 2018

Python not being my first nor preferred language. It does make it harder to quickly read and understand code.

I agree with the above sentiments. I thought the goal of linters was to make code consistent and easier to parse.

@pzelnip
Copy link
Contributor

pzelnip commented Aug 21, 2018

Please see the timing results at #2395 Those findings indicate that the difference in performance when logging messages are suppressed is minimal at best (~1%), and if logging statements are emitted then f-strings are actually more performant (though again, the performance difference is so small that it's not worth preferring either for performance reasons).

@joshburkart
Copy link

Yeah, this warning seems like hardcore premature optimization.

Commenting on this issue won't change anything, if you don't agree with the warning, feel free to disable it, but this is going to be the default in pylint.

These kinds of decisions for defaults should be made by consensus rather than by fiat. Also, the imperious tone is not appreciated.

@FrankDMartinez
Copy link

How about an option to exempt f-strings? By default, they would be flagged; with this option on, no.

@cra
Copy link

cra commented Oct 16, 2018

You know what's even faster? Not logging at all and not writing any messages.

How about an option on pylint prudent behaviour for any stdout output?

@theY4Kman
Copy link

There's one reason I drop down from f-strings to regular modulus interpolation:

queryset = MyModel.objects.relevant()
logger.debug('About to do the dirty to: %s', queryset)

If that queryset is evaluated when log level = INFO, we're talking far more than a 1% performance difference.

@uriva
Copy link

uriva commented Jan 19, 2019

The benefits of using f-strings in logs are proven - enhanced readability and less writing effort.

The downsides are highly speculative; only come into play if logging does not really occur && the f-string contains costly operations.

Controversial coding style practices should not be addressed in such a common dev tool.
("Controversial" being a huge understatement here to judge by the comments and votes)

primum non nocere

@eddie-dunn
Copy link
Author

Just commenting to add that I have no particular bone in this game, save that I value consistency over 'practicality'.

Also, if people had actually read the original bug report, you'd have noticed that my main gripe is that Pylint doesn't warn against attempting logging strings that will crash the logger in the case of f-strings, which can happen when you inadvertently mix different string interpolation methods.

url = 'http://example.com/%fe'
# This will crash the logger, but pylint will output logging-format-interpolation:
logging.error('url is {} | log method: %s'.format(url), 'logging with .format')

# This will also crash the logger, but pylint accepts it:
logging.error(f'url is {url} | log method: %s', 'logging with f-string')

Sure, it's an edge case, and that pylint warns against the first logging attempt is a lucky side effect of how the logging-format-interpolation warning works, but that warning still would prevent people from writing code that leads to runtime crashes, which I think is fairly practical.

In other words I see no problem with allowing logging.error(f'the url is {url}') (if @PCManticore agrees with the sentiment expressed here). I do think that logging.error(f'{url} %s', 'some string') (i.e., mixed interpolation) should produce a warning since it may lead to runtime crashes.

@chdsbd
Copy link

chdsbd commented Feb 7, 2019

It seems like everyone here is talking about performance. There are other benefits to letting logging do the interpolation.

  1. if interpolation fails, it will be logged instead of raising an exception
  2. things like Sentry can aggregate log messages. If you interpolate before logging, Sentry would see it as one event 0

@sscherfke
Copy link

This all has nothing to do with pylint.

If each kind of interpolation raises a different pylint warning, users can customize pylint to match their world-view on logging. The discussion which one is the best can be conducted elsewhere. ;-)

@kootenpv
Copy link

kootenpv commented Feb 9, 2019

@sscherfke I think the discussion is ultimately what should be the default for pylint.

connorskees added a commit to connorskees/Instagram-API-python that referenced this issue Feb 10, 2019
xiaket added a commit to xiaket/etc that referenced this issue May 29, 2019
pylint-dev/pylint#1788

Signed-off-by: Kai Xia <xiaket@gmail.com>
filak-sap pushed a commit to SAP/python-pyodata that referenced this issue Feb 22, 2021
* chore: %-formatting to f-string

pylint-dev/pylint#1788 (comment)

--- Jakub
I didn't agree with using f-strings in logging calls because
I don't think log messages need to be super simple to read.

If in the future you find out it was stupid idea, then remember
that it was a Jakub's stupid decision.
----

Closes  #144
@lazarillo
Copy link

There's one reason I drop down from f-strings to regular modulus interpolation:

queryset = MyModel.objects.relevant()
logger.debug('About to do the dirty to: %s', queryset)

If that queryset is evaluated when log level = INFO, we're talking far more than a 1% performance difference.

Can you explain what you mean, @theY4Kman ?

It seems to me that you are calculating queryset anyway. Are you saying there is some sort of lazy evaluation happening here? What is MyModel that it has guaranteed lazy eval? And why would modulus leave it lazy whereas f-string forces the eval?

Thanks!

@Pierre-Sassoulas
Copy link
Member

The configuration for fstring is avaialble since pylint 2.4 and was introduced in eeca6cf.

See, http://pylint.pycqa.org/en/latest/whatsnew/2.4.html?highlight=fstring%20interpolation

Allow the option of f-strings as a valid logging string formatting method.

logging-fstring--interpolation has been merged into logging-format-interpolation to allow the logging-format-style option to control which logging string format style is valid. To allow this, a new fstr value is valid for the logging-format-style option.

I'm going to lock to prevent further discussions here.

@pylint-dev pylint-dev locked as off-topic and limited conversation to collaborators Apr 19, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Enhancement ✨ Improvement to a component
Projects
None yet
Development

No branches or pull requests