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

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. 

14 

15"""Client for interacting with the Error Reporting API""" 

16 

17import os 

18import traceback 

19 

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 

24 

25 _HAVE_GRPC = False 

26else: 

27 from google.api_core.gapic_v1 import client_info 

28 

29 _HAVE_GRPC = True 

30 

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 

35 

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__) 

39 

40 

41class HTTPContext(object): 

42 """HTTPContext defines an object that captures the parameter for the 

43 httpRequest part of Error Reporting API 

44 

45 :type method: str 

46 :param method: The type of HTTP request, such as GET, POST, etc. 

47 

48 :type url: str 

49 :param url: The URL of the request 

50 

51 :type user_agent: str 

52 :param user_agent: The user agent information that is provided with the 

53 request. 

54 

55 :type referrer: str 

56 :param referrer: The referrer information that is provided with the 

57 request. 

58 

59 :type response_status_code: int 

60 :param response_status_code: The HTTP response status code for the request. 

61 

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 """ 

68 

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 

86 

87 

88class Client(ClientWithProject): 

89 """Error Reporting client. Currently Error Reporting is done by creating 

90 a Logging client. 

91 

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. 

96 

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. 

103 

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. 

112 

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. 

119 

120 

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. 

126 

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. 

134 

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. 

143 

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. 

148 

149 :raises: :class:`ValueError` if the project is neither passed in nor 

150 set in the environment. 

151 """ 

152 

153 SCOPE = ("https://www.googleapis.com/auth/cloud-platform",) 

154 """The scopes required for authenticating as an API consumer.""" 

155 

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 

171 

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 

176 

177 if _use_grpc is None: 

178 self._use_grpc = _USE_GRPC 

179 else: 

180 self._use_grpc = _use_grpc 

181 

182 DEFAULT_SERVICE = "python" 

183 

184 @property 

185 def report_errors_api(self): 

186 """Helper for logging-related API calls. 

187 

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 

191 

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 

210 

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. 

215 

216 This builds the object according to 

217 

218 https://cloud.google.com/error-reporting/docs/formatting-error-messages 

219 

220 :type message: str 

221 :param message: The stack trace that was reported or logged by the 

222 service. 

223 

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. 

230 

231 This should be a Python dict that contains the keys 'filePath', 

232 'lineNumber', and 'functionName' 

233 

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. 

237 

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 } 

254 

255 if self.version: 

256 payload["serviceContext"]["version"] = self.version 

257 

258 if report_location or http_context or user: 

259 payload["context"] = {} 

260 

261 if report_location: 

262 payload["context"]["reportLocation"] = report_location 

263 

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 

275 

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. 

280 

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. 

284 

285 :type message: str 

286 :param message: The stack trace that was reported or logged by the 

287 service. 

288 

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. 

295 

296 This should be a Python dict that contains the keys 'filePath', 

297 'lineNumber', and 'functionName' 

298 

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. 

302 

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) 

316 

317 def report(self, message, http_context=None, user=None): 

318 """Reports a message to Error Reporting 

319 

320 https://cloud.google.com/error-reporting/docs/formatting-error-messages 

321 

322 :type message: str 

323 :param message: A user-supplied message to report 

324 

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. 

328 

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. 

337 

338 Example: 

339 

340 .. code-block:: python 

341 

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 } 

354 

355 self._send_error_report( 

356 message, 

357 http_context=http_context, 

358 user=user, 

359 report_location=report_location, 

360 ) 

361 

362 def report_exception(self, http_context=None, user=None): 

363 """Reports the details of the latest exceptions to Error Reporting. 

364 

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. 

368 

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. 

377 

378 Example:: 

379 

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 )