Google 云日志记录将 python INFO 消息显示为错误

问题描述 投票:0回答:2

我有一个在谷歌云中的 Kubernetes 中运行的应用程序。该应用程序是使用 fastapi 用 python 编写的。该应用程序的日志可以通过谷歌云日志记录看到,但是它们的“serverity”似乎翻译错误: 虽然 fastapi 的访问日志正确写入为“INFO”严重性,但从自定义记录器写入的任何消息都会显示为错误,即使它们是由

logger.info
调用写入的。

我通过

--log-config
命令行选项将以下日志记录配置传递给 uvicorn:

{
  "version": 1,
  "disable_existing_loggers": false,
  "formatters": {
    "simple": {
      "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    },
    "default": {
      "()": "uvicorn.logging.DefaultFormatter",
      "datefmt": "%Y-%m-%dT%H:%M:%S",
      "format": "[%(asctime)s.%(msecs)04dZ] %(name)s %(levelprefix)s %(message).400s"
    },
    "access": {
      "()": "uvicorn.logging.AccessFormatter",
      "datefmt": "%Y-%m-%dT%H:%M:%S",
      "format": "[%(asctime)s.%(msecs)04dZ] %(name)s %(levelprefix)s %(message)s"
    }
  },
  "handlers": {
    "default": {
      "formatter": "default",
      "class": "logging.StreamHandler",
      "stream": "ext://sys.stderr"
    },
    "access": {
      "formatter": "access",
      "class": "logging.StreamHandler",
      "stream": "ext://sys.stdout"
    }
  },
  "loggers": {
    "uvicorn.error": {
      "level": "INFO",
      "handlers": ["default"],
      "propagate": false
    },
    "uvicorn.access": {
      "level": "INFO",
      "handlers": ["access"],
      "propagate": false
    },
    "uvicorn": {
      "level": "INFO",
      "handlers": ["default"],
      "propagate": false
    },
    "jsonrpc": {
      "level": "INFO",
      "handlers": ["default"],
      "propagate": false
    },
    "api": {
      "level": "INFO",
      "handlers": ["default"],
      "propagate": false
    }
  },
  "root": {
    "level": "INFO",
    "handlers": ["default"]
  }
}

所有

uvicorn*
记录器均已正确处理,但
jsonrpc
api
记录器始终在 google cloud 中显示为“错误”。

按照 google-cloud-logging 的文档,我使用以下内容来设置云日志记录:

import google.cloud.logging

client = google.cloud.logging.Client()
client.setup_logging()

我错过了什么?我的配置文件有问题吗?这是预期的还是已知的行为?

编辑: 看来这里的部分问题是控制台处理程序转到的流。将默认处理程序切换到 stdout 并添加打印到 stderr 的错误处理程序似乎可以解决问题。

python fastapi google-cloud-logging uvicorn
2个回答
3
投票

在此发帖是因为我遇到了类似的问题,对于部署在 Vertex AI 端点后面的 Docker 容器,所有日志均显示错误严重性,并且此处没有可用的解决方案。以下代码为我解决了这个问题:

import logging
import sys

info_stream_handler = logging.StreamHandler(stream=sys.stdout)
info_stream_handler.setLevel(logging.INFO)
cloud_logger = logging.getLogger('cloudLogger')
cloud_logger.setLevel(logging.INFO)
cloud_logger.addHandler(info_stream_handler)
cloud_logger.propagate = False

cloud_logger.info("Info Log Message")

为了解决此问题,我创建了一个 StreamHandler 并指定将 INFO 日志发送到 stdout 而不是 stderr,并使用 addHandler() 方法将此处理程序添加到根记录器。

在此之后,我遇到的最后一个问题是 GCP 上的重复日志,其中一个具有正确的 INFO 级别,以及具有错误严重性的同一日志的重复项。

原因是日志被传递到 INFO StreamHandler,后者正确地将这些日志写入 stdout,但由于 python 日志记录模块的性质,所有这些日志也被传递到记录器“祖先”根记录器,将相同的日志写入 stderr,并导致重复。日志记录模块的 python 文档解释了这一点:

解决此问题的方法是将 cloud_logger.propagate 设置为 False,并且不将日志传递给祖先记录器。


0
投票

文档说:

严重性: 默认情况下,写入标准输出的日志位于

INFO
级别和写入标准错误的日志位于
ERROR
等级。结构化日志可以包含
severity
字段,该字段定义 日志的严重性。

所以@melcon的解决方案实际上不起作用。它只是将所有日志级别设置为

INFO
(因为
StreamHandler
采用标准输出)。相反,您可以使用
StructuredLogHandler
:

import logging
import sys
import json
from google.cloud import logging as cloud_logging
from google.cloud.logging_v2.handlers import CloudLoggingHandler

class StructuredLogHandler(logging.StreamHandler):
    def emit(self, record):
        log_entry = {
            'message': record.getMessage(),
            'severity': record.levelname
        }
        self.stream.write(json.dumps(log_entry) + '\n')

# Initialize the Cloud Logging client
client = cloud_logging.Client()
cloud_handler = CloudLoggingHandler(client)

# Create a logger
logger = logging.getLogger('cloudLogger')
logger.setLevel(logging.DEBUG)  # Capture all levels of logging

# Create a structured log handler for stdout
structured_handler = StructuredLogHandler(stream = sys.stdout)
structured_handler.setLevel(logging.DEBUG)  # Capture all levels of logging

# Add the handlers to the logger
logger.addHandler(structured_handler)
logger.addHandler(cloud_handler)
logger.propagate = False

# Sample log messages with different severities
logger.debug("This is a DEBUG message")
logger.info("This is an INFO message")
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message")
logger.critical("This is a CRITICAL message")

因此日志将在 Google Cloud 中显示为:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.