在AWS lambda函数

时间:2018-06-18 12:44:08

标签: python amazon-web-services logging aws-lambda greengrass

我正在尝试创建一个集中模块来设置我的日志格式化程序,以便在我的lambda函数中的多个python模块之间共享。此功能最终将在本地内部部署设备上的AWS Greengrass上运行。

出于某种原因,当我添加自己的处理程序来格式化消息时,日志将被输出两次 - 一次是在正确的日志级别,第二次是在错误的级别。

如果我使用标准的python记录器而没有设置任何处理程序,它就可以正常工作。

main.py

import logging

logging.debug("test1")

cloudwatch logs

12:28:42 [DEBUG]-main.py:38,test1

我的目标是在我的代码上有一个格式化程序,它将这些日志消息格式化为JSON。然后它们将被摄入集中式日志记录数据库。但是,当我这样做时,我会得到两次日志消息。

loghelper.py

def setup_logging(name):

    formatter = logging.Formatter("%(name)s, %(asctime)s, %(message)s")

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

    logger = logging.getLogger(name)

    if logger.handlers:
        for handler in logger.handlers:
            logger.removeHandler(handler)

    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)
    return logger

main.py

import logging

logger = loghelper.setup_logging('main.test_function')

def test_function():
    logger.debug("test function log statement")

test_function()

现在运行lambda函数时,我在云监视日志中收到两次调试消息,如下所示:

cloudwatch logs

12:22:53 [DEBUG]-main.py:5, test function log statement
12:22:53 [INFO]-__init__.py:880,main.test_function,2018-06-18 12:22:53,099, test function log statement

请注意:

  • 第一个条目位于正确的级别,但格式错误。
  • 第二个条目报告错误的级别,错误的模块但格式正确。

我无法解释这种行为,并会对可能导致这种行为的任何想法表示感谢。我也不知道第880行存在哪个构造函数。这可能会对正在发生的事情有所了解。

参考文献:

  1. 设置全局格式化程序: How to define a logger in python once for the whole program?

  2. 清除默认的lambda日志处理程序: Using python Logging with AWS Lambda

  3. 创建全局记录器: Python: logging module - globally

2 个答案:

答案 0 :(得分:8)

AWS Lambda还在根记录器上设置了一个处理程序,捕获了写入stdout的任何内容,并将其记录为级别INFO。因此,您的日志消息被捕获两次:

  • 通过根记录器上的AWS Lambda处理程序(作为日志消息从嵌套子记录器传播到根目录),并且此记录器已配置自己的格式。
  • 通过AWS Lambda stdout-to-INFO记录器。

这就是所有消息都以(asctime) [(levelname)]-(module):(lineno),信息开头的原因;根记录器配置为输出具有该格式的消息,写入stdout的信息只是该输出中的另一个%(message)部分。

当您处于AWS环境时,不要设置处理程序,禁止将输出传播到根处理程序,并将所有消息记录为{{1 AWS的消息;在后一种情况下,您自己的格式化程序可以在输出中包含INFO级别信息。

您可以使用levelname禁用日志传播,此时您的消息将仅传递给您的处理程序,而不是传递给根处理程序。

另一种选择是仅依赖AWS根记录器配置。根据{{​​3}},根记录器配置为:

logger.propagate = False

这会将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) 上的time.localtime转换器替换为logging.Formatter(因此时间戳使用UTC而不是locatime),设置自定义处理程序以确保消息转到Lambda基础结构,配置格式,并添加一个过滤器对象,只对记录添加time.gmtime属性(所以上面的格式化程序可以包含它),但实际上并没有过滤任何东西。

您可以通过更新aws_request_id对象上的属性来更改该处理程序上的格式化程序:

handler.formatter

然后完全删除自己的记录器处理程序。你确实要小心这个; AWS Lambda基础架构很可能依赖于所使用的特定格式。您在问题中显示的输出不包含日期组件(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' 字符串的%Y-%m-%dT部分),这可能意味着该格式已被解析并正在呈现给您Web应用程序视图的数据。

答案 1 :(得分:1)

我不确定这是否是导致问题的原因,但默认情况下,Python的记录器会将其消息传播到日志记录层次结构。您可能知道,Python记录器在树中组织,顶部有root记录器,下面有其他记录器。在记录器名称中,点(.)引入了新的层次结构级别。所以,如果你这样做

logger = logging.getLogger('some_module.some_function`)

然后你实际上有3个记录器:

The root logger (`logging.getLogger()`)
    A logger at module level (`logging.getLogger('some_module'))
        A logger at function level (`logging.getLogger('some_module.some_function'))

如果您在记录器上发出日志消息并且未根据记录器最低级别丢弃该消息,则该消息将传递给记录器的处理程序到其父记录器。有关详细信息,请参阅this flowchart

如果该父记录器(或层次结构中较高的任何记录器)也有处理程序,那么它们也会被调用。

我怀疑在您的情况下,根记录器或main记录器会以某种方式最终附加一些处理程序,这会导致重复的消息。为避免这种情况,您可以将记录器中的propagate设置为False,或仅将处理程序附加到根记录器。