在 AWS Lambda 中使用 python 日志记录

Posted

技术标签:

【中文标题】在 AWS Lambda 中使用 python 日志记录【英文标题】:Using python Logging with AWS Lambda 【发布时间】:2016-10-08 18:53:34 【问题描述】:

正如 AWS 文档所建议的那样:

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')

现在我做了:

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

第一个 sn-p 代码打印在 Cloud Watch 控制台中,但第二个没有。

我没有看到任何区别,因为两个 sn-ps 使用的是根记录器。

【问题讨论】:

你缺少“return 'Hello World!'” 为什么不和第一个代码sn-p一样呢?获取已经实例化的记录器,然后使用该记录器。 @HEADLESS_0NE:我可以使用第一个。但我想了解为什么会出现这种行为。 还可以在pypi.org/project/python-cloud-logger 上查看 python-cloud-logger。它提供了具有上下文日志记录的选项,其中 requestId 和其他上下文变量可以保存到线程的上下文中。从那时起,每条日志都会携带上下文。 【参考方案1】:

日志记录似乎不起作用的原因是 AWS Lambda Python 运行时pre-configures a logging handler,根据所选运行时的版本,它可能会修改所记录消息的格式,并且还可能将一些元数据添加到记录(如果有)。 not 预配置的是日志级别。这意味着无论您尝试发送哪种类型的日志消息,它都不会实际打印。

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

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

如果您希望您的 Python 脚本既可以在 AWS Lambda 上执行,又可以通过您的本地 Python 解释器执行,您可以检查是否配置了处理程序,然后回退到 basicConfig(它会创建默认的 stderr -handler) 否则:

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)

【讨论】:

len(logging.getLogger().handlers) > 0代替logging.getLogger().hasHandlers()可能会更好。 @ebk 这是一个很好的观点,但 hasHandlers 仅在 Python 3.2 之后才可用。由于AWS still supports the Python 2.7 runtime,使用len(...handlers)是目前最便携的解决方案。 你是对的。我应该添加“for Python 3.2+”:) python if logging.getLogger().handlers: 怎么样?读起来类似于 3.2+ 版本,而且更惯用。【参考方案2】:

直接从问题@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)

【讨论】:

谢谢,太好了。我把它放在我的 import 语句之后,然后我的整个 lambda/module 可以访问我自己的个人记录器:) 刚刚在 basicConfig 之后添加了这一行:logger = logging.getLogger() 从 python 3.8 开始,有一个新参数 force 与您上面描述的完全一样:logging.basicConfig(level = logging.INFO, force=True)。 docs.python.org/3/library/logging.html#logging.basicConfig【参考方案3】:

我遇到了类似的问题,我怀疑 lambda 容器正在调用 logging.basicConfig 以在导入 lambda 代码之前添加处理程序。这似乎是一种糟糕的形式......

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

看这篇文章Python logging before you run logging.basicConfig?

【讨论】:

【参考方案4】:

我一直在努力解决这个确切的问题。适用于本地和 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!")

【讨论】:

这对我不起作用。不得不使用OP发布的第一个块代码。 哪个方面没用?在 AWS CloudWatch 上登录还是在本地登录? 本地记录按预期工作。除非出现错误,否则无法在 cloudwatch 上查看日志。 和@Gru有同样的经历【参考方案5】:

实际上可能没有引用同一个记录器。 在第一个 sn-p 中,记录返回:logging.Logger.manager.loggerDict

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

另外,来自logging 文档,关于logging.basicConfig 的重要说明:

通过使用默认格式化程序创建 StreamHandler 并将其添加到根记录器来为日志记录系统进行基本配置。如果没有为根记录器定义处理程序,函数 debug()、info()、warning()、error() 和 critical() 将自动调用 basicConfig()。

如果根记录器已经为其配置了处理程序,则此函数不执行任何操作。

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

【讨论】:

sn-ps 是分开的。所以第二个 sn -p 没有配置日志记录,所以它将配置根记录器。如果我调用 logging.info 它将使用根记录器。对我来说,和第一个 sn-p 没什么区别。 @HEADLESS_0NE 就在这里。似乎在 lambda 中已经配置了一个记录器。如果我执行上述操作但将级别设置为 DEBUG,那么我看到的日志比我产生的要多(我自己没有产生任何这些日志):[DEBUG] 2016-10-29T09:01:28.376Z 45e6c8bd-9db6-11e6-aa56-43d43acb066b Acquiring 0 [DEBUG] 2016-10-29T09:01:28.389Z 45e6c8bd-9db6-11e6-aa56-43d43acb066b IOWriteTask('offset': 0) about to wait for the following futures [] [DEBUG] 2016-10-29T09:01:28.389Z 45e6c8bd-9db6-11e6-aa56-43d43acb066b IOWriteTask('offset': 0) done waiting for dependent futures【参考方案6】:

基本上,AWS 日志记录猴子补丁需要以非常特殊的方式处理,其中:

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

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

【讨论】:

【参考方案7】:
    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”)
    )

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于在 AWS Lambda 中使用 python 日志记录的主要内容,如果未能解决你的问题,请参考以下文章

在python中自动测试aws lambda函数

在单个AWS Lambda中使用两个python函数boto3

如何在 AWS Lambda 中使用 Python 自定义包

在 AWS Lambda 的子目录中打包 Python 依赖项

使用 Lambda 在 AWS Codestar 中安装 Python 依赖项

AWS Lambda在Python 3.8中不显示原因 异常堆栈跟踪