Getting logs twice in AWS lambda function

前端 未结 2 1033
梦谈多话
梦谈多话 2021-02-08 10:15

I\'m attempting to create a centralized module to set up my log formatter to be shared across a number of python modules within my lambda function. This function will ultimately

2条回答
  •  我在风中等你
    2021-02-08 10:44

    AWS Lambda also sets up a handler, on the root logger, and anything written to stdout is captured and logged as level INFO. Your log message is thus captured twice:

    • By the AWS Lambda handler on the root logger (as log messages propagate from nested child loggers to the root), and this logger has its own format configured.
    • By the AWS Lambda stdout-to-INFO logger.

    This is why the messages all start with (asctime) [(levelname)]-(module):(lineno), information; the root logger is configured to output messages with that format and the information written to stdout is just another %(message) part in that output.

    Just don't set a handler when you are in the AWS environment, or, disable propagation of the output to the root handler and live with all your messages being recorded as INFO messages by AWS; in the latter case your own formatter could include the levelname level information in the output.

    You can disable log propagation with logger.propagate = False, at which point your message is only going to be passed to your handler, not to to the root handler as well.

    Another option is to just rely on the AWS root logger configuration. According to this excellent reverse engineering blog post the root logger is configured with:

    logging.Formatter.converter = time.gmtime
    logger = logging.getLogger()
    logger_handler = LambdaLoggerHandler()
    logger_handler.setFormatter(logging.Formatter(
        '[%(levelname)s]\t%(asctime)s.%(msecs)dZ\t%(aws_request_id)s\t%(message)s\n',
        '%Y-%m-%dT%H:%M:%S'
    ))
    logger_handler.addFilter(LambdaLoggerFilter())
    logger.addHandler(logger_handler)
    

    This replaces the time.localtime converter on logging.Formatter with time.gmtime (so timestamps use UTC rather than locatime), sets a custom handler that makes sure messages go to the Lambda infrastructure, configures a format, and adds a filter object that only adds aws_request_id attribute to records (so the above formatter can include it) but doesn't actually filter anything.

    You could alter the formatter on that handler by updating the attributes on the handler.formatter object:

    for handler in logging.getLogger().handlers:
        formatter = handler.formatter
        if formatter is not None and 'aws_request_id' in formatter._fmt:
            # this is the AWS Lambda formatter
            # formatter.datefmt => '%Y-%m-%dT%H:%M:%S'
            # formatter._style._fmt => 
            #    '[%(levelname)s]\t%(asctime)s.%(msecs)dZ'
            #    '\t%(aws_request_id)s\t%(message)s\n'
    

    and then just drop your own logger handler entirely. You do want to be careful with this; AWS Lambda infrastructure could well be counting on a specific format being used. The output you show in your question doesn't include the date component (the %Y-%m-%dT part of the formatter.datefmt string) which probably means that the format has been parsed out and is being presented to you in a web application view of the data.

提交回复
热议问题