1# Copyright 2016 Google LLC All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Logging handler for App Engine Flexible
16
17Sends logs to the Cloud Logging API with the appropriate resource
18and labels for App Engine logs.
19"""
20
21import logging
22import os
23import warnings
24
25from google.cloud.logging_v2.handlers._helpers import get_request_data
26from google.cloud.logging_v2.handlers._monitored_resources import (
27 _create_app_engine_resource,
28)
29from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport
30
31_DEFAULT_GAE_LOGGER_NAME = "app"
32
33_GAE_PROJECT_ENV_FLEX = "GCLOUD_PROJECT"
34_GAE_PROJECT_ENV_STANDARD = "GOOGLE_CLOUD_PROJECT"
35_GAE_SERVICE_ENV = "GAE_SERVICE"
36_GAE_VERSION_ENV = "GAE_VERSION"
37
38_TRACE_ID_LABEL = "appengine.googleapis.com/trace_id"
39
40_DEPRECATION_MSG = "AppEngineHandler is deprecated. Use CloudLoggingHandler instead."
41
42
43class AppEngineHandler(logging.StreamHandler):
44 """A logging handler that sends App Engine-formatted logs to Stackdriver.
45
46 DEPRECATED: use CloudLoggingHandler instead.
47 """
48
49 def __init__(
50 self,
51 client,
52 *,
53 name=_DEFAULT_GAE_LOGGER_NAME,
54 transport=BackgroundThreadTransport,
55 stream=None,
56 ):
57 """
58 Args:
59 client (~logging_v2.client.Client): The authenticated
60 Google Cloud Logging client for this handler to use.
61 name (Optional[str]): Name for the logger.
62 transport (Optional[~logging_v2.transports.Transport]):
63 The transport class. It should be a subclass
64 of :class:`.Transport`. If unspecified,
65 :class:`.BackgroundThreadTransport` will be used.
66 stream (Optional[IO]): Stream to be used by the handler.
67
68 """
69 super(AppEngineHandler, self).__init__(stream)
70 self.name = name
71 self.client = client
72 self.transport = transport(client, name)
73 self.project_id = os.environ.get(
74 _GAE_PROJECT_ENV_FLEX, os.environ.get(_GAE_PROJECT_ENV_STANDARD, "")
75 )
76 self.module_id = os.environ.get(_GAE_SERVICE_ENV, "")
77 self.version_id = os.environ.get(_GAE_VERSION_ENV, "")
78 self.resource = self.get_gae_resource()
79
80 warnings.warn(_DEPRECATION_MSG, DeprecationWarning)
81
82 def get_gae_resource(self):
83 """Return the GAE resource using the environment variables.
84
85 Returns:
86 google.cloud.logging_v2.resource.Resource: Monitored resource for GAE.
87 """
88 return _create_app_engine_resource()
89
90 def get_gae_labels(self):
91 """Return the labels for GAE app.
92
93 If the trace ID can be detected, it will be included as a label.
94 Currently, no other labels are included.
95
96 Returns:
97 dict: Labels for GAE app.
98 """
99 gae_labels = {}
100
101 _, trace_id, _, _ = get_request_data()
102 if trace_id is not None:
103 gae_labels[_TRACE_ID_LABEL] = trace_id
104
105 return gae_labels
106
107 def emit(self, record):
108 """Actually log the specified logging record.
109
110 Overrides the default emit behavior of ``StreamHandler``.
111
112 See https://docs.python.org/2/library/logging.html#handler-objects
113
114 Args:
115 record (logging.LogRecord): The record to be logged.
116 """
117 message = super(AppEngineHandler, self).format(record)
118 inferred_http, inferred_trace, _, _ = get_request_data()
119 if inferred_trace is not None:
120 inferred_trace = f"projects/{self.project_id}/traces/{inferred_trace}"
121 # allow user overrides
122 trace = getattr(record, "trace", inferred_trace)
123 span_id = getattr(record, "span_id", None)
124 http_request = getattr(record, "http_request", inferred_http)
125 resource = getattr(record, "resource", self.resource)
126 user_labels = getattr(record, "labels", {})
127 # merge labels
128 gae_labels = self.get_gae_labels()
129 gae_labels.update(user_labels)
130 # send off request
131 self.transport.send(
132 record,
133 message,
134 resource=resource,
135 labels=gae_labels,
136 trace=trace,
137 span_id=span_id,
138 http_request=http_request,
139 )