|
| 1 | +from bottle import request, response, SimpleTemplate |
| 2 | + |
| 3 | +from aws_xray_sdk.core.lambda_launcher import check_in_lambda, LambdaContext |
| 4 | +from aws_xray_sdk.core.models import http |
| 5 | +from aws_xray_sdk.core.utils import stacktrace |
| 6 | +from aws_xray_sdk.ext.util import calculate_sampling_decision, \ |
| 7 | + calculate_segment_name, construct_xray_header, prepare_response_header |
| 8 | + |
| 9 | + |
| 10 | +class XRayMiddleware(object): |
| 11 | + """ |
| 12 | + Middleware that wraps each incoming request to a segment. |
| 13 | + """ |
| 14 | + name = 'xray' |
| 15 | + api = 2 |
| 16 | + |
| 17 | + def __init__(self, recorder): |
| 18 | + self._recorder = recorder |
| 19 | + self._in_lambda_ctx = False |
| 20 | + |
| 21 | + if check_in_lambda() and type(self._recorder.context) == LambdaContext: |
| 22 | + self._in_lambda_ctx = True |
| 23 | + |
| 24 | + _patch_render(recorder) |
| 25 | + |
| 26 | + def apply(self, callback, route): |
| 27 | + """ |
| 28 | + Apply middleware directly to each route callback. |
| 29 | + """ |
| 30 | + def wrapper(*a, **ka): |
| 31 | + headers = request.headers |
| 32 | + xray_header = construct_xray_header(headers) |
| 33 | + name = calculate_segment_name(request.urlparts[1], self._recorder) |
| 34 | + |
| 35 | + sampling_req = { |
| 36 | + 'host': request.urlparts[1], |
| 37 | + 'method': request.method, |
| 38 | + 'path': request.path, |
| 39 | + 'service': name, |
| 40 | + } |
| 41 | + sampling_decision = calculate_sampling_decision( |
| 42 | + trace_header=xray_header, |
| 43 | + recorder=self._recorder, |
| 44 | + sampling_req=sampling_req, |
| 45 | + ) |
| 46 | + |
| 47 | + if self._in_lambda_ctx: |
| 48 | + segment = self._recorder.begin_subsegment(name) |
| 49 | + else: |
| 50 | + segment = self._recorder.begin_segment( |
| 51 | + name=name, |
| 52 | + traceid=xray_header.root, |
| 53 | + parent_id=xray_header.parent, |
| 54 | + sampling=sampling_decision, |
| 55 | + ) |
| 56 | + |
| 57 | + segment.save_origin_trace_header(xray_header) |
| 58 | + segment.put_http_meta(http.URL, request.url) |
| 59 | + segment.put_http_meta(http.METHOD, request.method) |
| 60 | + segment.put_http_meta(http.USER_AGENT, headers.get('User-Agent')) |
| 61 | + |
| 62 | + client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR') |
| 63 | + if client_ip: |
| 64 | + segment.put_http_meta(http.CLIENT_IP, client_ip) |
| 65 | + segment.put_http_meta(http.X_FORWARDED_FOR, True) |
| 66 | + else: |
| 67 | + segment.put_http_meta(http.CLIENT_IP, request.remote_addr) |
| 68 | + |
| 69 | + try: |
| 70 | + rv = callback(*a, **ka) |
| 71 | + except Exception as resp: |
| 72 | + segment.put_http_meta(http.STATUS, getattr(resp, 'status_code', 500)) |
| 73 | + stack = stacktrace.get_stacktrace(limit=self._recorder._max_trace_back) |
| 74 | + segment.add_exception(resp, stack) |
| 75 | + if self._in_lambda_ctx: |
| 76 | + self._recorder.end_subsegment() |
| 77 | + else: |
| 78 | + self._recorder.end_segment() |
| 79 | + |
| 80 | + raise resp |
| 81 | + |
| 82 | + segment.put_http_meta(http.STATUS, response.status_code) |
| 83 | + |
| 84 | + origin_header = segment.get_origin_trace_header() |
| 85 | + resp_header_str = prepare_response_header(origin_header, segment) |
| 86 | + response.set_header(http.XRAY_HEADER, resp_header_str) |
| 87 | + |
| 88 | + cont_len = response.headers.get('Content-Length') |
| 89 | + if cont_len: |
| 90 | + segment.put_http_meta(http.CONTENT_LENGTH, int(cont_len)) |
| 91 | + |
| 92 | + if self._in_lambda_ctx: |
| 93 | + self._recorder.end_subsegment() |
| 94 | + else: |
| 95 | + self._recorder.end_segment() |
| 96 | + |
| 97 | + return rv |
| 98 | + |
| 99 | + return wrapper |
| 100 | + |
| 101 | +def _patch_render(recorder): |
| 102 | + |
| 103 | + _render = SimpleTemplate.render |
| 104 | + |
| 105 | + @recorder.capture('template_render') |
| 106 | + def _traced_render(self, *args, **kwargs): |
| 107 | + if self.filename: |
| 108 | + recorder.current_subsegment().name = self.filename |
| 109 | + return _render(self, *args, **kwargs) |
| 110 | + |
| 111 | + SimpleTemplate.render = _traced_render |
0 commit comments