OLD | NEW |
| (Empty) |
1 # Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Report ts_mon metrics for HTTP requests made by the `requests` library. | |
6 | |
7 This module provides get(), post(), etc. methods that wrap the corresponding | |
8 methods in the requests module. They take an additional first 'name' argument | |
9 which is an identifier for the type of request being made. | |
10 | |
11 Example:: | |
12 | |
13 from infra_libs import instrumented_requests | |
14 r = instrumented_requests.get('myapi', 'https://example.com/api') | |
15 | |
16 Alternatively you can add the hook manually:: | |
17 | |
18 import requests | |
19 from infra_libs import instrumented_requests | |
20 r = requests.get( | |
21 'https://example.com/api', | |
22 hooks={'response': instrumented_requests.instrumentation_hook('myapi')}) | |
23 """ | |
24 | |
25 import functools | |
26 | |
27 import requests | |
28 | |
29 from infra_libs.ts_mon.common import http_metrics | |
30 | |
31 | |
32 def instrumentation_hook(name): | |
33 """Returns a hook function that records ts_mon metrics about the request. | |
34 | |
35 Usage:: | |
36 | |
37 r = requests.get( | |
38 'https://example.com/api', | |
39 hooks={'response': instrumented_requests.instrumentation_hook('myapi')}) | |
40 | |
41 Args: | |
42 name: An identifier for the HTTP requests made by this object. | |
43 """ | |
44 | |
45 def _content_length(headers): | |
46 if headers is None or 'content-length' not in headers: | |
47 return 0 | |
48 return int(headers['content-length']) | |
49 | |
50 def hook(response, *_args, **_kwargs): | |
51 request_bytes = _content_length(response.request.headers) | |
52 response_bytes = _content_length(response.headers) | |
53 duration_msec = response.elapsed.total_seconds() * 1000 | |
54 | |
55 fields = {'name': name, 'client': 'requests'} | |
56 http_metrics.request_bytes.add(request_bytes, fields=fields) | |
57 http_metrics.response_bytes.add(response_bytes, fields=fields) | |
58 http_metrics.durations.add(duration_msec, fields=fields) | |
59 | |
60 _update_status(name, response.status_code) | |
61 | |
62 return hook | |
63 | |
64 | |
65 def _update_status(name, status): | |
66 fields = {'status': status, 'name': name, 'client': 'requests'} | |
67 http_metrics.response_status.increment(fields=fields) | |
68 | |
69 | |
70 def _wrap(method, name, url, *args, **kwargs): | |
71 hooks = {'response': instrumentation_hook(name)} | |
72 if 'hooks' in kwargs: | |
73 hooks.update(kwargs['hooks']) | |
74 kwargs['hooks'] = hooks | |
75 | |
76 try: | |
77 return getattr(requests, method)(url, *args, **kwargs) | |
78 except requests.exceptions.ReadTimeout: | |
79 _update_status(name, http_metrics.STATUS_TIMEOUT) | |
80 raise | |
81 except requests.exceptions.ConnectionError: | |
82 _update_status(name, http_metrics.STATUS_ERROR) | |
83 raise | |
84 except requests.exceptions.RequestException: | |
85 _update_status(name, http_metrics.STATUS_EXCEPTION) | |
86 raise | |
87 | |
88 | |
89 request = functools.partial(_wrap, 'request') | |
90 get = functools.partial(_wrap, 'get') | |
91 head = functools.partial(_wrap, 'head') | |
92 post = functools.partial(_wrap, 'post') | |
93 patch = functools.partial(_wrap, 'patch') | |
94 put = functools.partial(_wrap, 'put') | |
95 delete = functools.partial(_wrap, 'delete') | |
96 options = functools.partial(_wrap, 'options') | |
OLD | NEW |