如何在Python中的函数入口,内部和退出时进行日志记录

时间:2014-05-02 19:22:17

标签: python logging

我希望能够使用Python日志工具在我的代码中进行简单一致的日志记录。

我可以执行以下操作:

  1. 我希望所有现有/未来的模块和功能都有“输入...”和“完成...”日志消息。
  2. 我不想在每个函数定义中添加相同的代码片段来定义日志记录参数(如don't want to add everywhere所示)。
  3. 我希望log.info(...)等构造在我在项目层次结构中定义的任何函数中都有效。
  4. 什么不起作用/我不知道该怎么做:

    1. 我想避免在我写的每个现有/新模块中定义相同的@log装饰器。
    2. # don't want to add everywhere
      FORMAT = '%(asctime)s - %(name)-20s - %(levelname)-5s - %(message)s'
      LEVEL = logging.DEBUG
      logging.basicConfig(format=FORMAT, level=LEVEL)
      log = logging.getLogger(__name__)
      


      我的Flask项目的示例代码:

      # app/__init__.py
      from a import b  # various other imports required for app
      import logging
      FORMAT = '%(asctime)s - %(name)-20s - %(levelname)-5s - %(message)s'
      LEVEL = logging.DEBUG
      logging.basicConfig(format=FORMAT, level=LEVEL)
      log = logging.getLogger(__name__)
      # ... various other app init code
      from app import views, models
      
      #app/views.py
      from c import d  # various other imports required for the module
      
      def logger(fn):
          from functools import wraps
          import inspect
          @wraps(fn)
          def wrapper(*args, **kwargs):
              global log
              log = logging.getLogger(inspect.stack()[1][3])
              log.info('About to run %s' % fn.__name__)
      
              out = apply(fn, args, kwargs)
      
              log.info('Done running %s' % fn.__name__)
              # Return the return value
              return out
          return wrapper
      
          @app.route('/this_func')
          @logger
          def this_func():
              log.info('I am doing logging without having to do bunch of definitions.')
              # some more code
      
          @app.route('/that_func')
          @logger
          def that_func():
              log.info('Yet more logging without having to do bunch of definitions.')
              log.info('I can simply refer to the log object and be done with it.')
              # some more code
      

2 个答案:

答案 0 :(得分:7)

对我有用的最终设置如下:

# At the beginning of every .py file in the project
def logger(fn):
    from functools import wraps
    import inspect
    @wraps(fn)
    def wrapper(*args, **kwargs):
        log = logging.getLogger(fn.__name__)
        log.info('About to run %s' % fn.__name__)

        out = apply(fn, args, kwargs)

        log.info('Done running %s' % fn.__name__)
        # Return the return value
        return out
    return wrapper

# Do the following section only in application's app/__init__.py
# Other files will pick it up from here.
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s'

# Change logging LEVEL according to debugging needs.
# Probably better to read this from a config or a launch parameter.
LEVEL = logging.DEBUG

logging.basicConfig(format=FORMAT, level=LEVEL)
# Up to here only for app/__init__.py

# This section again at the beginning of every .py file
log = logging.getLogger(__name__)
log.info('Entered module: %s' % __name__)

现在,为模块中的每个函数定义添加一个@logger装饰器:

@logger
def do_something():
    print('doing really useful stuff.')

答案 1 :(得分:1)

将contextlib与异常处理一起使用。

import logging
from time import time 
logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)s | %(processName)s | %(thread)d | %(message)s')
log = logging.getLogger(__name__)

@contextmanager
def EEL(methodDescr, raiseExcp=False):
  '''EEL = Entry Exit Log'''
  log.info(f"Starting function: {methodDescr}")
  start = time()
  try:
    yield
  except Exception as ex:
    log.exception("Got exception")
  finally:
    end = time()
    log.info(f"Time taken by {methodDescr}: {end-start}")

用法:

@EEL("Test method")
def test():
  x = 0
  for i in range(1,1000000):
    x = x + i

输出:

2020-09-22 11:09:17,418 | INFO | MainProcess | 139709531875200 | Starting function: Test method
2020-09-22 11:09:17,484 | INFO | MainProcess | 139709531875200 | Time taken by Test method: 0.06573295593261719