Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/error_reporting/client.py: 46%
69 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:45 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:45 +0000
1# Copyright 2016 Google LLC
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.
15"""Client for interacting with the Error Reporting API"""
17import os
18import traceback
20try:
21 from google.cloud.error_reporting._gapic import make_report_error_api
22except ImportError: # pragma: NO COVER
23 from google.api_core import client_info # noqa
25 _HAVE_GRPC = False
26else:
27 from google.api_core.gapic_v1 import client_info
29 _HAVE_GRPC = True
31from google.cloud.client import ClientWithProject
32from google.cloud.error_reporting import __version__
33from google.cloud.error_reporting._logging import _ErrorReportingLoggingAPI
34from google.cloud.environment_vars import DISABLE_GRPC
36_DISABLE_GRPC = os.getenv(DISABLE_GRPC, False)
37_USE_GRPC = _HAVE_GRPC and not _DISABLE_GRPC
38_CLIENT_INFO = client_info.ClientInfo(client_library_version=__version__)
41class HTTPContext(object):
42 """HTTPContext defines an object that captures the parameter for the
43 httpRequest part of Error Reporting API
45 :type method: str
46 :param method: The type of HTTP request, such as GET, POST, etc.
48 :type url: str
49 :param url: The URL of the request
51 :type user_agent: str
52 :param user_agent: The user agent information that is provided with the
53 request.
55 :type referrer: str
56 :param referrer: The referrer information that is provided with the
57 request.
59 :type response_status_code: int
60 :param response_status_code: The HTTP response status code for the request.
62 :type remote_ip: str
63 :param remote_ip: The IP address from which the request originated. This
64 can be IPv4, IPv6, or a token which is derived from
65 the IP address, depending on the data that has been
66 provided in the error report.
67 """
69 def __init__(
70 self,
71 method=None,
72 url=None,
73 user_agent=None,
74 referrer=None,
75 response_status_code=None,
76 remote_ip=None,
77 ):
78 self.method = method
79 self.url = url
80 # intentionally camel case for mapping to JSON API expects
81 # pylint: disable=invalid-name
82 self.userAgent = user_agent
83 self.referrer = referrer
84 self.responseStatusCode = response_status_code
85 self.remoteIp = remote_ip
88class Client(ClientWithProject):
89 """Error Reporting client. Currently Error Reporting is done by creating
90 a Logging client.
92 :type project: str
93 :param project: the project which the client acts on behalf of. If not
94 passed falls back to the default inferred from the
95 environment.
97 :type credentials: :class:`google.auth.credentials.Credentials` or
98 :class:`NoneType`
99 :param credentials: The authorization credentials to attach to requests.
100 These credentials identify this application to the service.
101 If none are specified, the client will attempt to ascertain
102 the credentials from the environment.
104 :type _http: :class:`~requests.Session`
105 :param _http: (Optional) HTTP object to make requests. Can be any object
106 that defines ``request()`` with the same interface as
107 :meth:`requests.Session.request`. If not passed, an
108 ``_http`` object is created that is bound to the
109 ``credentials`` for the current object.
110 This parameter should be considered private, and could
111 change in the future.
113 :type service: str
114 :param service: An identifier of the service, such as the name of the
115 executable, job, or Google App Engine service name. This
116 field is expected to have a low number of values that are
117 relatively stable over time, as opposed to version,
118 which can be changed whenever new code is deployed.
121 :type version: str
122 :param version: Represents the source code version that the developer
123 provided, which could represent a version label or a Git
124 SHA-1 hash, for example. If the developer did not provide
125 a version, the value is set to default.
127 :type _use_grpc: bool
128 :param _use_grpc: (Optional) Explicitly specifies whether
129 to use the gRPC transport or HTTP. If unset,
130 falls back to the ``GOOGLE_CLOUD_DISABLE_GRPC``
131 environment variable.
132 This parameter should be considered private, and could
133 change in the future.
135 :type client_info:
136 :class:`google.api_core.client_info.ClientInfo` or
137 :class:`google.api_core.gapic_v1.client_info.ClientInfo`
138 :param client_info:
139 The client info used to send a user-agent string along with API
140 requests. If ``None``, then default info will be used. Generally,
141 you only need to set this if you're developing your own library
142 or partner tool.
144 :type client_options: :class:`~google.api_core.client_options.ClientOptions`
145 or :class:`dict`
146 :param client_options: (Optional) Client options used to set user options
147 on the client. API Endpoint should be set through client_options.
149 :raises: :class:`ValueError` if the project is neither passed in nor
150 set in the environment.
151 """
153 SCOPE = ("https://www.googleapis.com/auth/cloud-platform",)
154 """The scopes required for authenticating as an API consumer."""
156 def __init__(
157 self,
158 project=None,
159 credentials=None,
160 _http=None,
161 service=None,
162 version=None,
163 client_info=_CLIENT_INFO,
164 client_options=None,
165 _use_grpc=None,
166 ):
167 super(Client, self).__init__(
168 project=project, credentials=credentials, _http=_http
169 )
170 self._report_errors_api = None
172 self.service = service if service else self.DEFAULT_SERVICE
173 self.version = version
174 self._client_info = client_info
175 self._client_options = client_options
177 if _use_grpc is None:
178 self._use_grpc = _USE_GRPC
179 else:
180 self._use_grpc = _use_grpc
182 DEFAULT_SERVICE = "python"
184 @property
185 def report_errors_api(self):
186 """Helper for logging-related API calls.
188 See
189 https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries
190 https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.logs
192 :rtype:
193 :class:`_gapic._ErrorReportingGapicApi`
194 or
195 :class:`._logging._ErrorReportingLoggingAPI`
196 :returns: A class that implements the report errors API.
197 """
198 if self._report_errors_api is None:
199 if self._use_grpc:
200 self._report_errors_api = make_report_error_api(self)
201 else:
202 self._report_errors_api = _ErrorReportingLoggingAPI(
203 self.project,
204 self._credentials,
205 self._http,
206 self._client_info,
207 self._client_options,
208 )
209 return self._report_errors_api
211 def _build_error_report(
212 self, message, report_location=None, http_context=None, user=None
213 ):
214 """Builds the Error Reporting object to report.
216 This builds the object according to
218 https://cloud.google.com/error-reporting/docs/formatting-error-messages
220 :type message: str
221 :param message: The stack trace that was reported or logged by the
222 service.
224 :type report_location: dict
225 :param report_location: The location in the source code where the
226 decision was made to report the error, usually the place
227 where it was logged. For a logged exception this would be the
228 source line where the exception is logged, usually close to
229 the place where it was caught.
231 This should be a Python dict that contains the keys 'filePath',
232 'lineNumber', and 'functionName'
234 :type http_context: :class`google.cloud.error_reporting.HTTPContext`
235 :param http_context: The HTTP request which was processed when the
236 error was triggered.
238 :type user: str
239 :param user: The user who caused or was affected by the crash. This can
240 be a user ID, an email address, or an arbitrary token that
241 uniquely identifies the user. When sending an error
242 report, leave this field empty if the user was not
243 logged in. In this case the Error Reporting system will
244 use other data, such as remote IP address,
245 to distinguish affected users.
246 :rtype: dict
247 :returns: A dict payload ready to be serialized to JSON and sent to
248 the API.
249 """
250 payload = {
251 "serviceContext": {"service": self.service},
252 "message": "{0}".format(message),
253 }
255 if self.version:
256 payload["serviceContext"]["version"] = self.version
258 if report_location or http_context or user:
259 payload["context"] = {}
261 if report_location:
262 payload["context"]["reportLocation"] = report_location
264 if http_context:
265 http_context_dict = http_context.__dict__
266 # strip out None values
267 payload["context"]["httpRequest"] = {
268 key: value
269 for key, value in http_context_dict.items()
270 if value is not None
271 }
272 if user:
273 payload["context"]["user"] = user
274 return payload
276 def _send_error_report(
277 self, message, report_location=None, http_context=None, user=None
278 ):
279 """Makes the call to the Error Reporting API.
281 This is the lower-level interface to build and send the payload,
282 generally users will use either report() or report_exception() to
283 automatically gather the parameters for this method.
285 :type message: str
286 :param message: The stack trace that was reported or logged by the
287 service.
289 :type report_location: dict
290 :param report_location: The location in the source code where the
291 decision was made to report the error, usually the place
292 where it was logged. For a logged exception this would be the
293 source line where the exception is logged, usually close to
294 the place where it was caught.
296 This should be a Python dict that contains the keys 'filePath',
297 'lineNumber', and 'functionName'
299 :type http_context: :class`google.cloud.error_reporting.HTTPContext`
300 :param http_context: The HTTP request which was processed when the
301 error was triggered.
303 :type user: str
304 :param user: The user who caused or was affected by the crash. This can
305 be a user ID, an email address, or an arbitrary token that
306 uniquely identifies the user. When sending an error
307 report, leave this field empty if the user was not
308 logged in. In this case the Error Reporting system will
309 use other data, such as remote IP address,
310 to distinguish affected users.
311 """
312 error_report = self._build_error_report(
313 message, report_location, http_context, user
314 )
315 self.report_errors_api.report_error_event(error_report)
317 def report(self, message, http_context=None, user=None):
318 """Reports a message to Error Reporting
320 https://cloud.google.com/error-reporting/docs/formatting-error-messages
322 :type message: str
323 :param message: A user-supplied message to report
325 :type http_context: :class`google.cloud.error_reporting.HTTPContext`
326 :param http_context: The HTTP request which was processed when the
327 error was triggered.
329 :type user: str
330 :param user: The user who caused or was affected by the crash. This
331 can be a user ID, an email address, or an arbitrary
332 token that uniquely identifies the user. When sending
333 an error report, leave this field empty if the user
334 was not logged in. In this case the Error Reporting
335 system will use other data, such as remote IP address,
336 to distinguish affected users.
338 Example:
340 .. code-block:: python
342 >>> client.report("Something went wrong!")
343 """
344 stack = traceback.extract_stack()
345 last_call = stack[-2]
346 file_path = last_call[0]
347 line_number = last_call[1]
348 function_name = last_call[2]
349 report_location = {
350 "filePath": file_path,
351 "lineNumber": line_number,
352 "functionName": function_name,
353 }
355 self._send_error_report(
356 message,
357 http_context=http_context,
358 user=user,
359 report_location=report_location,
360 )
362 def report_exception(self, http_context=None, user=None):
363 """Reports the details of the latest exceptions to Error Reporting.
365 :type http_context: :class`google.cloud.error_reporting.HTTPContext`
366 :param http_context: The HTTP request which was processed when the
367 error was triggered.
369 :type user: str
370 :param user: The user who caused or was affected by the crash. This
371 can be a user ID, an email address, or an arbitrary
372 token that uniquely identifies the user. When sending an
373 error report, leave this field empty if the user was
374 not logged in. In this case the Error Reporting system
375 will use other data, such as remote IP address,
376 to distinguish affected users.
378 Example::
380 >>> try:
381 >>> raise NameError
382 >>> except Exception:
383 >>> client.report_exception()
384 """
385 self._send_error_report(
386 traceback.format_exc(), http_context=http_context, user=user
387 )