Using python Logging with AWS Lambda

As the AWS documentation suggests:

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def my_logging_handler(event, context):
logger.info('got event{}'.format(event))
logger.error('something went wrong')

Now I made:

import logging
logging.basicConfig(level = logging.INFO)
logging.info("Hello World!")

The first snippet of code prints in the Cloud Watch console, but the second one no.

I didn't see any difference as the two snippets are using the root logger.

83206 次浏览

实际上,可能不是引用同一个日志记录器。 在第一个代码片段中,记录: logging.Logger.manager.loggerDict的返回值

它将返回已初始化的日志记录器的 dict

此外,从 logging文档中,关于 logging.basicConfig的一个重要说明是:

使用默认的 Formatter 创建一个 StreamHandler 并将其添加到根日志记录器,从而为日志记录系统进行基本配置。如果没有为根日志记录器定义任何处理程序,那么 debug ()、 info ()、 police ()、 error ()和 Crital ()函数将自动调用 basicConfig ()。

This function does nothing if the root logger already has handlers configured for it.

资料来源: https://docs.python.org/2/library/logging.html#logging.basicConfig

我遇到过类似的问题,我怀疑 lambda 容器在导入 lambda 代码之前调用 logging.basicConfig 来添加处理程序。这样不太好吧。

解决方案是查看是否配置了根日志记录器处理程序,如果配置了,则删除它们,添加我的格式化程序和所需的日志级别(使用 basicConfig) ,并恢复处理程序。

请参阅本文 运行 logging.basicConfig 之前的 Python 日志记录?

直接从问题@StevenBohrer 的答案链接中的顶部答案复制过来(这对我很有用,用我自己的配置替换了最后一行) :

root = logging.getLogger()
if root.handlers:
for handler in root.handlers:
root.removeHandler(handler)
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)

从本质上讲,AWS 日志猴子补丁需要以一种非常特殊的方式处理,其中:

  1. 日志级别是从脚本的 TOP 级别设置的(例如,在导入时)
  2. 您感兴趣的 log 语句是从 lambda 函数中调用的

由于在 Python 模块导入中不运行任意代码通常被认为是一种很好的形式,因此通常应该能够重新构造代码,以便只在 lambda 函数内部执行繁重的提升工作。

日志记录似乎不起作用的原因是 AWS Lambda Python 运行时 预配置日志处理程序,根据所选的运行时版本,它可能会修改已记录的消息的格式,如果可用,还可能会向记录添加一些元数据。没有预先配置的是日志级别。这意味着无论您尝试发送哪种类型的日志消息,它都不会实际打印。

作为 AWS 文档本身,要在 AWS Lambda 上下文中正确使用 logging库,您只需要为 root-logger 设置日志级别:

import logging
logging.getLogger().setLevel(logging.INFO)

如果你希望你的 Python 脚本在 AWS Lambda 上都是可执行的,但是在你的本地 Python 解释器上,你可以检查是否配置了一个处理程序,然后回到 basicConfig(它创建了默认的 stderr 处理程序) ,否则:

if len(logging.getLogger().handlers) > 0:
# The Lambda environment pre-configures a handler logging to stderr. If a handler is already configured,
# `.basicConfig` does not execute. Thus we set the level directly.
logging.getLogger().setLevel(logging.INFO)
else:
logging.basicConfig(level=logging.INFO)

我一直在努力解决这个问题。在本地和 AWS CloudWatch 上都可以使用的解决方案是像下面这样设置日志:

import logging


# Initialize you log configuration using the base class
logging.basicConfig(level = logging.INFO)


# Retrieve the logger instance
logger = logging.getLogger()


# Log your output to the retrieved logger instance
logger.info("Python for the win!")


    LOGGER = logging.getLogger()
HANDLER = LOGGER.handlers[0]
HANDLER.setFormatter(
logging.Formatter(“[%(asctime)s] %(levelname)s:%(name)s:%(message)s”, “%Y-%m-%d %H:%M:%S”)
)

我建议使用 Aws python lambda powertools。日志文档是 给你。代码示例:

from aws_lambda_powertools import Logger
logger = Logger() # Sets service via env var
# OR logger = Logger(service="example")

它在本地和 CloudWatch 上都可以工作。

这取决于 aws lambda python version

如果 python 版本3.8和 以上

import os
import logging


default_log_args = {
"level": logging.DEBUG if os.environ.get("DEBUG", False) else logging.INFO,
"format": "%(asctime)s [%(levelname)s] %(name)s - %(message)s",
"datefmt": "%d-%b-%y %H:%M",
"force": True,
}




logging.basicConfig(**default_log_args)
log = logging.getLogger("Run-Lambda")


log.info("I m here too)


如果 python 版本3.7和 下面

import os
import logging


root = logging.getLogger()
if root.handlers:
for handler in root.handlers:
root.removeHandler(handler)


default_log_args = {
"level": logging.DEBUG if os.environ.get("DEBUG", False) else logging.INFO,
"format": "%(asctime)s [%(levelname)s] %(name)s - %(message)s",
"datefmt": "%d-%b-%y %H:%M"
}


logging.basicConfig(**default_log_args)
log = logging.getLogger("Run-Lambda")


log.info("Iam here")


I have also solved this issue so that logging would not require change for local and on aws. Below is the sample code:

def set_default_logger():
if "LOG_LEVEL" in os.environ:
# For Lambda
log_level = os.environ["LOG_LEVEL"]
else:
log_level = DEFAULT_LOG_LEVEL # Set default log level for local


root = logging.getLogger()
if len(logging.getLogger().handlers) > 0:
# For Lambda
for handler in root.handlers:
root.removeHandler(handler)
logging.basicConfig(level=log_level,
format='[%(asctime)s.%(msecs)03d] [%(levelname)s] [%(module)s] [%(funcName)s] [L%(lineno)d] [P%(process)d] [T%(thread)d] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
else:
# For Local
l_name = os.getcwd()+'/'+'count_mac_module.log'
logging.basicConfig(filename=l_name, level=log_level,
format='[%(asctime)s.%(msecs)03d] [%(levelname)s] [%(module)s] [%(funcName)s] [L%(lineno)d] [P%(process)d] [T%(thread)d] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
    

logger = logging.getLogger(__name__)
logger.debug(f"************* logging set for Lambda {os.getenv('AWS_LAMBDA_FUNCTION_NAME') } *************")