如何在Python中进行调试打印?

4

我有一个这样的函数...

def validate_phone(raw_number, debug=False):

我想让debug标志控制它是否输出日志语句。例如:

if (debug):
  print('Before splitting numbers', file=sys.stderr)

split_version = raw_number.split('-')

if (debug):
  print('After splitting numbers', file=sys.stderr)

然而,那段代码非常重复。如何以最简洁(最DRY?)的方式处理这种if-标志-然后记录逻辑?


4
为什么不使用 logging 模块? - Haifeng Zhang
为什么不使用单元测试来测试你的代码,而不是用调试输出来使其混乱呢? - Dirk Herrmann
虽然不是答案(logging是正确答案),但您始终可以使用动态定义的函数。只需在函数内部执行以下操作:debugprint = print if debug else lambda *a, **k: None,然后在整个函数中使用 debugprint 来代替 print 进行调试打印;这样做会评估参数并产生函数调用开销,但在非调试模式下不会执行任何操作,在调试模式下则会打印输出。 - ShadowRanger
4个回答

6

我认为在运行 Python 脚本时,使用日志记录是打印调试信息的最佳解决方案。我编写了一个 DebugPrint 模块,帮助更轻松地使用日志记录:

#DebugPrint.py
import logging
import os
import time

DEBUGMODE=True

logging.basicConfig(level=logging.DEBUG)
log=logging.getLogger('=>')    


#DebugPrint.py
#DbgPrint=logging.debug 
def DbgPrint(*args, **kwargs):
    if DEBUGMODE:
        #get module, class, function, linenumber information
        import inspect
        className = None
        try:
            className = inspect.stack()[2][0].f_locals['self'].__class__.__name__
        except:
            pass
        modName=None
        try:
            modName = os.path.basename(inspect.stack()[2][1])
        except:
            pass
        lineNo=inspect.stack()[2][2]
        fnName=None
        try:
            fnName = inspect.stack()[2][3]
        except:
            pass
        DbgText="line#{}:{}->{}->{}()".format(lineNo, modName,className, fnName)
        argCnt=len(args)
        kwargCnt=len(kwargs)
        #print("argCnt:{} kwargCnt:{}".format(argCnt,kwargCnt))
        fmt=""
        fmt1=DbgText+":"+time.strftime("%H:%M:%S")+"->"
        if argCnt > 0:
            fmt1+=(argCnt-1)*"%s,"
            fmt1+="%s"
            fmt+=fmt1

        if kwargCnt>0:
            fmt2="%s"
            args+=("{}".format(kwargs),)
            if len(fmt)>0:
                fmt+=","+fmt2
            else:
                fmt+=fmt2


        #print("fmt:{}".format(fmt))
        log.debug(fmt,*args)


if __name__=="__main__":
    def myTest():
        print("Running myTest()")
        DbgPrint("Hello","World")

myTest()

如果DEBUGMODE变量为false,则不会打印任何内容。
如果为true,则上面的示例代码将打印出:
DEBUG:=>:16:24:14:line#78:DebugPrint.py->None->myTest():->Hello,World

现在我将使用一个定义类的模块来测试DebugPrint。
#testDebugPrint.py
from DebugPrint import DbgPrint


class myTestClass(object):
    def __init__(self):
        DbgPrint("Initializing the class")

    def doSomething(self, arg):
        DbgPrint("I'm doing something with {}".format(arg))

if __name__=='__main__':
    test=myTestClass()
    test.doSomething("a friend!")

当运行此脚本时,输出如下:
DEBUG:=>:16:25:02:line#7:testDebugPrint.py->myTestClass->__init__():->Initializing the class
DEBUG:=>:16:25:02:line#10:testDebugPrint.py->myTestClass->doSomething():->I'm doing something with a friend!

请注意,控制台打印的模块名称、类名、函数名称和行号以及语句打印时间是正确的。希望您会发现这个实用程序很有用。

1
我会使用日志模块来实现它。这个模块就是为此而设计的。
> cat a.py       
import logging

log = logging.getLogger(__name__)


def main():
    log.debug('This is debug')
    log.info('This is info')
    log.warn('This is warn')
    log.fatal('This is fatal')
    try:
        raise Exception("this is exception")
    except Exception:
        log.warn('Failed with exception', exc_info=True)
        raise

if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser(description='something')
    parser.add_argument(
        '-v', '--verbose', action='count', default=0, dest='verbosity')
    args = parser.parse_args()

    logging.basicConfig()
    logging.getLogger().setLevel(logging.WARN - 10 * args.verbosity)

    main()
> python a.py 
WARNING:__main__:This is warn
CRITICAL:__main__:This is fatal
WARNING:__main__:Failed with exception
Traceback (most recent call last):
  File "a.py", line 12, in main
    raise Exception("this is exception")
Exception: this is exception
Traceback (most recent call last):
  File "a.py", line 27, in <module>
    main()
  File "a.py", line 12, in main
    raise Exception("this is exception")
Exception: this is exception
> python a.py -v
INFO:__main__:This is info
WARNING:__main__:This is warn
CRITICAL:__main__:This is fatal
WARNING:__main__:Failed with exception
Traceback (most recent call last):
  File "a.py", line 12, in main
    raise Exception("this is exception")
Exception: this is exception
Traceback (most recent call last):
  File "a.py", line 27, in <module>
    main()
  File "a.py", line 12, in main
    raise Exception("this is exception")
Exception: this is exception
> python a.py -vv
DEBUG:__main__:This is debug
INFO:__main__:This is info
WARNING:__main__:This is warn
CRITICAL:__main__:This is fatal
WARNING:__main__:Failed with exception
Traceback (most recent call last):
  File "a.py", line 12, in main
    raise Exception("this is exception")
Exception: this is exception
Traceback (most recent call last):
  File "a.py", line 27, in <module>
    main()
  File "a.py", line 12, in main
    raise Exception("this is exception")
Exception: this is exception

0
你应该使用日志记录模块:
import logging
import sys
# Get the "root" level logger.
root = logging.getLogger()
# Set the log level to debug.
root.setLevel(logging.DEBUG)
# Add a handler to output log messages as a stream (to a file/handle)
# in this case, sys.stderr
ch = logging.StreamHandler(sys.stderr)
# This handler can log debug messages - multiple handlers could log
# different "levels" of log messages.
ch.setLevel(logging.DEBUG)
# Format the output to include the time, etc.
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
# Add the formatter to the root handler...
root.addHandler(ch)
# Get a logger object - this is what we use to omit log messages.
logger=logging.getLogger(__name__)

# Generate a bunch of log messages as a demonstration
for i in xrange(100):
   logger.debug('The value of i is: %s', i)

# Demonstrate a useful example of logging full exception tracebacks
# if the logger will output debug, but a warning in other modes.
try:
   a='a'+12
except Exception as e:
   # Only log exceptions if debug is enabled..
   if logger.isEnabledFor(logging.DEBUG):
       # Log the traceback w/ the exception message.
       logger.exception('Something went wrong: %s', e)
   else:
       logger.warning('Something went wrong: %s', e)

点击此处了解更多相关信息: https://docs.python.org/2/library/logging.html

要禁用日志记录,只需将级别(logging.DEBUG)设置为其他值(例如logging.INFO)。请注意,将消息重定向到其他地方(如文件)或将某些消息(调试)发送到某个地方,将其他消息(警告)发送到其他地方也很容易。


0
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')

你可以更改日志级别为 INFO/DEBUG/WARNING/ERROR/CRITICAL,而且 logging 模块还可以为你记录时间戳,并且它也是可配置的。

查看链接 python3 logging HowTo


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接