This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Simplify customization of the logging time through datefmt
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: mariocj89, p-ganssle, vinay.sajip
Priority: normal Keywords:

Created on 2019-05-31 18:44 by mariocj89, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (5)
msg344102 - (view) Author: Mario Corchero (mariocj89) * (Python triager) Date: 2019-05-31 18:44
Users that want to provide a custom template for the timestamp part of logging cannot customize the milliseconds part of asctime.

They can use the msecs attribute of the Logger but that effectively requires to break the formatting in two parts. Note that it is not necessary to provide msecs on the default template as there is code that handles it in https://github.com/python/cpython/blob/c8d5bf6c3fa09b43f6a5ee779d493d251dbcc53c/Lib/logging/__init__.py#L603

Something we can do to improve this situation is changing the default converter to produce a datetime rather than a timetuple, as strftime of datetime and time uses the same template format.

This will allow changing the format including milliseconds through the datefmt argument.

formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y%m%d %H:%M:%S.%f")

Compare this to the current soltution:

formatter = logging.Formatter("%(asctime)s%(msecs)d %(message)s", datefmt="%Y%m%d %H:%M:%S")

Note how you need to split the formatting of time into two different parts. This becomes even more diserse if you also want to add something after the time formatting (Example: a Z to note UTC).

One more reason why this is quite powerful is that once we get timezones added to the standard library we will be able to (in a backward compatible way) just pass the timezone when we do `datetime.fromtimestamp`, which will add the timezone information to the datetime and users will be able to use %z to add the offset of the logging timestamp (Hurray! timestamps with offsets).

Sample implementation: https://github.com/mariocj89/cpython/commit/5047d730c0a0dcd6276f40c5b66762071dfcb448

If it looks interesting I can update the docs, add some further tests and send the PR. Wanted to confirm it is before putting time into that.

I cannot come with any "downside" of not doing it and I think it will simplify considerably the way users can format timestamps in logs. With this change we will just be able to say "To change the formatting of timestamps in a formatter, just use the datefmt argument".

Thoughts?
msg344108 - (view) Author: Vinay Sajip (vinay.sajip) * (Python committer) Date: 2019-05-31 19:08
The documentation mentions that the converter attribute of the formatter should be signature-compatible with time.localtime() and time.gmtime(), and some users will have set this attribute with one or the other or a compatible function. Doesn't your change break backward compatibility by changing the signature of the converter?

If one wants to implement the functionality you're suggesting, one could implement a Formatter subclass that does it, isn't that right? That wouldn't cause backward compatibility problems, and could be added in a cookbook recipe.
msg344134 - (view) Author: Mario Corchero (mariocj89) * (Python triager) Date: 2019-05-31 22:16
AFAIK, the converter attribute is only to "be set" with a function of such signature. It is not documented that it can be used and it is only used on the format time function.

The only situation where this might break someone is if they inherit from formatter, substitute the formatTime function by their own implementation and rely on the function returning a time tuple. Doing that is relying on the implementation of the formatter though, as the docs explain is just something to be set.

This could be released as part of a new Python version, I'd be surprised if this is an issue and it will improve quite nicely the way users can configure the formatting of timestamps.

Even if adding it to the cookbook would "work" I think it would be much better to have this change and provide a nicer experience with the default class.

This is your call though! But I'd say it would be quite a nice improvement, was speaking with some other devs at PyCon and they seemed quite excited.
msg344159 - (view) Author: Vinay Sajip (vinay.sajip) * (Python committer) Date: 2019-06-01 09:16
> AFAIK, the converter attribute is only to "be set" with a function of such signature. It is not documented that it can be used and it is only used on the format time function.

What do you mean by "it is not documented"? It is documented here:

https://docs.python.org/3/library/logging.html#logging.Formatter.formatTime

See also questions on Stack Overflow relating to it:

https://stackoverflow.com/search?q=%5Bpython%5D+%5Blogging%5D+converter

Not sure what you mean by "only to be set". Obviously, it is also read *somewhere* - otherwise why would you set it?

> The only situation where this might break someone is if they inherit from formatter, substitute the formatTime function by their own implementation and rely on the function returning a time tuple. Doing that is relying on the implementation of the formatter though, as the docs explain is just something to be set.

But the converter is documented as returning a time tuple, and there conceivably exists code that relies on that. There is no reason why someone couldn't have implemented a Formatter subclass with an overridden formatTime method that assumes the converter returns a time tuple. We can't know the impact of this change in terms of the number of people/amount of software affected, but that it might break *someone's* code is IMO enough reason not to proceed with it.

> Note how you need to split the formatting of time into two different parts.

It's still a one-liner both ways in your example - just a slightly different format string. Also, note that %f doesn't give you all that much flexibility - you get six places whether you like it or not. So it's hardly something which will meet everybody's formatting requirements. So "hey, just use datefmt" won't cut it for all scenarios. Ditto for timezone display - there is a school of thought that says logs should only hold UTC time. Users can always override formatTime in their Formatter subclass to do exactly what they want - it could be a one-time thing that they can then use wherever they want.

I don't want to give the impression that I'm against *any* change - I'm not. You can see lots of changes in the logging codebase contributed by various people. A recent example in the area of logging Formatter is

https://github.com/python/cpython/pull/9703

So by all means I welcome contributions, but you might find me a bit more conservative than you would like!
msg344163 - (view) Author: Mario Corchero (mariocj89) * (Python triager) Date: 2019-06-01 09:48
> What do you mean by "it is not documented"? It is documented here:

As it says:

"This function uses a user-configurable function to convert the creation time to a tuple. By default, time.localtime() is used; to change this for a particular formatter instance, set the converter attribute to a function with the same signature as time.localtime() or time.gmtime(). To change it for all formatters, for example if you want all logging times to be shown in GMT, set the converter attribute in the Formatter class." I assumed it means users can set such attribute but not something in the formatter class where people will do formatter.converter(xyz). If the intention was for users to also be able to use the converter attribute then I misunderstood that.

> Obviously, it is also read *somewhere* - otherwise why would you set it?

The implementation of formatTime uses it only at the moment.

> See also questions on Stack Overflow relating to it: https://stackoverflow.com/search?q=%5Bpython%5D+%5Blogging%5D+converter

Through all the questions and answers in stackoverflow they are all setting the converter (which will still work) and some (2 I think) only are reading it when they are also setting it in the class (Example: https://stackoverflow.com/questions/6290739/python-logging-use-milliseconds-in-time-format/6290946#6290946, which by chance is effectively trying to solve the same problem brought up here).

> But the converter is documented as returning a time tuple,

It just says "set the converter attribute to a function with the same signature as time.localtime() or time.gmtime()" there is no reference to what the default value has, it could even be None when user is reading it. Of course, you wrote it hehe, so if you say the intent is for people to use the default formatter that is a different thing :).

>  but that it might break *someone's* code is IMO enough reason not to proceed with it.

I looked hard and could not find any of those in Github (it does not mean that there are none indeed). I would agree on not releasing this as a patch release, but as part of a new minor I would argue that it is fair. From my point of view, it only breaks people depending on the implementation of it and using the formatter in a not common way (at least from what was seen in stackoverflow and github, there is also no cookbook that recommends doing anything like that).

> It's still a one-liner both ways in your example

Yeah but it requires to deeply understand how Formatter is formatting time rather than just using a template.

> Users can always override formatTime in their Formatter subclass to do exactly what they want - it could be a one-time thing that they can then use wherever they want

They need to copy paste this code around on all their scripts/apps or create their own library to include this.

> I don't want to give the impression that I'm against *any* change - I'm not

Don't worry, I can totally understand :), if you still think it is not worth pursuing this feel free to close the issue. It was an idea I thought it was worth pushing but it is totally your call (I won't take it at all personal ^^). Thanks for taking the time to answer!
History
Date User Action Args
2022-04-11 14:59:16adminsetgithub: 81298
2019-06-01 09:56:13vinay.sajipsetstatus: open -> closed
resolution: not a bug
stage: resolved
2019-06-01 09:48:20mariocj89setmessages: + msg344163
2019-06-01 09:16:02vinay.sajipsetmessages: + msg344159
2019-05-31 22:16:22mariocj89setmessages: + msg344134
2019-05-31 19:08:23vinay.sajipsetmessages: + msg344108
2019-05-31 18:44:02mariocj89create