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

Skip to content

Commit 8593ae6

Browse files
committed
Logging: added stack_info argument.
1 parent b6b76c2 commit 8593ae6

3 files changed

Lines changed: 113 additions & 21 deletions

File tree

Doc/library/logging.rst

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -757,13 +757,31 @@ functions.
757757
*msg* using the string formatting operator. (Note that this means that you can
758758
use keywords in the format string, together with a single dictionary argument.)
759759

760-
There are two keyword arguments in *kwargs* which are inspected: *exc_info*
760+
There are three keyword arguments in *kwargs* which are inspected: *exc_info*
761761
which, if it does not evaluate as false, causes exception information to be
762762
added to the logging message. If an exception tuple (in the format returned by
763763
:func:`sys.exc_info`) is provided, it is used; otherwise, :func:`sys.exc_info`
764764
is called to get the exception information.
765765

766-
The other optional keyword argument is *extra* which can be used to pass a
766+
The second optional keyword argument is *stack_info*, which defaults to
767+
False. If specified as True, stack information is added to the logging
768+
message, including the actual logging call. Note that this is not the same
769+
stack information as that displayed through specifying *exc_info*: The
770+
former is stack frames from the bottom of the stack up to the logging call
771+
in the current thread, whereas the latter is information about stack frames
772+
which have been unwound, following an exception, while searching for
773+
exception handlers.
774+
775+
You can specify *stack_info* independently of *exc_info*, e.g. to just show
776+
how you got to a certain point in your code, even when no exceptions were
777+
raised. The stack frames are printed following a header line which says::
778+
779+
Stack (most recent call last):
780+
781+
This mimics the `Traceback (most recent call last):` which is used when
782+
displaying exception frames.
783+
784+
The third optional keyword argument is *extra* which can be used to pass a
767785
dictionary which is used to populate the __dict__ of the LogRecord created for
768786
the logging event with user-defined attributes. These custom attributes can then
769787
be used as you like. For example, they could be incorporated into logged
@@ -796,6 +814,8 @@ functions.
796814
above example). In such circumstances, it is likely that specialized
797815
:class:`Formatter`\ s would be used with particular :class:`Handler`\ s.
798816

817+
.. versionadded:: 3.2
818+
The *stack_info* parameter was added.
799819

800820
.. function:: info(msg, *args, **kwargs)
801821

@@ -1038,13 +1058,31 @@ instantiated directly, but always through the module-level function
10381058
*msg* using the string formatting operator. (Note that this means that you can
10391059
use keywords in the format string, together with a single dictionary argument.)
10401060

1041-
There are two keyword arguments in *kwargs* which are inspected: *exc_info*
1061+
There are three keyword arguments in *kwargs* which are inspected: *exc_info*
10421062
which, if it does not evaluate as false, causes exception information to be
10431063
added to the logging message. If an exception tuple (in the format returned by
10441064
:func:`sys.exc_info`) is provided, it is used; otherwise, :func:`sys.exc_info`
10451065
is called to get the exception information.
10461066

1047-
The other optional keyword argument is *extra* which can be used to pass a
1067+
The second optional keyword argument is *stack_info*, which defaults to
1068+
False. If specified as True, stack information is added to the logging
1069+
message, including the actual logging call. Note that this is not the same
1070+
stack information as that displayed through specifying *exc_info*: The
1071+
former is stack frames from the bottom of the stack up to the logging call
1072+
in the current thread, whereas the latter is information about stack frames
1073+
which have been unwound, following an exception, while searching for
1074+
exception handlers.
1075+
1076+
You can specify *stack_info* independently of *exc_info*, e.g. to just show
1077+
how you got to a certain point in your code, even when no exceptions were
1078+
raised. The stack frames are printed following a header line which says::
1079+
1080+
Stack (most recent call last):
1081+
1082+
This mimics the `Traceback (most recent call last):` which is used when
1083+
displaying exception frames.
1084+
1085+
The third keyword argument is *extra* which can be used to pass a
10481086
dictionary which is used to populate the __dict__ of the LogRecord created for
10491087
the logging event with user-defined attributes. These custom attributes can then
10501088
be used as you like. For example, they could be incorporated into logged
@@ -1078,6 +1116,9 @@ instantiated directly, but always through the module-level function
10781116
above example). In such circumstances, it is likely that specialized
10791117
:class:`Formatter`\ s would be used with particular :class:`Handler`\ s.
10801118

1119+
.. versionadded:: 3.2
1120+
The *stack_info* parameter was added.
1121+
10811122

10821123
.. method:: Logger.info(msg, *args, **kwargs)
10831124

@@ -1142,10 +1183,11 @@ instantiated directly, but always through the module-level function
11421183
Removes the specified handler *hdlr* from this logger.
11431184

11441185

1145-
.. method:: Logger.findCaller()
1186+
.. method:: Logger.findCaller(stack_info=False)
11461187

11471188
Finds the caller's source filename and line number. Returns the filename, line
1148-
number and function name as a 3-element tuple.
1189+
number, function name and stack information as a 4-element tuple. The stack
1190+
information is returned as *None* unless *stack_info* is *True*.
11491191

11501192

11511193
.. method:: Logger.handle(record)
@@ -1156,7 +1198,7 @@ instantiated directly, but always through the module-level function
11561198
Logger-level filtering is applied using :meth:`~Logger.filter`.
11571199

11581200

1159-
.. method:: Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info, func=None, extra=None)
1201+
.. method:: Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)
11601202

11611203
This is a factory method which can be overridden in subclasses to create
11621204
specialized :class:`LogRecord` instances.
@@ -3043,6 +3085,9 @@ Currently, the useful mapping keys in a :class:`LogRecord` are:
30433085
formatter to handle the event doesn't use the cached value but
30443086
recalculates it afresh.
30453087

3088+
If stack information is available, it's appended after the exception
3089+
information, using :meth:`formatStack` to transform it if necessary.
3090+
30463091

30473092
.. method:: formatTime(record, datefmt=None)
30483093

@@ -3062,6 +3107,12 @@ Currently, the useful mapping keys in a :class:`LogRecord` are:
30623107
just uses :func:`traceback.print_exception`. The resulting string is
30633108
returned.
30643109

3110+
.. method:: formatStack(stack_info)
3111+
3112+
Formats the specified stack information (a string as returned by
3113+
:func:`traceback.print_stack`, but with the last newline removed) as a
3114+
string. This default implementation just returns the input value.
3115+
30653116
.. _filter:
30663117

30673118
Filter Objects
@@ -3131,7 +3182,7 @@ every time something is logged, and can be created manually via
31313182
wire).
31323183

31333184

3134-
.. class:: LogRecord(name, lvl, pathname, lineno, msg, args, exc_info, func=None)
3185+
.. class:: LogRecord(name, lvl, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)
31353186

31363187
Contains all the information pertinent to the event being logged.
31373188

@@ -3178,6 +3229,12 @@ wire).
31783229

31793230
Absolute pathname of the source file of origin.
31803231

3232+
.. attribute:: stack_info
3233+
3234+
Stack frame information (where available) from the bottom of the stack
3235+
in the current thread, up to and including the stack frame of the
3236+
logging call which resulted in the creation of this record.
3237+
31813238
.. method:: getMessage()
31823239

31833240
Returns the message for this :class:`LogRecord` instance after merging any

Lib/logging/__init__.py

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ class LogRecord(object):
238238
information to be logged.
239239
"""
240240
def __init__(self, name, level, pathname, lineno,
241-
msg, args, exc_info, func=None):
241+
msg, args, exc_info, func=None, sinfo=None):
242242
"""
243243
Initialize a logging record with interesting information.
244244
"""
@@ -272,6 +272,7 @@ def __init__(self, name, level, pathname, lineno,
272272
self.module = "Unknown module"
273273
self.exc_info = exc_info
274274
self.exc_text = None # used to cache the traceback text
275+
self.stack_info = sinfo
275276
self.lineno = lineno
276277
self.funcName = func
277278
self.created = ct
@@ -515,6 +516,19 @@ def usesTime(self):
515516
def formatMessage(self, record):
516517
return self._style.format(record)
517518

519+
def formatStack(self, stack_info):
520+
"""
521+
This method is provided as an extension point for specialized
522+
formatting of stack information.
523+
524+
The input data is a string as returned from a call to
525+
:func:`traceback.print_stack`, but with the last trailing newline
526+
removed.
527+
528+
The base implementation just returns the value passed in.
529+
"""
530+
return stack_info
531+
518532
def format(self, record):
519533
"""
520534
Format the specified record as text.
@@ -541,6 +555,10 @@ def format(self, record):
541555
if s[-1:] != "\n":
542556
s = s + "\n"
543557
s = s + record.exc_text
558+
if record.stack_info:
559+
if s[-1:] != "\n":
560+
s = s + "\n"
561+
s = s + self.formatStack(record.stack_info)
544562
return s
545563

546564
#
@@ -1213,11 +1231,12 @@ def error(self, msg, *args, **kwargs):
12131231
if self.isEnabledFor(ERROR):
12141232
self._log(ERROR, msg, args, **kwargs)
12151233

1216-
def exception(self, msg, *args):
1234+
def exception(self, msg, *args, **kwargs):
12171235
"""
12181236
Convenience method for logging an ERROR with exception information.
12191237
"""
1220-
self.error(msg, exc_info=1, *args)
1238+
kwargs['exc_info'] = True
1239+
self.error(msg, *args, **kwargs)
12211240

12221241
def critical(self, msg, *args, **kwargs):
12231242
"""
@@ -1250,7 +1269,7 @@ def log(self, level, msg, *args, **kwargs):
12501269
if self.isEnabledFor(level):
12511270
self._log(level, msg, args, **kwargs)
12521271

1253-
def findCaller(self):
1272+
def findCaller(self, stack_info=False):
12541273
"""
12551274
Find the stack frame of the caller so that we can note the source
12561275
file name, line number and function name.
@@ -1260,49 +1279,62 @@ def findCaller(self):
12601279
#IronPython isn't run with -X:Frames.
12611280
if f is not None:
12621281
f = f.f_back
1263-
rv = "(unknown file)", 0, "(unknown function)"
1282+
rv = "(unknown file)", 0, "(unknown function)", None
12641283
while hasattr(f, "f_code"):
12651284
co = f.f_code
12661285
filename = os.path.normcase(co.co_filename)
12671286
if filename == _srcfile:
12681287
f = f.f_back
12691288
continue
1270-
rv = (co.co_filename, f.f_lineno, co.co_name)
1289+
sinfo = None
1290+
if stack_info:
1291+
sio = io.StringIO()
1292+
sio.write('Stack (most recent call last):\n')
1293+
traceback.print_stack(f, file=sio)
1294+
sinfo = sio.getvalue()
1295+
if sinfo[-1] == '\n':
1296+
sinfo = sinfo[:-1]
1297+
sio.close()
1298+
rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
12711299
break
12721300
return rv
12731301

1274-
def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
1302+
def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
1303+
func=None, extra=None, sinfo=None):
12751304
"""
12761305
A factory method which can be overridden in subclasses to create
12771306
specialized LogRecords.
12781307
"""
1279-
rv = _logRecordClass(name, level, fn, lno, msg, args, exc_info, func)
1308+
rv = _logRecordClass(name, level, fn, lno, msg, args, exc_info, func,
1309+
sinfo)
12801310
if extra is not None:
12811311
for key in extra:
12821312
if (key in ["message", "asctime"]) or (key in rv.__dict__):
12831313
raise KeyError("Attempt to overwrite %r in LogRecord" % key)
12841314
rv.__dict__[key] = extra[key]
12851315
return rv
12861316

1287-
def _log(self, level, msg, args, exc_info=None, extra=None):
1317+
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
12881318
"""
12891319
Low-level logging routine which creates a LogRecord and then calls
12901320
all the handlers of this logger to handle the record.
12911321
"""
1322+
sinfo = None
12921323
if _srcfile:
12931324
#IronPython doesn't track Python frames, so findCaller throws an
12941325
#exception on some versions of IronPython. We trap it here so that
12951326
#IronPython can use logging.
12961327
try:
1297-
fn, lno, func = self.findCaller()
1328+
fn, lno, func, sinfo = self.findCaller(stack_info)
12981329
except ValueError:
12991330
fn, lno, func = "(unknown file)", 0, "(unknown function)"
13001331
else:
13011332
fn, lno, func = "(unknown file)", 0, "(unknown function)"
13021333
if exc_info:
13031334
if not isinstance(exc_info, tuple):
13041335
exc_info = sys.exc_info()
1305-
record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
1336+
record = self.makeRecord(self.name, level, fn, lno, msg, args,
1337+
exc_info, func, extra, sinfo)
13061338
self.handle(record)
13071339

13081340
def handle(self, record):
@@ -1657,12 +1689,13 @@ def error(msg, *args, **kwargs):
16571689
basicConfig()
16581690
root.error(msg, *args, **kwargs)
16591691

1660-
def exception(msg, *args):
1692+
def exception(msg, *args, **kwargs):
16611693
"""
16621694
Log a message with severity 'ERROR' on the root logger,
16631695
with exception information.
16641696
"""
1665-
error(msg, exc_info=1, *args)
1697+
kwargs['exc_info'] = True
1698+
error(msg, *args, **kwargs)
16661699

16671700
def warning(msg, *args, **kwargs):
16681701
"""

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Core and Builtins
6363
Library
6464
-------
6565

66+
- Issue #1553375: logging: Added stack_info kwarg to display stack information.
67+
6668
- Issue #5111: IPv6 Host in the Header is wrapped inside [ ]. Patch by Chandru.
6769

6870
- Fix Fraction.__hash__ so that Fraction.__hash__(-1) is -2. (See also issue

0 commit comments

Comments
 (0)