使用Google Cloud Stackdriver在Kubernetes引擎上记录Python代码的重复日志条目

使用Google Cloud Stackdriver在Kubernetes引擎上记录Python代码的重复日志条目,python,logging,google-cloud-stackdriver,google-kubernetes-engine,Python,Logging,Google Cloud Stackdriver,Google Kubernetes Engine,我有一个简单的Python应用程序运行在Google Kubernetes引擎的容器中。我正在尝试将标准Python日志记录连接到Google Stackdriver日志记录。我几乎成功了,但是我得到了重复的日志条目,其中一个总是处于“错误”级别 Stackdriver日志的屏幕截图,显示重复条目 这是我的python代码,它根据上述指南设置日志: import webapp2 from paste import httpserver import rpc # Imports the Go

我有一个简单的Python应用程序运行在Google Kubernetes引擎的容器中。我正在尝试将标准Python日志记录连接到Google Stackdriver日志记录。我几乎成功了,但是我得到了重复的日志条目,其中一个总是处于“错误”级别


Stackdriver日志的屏幕截图,显示重复条目

这是我的python代码,它根据上述指南设置日志:

import webapp2
from paste import httpserver
import rpc

# Imports the Google Cloud client library
import google.cloud.logging
# Instantiates a client
client = google.cloud.logging.Client()
# Connects the logger to the root logging handler; by default this captures
# all logs at INFO level and higher
client.setup_logging()

app = webapp2.WSGIApplication([('/rpc/([A-Za-z]+)', rpc.RpcHandler),], debug=True)
httpserver.serve(app, host='0.0.0.0', port='80')
以下是从屏幕截图中触发日志的代码:

import logging

logging.info("INFO Entering PostEchoPost...")
logging.warning("WARNING Entering PostEchoPost...")
logging.error("ERROR Entering PostEchoPost...")
logging.critical("CRITICAL Entering PostEchoPost...")
以下是完整的Stackdriver日志,从屏幕截图展开,错误级别解释不正确:

{
 insertId:  "1mk4fkaga4m63w1"  
 labels: {
  compute.googleapis.com/resource_name:  "gke-alg-microservice-default-pool-xxxxxxxxxx-ttnz"   
  container.googleapis.com/namespace_name:  "default"   
  container.googleapis.com/pod_name:  "esp-alg-xxxxxxxxxx-xj2p2"   
  container.googleapis.com/stream:  "stderr"   
 }
 logName:  "projects/projectname/logs/algorithm"  
 receiveTimestamp:  "2018-01-03T12:18:22.479058645Z"  
 resource: {
  labels: {
   cluster_name:  "alg-microservice"    
   container_name:  "alg"    
   instance_id:  "703849119xxxxxxxxxx"   
   namespace_id:  "default"    
   pod_id:  "esp-alg-xxxxxxxxxx-xj2p2"    
   project_id:  "projectname"    
   zone:  "europe-west1-b"    
  }
  type:  "container"   
 }
 severity:  "ERROR"  
 textPayload:  "INFO Entering PostEchoPost...
"  
 timestamp:  "2018-01-03T12:18:20Z"  
}
以下是完整的Stackdriver日志,从屏幕截图展开,具有正确解释的信息级别:

{
 insertId:  "1mk4fkaga4m63w0"  
 jsonPayload: {
  message:  "INFO Entering PostEchoPost..."   
  thread:  140348659595008   
 }
 labels: {
  compute.googleapis.com/resource_name:  "gke-alg-microservi-default-pool-xxxxxxxxxx-ttnz"   
  container.googleapis.com/namespace_name:  "default"   
  container.googleapis.com/pod_name:  "esp-alg-xxxxxxxxxx-xj2p2"   
  container.googleapis.com/stream:  "stderr"   
 }
 logName:  "projects/projectname/logs/algorithm"  
 receiveTimestamp:  "2018-01-03T12:18:22.479058645Z"  
 resource: {
  labels: {
   cluster_name:  "alg-microservice"    
   container_name:  "alg"    
   instance_id:  "703849119xxxxxxxxxx"    
   namespace_id:  "default"    
   pod_id:  "esp-alg-xxxxxxxxxx-xj2p2"    
   project_id:  "projectname"    
   zone:  "europe-west1-b"    
  }
  type:  "container"   
 }
 severity:  "INFO"  
 timestamp:  "2018-01-03T12:18:20.260099887Z"  
}
因此,此条目可能是关键:

container.googleapis.com/stream:  "stderr" 
看起来除了我的日志设置工作之外,容器中的所有日志都被发送到容器中的stderr,我相信默认情况下,至少在Kubernetes容器引擎上,所有stdout/stderr都是由Google Stackdriver通过FluentD获取的。。。话虽如此,我在这一点上已经力不从心了


知道我为什么会得到这些重复条目吗?

问题在于日志客户端如何初始化根日志记录器

logger=logging.getLogger()
logger.setLevel(日志级别)
logger.addHandler(处理程序)
logger.addHandler(logging.StreamHandler())
除了Stackdriver处理程序之外,它还添加了默认的流处理程序。 我现在的解决方法是手动初始化相应的Stackdriver处理程序:

#这基本上是手动设置与GKE/fluentd兼容的记录器
#作为LoggingClient自动添加另一个StreamHandler-因此
#日志记录是重复的
从google.cloud.logging.handlers导入ContainerEngineHandler
格式化程序=logging.formatter(“%(消息)s”)
handler=ContainerEngineHandler(stream=sys.stderr)
handler.setFormatter(格式化程序)
handler.setLevel(级别)
root=logging.getLogger()
addHandler(处理程序)
root.setLevel(级别)

在调用
设置\u logging
方法后,我立即覆盖根记录器上的
处理程序属性,从而解决了这个问题

import logging
from google.cloud import logging as gcp_logging
from google.cloud.logging.handlers import CloudLoggingHandler, ContainerEngineHandler, AppEngineHandler

logging_client = gcp_logging.Client()
logging_client.setup_logging(log_level=logging.INFO)
root_logger = logging.getLogger()
# use the GCP handler ONLY in order to prevent logs from getting written to STDERR
root_logger.handlers = [handler
                        for handler in root_logger.handlers
                        if isinstance(handler, (CloudLoggingHandler, ContainerEngineHandler, AppEngineHandler))]
为了详细说明这一点,
client.setup\u logging
方法设置了2个处理程序,一个普通的
logging.StreamHandler
和一个特定于GCP的处理程序。因此,日志将同时转到stderr和云日志。您需要从处理程序列表中删除流处理程序以防止重复

编辑:
我已经向谷歌提交了一份文件,添加了一个参数,以减少黑客攻击。

链接到的文档声明:“注意:写入stdout和stderr的日志会自动发送给Stackdriver日志记录,而无需使用Python的Stackdriver日志库。”这意味着您需要的信息已经存在。如注释文本所示,您不需要使用Stackdriver日志库,也不需要生成包含重复信息的额外日志。@George,如果默认的云日志记录没有写出错误的日志级别,比如“警告为错误”。我也有同样的问题。我使用下面链接的这个指南来正确设置消息的严重性,但我也得到了一组严重性更高的副本:只是提醒其他人,谷歌云功能(而不是k8s)遇到类似问题。删除流处理程序似乎会导致根本没有日志。