def _test_cat_headers(): transaction = current_transaction() payload = ExternalTrace.generate_request_headers(transaction) assert not payload trace = ExternalTrace('testlib', 'http://example.com') response_headers = [('X-NewRelic-App-Data', 'Cookies')] with trace: trace.process_response_headers(response_headers) assert transaction.settings.cross_application_tracer.enabled is False
def wrap_next(_wrapped, _instance, _args, _kwargs): _start = time.time() try: result = _wrapped(*_args, **_kwargs) except StopIteration: raise except Exception: with ExternalTrace(transaction, library, _url, method) as t: t.start_time = _start raise else: with ExternalTrace(transaction, library, _url, method) as t: t.start_time = _start return result
def wrap_result(_wrapped, _instance, _args, _kwargs): if _result_called: return _wrapped(*_args, **_kwargs) _result_called.append(True) try: result = _wrapped(*_args, **_kwargs) except Exception: with ExternalTrace(transaction, library, _url, method) as t: t.start_time = _nr_start_time raise else: with ExternalTrace(transaction, library, _url, method) as t: t.start_time = _nr_start_time return result
def _nr_aiohttp_add_cat_headers_(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) try: cat_headers = ExternalTrace.generate_request_headers(transaction) except: return wrapped(*args, **kwargs) tmp = instance.headers instance.headers = HeaderProxy(tmp, cat_headers) if is_coroutine_function(wrapped): @asyncio.coroutine def new_coro(): try: result = yield from wrapped(*args, **kwargs) return result finally: instance.headers = tmp return new_coro() else: try: return wrapped(*args, **kwargs) finally: instance.headers = tmp
def _nr_aiohttp_request_wrapper_(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) method, url = _bind_request(*args, **kwargs) trace = ExternalTrace('aiohttp', str(url), method) @asyncio.coroutine def _coro(): try: response = yield from wrapped(*args, **kwargs) try: trace.process_response_headers(response.headers.items()) except: pass return response except Exception as e: try: trace.process_response_headers(e.headers.items()) except: pass raise return async_wrapper(wrapped)(_coro, trace)()
def _nr_wrapper_httplib2_connect_wrapper_inner(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) def _connect_unbound(instance, *args, **kwargs): return instance if instance is None: instance = _connect_unbound(*args, **kwargs) connection = instance url = '%s://%s:%s' % (scheme, connection.host, connection.port) with ExternalTrace(transaction, library='httplib2', url=url) as tracer: # Add the tracer to the connection object. The tracer will be # used in getresponse() to add back into the external trace, # after the trace has already completed, details from the # response headers. connection._nr_external_tracer = tracer return wrapped(*args, **kwargs)
def _nr_endpoint_make_request_(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) operation_model, request_dict = _bind_make_request_params(*args, **kwargs) url = request_dict.get('url', '') method = request_dict.get('method', None) with ExternalTrace(transaction, library='botocore', url=url, method=method) as trace: try: trace._add_agent_attribute('aws.operation', operation_model.name) except: pass result = wrapped(*args, **kwargs) try: request_id = result[1]['ResponseMetadata']['RequestId'] trace._add_agent_attribute('aws.requestId', request_id) except: pass return result
def httplib_endheaders_wrapper(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) connection = instance # Check if the NR headers have already been added. This is just in # case a higher level library which uses httplib underneath so # happened to have been instrumented to also add the headers. try: skip_headers = getattr(connection, '_nr_skip_headers', False) if skip_headers: return wrapped(*args, **kwargs) outgoing_headers = ExternalTrace.generate_request_headers(transaction) for header_name, header_value in outgoing_headers: connection.putheader(header_name, header_value) return wrapped(*args, **kwargs) finally: try: del connection._nr_skip_headers except AttributeError: pass
def target_wsgi_application(environ, start_response): start_response('200 OK', [('Content-Type', 'application/json')]) txn = current_transaction() if txn._sampled is None: txn._sampled = True txn._priority = 1.2 headers = ExternalTrace.generate_request_headers(txn) return [json.dumps(headers).encode('utf-8')]
def _test(): transaction = current_transaction() transaction._sampled = True with ExternalTrace(library='library', url='http://example.com/foo?secret=123', method='get'): pass
def _nr_wrapper_make_request_(wrapped, instance, args, kwargs): def _bind_params(conn, method, url, *args, **kwargs): return "%s://%s:%s" % (instance.scheme, conn.host, conn.port) url_for_apm_ui = _bind_params(*args, **kwargs) with ExternalTrace('urllib3', url_for_apm_ui): return wrapped(*args, **kwargs)
def _call_wrapper(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) uri, method = _get_uri_method(instance) with ExternalTrace('gRPC', uri, method, source=wrapped): args, kwargs = prepare(transaction, None, *args, **kwargs) return wrapped(*args, **kwargs)
def wrap_next(_wrapped, _instance, _args, _kwargs): _nr_args = getattr(_instance, '_nr_args', None) if not _nr_args: return _wrapped(*_args, **_kwargs) _start = time.time() try: result = _wrapped(*_args, **_kwargs) except StopIteration: raise except Exception: with ExternalTrace(*_nr_args) as t: t.start_time = _start raise else: with ExternalTrace(*_nr_args) as t: t.start_time = _start return result
def target_wsgi_application(environ, start_response): status = '200 OK' txn_name = environ.get('txn') if six.PY2: txn_name = txn_name.decode('UTF-8') txn_name = txn_name.split('/', 3) guid = environ.get('guid') old_cat = environ.get('old_cat') == 'True' txn = current_transaction() txn.guid = guid for req in OUTBOUD_REQUESTS: # Change the transaction name before making an outbound call. outgoing_name = req['outboundTxnName'].split('/', 3) if outgoing_name[0] != 'WebTransaction': set_background_task(True) set_transaction_name(outgoing_name[2], group=outgoing_name[1]) expected_outbound_header = obfuscate( json_encode(req['expectedOutboundPayload']), ENCODING_KEY) generated_outbound_header = dict( ExternalTrace.generate_request_headers(txn)) # A 500 error is returned because 'assert' statements in the wsgi app # are ignored. if old_cat: if (expected_outbound_header != generated_outbound_header['X-NewRelic-Transaction']): status = '500 Outbound Headers Check Failed.' else: if 'X-NewRelic-Transaction' in generated_outbound_header: status = '500 Outbound Headers Check Failed.' r = urlopen(environ['server_url']) r.read(10) # Set the final transaction name. if txn_name[0] != 'WebTransaction': set_background_task(True) set_transaction_name(txn_name[2], group=txn_name[1]) text = '<html><head>%s</head><body><p>RESPONSE</p>%s</body></html>' output = (text % (get_browser_timing_header(), get_browser_timing_footer())).encode('UTF-8') response_headers = [('Content-type', 'text/html; charset=utf-8'), ('Content-Length', str(len(output)))] start_response(status, response_headers) return [output]
def wrap_result(_wrapped, _instance, _args, _kwargs): _nr_args = getattr(_instance, '_nr_args', None) if not _nr_args: return _wrapped(*_args, **_kwargs) delattr(_instance, '_nr_args') _nr_start_time = getattr(_instance, '_nr_start_time', 0.0) _nr_guid = getattr(_instance, '_nr_guid', None) try: result = _wrapped(*_args, **_kwargs) except Exception: with ExternalTrace(*_nr_args) as t: t.start_time = _nr_start_time or t.start_time t.guid = _nr_guid or t.guid raise else: with ExternalTrace(*_nr_args) as t: t.start_time = _nr_start_time or t.start_time t.guid = _nr_guid or t.guid return result
def wrap_httpclient_fetch(wrapped, instance, args, kwargs): try: req, raise_error = _prepare_request(*args, **kwargs) except: return wrapped(*args, **kwargs) trace = ExternalTrace('tornado', req.url, req.method.upper()) outgoing_headers = trace.generate_request_headers(current_transaction()) for header_name, header_value in outgoing_headers: # User headers should override our CAT headers if header_name in req.headers: continue req.headers[header_name] = header_value try: fetch = create_client_wrapper(wrapped, trace) except: return wrapped(*args, **kwargs) return convert_yielded(fetch(req, raise_error))
async def async_send_wrapper(wrapped, instance, args, kwargs): request = bind_request(*args, **kwargs) with ExternalTrace("httpx", str(request.url), request.method) as tracer: if hasattr(tracer, "generate_request_headers"): outgoing_headers = tracer.generate_request_headers( tracer.transaction) for header_name, header_value in outgoing_headers: # User headers should override our CAT headers if header_name not in request.headers: request.headers[header_name] = header_value return await wrapped(*args, **kwargs)
def wrap_result(_wrapped, _instance, _args, _kwargs): _result_called = getattr(_instance, '_nr_result_called', True) if _result_called: return _wrapped(*_args, **_kwargs) _instance._nr_result_called = True _nr_args = getattr(_instance, '_nr_args', None) if not _nr_args: return _wrapped(*_args, **_kwargs) _nr_start_time = getattr(_instance, '_nr_start_time', 0.0) try: result = _wrapped(*_args, **_kwargs) except Exception: with ExternalTrace(*_nr_args) as t: t.start_time = _nr_start_time raise else: with ExternalTrace(*_nr_args) as t: t.start_time = _nr_start_time return result
def _nr_wrapper_make_request_(wrapped, instance, args, kwargs, scheme): def _bind_params(conn, method, url, *args, **kwargs): return "%s://%s:%s" % (scheme, conn.host, conn.port) transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) url_for_apm_ui = _bind_params(*args, **kwargs) with ExternalTrace(transaction, 'urllib3', url_for_apm_ui): return wrapped(*args, **kwargs)
def _nr_wrapper(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) url = bind_params_fn(*args, **kwargs) details = urlparse.urlparse(url) if details.hostname is None: return wrapped(*args, **kwargs) with ExternalTrace(transaction, library, url): return wrapped(*args, **kwargs)
def _nr_aiohttp_add_cat_headers_simple_(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) try: cat_headers = ExternalTrace.generate_request_headers(transaction) except: return wrapped(*args, **kwargs) for k, _ in cat_headers: if k in instance.headers: return wrapped(*args, **kwargs) instance.headers.update(cat_headers) return wrapped(*args, **kwargs)
def _nr_wrap_PayloadWriter_write_headers(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) status_line, headers, _args, _kwargs = _bind_write_headers( *args, **kwargs) try: cat_headers = ExternalTrace.generate_request_headers(transaction) updated_headers = dict(cat_headers + [(k, v) for k, v in headers.items()]) except: return wrapped(*args, **kwargs) return wrapped(status_line, updated_headers, *_args, **_kwargs)
def httplib_endheaders_wrapper(wrapped, instance, args, kwargs, scheme, library): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) def _connect_unbound(instance, *args, **kwargs): return instance if instance is None: instance = _connect_unbound(*args, **kwargs) connection = instance if hasattr(connection, '_nr_library_info'): library, scheme = connection._nr_library_info url = '%s://%s:%s' % (scheme, connection.host, connection.port) # Check if the NR headers have already been added. This is just in # case a higher level library which uses httplib underneath so # happened to have been instrumented to also add the headers. try: skip_headers = getattr(connection, '_nr_skip_headers', False) with ExternalTrace(library=library, url=url) as tracer: # Add the tracer to the connection object. The tracer will be # used in getresponse() to add back into the external trace, # after the trace has already completed, details from the # response headers. if not skip_headers and hasattr(tracer, 'generate_request_headers'): outgoing_headers = tracer.generate_request_headers(transaction) for header_name, header_value in outgoing_headers: connection.putheader(header_name, header_value) connection._nr_external_tracer = tracer return wrapped(*args, **kwargs) finally: try: del connection._nr_skip_headers except AttributeError: pass
def test_cat_fips_compliance(monkeypatch, fips_enabled): # Set md5 to raise a ValueError to simulate FIPS compliance issues. def md5_crash(*args, **kwargs): raise ValueError() if fips_enabled: # monkeypatch.setattr("hashlib.md5", md5_crash) import hashlib monkeypatch.setattr(hashlib, "md5", md5_crash) # Generate and send request using actual transaction api instead of fixture. # Otherwise the proper code paths are not exercised. with ExternalTrace("cat_test", "http://localhost/200") as tracer: headers = tracer.generate_request_headers(tracer.transaction) expected = not fips_enabled # Invert to make more human readable assert ('X-NewRelic-Transaction' in dict(headers)) == expected
def wrapper_GearmanConnectionManager_poll_connections_until_stopped( wrapped, instance, args, kwargs): def _bind_params(submitted_connections, *args, **kwargs): return submitted_connections transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) # Because gearman uses a custom message based protocol over a raw # socket, we can't readily wrap a single function which is # performing a request and then returning a response. The best we # can do is wrap as an external the poll_connections_until_stopped() # function. This is what manages looking for whether data is # available from the server, or whether data can be written, and # then handles those events. # # This is complicated somewhat though due to a gearman client being # able to be supplied multiple servers to communicate with. We can # not actually determine which server communication will occur with # until the specific handle function for read, write or error is # called but that is too late in cases where a failure of some sort # occurs such as a timeout. What we therefore do is presume # initially that the server will be whatever is the first in the # list of server connections and we will override this latter based # on which server we ended up communicating with. It is possible this # still will not always be correct if data is handled for multiple # servers in the one call, but it is likely as close as we can get. # As likely that most clients will only be talking to a single # server, it likely will not matter too much. submitted_connections = _bind_params(*args, **kwargs) if not submitted_connections: return wrapped(*args, **kwargs) first_connection = list(submitted_connections)[0] url = 'gearman://%s:%s' % (first_connection.gearman_host, first_connection.gearman_port) with ExternalTrace(transaction, 'gearman', url): return wrapped(*args, **kwargs)
def _nr_wrapper_httpclient_AsyncHTTPClient_fetch_(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) try: req, _cb, _raise_error = _prepare_request(*args, **kwargs) except: return wrapped(*args, **kwargs) # Prepare outgoing CAT headers outgoing_headers = ExternalTrace.generate_request_headers(transaction) for header_name, header_value in outgoing_headers: # User headers should override our CAT headers if header_name in req.headers: continue req.headers[header_name] = header_value trace = ExternalTrace(transaction, 'tornado.httpclient', req.url, req.method.upper()) def external_trace_done(future): exc_info = future.exc_info() if exc_info: trace.__exit__(*exc_info) else: response = future.result() # Process CAT response headers trace.process_response_headers(response.headers.get_all()) trace.__exit__(None, None, None) trace.__enter__() if trace.transaction and trace.transaction.current_node is trace: # externals should not have children trace.transaction._pop_current(trace) try: future = wrapped(req, _cb, _raise_error) future.add_done_callback(external_trace_done) except Exception: trace.__exit__(*sys.exc_info()) raise return future
def _validate_synthetics_external_trace_header(wrapped, instance, args, kwargs): def _bind_params(transaction, *args, **kwargs): return transaction transaction = _bind_params(*args, **kwargs) try: result = wrapped(*args, **kwargs) except: raise else: if should_exist: # XXX This validation routine is technically # broken as the argument to record_transaction() # is not actually an instance of the Transaction # object. Instead it is a TransactionNode object. # The static method generate_request_headers() is # expecting a Transaction object and not # TransactionNode. The latter provides attributes # which are not updatable by the static method # generate_request_headers(), which it wants to # update, so would fail. For now what we do is use # a little proxy wrapper so that updates do not # fail. The use of this wrapper needs to be # reviewed and a better way of achieving what is # required found. class _Transaction(object): def __init__(self, wrapped): self.__wrapped__ = wrapped def __getattr__(self, name): return getattr(self.__wrapped__, name) external_headers = ExternalTrace.generate_request_headers( _Transaction(transaction)) assert required_header in external_headers, ( 'required_header=%r, ''external_headers=%r' % ( required_header, external_headers)) return result
def _nr_wrapper_httpclient_AsyncHTTPClient_fetch_(wrapped, instance, args, kwargs): transaction = retrieve_current_transaction() if transaction is None: return wrapped(*args, **kwargs) req, _cb, _raise_error = _prepare_request(*args, **kwargs) # Prepare outgoing CAT headers outgoing_headers = ExternalTrace.generate_request_headers(transaction) for header_name, header_value in outgoing_headers: req.headers[header_name] = header_value trace = ExternalTrace(transaction, 'tornado.httpclient', req.url) def external_trace_done(future): exc_info = future.exc_info() if exc_info: trace.__exit__(*exc_info) else: response = future.result() # Process CAT response headers trace.process_response_headers(response.headers.get_all()) trace.__exit__(None, None, None) transaction._ref_count -= 1 transaction._ref_count += 1 trace.__enter__() # Because traces are terminal but can be generated concurrently in # tornado, pop the trace immediately after entering. if trace.transaction and trace.transaction.current_node is trace: trace.transaction._pop_current(trace) try: future = wrapped(req, _cb, _raise_error) future.add_done_callback(external_trace_done) except Exception: transaction._ref_count -= 1 trace.__exit__(*sys.exc_info()) raise return future
def _nr_wrapper_opener_director_open_(wrapped, instance, args, kwargs): transaction = current_transaction() if transaction is None: return wrapped(*args, **kwargs) def _bind_params(fullurl, *args, **kwargs): if isinstance(fullurl, six.string_types): return fullurl else: return fullurl.get_full_url() url = _bind_params(*args, **kwargs) details = urlparse.urlparse(url) if details.hostname is None: return wrapped(*args, **kwargs) with ExternalTrace('urllib2', url): return wrapped(*args, **kwargs)
def test_inbound_distributed_trace(mock_grpc_server, method_name, streaming_request, stub): request = create_request(streaming_request) transaction = Transaction(application_instance()) dt_headers = ExternalTrace.generate_request_headers(transaction) @validate_transaction_metrics( 'sample_application:SampleApplicationServicer.' + method_name, rollup_metrics=(('Supportability/TraceContext/Accept/Success', 1), ), ) @wait_for_transaction_completion def _test(): method = getattr(stub, method_name) response = method(request, metadata=dt_headers) try: list(response) except Exception: pass _test()