Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-102402: Fix floating point math issue by using time.time_ns() in logging.LogRecord #102412

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

Merged
merged 24 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2f046c2
Add test for 102402 100msecs
dougthor42 Mar 4, 2023
2460f88
Update logging.LogRecord to use time_ns.
dougthor42 Mar 4, 2023
059f856
Simplify msecs calculation
dougthor42 Mar 4, 2023
997bd41
Add blurb
dougthor42 Mar 4, 2023
2e7cde3
Fix comments, remove superfluous comment
dougthor42 Mar 4, 2023
5505cea
Merge branch 'main' into fix-gh-102402-logging-msecs
dougthor42 Mar 4, 2023
411a421
Switch to math algorithm rather than string manipulation.
dougthor42 Mar 5, 2023
d3a1f78
Correct and update comment
dougthor42 Apr 15, 2023
5fa370b
Use unittest.mock.patch instead of manual monkeypatching
dougthor42 Apr 15, 2023
39cab9d
formatting in test_logging.py
dougthor42 Mar 2, 2024
920bf74
Update docs
dougthor42 Mar 2, 2024
8bdc406
Keep ref to gh-89047
dougthor42 Mar 2, 2024
bf68b26
Use modulo arithmetic, update comment
dougthor42 Mar 2, 2024
e404ca0
Add comment
dougthor42 Mar 2, 2024
9661499
Correct comment
dougthor42 Mar 3, 2024
ec74b0e
Rename test
dougthor42 Mar 3, 2024
710f3ac
Add test for relativeCreated
dougthor42 Mar 3, 2024
ff8140c
remove issue num from test names; move to comment
dougthor42 Mar 3, 2024
fd45694
Use importhelper instead of importlib.reload
dougthor42 Mar 3, 2024
84b931b
Also assert record.created
dougthor42 Mar 3, 2024
5a604f9
Update test to check multiple offset values
dougthor42 Mar 3, 2024
dc90919
Optimize loop vs import CM
dougthor42 Mar 4, 2024
502ff65
Move item to variable
dougthor42 Mar 4, 2024
65ea1b5
Merge branch 'main' into fix-gh-102402-logging-msecs
vsajip Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/library/logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ the options available to you.
| | | portion of the time). |
+----------------+-------------------------+-----------------------------------------------+
| created | ``%(created)f`` | Time when the :class:`LogRecord` was created |
| | | (as returned by :func:`time.time`). |
| | | (as returned by :func:`time.time_ns` / 1e9). |
+----------------+-------------------------+-----------------------------------------------+
| exc_info | You shouldn't need to | Exception tuple (à la ``sys.exc_info``) or, |
| | format this yourself. | if no exception has occurred, ``None``. |
Expand Down
17 changes: 11 additions & 6 deletions Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
#
#_startTime is used as the base when calculating the relative time of events
#
_startTime = time.time()
_startTime = time.time_ns()

#
#raiseExceptions is used to see if exceptions during handling should be
Expand Down Expand Up @@ -300,7 +300,7 @@ def __init__(self, name, level, pathname, lineno,
"""
Initialize a logging record with interesting information.
"""
ct = time.time()
ct = time.time_ns()
self.name = name
self.msg = msg
#
Expand Down Expand Up @@ -339,9 +339,14 @@ def __init__(self, name, level, pathname, lineno,
self.stack_info = sinfo
self.lineno = lineno
self.funcName = func
self.created = ct
self.msecs = int((ct - int(ct)) * 1000) + 0.0 # see gh-89047
self.relativeCreated = (self.created - _startTime) * 1000
self.created = ct / 1e9 # ns to float seconds

# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
# Convert to float by adding 0.0 for historical reasons. See gh-89047
self.msecs = (ct % 1_000_000_000) // 1_000_000 + 0.0

self.relativeCreated = (ct - _startTime) / 1e6
if logThreads:
self.thread = threading.get_ident()
self.threadName = threading.current_thread().name
Expand Down Expand Up @@ -572,7 +577,7 @@ class Formatter(object):
%(lineno)d Source line number where the logging call was issued
(if available)
%(funcName)s Function name
%(created)f Time when the LogRecord was created (time.time()
%(created)f Time when the LogRecord was created (time.time_ns() / 1e9
return value)
%(asctime)s Textual time when the LogRecord was created
%(msecs)d Millisecond portion of the creation time
Expand Down
39 changes: 39 additions & 0 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import weakref

from http.server import HTTPServer, BaseHTTPRequestHandler
from unittest.mock import patch
from urllib.parse import urlparse, parse_qs
from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
ThreadingTCPServer, StreamRequestHandler)
Expand Down Expand Up @@ -4552,6 +4553,44 @@ def test_issue_89047(self):
s = f.format(r)
self.assertNotIn('.1000', s)

def test_msecs_has_no_floating_point_precision_loss(self):
# See issue gh-102402
tests = (
# time_ns is approx. 2023-03-04 04:25:20 UTC
# (time_ns, expected_msecs_value)
(1_677_902_297_100_000_000, 100.0), # exactly 100ms
(1_677_903_920_999_998_503, 999.0), # check truncating doesn't round
(1_677_903_920_000_998_503, 0.0), # check truncating doesn't round
)
for ns, want in tests:
with patch('time.time_ns') as patched_ns:
patched_ns.return_value = ns
record = logging.makeLogRecord({'msg': 'test'})
self.assertEqual(record.msecs, want)
self.assertEqual(record.created, ns / 1e9)

def test_relativeCreated_has_higher_precision(self):
# See issue gh-102402
ns = 1_677_903_920_000_998_503 # approx. 2023-03-04 04:25:20 UTC
offsets_ns = (200, 500, 12_354, 99_999, 1_677_903_456_999_123_456)
orig_modules = import_helper._save_and_remove_modules(['logging'])
try:
with patch("time.time_ns") as patched_ns:
# mock for module import
patched_ns.return_value = ns
import logging
for offset_ns in offsets_ns:
new_ns = ns + offset_ns
# mock for log record creation
patched_ns.return_value = new_ns
record = logging.makeLogRecord({'msg': 'test'})
self.assertAlmostEqual(record.created, new_ns / 1e9, places=6)
# After PR gh-102412, precision (places) increases from 3 to 7
self.assertAlmostEqual(record.relativeCreated, offset_ns / 1e6, places=7)
finally:
import_helper._save_and_remove_modules(['logging'])
sys.modules.update(orig_modules)


class TestBufferingFormatter(logging.BufferingFormatter):
def formatHeader(self, records):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Adjust ``logging.LogRecord`` to use ``time.time_ns()`` and fix minor bug
related to floating point math.