mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-91513: Add 'asyncio' taskName to logging LogRecord attributes. (GH-93193)
This commit is contained in:
parent
5185956527
commit
cc377063ef
5 changed files with 88 additions and 7 deletions
|
@ -1101,6 +1101,9 @@ need:
|
|||
| Current process name when using ``multiprocessing`` | Set ``logging.logMultiprocessing`` to ``False``. |
|
||||
| to manage multiple processes. | |
|
||||
+-----------------------------------------------------+---------------------------------------------------+
|
||||
| Current :class:`asyncio.Task` name when using | Set ``logging.logAsyncioTasks`` to ``False``. |
|
||||
| ``asyncio``. | |
|
||||
+-----------------------------------------------------+---------------------------------------------------+
|
||||
|
||||
Also note that the core logging module only includes the basic handlers. If
|
||||
you don't import :mod:`logging.handlers` and :mod:`logging.config`, they won't
|
||||
|
|
|
@ -872,10 +872,14 @@ the options available to you.
|
|||
+----------------+-------------------------+-----------------------------------------------+
|
||||
| threadName | ``%(threadName)s`` | Thread name (if available). |
|
||||
+----------------+-------------------------+-----------------------------------------------+
|
||||
| taskName | ``%(taskName)s`` | :class:`asyncio.Task` name (if available). |
|
||||
+----------------+-------------------------+-----------------------------------------------+
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
*processName* was added.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
*taskName* was added.
|
||||
|
||||
.. _logger-adapter:
|
||||
|
||||
|
|
|
@ -64,20 +64,25 @@ _startTime = time.time()
|
|||
raiseExceptions = True
|
||||
|
||||
#
|
||||
# If you don't want threading information in the log, set this to zero
|
||||
# If you don't want threading information in the log, set this to False
|
||||
#
|
||||
logThreads = True
|
||||
|
||||
#
|
||||
# If you don't want multiprocessing information in the log, set this to zero
|
||||
# If you don't want multiprocessing information in the log, set this to False
|
||||
#
|
||||
logMultiprocessing = True
|
||||
|
||||
#
|
||||
# If you don't want process information in the log, set this to zero
|
||||
# If you don't want process information in the log, set this to False
|
||||
#
|
||||
logProcesses = True
|
||||
|
||||
#
|
||||
# If you don't want asyncio task information in the log, set this to False
|
||||
#
|
||||
logAsyncioTasks = True
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Level related stuff
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -361,6 +366,15 @@ class LogRecord(object):
|
|||
else:
|
||||
self.process = None
|
||||
|
||||
self.taskName = None
|
||||
if logAsyncioTasks:
|
||||
asyncio = sys.modules.get('asyncio')
|
||||
if asyncio:
|
||||
try:
|
||||
self.taskName = asyncio.current_task().get_name()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,
|
||||
self.pathname, self.lineno, self.msg)
|
||||
|
@ -566,6 +580,7 @@ class Formatter(object):
|
|||
(typically at application startup time)
|
||||
%(thread)d Thread ID (if available)
|
||||
%(threadName)s Thread name (if available)
|
||||
%(taskName)s Task name (if available)
|
||||
%(process)d Process ID (if available)
|
||||
%(message)s The result of record.getMessage(), computed just as
|
||||
the record is emitted
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
Copyright (C) 2001-2021 Vinay Sajip. All Rights Reserved.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
import logging.config
|
||||
|
@ -50,6 +49,7 @@ from test.support import warnings_helper
|
|||
from test.support.logging_helper import TestHandler
|
||||
import textwrap
|
||||
import threading
|
||||
import asyncio
|
||||
import time
|
||||
import unittest
|
||||
import warnings
|
||||
|
@ -4552,29 +4552,63 @@ class LogRecordTest(BaseTest):
|
|||
import multiprocessing
|
||||
|
||||
def test_optional(self):
|
||||
r = logging.makeLogRecord({})
|
||||
NONE = self.assertIsNone
|
||||
NOT_NONE = self.assertIsNotNone
|
||||
|
||||
r = logging.makeLogRecord({})
|
||||
NOT_NONE(r.thread)
|
||||
NOT_NONE(r.threadName)
|
||||
NOT_NONE(r.process)
|
||||
NOT_NONE(r.processName)
|
||||
NONE(r.taskName)
|
||||
log_threads = logging.logThreads
|
||||
log_processes = logging.logProcesses
|
||||
log_multiprocessing = logging.logMultiprocessing
|
||||
log_asyncio_tasks = logging.logAsyncioTasks
|
||||
try:
|
||||
logging.logThreads = False
|
||||
logging.logProcesses = False
|
||||
logging.logMultiprocessing = False
|
||||
logging.logAsyncioTasks = False
|
||||
r = logging.makeLogRecord({})
|
||||
NONE = self.assertIsNone
|
||||
|
||||
NONE(r.thread)
|
||||
NONE(r.threadName)
|
||||
NONE(r.process)
|
||||
NONE(r.processName)
|
||||
NONE(r.taskName)
|
||||
finally:
|
||||
logging.logThreads = log_threads
|
||||
logging.logProcesses = log_processes
|
||||
logging.logMultiprocessing = log_multiprocessing
|
||||
logging.logAsyncioTasks = log_asyncio_tasks
|
||||
|
||||
async def _make_record_async(self, assertion):
|
||||
r = logging.makeLogRecord({})
|
||||
assertion(r.taskName)
|
||||
|
||||
def test_taskName_with_asyncio_imported(self):
|
||||
try:
|
||||
make_record = self._make_record_async
|
||||
with asyncio.Runner() as runner:
|
||||
logging.logAsyncioTasks = True
|
||||
runner.run(make_record(self.assertIsNotNone))
|
||||
logging.logAsyncioTasks = False
|
||||
runner.run(make_record(self.assertIsNone))
|
||||
finally:
|
||||
asyncio.set_event_loop_policy(None)
|
||||
|
||||
def test_taskName_without_asyncio_imported(self):
|
||||
try:
|
||||
make_record = self._make_record_async
|
||||
with asyncio.Runner() as runner, support.swap_item(sys.modules, 'asyncio', None):
|
||||
logging.logAsyncioTasks = True
|
||||
runner.run(make_record(self.assertIsNone))
|
||||
logging.logAsyncioTasks = False
|
||||
runner.run(make_record(self.assertIsNone))
|
||||
finally:
|
||||
asyncio.set_event_loop_policy(None)
|
||||
|
||||
|
||||
class BasicConfigTest(unittest.TestCase):
|
||||
|
||||
|
@ -4853,6 +4887,30 @@ class BasicConfigTest(unittest.TestCase):
|
|||
# didn't write anything due to the encoding error
|
||||
self.assertEqual(data, r'')
|
||||
|
||||
def test_log_taskName(self):
|
||||
async def log_record():
|
||||
logging.warning('hello world')
|
||||
|
||||
try:
|
||||
encoding = 'utf-8'
|
||||
logging.basicConfig(filename='test.log', errors='strict', encoding=encoding,
|
||||
format='%(taskName)s - %(message)s', level=logging.WARNING)
|
||||
|
||||
self.assertEqual(len(logging.root.handlers), 1)
|
||||
handler = logging.root.handlers[0]
|
||||
self.assertIsInstance(handler, logging.FileHandler)
|
||||
|
||||
with asyncio.Runner(debug=True) as runner:
|
||||
logging.logAsyncioTasks = True
|
||||
runner.run(log_record())
|
||||
finally:
|
||||
asyncio.set_event_loop_policy(None)
|
||||
handler.close()
|
||||
with open('test.log', encoding='utf-8') as f:
|
||||
data = f.read().strip()
|
||||
os.remove('test.log')
|
||||
self.assertRegex(data, r'Task-\d+ - hello world')
|
||||
|
||||
|
||||
def _test_log(self, method, level=None):
|
||||
# logging.root has no handlers so basicConfig should be called
|
||||
|
@ -5644,7 +5702,7 @@ class MiscTestCase(unittest.TestCase):
|
|||
'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
|
||||
'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
|
||||
'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
|
||||
'threading'}
|
||||
'threading', 'logAsyncioTasks'}
|
||||
support.check__all__(self, logging, not_exported=not_exported)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Added ``taskName`` attribute to :mod:`logging` module for use with :mod:`asyncio` tasks.
|
Loading…
Add table
Add a link
Reference in a new issue