为什么日志记录在一次调用中起作用,而不是从 main 函数中起作用?

Why logging works in a single call but not from main function?

我有一个名为 setup_logger() 的函数可以为我的应用程序设置日志系统,它位于 controllers.py 文件中。 它工作得很好。但有时当我 运行 来自 main.py 的函数时,日志记录没有被调用(我对某些函数使用 asyncio)。

当我调用我的函数 get_browser() 本身时,logging 工作并将消息发送到 StreamHandlerFileHandler,因为它应该这样做。
但是当我从我的文件 main.py 中调用相同的函数 get_browser() 时,日志记录没有做任何事情。

我想知道是什么阻止了调用日志记录功能,如果是 asyncio 或其他原因。

我的回购:https://github.com/guimatheus92/Bot_BombCrypto

我的项目:

└── Project
    ├── main.py         # setup our app
    ├── controllers.py  # this file contains setup_logger() and get_browser()

get_browser()函数:

def get_browser():
    logger = setup_logger(telegram_integration=True)
    logger.info("Profiles selected: ...")

    applications = []
    website_browser = []

    return applications, website_browser

setup_logger()函数:

def setup_logger(telegram_integration=False, bot_name=''):
    if bot_name != '':
        formatter = logging.Formatter('%(levelname)s | Function: %(funcName)s | %(asctime)s: Bot (' + str(bot_name) + '):  %(message)s', datefmt='%m/%d/%Y %H:%M:%S')
    else:
        formatter = logging.Formatter('%(levelname)s | Function: %(funcName)s | %(asctime)s: %(message)s', datefmt='%m/%d/%Y %H:%M:%S')

    level = logging.INFO

    consolehandler = logging.StreamHandler(sys.stdout)
    consolehandler.setFormatter(formatter)

    logger = logging.getLogger('logs')
    if logger.hasHandlers():
        # Logger is already configured, remove all handlers
        logger.handlers = []
    else:
        logger.setLevel(level)
        logger.addHandler(consolehandler)

    return logger

main.py 我刚打电话:

applications, website_browser = get_browser()
async def main():
    logger = setup_logger(telegram_integration=True)

    logger.info('------------------- New Execution ----------------\n')
    logger.info('Starting Bot..... Bot started!')

    applications, website_browser = get_browser()
    logger.info('Number of accounts that the bot will run: %s' % (len(applications)))


if __name__ == "__main__":
    try:                
        loop = asyncio.get_event_loop()
        loop.create_task(main())
        loop.run_forever()
    except Exception as e:
        print("Exception: " + str(e))
        exit()
  1. main() 调用 setup_logger() 两次:

    • main()setup_logger()
    • main()get_browser()setup_logger()
  2. setup_logger 删除所有已配置的处理程序,仅在未配置的情况下进行配置:

def setup_logger(telegram_integration=False, bot_name=''):
    # Prepare handlers
    # ...

    logger = logging.getLogger('logs')
    if logger.hasHandlers():
        # Logger is already configured, remove all handlers
        logger.handlers = []
    else:
        logger.setLevel(level)
        logger.addHandler(consolehandler)

解决方案

将配置移出 else 块:

def setup_logger(telegram_integration=False, bot_name=''):
    # Prepare handlers
    # ...

    logger = logging.getLogger('logs')
    if logger.hasHandlers():
        # Logger is already configured, remove all handlers
        logger.handlers = []
    
    logger.setLevel(level)
    logger.addHandler(consolehandler)

理想情况下,日志记录工具应该只配置一次,除非您无法承受应用程序的冷重载。

您是否考虑过使用标准 logging.config.dictConfig 而不是所有 setup_logger?只需使用您需要的任何参数化动态制作字典配置,并发出配置一次,甚至在启动事件循环之前。稍后,直接使用 logging.getLogger

获取配置的记录器

我认为这会帮助您组织代码。

class TelegramHandler(logging.Handler):
    def __init__(self, bot_name=''):
        super().__init__()
        self.bot_name = bot_name

    def emit(self, record):
        message = self.format(record)
        send_telegram_msg(message, self.bot_name)


def get_logging_cfg(telegram_integration=False, bot_name=''):
    return {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'default': {
                'format':
                    '%(levelname)s | Function: %(funcName)s | %(asctime)s: Bot (' + str(bot_name) + '):  %(message)s'
                    if bot_name else
                    '%(levelname)s | Function: %(funcName)s | %(asctime)s: %(message)s',
                'datefmt': '%m/%d/%Y %H:%M:%S',
            },
        },
        'handlers': {
            'file': {
                'formatter': 'default',
                'class': 'logging.handlers.TimedRotatingFileHandler',
                'filename': os.path.join(pathlib.Path(__file__).parent.resolve(), 'logs', 'myapp.log'),
                'when': 'D',
            },
            'telegram': {
                'formatter': 'default',
                'class': 'import.path.to.you.module.TelegramHandler',
                'bot_name': bot_name,
            },
            'null': {
                'formatter': 'default',
                'class': 'logging.handlers.NullHandler',
            }
        },
        'loggers': {
            'logs': {
                'handlers':
                    (['file'] if create_logfiles else [])
                    + (['telegram'] if telegram_integration and telegram_token else [])
            }
        },
        'root': {
            # consider something else, for the sake of the logging happening beyond your own code
            'handlers': ['null'],
            'level': 'INFO',
        },
    }


async def main():
    logger = logging.getLogger('logs')
    logger.info('start')
    applications, website_browser = get_browser()
    # ...


def get_browser():
    logger = logging.getLogger('logs')
    logger.info('whatever')
    # ...


if __name__ == "__main__":
    logging.config.dictConfig(
        get_logging_cfg(
            telegram_integration=True,
            bot_name='foo',
        )
    )
    loop = asyncio.get_event_loop()
    loop.create_task(main())
    loop.run_forever()

奖励:使用 TimedRotatingFileHandler 而不是所有手动路径制作。

Python 的 logging 是一个美丽的图书馆,如果你阅读它 docs thoroughly

你会更开心