From 154528255e830e47bd37ea92af655b32be2fc175 Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Fri, 7 Oct 2022 13:28:51 -0700 Subject: [PATCH 1/5] Deprecate add_custom_parameter(s) API --- newrelic/admin/validate_config.py | 99 +-- newrelic/agent.py | 659 ++++++-------- newrelic/api/transaction.py | 84 +- tests/agent_features/test_asgi_browser.py | 747 ++++++++-------- tests/agent_features/test_attribute.py | 341 ++++---- .../test_attributes_in_action.py | 6 +- tests/agent_features/test_browser.py | 823 +++++++++--------- .../agent_features/test_high_security_mode.py | 14 +- tests/agent_features/test_span_events.py | 726 +++++++-------- tests/cross_agent/test_rum_client_config.py | 98 ++- tests/testing_support/sample_applications.py | 107 +-- .../sample_asgi_applications.py | 6 +- 12 files changed, 1915 insertions(+), 1795 deletions(-) diff --git a/newrelic/admin/validate_config.py b/newrelic/admin/validate_config.py index a842df7bed..ac25b715e1 100644 --- a/newrelic/admin/validate_config.py +++ b/newrelic/admin/validate_config.py @@ -25,17 +25,15 @@ def _run_validation_test(): from newrelic.api.error_trace import error_trace from newrelic.api.external_trace import external_trace from newrelic.api.function_trace import function_trace - from newrelic.api.transaction import add_custom_parameter from newrelic.api.time_trace import notice_error + from newrelic.api.transaction import add_custom_attribute from newrelic.api.wsgi_application import wsgi_application - @external_trace(library='test', - url='http://localhost/test', method='GET') + @external_trace(library="test", url="http://localhost/test", method="GET") def _external1(): time.sleep(0.1) - @function_trace(label='label', - params={'fun-key-1': '1', 'fun-key-2': 2, 'fun-key-3': 3.0}) + @function_trace(label="label", params={"fun-key-1": "1", "fun-key-2": 2, "fun-key-3": 3.0}) def _function1(): _external1() @@ -47,33 +45,29 @@ def _function2(): @error_trace() @function_trace() def _function3(): - add_custom_parameter('txn-key-1', 1) + add_custom_attribute("txn-key-1", 1) _function4() - raise RuntimeError('This is a test error and can be ignored.') + raise RuntimeError("This is a test error and can be ignored.") @function_trace() def _function4(params=None, application=None): try: _function5() except: - notice_error(attributes=(params or { - 'err-key-2': 2, 'err-key-3': 3.0}), - application=application) + notice_error(attributes=(params or {"err-key-2": 2, "err-key-3": 3.0}), application=application) @function_trace() def _function5(): - raise NotImplementedError( - 'This is a test error and can be ignored.') + raise NotImplementedError("This is a test error and can be ignored.") @wsgi_application() def _wsgi_application(environ, start_response): - status = '200 OK' - output = 'Hello World!' + status = "200 OK" + output = "Hello World!" - response_headers = [('Content-type', 'text/plain'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-type", "text/plain"), ("Content-Length", str(len(output)))] start_response(status, response_headers) for i in range(10): @@ -107,16 +101,15 @@ def _background_task(): def _start_response(*args): pass - _environ = { 'SCRIPT_NAME': '', 'PATH_INFO': '/test', - 'QUERY_STRING': 'key=value' } + _environ = {"SCRIPT_NAME": "", "PATH_INFO": "/test", "QUERY_STRING": "key=value"} _iterable = _wsgi_application(_environ, _start_response) _iterable.close() _background_task() - _function4(params={'err-key-4': 4, 'err-key-5': 5.0}, - application=application()) + _function4(params={"err-key-4": 4, "err-key-5": 5.0}, application=application()) + _user_message = """ Running Python agent test. @@ -136,19 +129,23 @@ def _start_response(*args): data to the New Relic UI. """ -@command('validate-config', 'config_file [log_file]', -"""Validates the syntax of . Also tests connectivity to New + +@command( + "validate-config", + "config_file [log_file]", + """Validates the syntax of . Also tests connectivity to New Relic core application by connecting to the account corresponding to the license key listed in the configuration file, and reporting test data under -the application name 'Python Agent Test'.""") +the application name 'Python Agent Test'.""", +) def validate_config(args): + import logging import os import sys - import logging import time if len(args) == 0: - usage('validate-config') + usage("validate-config") sys.exit(1) from newrelic.api.application import register_application @@ -158,7 +155,7 @@ def validate_config(args): if len(args) >= 2: log_file = args[1] else: - log_file = '/tmp/python-agent-test.log' + log_file = "/tmp/python-agent-test.log" # nosec log_level = logging.DEBUG @@ -168,21 +165,20 @@ def validate_config(args): pass config_file = args[0] - environment = os.environ.get('NEW_RELIC_ENVIRONMENT') + environment = os.environ.get("NEW_RELIC_ENVIRONMENT") - if config_file == '-': - config_file = os.environ.get('NEW_RELIC_CONFIG_FILE') + if config_file == "-": + config_file = os.environ.get("NEW_RELIC_CONFIG_FILE") - initialize(config_file, environment, ignore_errors=False, - log_file=log_file, log_level=log_level) + initialize(config_file, environment, ignore_errors=False, log_file=log_file, log_level=log_level) _logger = logging.getLogger(__name__) - _logger.debug('Starting agent validation.') + _logger.debug("Starting agent validation.") _settings = global_settings() - app_name = os.environ.get('NEW_RELIC_TEST_APP_NAME', 'Python Agent Test') + app_name = os.environ.get("NEW_RELIC_TEST_APP_NAME", "Python Agent Test") _settings.app_name = app_name _settings.transaction_tracer.transaction_threshold = 0 @@ -194,17 +190,17 @@ def validate_config(args): print(_user_message % dict(app_name=app_name, log_file=log_file)) - _logger.debug('Register test application.') + _logger.debug("Register test application.") - _logger.debug('Collector host is %r.', _settings.host) - _logger.debug('Collector port is %r.', _settings.port) + _logger.debug("Collector host is %r.", _settings.host) + _logger.debug("Collector port is %r.", _settings.port) - _logger.debug('Proxy scheme is %r.', _settings.proxy_scheme) - _logger.debug('Proxy host is %r.', _settings.proxy_host) - _logger.debug('Proxy port is %r.', _settings.proxy_port) - _logger.debug('Proxy user is %r.', _settings.proxy_user) + _logger.debug("Proxy scheme is %r.", _settings.proxy_scheme) + _logger.debug("Proxy host is %r.", _settings.proxy_host) + _logger.debug("Proxy port is %r.", _settings.proxy_port) + _logger.debug("Proxy user is %r.", _settings.proxy_user) - _logger.debug('License key is %r.', _settings.license_key) + _logger.debug("License key is %r.", _settings.license_key) _timeout = 30.0 @@ -215,24 +211,25 @@ def validate_config(args): _duration = _end - _start if not _application.active: - _logger.error('Unable to register application for test, ' - 'connection could not be established within %s seconds.', - _timeout) + _logger.error( + "Unable to register application for test, " "connection could not be established within %s seconds.", + _timeout, + ) return - if hasattr(_application.settings, 'messages'): + if hasattr(_application.settings, "messages"): for message in _application.settings.messages: - if message['message'].startswith('Reporting to:'): - parts = message['message'].split('Reporting to:') + if message["message"].startswith("Reporting to:"): + parts = message["message"].split("Reporting to:") url = parts[1].strip() - print('Registration successful. Reporting to:') + print("Registration successful. Reporting to:") print() - print(' %s' % url) + print(" %s" % url) print() break - _logger.debug('Registration took %s seconds.', _duration) + _logger.debug("Registration took %s seconds.", _duration) - _logger.debug('Run the validation test.') + _logger.debug("Run the validation test.") _run_validation_test() diff --git a/newrelic/agent.py b/newrelic/agent.py index f635b866f0..e532d1c6e6 100644 --- a/newrelic/agent.py +++ b/newrelic/agent.py @@ -12,412 +12,327 @@ # See the License for the specific language governing permissions and # limitations under the License. -from newrelic.config import ( - initialize as __initialize, - extra_settings as __extra_settings) - -from newrelic.core.config import global_settings as __global_settings - -from newrelic.core.agent import ( - shutdown_agent as __shutdown_agent, - register_data_source as __register_data_source) - -from newrelic.samplers.decorators import ( - data_source_generator as __data_source_generator, - data_source_factory as __data_source_factory) - -from newrelic.api.log import NewRelicContextFormatter - -from newrelic.api.application import ( - application_instance as __application, - register_application as __register_application, - application_settings as __application_settings) +from newrelic.api.application import application_instance as __application +from newrelic.api.application import application_settings as __application_settings +from newrelic.api.application import register_application as __register_application +# from newrelic.api.log import NewRelicContextFormatter from newrelic.api.time_trace import ( - current_trace as __current_trace, - get_linking_metadata as __get_linking_metadata, - add_custom_span_attribute as __add_custom_span_attribute, - record_exception as __record_exception, - notice_error as __notice_error) - + add_custom_span_attribute as __add_custom_span_attribute, +) +from newrelic.api.time_trace import current_trace as __current_trace +from newrelic.api.time_trace import get_linking_metadata as __get_linking_metadata +from newrelic.api.time_trace import notice_error as __notice_error +from newrelic.api.time_trace import record_exception as __record_exception from newrelic.api.transaction import ( - current_transaction as __current_transaction, - set_transaction_name as __set_transaction_name, - end_of_transaction as __end_of_transaction, - set_background_task as __set_background_task, - ignore_transaction as __ignore_transaction, - suppress_apdex_metric as __suppress_apdex_metric, - capture_request_params as __capture_request_params, - add_custom_parameter as __add_custom_parameter, - add_custom_parameters as __add_custom_parameters, - add_framework_info as __add_framework_info, - get_browser_timing_header as __get_browser_timing_header, - get_browser_timing_footer as __get_browser_timing_footer, - disable_browser_autorum as __disable_browser_autorum, - suppress_transaction_trace as __suppress_transaction_trace, - record_custom_metric as __record_custom_metric, - record_custom_metrics as __record_custom_metrics, - record_custom_event as __record_custom_event, - accept_distributed_trace_payload as __accept_distributed_trace_payload, - create_distributed_trace_payload as __create_distributed_trace_payload, - accept_distributed_trace_headers as __accept_distributed_trace_headers, - insert_distributed_trace_headers as __insert_distributed_trace_headers, - current_trace_id as __current_trace_id, - current_span_id as __current_span_id) - + accept_distributed_trace_headers as __accept_distributed_trace_headers, +) +from newrelic.api.transaction import ( + accept_distributed_trace_payload as __accept_distributed_trace_payload, +) +from newrelic.api.transaction import add_custom_attribute as __add_custom_attribute +from newrelic.api.transaction import add_custom_attributes as __add_custom_attributes +from newrelic.api.transaction import add_custom_parameter as __add_custom_parameter +from newrelic.api.transaction import add_custom_parameters as __add_custom_parameters +from newrelic.api.transaction import add_framework_info as __add_framework_info +from newrelic.api.transaction import capture_request_params as __capture_request_params +from newrelic.api.transaction import ( + create_distributed_trace_payload as __create_distributed_trace_payload, +) +from newrelic.api.transaction import current_span_id as __current_span_id +from newrelic.api.transaction import current_trace_id as __current_trace_id +from newrelic.api.transaction import current_transaction as __current_transaction +from newrelic.api.transaction import ( + disable_browser_autorum as __disable_browser_autorum, +) +from newrelic.api.transaction import end_of_transaction as __end_of_transaction +from newrelic.api.transaction import ( + get_browser_timing_footer as __get_browser_timing_footer, +) +from newrelic.api.transaction import ( + get_browser_timing_header as __get_browser_timing_header, +) +from newrelic.api.transaction import ignore_transaction as __ignore_transaction +from newrelic.api.transaction import ( + insert_distributed_trace_headers as __insert_distributed_trace_headers, +) +from newrelic.api.transaction import record_custom_event as __record_custom_event +from newrelic.api.transaction import record_custom_metric as __record_custom_metric +from newrelic.api.transaction import record_custom_metrics as __record_custom_metrics +from newrelic.api.transaction import set_background_task as __set_background_task +from newrelic.api.transaction import set_transaction_name as __set_transaction_name +from newrelic.api.transaction import suppress_apdex_metric as __suppress_apdex_metric +from newrelic.api.transaction import ( + suppress_transaction_trace as __suppress_transaction_trace, +) +from newrelic.api.wsgi_application import ( + WSGIApplicationWrapper as __WSGIApplicationWrapper, +) from newrelic.api.wsgi_application import ( - wsgi_application as __wsgi_application, - WSGIApplicationWrapper as __WSGIApplicationWrapper, - wrap_wsgi_application as __wrap_wsgi_application) + wrap_wsgi_application as __wrap_wsgi_application, +) +from newrelic.api.wsgi_application import wsgi_application as __wsgi_application +from newrelic.config import extra_settings as __extra_settings +from newrelic.config import initialize as __initialize +from newrelic.core.agent import register_data_source as __register_data_source +from newrelic.core.agent import shutdown_agent as __shutdown_agent +from newrelic.core.config import global_settings as __global_settings +from newrelic.samplers.decorators import data_source_factory as __data_source_factory +from newrelic.samplers.decorators import ( + data_source_generator as __data_source_generator, +) try: from newrelic.api.asgi_application import ( - asgi_application as __asgi_application, - ASGIApplicationWrapper as __ASGIApplicationWrapper, - wrap_asgi_application as __wrap_asgi_application) + ASGIApplicationWrapper as __ASGIApplicationWrapper, + ) + from newrelic.api.asgi_application import asgi_application as __asgi_application + from newrelic.api.asgi_application import ( + wrap_asgi_application as __wrap_asgi_application, + ) except SyntaxError: + def __asgi_application(*args, **kwargs): pass __ASGIApplicationWrapper = __asgi_application __wrap_asgi_application = __asgi_application -from newrelic.api.web_transaction import ( - WebTransaction as __WebTransaction, - web_transaction as __web_transaction, - WebTransactionWrapper as __WebTransactionWrapper, - wrap_web_transaction as __wrap_web_transaction) - +from newrelic.api.background_task import BackgroundTask as __BackgroundTask from newrelic.api.background_task import ( - background_task as __background_task, - BackgroundTask as __BackgroundTask, - BackgroundTaskWrapper as __BackgroundTaskWrapper, - wrap_background_task as __wrap_background_task) - -from newrelic.api.lambda_handler import ( - LambdaHandlerWrapper as __LambdaHandlerWrapper, - lambda_handler as __lambda_handler) - + BackgroundTaskWrapper as __BackgroundTaskWrapper, +) +from newrelic.api.background_task import background_task as __background_task +from newrelic.api.background_task import wrap_background_task as __wrap_background_task +from newrelic.api.database_trace import DatabaseTrace as __DatabaseTrace +from newrelic.api.database_trace import DatabaseTraceWrapper as __DatabaseTraceWrapper +from newrelic.api.database_trace import database_trace as __database_trace +from newrelic.api.database_trace import ( + register_database_client as __register_database_client, +) +from newrelic.api.database_trace import wrap_database_trace as __wrap_database_trace +from newrelic.api.datastore_trace import DatastoreTrace as __DatastoreTrace +from newrelic.api.datastore_trace import ( + DatastoreTraceWrapper as __DatastoreTraceWrapper, +) +from newrelic.api.datastore_trace import datastore_trace as __datastore_trace +from newrelic.api.datastore_trace import wrap_datastore_trace as __wrap_datastore_trace +from newrelic.api.error_trace import ErrorTrace as __ErrorTrace +from newrelic.api.error_trace import ErrorTraceWrapper as __ErrorTraceWrapper +from newrelic.api.error_trace import error_trace as __error_trace +from newrelic.api.error_trace import wrap_error_trace as __wrap_error_trace +from newrelic.api.external_trace import ExternalTrace as __ExternalTrace +from newrelic.api.external_trace import ExternalTraceWrapper as __ExternalTraceWrapper +from newrelic.api.external_trace import external_trace as __external_trace +from newrelic.api.external_trace import wrap_external_trace as __wrap_external_trace +from newrelic.api.function_trace import FunctionTrace as __FunctionTrace +from newrelic.api.function_trace import FunctionTraceWrapper as __FunctionTraceWrapper +from newrelic.api.function_trace import function_trace as __function_trace +from newrelic.api.function_trace import wrap_function_trace as __wrap_function_trace +from newrelic.api.generator_trace import ( + GeneratorTraceWrapper as __GeneratorTraceWrapper, +) +from newrelic.api.generator_trace import generator_trace as __generator_trace +from newrelic.api.generator_trace import wrap_generator_trace as __wrap_generator_trace +from newrelic.api.html_insertion import insert_html_snippet as __insert_html_snippet +from newrelic.api.html_insertion import verify_body_exists as __verify_body_exists +from newrelic.api.lambda_handler import LambdaHandlerWrapper as __LambdaHandlerWrapper +from newrelic.api.lambda_handler import lambda_handler as __lambda_handler +from newrelic.api.message_trace import MessageTrace as __MessageTrace +from newrelic.api.message_trace import MessageTraceWrapper as __MessageTraceWrapper +from newrelic.api.message_trace import message_trace as __message_trace +from newrelic.api.message_trace import wrap_message_trace as __wrap_message_trace +from newrelic.api.message_transaction import MessageTransaction as __MessageTransaction +from newrelic.api.message_transaction import ( + MessageTransactionWrapper as __MessageTransactionWrapper, +) +from newrelic.api.message_transaction import ( + message_transaction as __message_transaction, +) +from newrelic.api.message_transaction import ( + wrap_message_transaction as __wrap_message_transaction, +) +from newrelic.api.profile_trace import ProfileTraceWrapper as __ProfileTraceWrapper +from newrelic.api.profile_trace import profile_trace as __profile_trace +from newrelic.api.profile_trace import wrap_profile_trace as __wrap_profile_trace +from newrelic.api.supportability import wrap_api_call as __wrap_api_call from newrelic.api.transaction_name import ( - transaction_name as __transaction_name, - TransactionNameWrapper as __TransactionNameWrapper, - wrap_transaction_name as __wrap_transaction_name) - -from newrelic.api.function_trace import ( - function_trace as __function_trace, - FunctionTrace as __FunctionTrace, - FunctionTraceWrapper as __FunctionTraceWrapper, - wrap_function_trace as __wrap_function_trace) + TransactionNameWrapper as __TransactionNameWrapper, +) +from newrelic.api.transaction_name import transaction_name as __transaction_name +from newrelic.api.transaction_name import ( + wrap_transaction_name as __wrap_transaction_name, +) +from newrelic.api.web_transaction import WebTransaction as __WebTransaction +from newrelic.api.web_transaction import ( + WebTransactionWrapper as __WebTransactionWrapper, +) +from newrelic.api.web_transaction import web_transaction as __web_transaction +from newrelic.api.web_transaction import wrap_web_transaction as __wrap_web_transaction +from newrelic.common.object_names import callable_name as __callable_name +from newrelic.common.object_wrapper import FunctionWrapper as __FunctionWrapper +from newrelic.common.object_wrapper import InFunctionWrapper as __InFunctionWrapper +from newrelic.common.object_wrapper import ObjectProxy as __ObjectProxy +from newrelic.common.object_wrapper import ObjectWrapper as __ObjectWrapper +from newrelic.common.object_wrapper import OutFunctionWrapper as __OutFunctionWrapper +from newrelic.common.object_wrapper import PostFunctionWrapper as __PostFunctionWrapper +from newrelic.common.object_wrapper import PreFunctionWrapper as __PreFunctionWrapper +from newrelic.common.object_wrapper import function_wrapper as __function_wrapper +from newrelic.common.object_wrapper import in_function as __in_function +from newrelic.common.object_wrapper import out_function as __out_function +from newrelic.common.object_wrapper import ( + patch_function_wrapper as __patch_function_wrapper, +) +from newrelic.common.object_wrapper import post_function as __post_function +from newrelic.common.object_wrapper import pre_function as __pre_function +from newrelic.common.object_wrapper import resolve_path as __resolve_path +from newrelic.common.object_wrapper import ( + transient_function_wrapper as __transient_function_wrapper, +) +from newrelic.common.object_wrapper import ( + wrap_function_wrapper as __wrap_function_wrapper, +) +from newrelic.common.object_wrapper import wrap_in_function as __wrap_in_function +from newrelic.common.object_wrapper import wrap_object as __wrap_object +from newrelic.common.object_wrapper import ( + wrap_object_attribute as __wrap_object_attribute, +) +from newrelic.common.object_wrapper import wrap_out_function as __wrap_out_function +from newrelic.common.object_wrapper import wrap_post_function as __wrap_post_function +from newrelic.common.object_wrapper import wrap_pre_function as __wrap_pre_function # EXPERIMENTAL - Generator traces are currently experimental and may not # exist in this form in future versions of the agent. -from newrelic.api.generator_trace import ( - generator_trace as __generator_trace, - GeneratorTraceWrapper as __GeneratorTraceWrapper, - wrap_generator_trace as __wrap_generator_trace) # EXPERIMENTAL - Profile traces are currently experimental and may not # exist in this form in future versions of the agent. -from newrelic.api.profile_trace import ( - profile_trace as __profile_trace, - ProfileTraceWrapper as __ProfileTraceWrapper, - wrap_profile_trace as __wrap_profile_trace) - -from newrelic.api.database_trace import ( - database_trace as __database_trace, - DatabaseTrace as __DatabaseTrace, - DatabaseTraceWrapper as __DatabaseTraceWrapper, - wrap_database_trace as __wrap_database_trace, - register_database_client as __register_database_client) - -from newrelic.api.datastore_trace import ( - datastore_trace as __datastore_trace, - DatastoreTrace as __DatastoreTrace, - DatastoreTraceWrapper as __DatastoreTraceWrapper, - wrap_datastore_trace as __wrap_datastore_trace) - -from newrelic.api.external_trace import ( - external_trace as __external_trace, - ExternalTrace as __ExternalTrace, - ExternalTraceWrapper as __ExternalTraceWrapper, - wrap_external_trace as __wrap_external_trace) - -from newrelic.api.error_trace import ( - error_trace as __error_trace, - ErrorTrace as __ErrorTrace, - ErrorTraceWrapper as __ErrorTraceWrapper, - wrap_error_trace as __wrap_error_trace) - -from newrelic.api.message_trace import ( - message_trace as __message_trace, - MessageTrace as __MessageTrace, - MessageTraceWrapper as __MessageTraceWrapper, - wrap_message_trace as __wrap_message_trace) - -from newrelic.api.message_transaction import ( - message_transaction as __message_transaction, - MessageTransaction as __MessageTransaction, - MessageTransactionWrapper as __MessageTransactionWrapper, - wrap_message_transaction as __wrap_message_transaction) - -from newrelic.common.object_names import callable_name as __callable_name - -from newrelic.common.object_wrapper import ( - ObjectProxy as __ObjectProxy, - wrap_object as __wrap_object, - wrap_object_attribute as __wrap_object_attribute, - resolve_path as __resolve_path, - transient_function_wrapper as __transient_function_wrapper, - FunctionWrapper as __FunctionWrapper, - function_wrapper as __function_wrapper, - wrap_function_wrapper as __wrap_function_wrapper, - patch_function_wrapper as __patch_function_wrapper, - ObjectWrapper as __ObjectWrapper, - pre_function as __pre_function, - PreFunctionWrapper as __PreFunctionWrapper, - wrap_pre_function as __wrap_pre_function, - post_function as __post_function, - PostFunctionWrapper as __PostFunctionWrapper, - wrap_post_function as __wrap_post_function, - in_function as __in_function, - InFunctionWrapper as __InFunctionWrapper, - wrap_in_function as __wrap_in_function, - out_function as __out_function, - OutFunctionWrapper as __OutFunctionWrapper, - wrap_out_function as __wrap_out_function) - -from newrelic.api.html_insertion import ( - insert_html_snippet as __insert_html_snippet, - verify_body_exists as __verify_body_exists) - -from newrelic.api.supportability import wrap_api_call as __wrap_api_call initialize = __initialize -extra_settings = __wrap_api_call(__extra_settings, - 'extra_settings') -global_settings = __wrap_api_call(__global_settings, - 'global_settings') -shutdown_agent = __wrap_api_call(__shutdown_agent, - 'shutdown_agent') -register_data_source = __wrap_api_call(__register_data_source, - 'register_data_source') -data_source_generator = __wrap_api_call(__data_source_generator, - 'data_source_generator') -data_source_factory = __wrap_api_call(__data_source_factory, - 'data_source_factory') -application = __wrap_api_call(__application, - 'application') +extra_settings = __wrap_api_call(__extra_settings, "extra_settings") +global_settings = __wrap_api_call(__global_settings, "global_settings") +shutdown_agent = __wrap_api_call(__shutdown_agent, "shutdown_agent") +register_data_source = __wrap_api_call(__register_data_source, "register_data_source") +data_source_generator = __wrap_api_call(__data_source_generator, "data_source_generator") +data_source_factory = __wrap_api_call(__data_source_factory, "data_source_factory") +application = __wrap_api_call(__application, "application") register_application = __register_application -application_settings = __wrap_api_call(__application_settings, - 'application_settings') -current_trace = __wrap_api_call(__current_trace, - 'current_trace') -get_linking_metadata = __wrap_api_call(__get_linking_metadata, - 'get_linking_metadata') -add_custom_span_attribute = __wrap_api_call(__add_custom_span_attribute, - 'add_custom_span_attribute') -current_transaction = __wrap_api_call(__current_transaction, - 'current_transaction') -set_transaction_name = __wrap_api_call(__set_transaction_name, - 'set_transaction_name') -end_of_transaction = __wrap_api_call(__end_of_transaction, - 'end_of_transaction') -set_background_task = __wrap_api_call(__set_background_task, - 'set_background_task') -ignore_transaction = __wrap_api_call(__ignore_transaction, - 'ignore_transaction') -suppress_apdex_metric = __wrap_api_call(__suppress_apdex_metric, - 'suppress_apdex_metric') -capture_request_params = __wrap_api_call(__capture_request_params, - 'capture_request_params') -add_custom_parameter = __wrap_api_call(__add_custom_parameter, - 'add_custom_parameter') -add_custom_parameters = __wrap_api_call(__add_custom_parameters, - 'add_custom_parameters') -add_framework_info = __wrap_api_call(__add_framework_info, - 'add_framework_info') -record_exception = __wrap_api_call(__record_exception, - 'record_exception') -notice_error = __wrap_api_call(__notice_error, - 'notice_error') -get_browser_timing_header = __wrap_api_call(__get_browser_timing_header, - 'get_browser_timing_header') -get_browser_timing_footer = __wrap_api_call(__get_browser_timing_footer, - 'get_browser_timing_footer') -disable_browser_autorum = __wrap_api_call(__disable_browser_autorum, - 'disable_browser_autorum') -suppress_transaction_trace = __wrap_api_call(__suppress_transaction_trace, - 'suppress_transaction_trace') -record_custom_metric = __wrap_api_call(__record_custom_metric, - 'record_custom_metric') -record_custom_metrics = __wrap_api_call(__record_custom_metrics, - 'record_custom_metrics') -record_custom_event = __wrap_api_call(__record_custom_event, - 'record_custom_event') +application_settings = __wrap_api_call(__application_settings, "application_settings") +current_trace = __wrap_api_call(__current_trace, "current_trace") +get_linking_metadata = __wrap_api_call(__get_linking_metadata, "get_linking_metadata") +add_custom_span_attribute = __wrap_api_call(__add_custom_span_attribute, "add_custom_span_attribute") +current_transaction = __wrap_api_call(__current_transaction, "current_transaction") +set_transaction_name = __wrap_api_call(__set_transaction_name, "set_transaction_name") +end_of_transaction = __wrap_api_call(__end_of_transaction, "end_of_transaction") +set_background_task = __wrap_api_call(__set_background_task, "set_background_task") +ignore_transaction = __wrap_api_call(__ignore_transaction, "ignore_transaction") +suppress_apdex_metric = __wrap_api_call(__suppress_apdex_metric, "suppress_apdex_metric") +capture_request_params = __wrap_api_call(__capture_request_params, "capture_request_params") +add_custom_parameter = __wrap_api_call(__add_custom_parameter, "add_custom_parameter") +add_custom_parameters = __wrap_api_call(__add_custom_parameters, "add_custom_parameters") +add_custom_attribute = __wrap_api_call(__add_custom_attribute, "add_custom_attribute") +add_custom_attributes = __wrap_api_call(__add_custom_attributes, "add_custom_attributes") +add_framework_info = __wrap_api_call(__add_framework_info, "add_framework_info") +record_exception = __wrap_api_call(__record_exception, "record_exception") +notice_error = __wrap_api_call(__notice_error, "notice_error") +get_browser_timing_header = __wrap_api_call(__get_browser_timing_header, "get_browser_timing_header") +get_browser_timing_footer = __wrap_api_call(__get_browser_timing_footer, "get_browser_timing_footer") +disable_browser_autorum = __wrap_api_call(__disable_browser_autorum, "disable_browser_autorum") +suppress_transaction_trace = __wrap_api_call(__suppress_transaction_trace, "suppress_transaction_trace") +record_custom_metric = __wrap_api_call(__record_custom_metric, "record_custom_metric") +record_custom_metrics = __wrap_api_call(__record_custom_metrics, "record_custom_metrics") +record_custom_event = __wrap_api_call(__record_custom_event, "record_custom_event") accept_distributed_trace_payload = __wrap_api_call( - __accept_distributed_trace_payload, 'accept_distributed_trace_payload') + __accept_distributed_trace_payload, "accept_distributed_trace_payload" +) create_distributed_trace_payload = __wrap_api_call( - __create_distributed_trace_payload, - 'create_distributed_trace_payload') + __create_distributed_trace_payload, "create_distributed_trace_payload" +) accept_distributed_trace_headers = __wrap_api_call( - __accept_distributed_trace_headers, - 'accept_distributed_trace_headers') + __accept_distributed_trace_headers, "accept_distributed_trace_headers" +) insert_distributed_trace_headers = __wrap_api_call( - __insert_distributed_trace_headers, - 'insert_distributed_trace_headers') -current_trace_id = __wrap_api_call(__current_trace_id, 'current_trace_id') -current_span_id = __wrap_api_call(__current_span_id, 'current_span_id') + __insert_distributed_trace_headers, "insert_distributed_trace_headers" +) +current_trace_id = __wrap_api_call(__current_trace_id, "current_trace_id") +current_span_id = __wrap_api_call(__current_span_id, "current_span_id") wsgi_application = __wsgi_application asgi_application = __asgi_application -WebTransaction = __wrap_api_call(__WebTransaction, - 'WebTransaction') -web_transaction = __wrap_api_call(__web_transaction, - 'web_transaction') -WebTransactionWrapper = __wrap_api_call(__WebTransactionWrapper, - 'WebTransactionWrapper') -wrap_web_transaction = __wrap_api_call(__wrap_web_transaction, - 'wrap_web_transaction') +WebTransaction = __wrap_api_call(__WebTransaction, "WebTransaction") +web_transaction = __wrap_api_call(__web_transaction, "web_transaction") +WebTransactionWrapper = __wrap_api_call(__WebTransactionWrapper, "WebTransactionWrapper") +wrap_web_transaction = __wrap_api_call(__wrap_web_transaction, "wrap_web_transaction") WSGIApplicationWrapper = __WSGIApplicationWrapper wrap_wsgi_application = __wrap_wsgi_application ASGIApplicationWrapper = __ASGIApplicationWrapper wrap_asgi_application = __wrap_asgi_application -background_task = __wrap_api_call(__background_task, - 'background_task') -BackgroundTask = __wrap_api_call(__BackgroundTask, - 'BackgroundTask') -BackgroundTaskWrapper = __wrap_api_call(__BackgroundTaskWrapper, - 'BackgroundTaskWrapper') -wrap_background_task = __wrap_api_call(__wrap_background_task, - 'wrap_background_task') -LambdaHandlerWrapper = __wrap_api_call(__LambdaHandlerWrapper, - 'LambdaHandlerWrapper') -lambda_handler = __wrap_api_call(__lambda_handler, - 'lambda_handler') -transaction_name = __wrap_api_call(__transaction_name, - 'transaction_name') -TransactionNameWrapper = __wrap_api_call(__TransactionNameWrapper, - 'TransactionNameWrapper') -wrap_transaction_name = __wrap_api_call(__wrap_transaction_name, - 'wrap_transaction_name') -function_trace = __wrap_api_call(__function_trace, - 'function_trace') -FunctionTrace = __wrap_api_call(__FunctionTrace, - 'FunctionTrace') -FunctionTraceWrapper = __wrap_api_call(__FunctionTraceWrapper, - 'FunctionTraceWrapper') -wrap_function_trace = __wrap_api_call(__wrap_function_trace, - 'wrap_function_trace') -generator_trace = __wrap_api_call(__generator_trace, - 'generator_trace') -GeneratorTraceWrapper = __wrap_api_call(__GeneratorTraceWrapper, - 'GeneratorTraceWrapper') -wrap_generator_trace = __wrap_api_call(__wrap_generator_trace, - 'wrap_generator_trace') -profile_trace = __wrap_api_call(__profile_trace, - 'profile_trace') -ProfileTraceWrapper = __wrap_api_call(__ProfileTraceWrapper, - 'ProfileTraceWrapper') -wrap_profile_trace = __wrap_api_call(__wrap_profile_trace, - 'wrap_profile_trace') -database_trace = __wrap_api_call(__database_trace, - 'database_trace') -DatabaseTrace = __wrap_api_call(__DatabaseTrace, - 'DatabaseTrace') -DatabaseTraceWrapper = __wrap_api_call(__DatabaseTraceWrapper, - 'DatabaseTraceWrapper') -wrap_database_trace = __wrap_api_call(__wrap_database_trace, - 'wrap_database_trace') -register_database_client = __wrap_api_call(__register_database_client, - 'register_database_client') -datastore_trace = __wrap_api_call(__datastore_trace, - 'datastore_trace') -DatastoreTrace = __wrap_api_call(__DatastoreTrace, - 'DatastoreTrace') -DatastoreTraceWrapper = __wrap_api_call(__DatastoreTraceWrapper, - 'DatastoreTraceWrapper') -wrap_datastore_trace = __wrap_api_call(__wrap_datastore_trace, - 'wrap_datastore_trace') -external_trace = __wrap_api_call(__external_trace, - 'external_trace') -ExternalTrace = __wrap_api_call(__ExternalTrace, - 'ExternalTrace') -ExternalTraceWrapper = __wrap_api_call(__ExternalTraceWrapper, - 'ExternalTraceWrapper') -wrap_external_trace = __wrap_api_call(__wrap_external_trace, - 'wrap_external_trace') -error_trace = __wrap_api_call(__error_trace, - 'error_trace') -ErrorTrace = __wrap_api_call(__ErrorTrace, - 'ErrorTrace') -ErrorTraceWrapper = __wrap_api_call(__ErrorTraceWrapper, - 'ErrorTraceWrapper') -wrap_error_trace = __wrap_api_call(__wrap_error_trace, - 'wrap_error_trace') -message_trace = __wrap_api_call(__message_trace, - 'message_trace') -MessageTrace = __wrap_api_call(__MessageTrace, - 'MessageTrace') -MessageTraceWrapper = __wrap_api_call(__MessageTraceWrapper, - 'MessageTraceWrapper') -wrap_message_trace = __wrap_api_call(__wrap_message_trace, - 'wrap_message_trace') -message_transaction = __wrap_api_call(__message_transaction, - 'message_trace') -MessageTransaction = __wrap_api_call(__MessageTransaction, - 'MessageTransaction') -MessageTransactionWrapper = __wrap_api_call(__MessageTransactionWrapper, - 'MessageTransactionWrapper') -wrap_message_transaction = __wrap_api_call(__wrap_message_transaction, - 'wrap_message_transaction') -callable_name = __wrap_api_call(__callable_name, - 'callable_name') -ObjectProxy = __wrap_api_call(__ObjectProxy, - 'ObjectProxy') -wrap_object = __wrap_api_call(__wrap_object, - 'wrap_object') -wrap_object_attribute = __wrap_api_call(__wrap_object_attribute, - 'wrap_object_attribute') -resolve_path = __wrap_api_call(__resolve_path, - 'resolve_path') -transient_function_wrapper = __wrap_api_call(__transient_function_wrapper, - 'transient_function_wrapper') -FunctionWrapper = __wrap_api_call(__FunctionWrapper, - 'FunctionWrapper') -function_wrapper = __wrap_api_call(__function_wrapper, - 'function_wrapper') -wrap_function_wrapper = __wrap_api_call(__wrap_function_wrapper, - 'wrap_function_wrapper') -patch_function_wrapper = __wrap_api_call(__patch_function_wrapper, - 'patch_function_wrapper') -ObjectWrapper = __wrap_api_call(__ObjectWrapper, - 'ObjectWrapper') -pre_function = __wrap_api_call(__pre_function, - 'pre_function') -PreFunctionWrapper = __wrap_api_call(__PreFunctionWrapper, - 'PreFunctionWrapper') -wrap_pre_function = __wrap_api_call(__wrap_pre_function, - 'wrap_pre_function') -post_function = __wrap_api_call(__post_function, - 'post_function') -PostFunctionWrapper = __wrap_api_call(__PostFunctionWrapper, - 'PostFunctionWrapper') -wrap_post_function = __wrap_api_call(__wrap_post_function, - 'wrap_post_function') -in_function = __wrap_api_call(__in_function, - 'in_function') -InFunctionWrapper = __wrap_api_call(__InFunctionWrapper, - 'InFunctionWrapper') -wrap_in_function = __wrap_api_call(__wrap_in_function, - 'wrap_in_function') -out_function = __wrap_api_call(__out_function, - 'out_function') -OutFunctionWrapper = __wrap_api_call(__OutFunctionWrapper, - 'OutFunctionWrapper') -wrap_out_function = __wrap_api_call(__wrap_out_function, - 'wrap_out_function') -insert_html_snippet = __wrap_api_call(__insert_html_snippet, - 'insert_html_snippet') -verify_body_exists = __wrap_api_call(__verify_body_exists, - 'verify_body_exists') +background_task = __wrap_api_call(__background_task, "background_task") +BackgroundTask = __wrap_api_call(__BackgroundTask, "BackgroundTask") +BackgroundTaskWrapper = __wrap_api_call(__BackgroundTaskWrapper, "BackgroundTaskWrapper") +wrap_background_task = __wrap_api_call(__wrap_background_task, "wrap_background_task") +LambdaHandlerWrapper = __wrap_api_call(__LambdaHandlerWrapper, "LambdaHandlerWrapper") +lambda_handler = __wrap_api_call(__lambda_handler, "lambda_handler") +transaction_name = __wrap_api_call(__transaction_name, "transaction_name") +TransactionNameWrapper = __wrap_api_call(__TransactionNameWrapper, "TransactionNameWrapper") +wrap_transaction_name = __wrap_api_call(__wrap_transaction_name, "wrap_transaction_name") +function_trace = __wrap_api_call(__function_trace, "function_trace") +FunctionTrace = __wrap_api_call(__FunctionTrace, "FunctionTrace") +FunctionTraceWrapper = __wrap_api_call(__FunctionTraceWrapper, "FunctionTraceWrapper") +wrap_function_trace = __wrap_api_call(__wrap_function_trace, "wrap_function_trace") +generator_trace = __wrap_api_call(__generator_trace, "generator_trace") +GeneratorTraceWrapper = __wrap_api_call(__GeneratorTraceWrapper, "GeneratorTraceWrapper") +wrap_generator_trace = __wrap_api_call(__wrap_generator_trace, "wrap_generator_trace") +profile_trace = __wrap_api_call(__profile_trace, "profile_trace") +ProfileTraceWrapper = __wrap_api_call(__ProfileTraceWrapper, "ProfileTraceWrapper") +wrap_profile_trace = __wrap_api_call(__wrap_profile_trace, "wrap_profile_trace") +database_trace = __wrap_api_call(__database_trace, "database_trace") +DatabaseTrace = __wrap_api_call(__DatabaseTrace, "DatabaseTrace") +DatabaseTraceWrapper = __wrap_api_call(__DatabaseTraceWrapper, "DatabaseTraceWrapper") +wrap_database_trace = __wrap_api_call(__wrap_database_trace, "wrap_database_trace") +register_database_client = __wrap_api_call(__register_database_client, "register_database_client") +datastore_trace = __wrap_api_call(__datastore_trace, "datastore_trace") +DatastoreTrace = __wrap_api_call(__DatastoreTrace, "DatastoreTrace") +DatastoreTraceWrapper = __wrap_api_call(__DatastoreTraceWrapper, "DatastoreTraceWrapper") +wrap_datastore_trace = __wrap_api_call(__wrap_datastore_trace, "wrap_datastore_trace") +external_trace = __wrap_api_call(__external_trace, "external_trace") +ExternalTrace = __wrap_api_call(__ExternalTrace, "ExternalTrace") +ExternalTraceWrapper = __wrap_api_call(__ExternalTraceWrapper, "ExternalTraceWrapper") +wrap_external_trace = __wrap_api_call(__wrap_external_trace, "wrap_external_trace") +error_trace = __wrap_api_call(__error_trace, "error_trace") +ErrorTrace = __wrap_api_call(__ErrorTrace, "ErrorTrace") +ErrorTraceWrapper = __wrap_api_call(__ErrorTraceWrapper, "ErrorTraceWrapper") +wrap_error_trace = __wrap_api_call(__wrap_error_trace, "wrap_error_trace") +message_trace = __wrap_api_call(__message_trace, "message_trace") +MessageTrace = __wrap_api_call(__MessageTrace, "MessageTrace") +MessageTraceWrapper = __wrap_api_call(__MessageTraceWrapper, "MessageTraceWrapper") +wrap_message_trace = __wrap_api_call(__wrap_message_trace, "wrap_message_trace") +message_transaction = __wrap_api_call(__message_transaction, "message_trace") +MessageTransaction = __wrap_api_call(__MessageTransaction, "MessageTransaction") +MessageTransactionWrapper = __wrap_api_call(__MessageTransactionWrapper, "MessageTransactionWrapper") +wrap_message_transaction = __wrap_api_call(__wrap_message_transaction, "wrap_message_transaction") +callable_name = __wrap_api_call(__callable_name, "callable_name") +ObjectProxy = __wrap_api_call(__ObjectProxy, "ObjectProxy") +wrap_object = __wrap_api_call(__wrap_object, "wrap_object") +wrap_object_attribute = __wrap_api_call(__wrap_object_attribute, "wrap_object_attribute") +resolve_path = __wrap_api_call(__resolve_path, "resolve_path") +transient_function_wrapper = __wrap_api_call(__transient_function_wrapper, "transient_function_wrapper") +FunctionWrapper = __wrap_api_call(__FunctionWrapper, "FunctionWrapper") +function_wrapper = __wrap_api_call(__function_wrapper, "function_wrapper") +wrap_function_wrapper = __wrap_api_call(__wrap_function_wrapper, "wrap_function_wrapper") +patch_function_wrapper = __wrap_api_call(__patch_function_wrapper, "patch_function_wrapper") +ObjectWrapper = __wrap_api_call(__ObjectWrapper, "ObjectWrapper") +pre_function = __wrap_api_call(__pre_function, "pre_function") +PreFunctionWrapper = __wrap_api_call(__PreFunctionWrapper, "PreFunctionWrapper") +wrap_pre_function = __wrap_api_call(__wrap_pre_function, "wrap_pre_function") +post_function = __wrap_api_call(__post_function, "post_function") +PostFunctionWrapper = __wrap_api_call(__PostFunctionWrapper, "PostFunctionWrapper") +wrap_post_function = __wrap_api_call(__wrap_post_function, "wrap_post_function") +in_function = __wrap_api_call(__in_function, "in_function") +InFunctionWrapper = __wrap_api_call(__InFunctionWrapper, "InFunctionWrapper") +wrap_in_function = __wrap_api_call(__wrap_in_function, "wrap_in_function") +out_function = __wrap_api_call(__out_function, "out_function") +OutFunctionWrapper = __wrap_api_call(__OutFunctionWrapper, "OutFunctionWrapper") +wrap_out_function = __wrap_api_call(__wrap_out_function, "wrap_out_function") +insert_html_snippet = __wrap_api_call(__insert_html_snippet, "insert_html_snippet") +verify_body_exists = __wrap_api_call(__verify_body_exists, "verify_body_exists") diff --git a/newrelic/api/transaction.py b/newrelic/api/transaction.py index f486989b4b..cbe61e0438 100644 --- a/newrelic/api/transaction.py +++ b/newrelic/api/transaction.py @@ -25,13 +25,12 @@ import weakref from collections import OrderedDict -from newrelic.api.application import application_instance import newrelic.core.database_node import newrelic.core.error_node -from newrelic.core.log_event_node import LogEventNode import newrelic.core.root_node import newrelic.core.transaction_node import newrelic.packages.six as six +from newrelic.api.application import application_instance from newrelic.api.time_trace import TimeTrace, get_linking_metadata from newrelic.common.encoding_utils import ( DistributedTracePayload, @@ -63,6 +62,7 @@ ) from newrelic.core.config import DEFAULT_RESERVOIR_SIZE, LOG_EVENT_RESERVOIR_SIZE from newrelic.core.custom_event import create_custom_event +from newrelic.core.log_event_node import LogEventNode from newrelic.core.stack_trace import exception_stack from newrelic.core.stats_engine import CustomMetrics, SampledDataSet from newrelic.core.thread_utilization import utilization_tracker @@ -324,8 +324,12 @@ def __init__(self, application, enabled=None, source=None): self.enabled = True if self._settings: - self._custom_events = SampledDataSet(capacity=self._settings.event_harvest_config.harvest_limits.custom_event_data) - self._log_events = SampledDataSet(capacity=self._settings.event_harvest_config.harvest_limits.log_event_data) + self._custom_events = SampledDataSet( + capacity=self._settings.event_harvest_config.harvest_limits.custom_event_data + ) + self._log_events = SampledDataSet( + capacity=self._settings.event_harvest_config.harvest_limits.log_event_data + ) else: self._custom_events = SampledDataSet(capacity=DEFAULT_RESERVOIR_SIZE) self._log_events = SampledDataSet(capacity=LOG_EVENT_RESERVOIR_SIZE) @@ -1473,31 +1477,35 @@ def set_transaction_name(self, name, group=None, priority=None): self._group = group self._name = name - def record_log_event(self, message, level=None, timestamp=None, priority=None): settings = self.settings - if not (settings and settings.application_logging and settings.application_logging.enabled and settings.application_logging.forwarding and settings.application_logging.forwarding.enabled): + if not ( + settings + and settings.application_logging + and settings.application_logging.enabled + and settings.application_logging.forwarding + and settings.application_logging.forwarding.enabled + ): return - + timestamp = timestamp if timestamp is not None else time.time() level = str(level) if level is not None else "UNKNOWN" - + if not message or message.isspace(): _logger.debug("record_log_event called where message was missing. No log event will be sent.") return - + message = truncate(message, MAX_LOG_MESSAGE_LENGTH) event = LogEventNode( timestamp=timestamp, level=level, message=message, - attributes=get_linking_metadata(), + attributes=get_linking_metadata(), ) self._log_events.add(event, priority=priority) - def record_exception(self, exc=None, value=None, tb=None, params=None, ignore_errors=None): # Deprecation Warning warnings.warn( @@ -1633,12 +1641,12 @@ def stop_recording(self): self._cpu_user_time_end = os.times()[0] - def add_custom_parameter(self, name, value): + def add_custom_attribute(self, name, value): if not self._settings: return False if self._settings.high_security: - _logger.debug("Cannot add custom parameter in High Security Mode.") + _logger.debug("Cannot add custom attribute in High Security Mode.") return False if len(self._custom_params) >= MAX_NUM_USER_ATTRIBUTES: @@ -1653,15 +1661,31 @@ def add_custom_parameter(self, name, value): self._custom_params[key] = val return True - def add_custom_parameters(self, items): + def add_custom_attributes(self, items): result = True # items is a list of (name, value) tuples. for name, value in items: - result &= self.add_custom_parameter(name, value) + result &= self.add_custom_attribute(name, value) return result + def add_custom_parameter(self, name, value): + # Deprecation warning + warnings.warn( + ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + DeprecationWarning, + ) + return self.add_custom_attribute(name, value) + + def add_custom_parameters(self, items): + # Deprecation warning + warnings.warn( + ("The add_custom_parameters API has been deprecated. " "Please use the add_custom_attributes API."), + DeprecationWarning, + ) + return self.add_custom_attributes(items) + def add_framework_info(self, name, version=None): if name: self._frameworks.add((name, version)) @@ -1734,22 +1758,40 @@ def capture_request_params(flag=True): transaction.capture_params = flag -def add_custom_parameter(key, value): +def add_custom_attribute(key, value): transaction = current_transaction() if transaction: - return transaction.add_custom_parameter(key, value) + return transaction.add_custom_attribute(key, value) else: return False -def add_custom_parameters(items): +def add_custom_attributes(items): transaction = current_transaction() if transaction: - return transaction.add_custom_parameters(items) + return transaction.add_custom_attributes(items) else: return False +def add_custom_parameter(key, value): + # Deprecation warning + warnings.warn( + ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + DeprecationWarning, + ) + return add_custom_attribute(key, value) + + +def add_custom_parameters(items): + # Deprecation warning + warnings.warn( + ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + DeprecationWarning, + ) + return add_custom_attributes(items) + + def add_framework_info(name, version=None): transaction = current_transaction() if transaction: @@ -1869,7 +1911,9 @@ def record_log_event(message, level=None, timestamp=None, application=None, prio "record_log_event has been called but no transaction or application was running. As a result, " "the following event has not been recorded. message: %r level: %r timestamp %r. To correct " "this problem, supply an application object as a parameter to this record_log_event call.", - message, level, timestamp, + message, + level, + timestamp, ) elif application.enabled: application.record_log_event(message, level, timestamp, priority=priority) diff --git a/tests/agent_features/test_asgi_browser.py b/tests/agent_features/test_asgi_browser.py index c2c7ce7157..9cce245e41 100644 --- a/tests/agent_features/test_asgi_browser.py +++ b/tests/agent_features/test_asgi_browser.py @@ -12,48 +12,55 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys import json import pytest import six - -from testing_support.fixtures import (override_application_settings, - validate_transaction_errors, validate_custom_parameters) +from bs4 import BeautifulSoup from testing_support.asgi_testing import AsgiTest +from testing_support.fixtures import ( + override_application_settings, + validate_custom_parameters, + validate_transaction_errors, +) from newrelic.api.application import application_settings -from newrelic.api.transaction import (get_browser_timing_header, - get_browser_timing_footer, add_custom_parameter, - disable_browser_autorum) from newrelic.api.asgi_application import asgi_application +from newrelic.api.transaction import ( + add_custom_attribute, + disable_browser_autorum, + get_browser_timing_footer, + get_browser_timing_header, +) from newrelic.common.encoding_utils import deobfuscate -from bs4 import BeautifulSoup +_runtime_error_name = RuntimeError.__module__ + ":" + RuntimeError.__name__ -_runtime_error_name = (RuntimeError.__module__ + ':' + RuntimeError.__name__) @asgi_application() async def target_asgi_application_manual_rum(scope, receive, send): - text = '%s

RESPONSE

%s' + text = "%s

RESPONSE

%s" - output = (text % (get_browser_timing_header(), - get_browser_timing_footer())).encode('UTF-8') + output = (text % (get_browser_timing_header(), get_browser_timing_footer())).encode("UTF-8") - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode('utf-8'))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) + target_application_manual_rum = AsgiTest(target_asgi_application_manual_rum) _test_footer_attributes = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "js_agent_loader": "", } + @override_application_settings(_test_footer_attributes) def test_footer_attributes(): settings = application_settings() @@ -67,589 +74,632 @@ def test_footer_attributes(): assert settings.beacon assert settings.error_beacon - token = '0123456789ABCDEF' - headers = { 'Cookie': 'NRAGENT=tk=%s' % token } + token = "0123456789ABCDEF" # nosec + headers = {"Cookie": "NRAGENT=tk=%s" % token} - response = target_application_manual_rum.get('/', headers=headers) + response = target_application_manual_rum.get("/", headers=headers) - html = BeautifulSoup(response.body, 'html.parser') + html = BeautifulSoup(response.body, "html.parser") header = html.html.head.script.string content = html.html.body.p.string footer = html.html.body.script.string # Validate actual body content. - assert content == 'RESPONSE' + assert content == "RESPONSE" # Validate the insertion of RUM header. - assert header.find('NREUM HEADER') != -1 + assert header.find("NREUM HEADER") != -1 # Now validate the various fields of the footer. The fields are # held by a JSON dictionary. - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) - assert data['licenseKey'] == settings.browser_key - assert data['applicationID'] == settings.application_id + assert data["licenseKey"] == settings.browser_key + assert data["applicationID"] == settings.application_id - assert data['agent'] == settings.js_agent_file - assert data['beacon'] == settings.beacon - assert data['errorBeacon'] == settings.error_beacon + assert data["agent"] == settings.js_agent_file + assert data["beacon"] == settings.beacon + assert data["errorBeacon"] == settings.error_beacon - assert data['applicationTime'] >= 0 - assert data['queueTime'] >= 0 + assert data["applicationTime"] >= 0 + assert data["queueTime"] >= 0 obfuscation_key = settings.license_key[:13] - assert type(data['transactionName']) == type(u'') + assert isinstance(data["transactionName"], str) + # assert type(data["transactionName"]) == type("") - txn_name = deobfuscate(data['transactionName'], obfuscation_key) + txn_name = deobfuscate(data["transactionName"], obfuscation_key) - assert txn_name == u'WebTransaction/Uri/' + assert txn_name == "WebTransaction/Uri/" + + assert "atts" not in data - assert 'atts' not in data _test_rum_ssl_for_http_is_none = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'browser_monitoring.ssl_for_http': None, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "browser_monitoring.ssl_for_http": None, + "js_agent_loader": "", } + @override_application_settings(_test_rum_ssl_for_http_is_none) def test_ssl_for_http_is_none(): settings = application_settings() assert settings.browser_monitoring.ssl_for_http is None - response = target_application_manual_rum.get('/') - html = BeautifulSoup(response.body, 'html.parser') + response = target_application_manual_rum.get("/") + html = BeautifulSoup(response.body, "html.parser") footer = html.html.body.script.string - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) + + assert "sslForHttp" not in data - assert 'sslForHttp' not in data _test_rum_ssl_for_http_is_true = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'browser_monitoring.ssl_for_http': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "browser_monitoring.ssl_for_http": True, + "js_agent_loader": "", } + @override_application_settings(_test_rum_ssl_for_http_is_true) def test_ssl_for_http_is_true(): settings = application_settings() assert settings.browser_monitoring.ssl_for_http is True - response = target_application_manual_rum.get('/') - html = BeautifulSoup(response.body, 'html.parser') + response = target_application_manual_rum.get("/") + html = BeautifulSoup(response.body, "html.parser") footer = html.html.body.script.string - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) + + assert data["sslForHttp"] is True - assert data['sslForHttp'] is True _test_rum_ssl_for_http_is_false = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'browser_monitoring.ssl_for_http': False, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "browser_monitoring.ssl_for_http": False, + "js_agent_loader": "", } + @override_application_settings(_test_rum_ssl_for_http_is_false) def test_ssl_for_http_is_false(): settings = application_settings() assert settings.browser_monitoring.ssl_for_http is False - response = target_application_manual_rum.get('/') - html = BeautifulSoup(response.body, 'html.parser') + response = target_application_manual_rum.get("/") + html = BeautifulSoup(response.body, "html.parser") footer = html.html.body.script.string - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) + + assert data["sslForHttp"] is False - assert data['sslForHttp'] is False @asgi_application() async def target_asgi_application_yield_single_no_head(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode('utf-8'))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_yield_single_no_head = AsgiTest( - target_asgi_application_yield_single_no_head) + +target_application_yield_single_no_head = AsgiTest(target_asgi_application_yield_single_no_head) _test_html_insertion_yield_single_no_head_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_single_no_head_settings) def test_html_insertion_yield_single_no_head(): - response = target_application_yield_single_no_head.get('/') + response = target_application_yield_single_no_head.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' in response.body - assert b'NREUM.info' in response.body + assert b"NREUM HEADER" in response.body + assert b"NREUM.info" in response.body + @asgi_application() async def target_asgi_application_yield_multi_no_head(scope, receive, send): - output = [ b'', b'

RESPONSE

' ] + output = [b"", b"

RESPONSE

"] - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(b''.join(output))).encode('utf-8'))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(b"".join(output))).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) for data in output: more_body = data is not output[-1] await send({"type": "http.response.body", "body": data, "more_body": more_body}) -target_application_yield_multi_no_head = AsgiTest( - target_asgi_application_yield_multi_no_head) + +target_application_yield_multi_no_head = AsgiTest(target_asgi_application_yield_multi_no_head) _test_html_insertion_yield_multi_no_head_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_multi_no_head_settings) def test_html_insertion_yield_multi_no_head(): - response = target_application_yield_multi_no_head.get('/') + response = target_application_yield_multi_no_head.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' in response.body - assert b'NREUM.info' in response.body + assert b"NREUM HEADER" in response.body + assert b"NREUM.info" in response.body + @asgi_application() async def target_asgi_application_unnamed_attachment_header(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode('utf-8')), - (b'content-disposition', b'attachment')] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + (b"content-disposition", b"attachment"), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_unnamed_attachment_header = AsgiTest( - target_asgi_application_unnamed_attachment_header) + +target_application_unnamed_attachment_header = AsgiTest(target_asgi_application_unnamed_attachment_header) _test_html_insertion_unnamed_attachment_header_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_unnamed_attachment_header_settings) + +@override_application_settings(_test_html_insertion_unnamed_attachment_header_settings) def test_html_insertion_unnamed_attachment_header(): - response = target_application_unnamed_attachment_header.get('/') + response = target_application_unnamed_attachment_header.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers - assert 'content-disposition' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers + assert "content-disposition" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body + @asgi_application() async def target_asgi_application_named_attachment_header(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode('utf-8')), - (b'content-disposition', b'Attachment; filename="X"')] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + (b"content-disposition", b'Attachment; filename="X"'), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_named_attachment_header = AsgiTest( - target_asgi_application_named_attachment_header) + +target_application_named_attachment_header = AsgiTest(target_asgi_application_named_attachment_header) _test_html_insertion_named_attachment_header_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_named_attachment_header_settings) + +@override_application_settings(_test_html_insertion_named_attachment_header_settings) def test_html_insertion_named_attachment_header(): - response = target_application_named_attachment_header.get('/') + response = target_application_named_attachment_header.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers - assert 'content-disposition' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers + assert "content-disposition" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body + @asgi_application() async def target_asgi_application_inline_attachment_header(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode('utf-8')), - (b'content-disposition', b'inline; filename="attachment"')] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + (b"content-disposition", b'inline; filename="attachment"'), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_inline_attachment_header = AsgiTest( - target_asgi_application_inline_attachment_header) + +target_application_inline_attachment_header = AsgiTest(target_asgi_application_inline_attachment_header) _test_html_insertion_inline_attachment_header_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_inline_attachment_header_settings) + +@override_application_settings(_test_html_insertion_inline_attachment_header_settings) def test_html_insertion_inline_attachment_header(): - response = target_application_inline_attachment_header.get('/') + response = target_application_inline_attachment_header.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers - assert 'content-disposition' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers + assert "content-disposition" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' in response.body - assert b'NREUM.info' in response.body + assert b"NREUM HEADER" in response.body + assert b"NREUM.info" in response.body + @asgi_application() async def target_asgi_application_empty(scope, receive, send): - status = '200 OK' + status = "200 OK" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', b'0')] + response_headers = [(b"content-type", b"text/html; charset=utf-8"), (b"content-length", b"0")] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body"}) -target_application_empty = AsgiTest( - target_asgi_application_empty) + +target_application_empty = AsgiTest(target_asgi_application_empty) _test_html_insertion_empty_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_empty_settings) + +@override_application_settings(_test_html_insertion_empty_settings) def test_html_insertion_empty(): - response = target_application_empty.get('/') + response = target_application_empty.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body assert len(response.body) == 0 + @asgi_application() async def target_asgi_application_single_empty_string(scope, receive, send): - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', b'0')] + response_headers = [(b"content-type", b"text/html; charset=utf-8"), (b"content-length", b"0")] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": b""}) -target_application_single_empty_string = AsgiTest( - target_asgi_application_single_empty_string) + +target_application_single_empty_string = AsgiTest(target_asgi_application_single_empty_string) _test_html_insertion_single_empty_string_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_single_empty_string_settings) + +@override_application_settings(_test_html_insertion_single_empty_string_settings) def test_html_insertion_single_empty_string(): - response = target_application_single_empty_string.get('/') + response = target_application_single_empty_string.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body assert len(response.body) == 0 + @asgi_application() async def target_asgi_application_multiple_empty_string(scope, receive, send): - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', b'0')] + response_headers = [(b"content-type", b"text/html; charset=utf-8"), (b"content-length", b"0")] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": b"", "more_body": True}) await send({"type": "http.response.body", "body": b""}) -target_application_multiple_empty_string = AsgiTest( - target_asgi_application_multiple_empty_string) + +target_application_multiple_empty_string = AsgiTest(target_asgi_application_multiple_empty_string) _test_html_insertion_multiple_empty_string_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_multiple_empty_string_settings) + +@override_application_settings(_test_html_insertion_multiple_empty_string_settings) def test_html_insertion_multiple_empty_string(): - response = target_application_multiple_empty_string.get('/') + response = target_application_multiple_empty_string.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body assert len(response.body) == 0 + @asgi_application() async def target_asgi_application_single_large_prelude(scope, receive, send): - output = 64*1024*b' ' + b'' + output = 64 * 1024 * b" " + b"" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_single_large_prelude = AsgiTest( - target_asgi_application_single_large_prelude) + +target_application_single_large_prelude = AsgiTest(target_asgi_application_single_large_prelude) _test_html_insertion_single_large_prelude_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_single_large_prelude_settings) + +@override_application_settings(_test_html_insertion_single_large_prelude_settings) def test_html_insertion_single_large_prelude(): - response = target_application_single_large_prelude.get('/') + response = target_application_single_large_prelude.get("/") assert response.status == 200 # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers + + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + output = [32 * 1024 * b" ", 32 * 1024 * b" ", b""] - output = [32*1024*b' ', 32*1024*b' ', b''] + assert len(response.body) == len(b"".join(output)) - assert len(response.body) == len(b''.join(output)) @asgi_application() async def target_asgi_application_multi_large_prelude(scope, receive, send): - output = [32*1024*b' ', 32*1024*b' ', b''] + output = [32 * 1024 * b" ", 32 * 1024 * b" ", b""] - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(b''.join(output))).encode("utf-8"))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(b"".join(output))).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) for data in output: more_body = data is not output[-1] await send({"type": "http.response.body", "body": data, "more_body": more_body}) -target_application_multi_large_prelude = AsgiTest( - target_asgi_application_multi_large_prelude) + +target_application_multi_large_prelude = AsgiTest(target_asgi_application_multi_large_prelude) _test_html_insertion_multi_large_prelude_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_multi_large_prelude_settings) + +@override_application_settings(_test_html_insertion_multi_large_prelude_settings) def test_html_insertion_multi_large_prelude(): - response = target_application_multi_large_prelude.get('/') + response = target_application_multi_large_prelude.get("/") assert response.status == 200 # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers + + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + output = [32 * 1024 * b" ", 32 * 1024 * b" ", b""] - output = [32*1024*b' ', 32*1024*b' ', b''] + assert len(response.body) == len(b"".join(output)) - assert len(response.body) == len(b''.join(output)) @asgi_application() async def target_asgi_application_yield_before_start(scope, receive, send): # This is not legal but we should see what happens with our middleware await send({"type": "http.response.body", "body": b"", "more_body": True}) - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_yield_before_start = AsgiTest( - target_asgi_application_yield_before_start) + +target_application_yield_before_start = AsgiTest(target_asgi_application_yield_before_start) _test_html_insertion_yield_before_start_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_before_start_settings) def test_html_insertion_yield_before_start(): # The application should complete as pass through, but an assertion error # would be raised in the AsgiTest class with pytest.raises(AssertionError): - target_application_yield_before_start.get('/') + target_application_yield_before_start.get("/") + @asgi_application() async def target_asgi_application_start_yield_start(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": b""}) await send({"type": "http.response.start", "status": 200, "headers": response_headers}) -target_application_start_yield_start = AsgiTest( - target_asgi_application_start_yield_start) + +target_application_start_yield_start = AsgiTest(target_asgi_application_start_yield_start) _test_html_insertion_start_yield_start_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_start_yield_start_settings) def test_html_insertion_start_yield_start(): # The application should complete as pass through, but an assertion error # would be raised in the AsgiTest class with pytest.raises(AssertionError): - target_application_start_yield_start.get('/') + target_application_start_yield_start.get("/") + @asgi_application() async def target_asgi_application_invalid_content_length(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', b'XXX')] + response_headers = [(b"content-type", b"text/html; charset=utf-8"), (b"content-length", b"XXX")] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_invalid_content_length = AsgiTest( - target_asgi_application_invalid_content_length) + +target_application_invalid_content_length = AsgiTest(target_asgi_application_invalid_content_length) _test_html_insertion_invalid_content_length_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_invalid_content_length_settings) def test_html_insertion_invalid_content_length(): - response = target_application_invalid_content_length.get('/') + response = target_application_invalid_content_length.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers - assert response.headers['content-length'] == 'XXX' + assert response.headers["content-length"] == "XXX" + + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body @asgi_application() async def target_asgi_application_content_encoding(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8")), - (b'content-encoding', b'identity')] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + (b"content-encoding", b"identity"), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_content_encoding = AsgiTest( - target_asgi_application_content_encoding) + +target_application_content_encoding = AsgiTest(target_asgi_application_content_encoding) _test_html_insertion_content_encoding_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_content_encoding_settings) def test_html_insertion_content_encoding(): - response = target_application_content_encoding.get('/') + response = target_application_content_encoding.get("/") assert response.status == 200 # Technically 'identity' should not be used in Content-Encoding @@ -657,181 +707,190 @@ def test_html_insertion_content_encoding(): # RUM for this test. Other option is to compress the response # and use 'gzip'. - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers + + assert response.headers["content-encoding"] == "identity" - assert response.headers['content-encoding'] == 'identity' + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body @asgi_application() async def target_asgi_application_no_content_type(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [(b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [(b"content-length", str(len(output)).encode("utf-8"))] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_no_content_type = AsgiTest( - target_asgi_application_no_content_type) + +target_application_no_content_type = AsgiTest(target_asgi_application_no_content_type) _test_html_insertion_no_content_type_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_no_content_type_settings) def test_html_insertion_no_content_type(): - response = target_application_no_content_type.get('/') + response = target_application_no_content_type.get("/") assert response.status == 200 - assert 'content-type' not in response.headers - assert 'content-length' in response.headers + assert "content-type" not in response.headers + assert "content-length" in response.headers + + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body @asgi_application() async def target_asgi_application_plain_text(scope, receive, send): - output = b'RESPONSE' + output = b"RESPONSE" - response_headers = [ - (b'content-type', b'text/plain'), - (b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [(b"content-type", b"text/plain"), (b"content-length", str(len(output)).encode("utf-8"))] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_plain_text = AsgiTest( - target_asgi_application_plain_text) + +target_application_plain_text = AsgiTest(target_asgi_application_plain_text) _test_html_insertion_plain_text_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_plain_text_settings) def test_html_insertion_plain_text(): - response = target_application_plain_text.get('/') + response = target_application_plain_text.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body @asgi_application() async def target_asgi_application_param(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" response_headers = [ - (b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] - add_custom_parameter('key', 'value') + add_custom_attribute("key", "value") await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_param = AsgiTest( - target_asgi_application_param) +target_application_param = AsgiTest(target_asgi_application_param) _test_html_insertion_param_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } @override_application_settings(_test_html_insertion_param_settings) -@validate_custom_parameters(required_params=[('key', 'value')]) +@validate_custom_parameters(required_params=[("key", "value")]) def test_html_insertion_param(): - response = target_application_param.get('/') + response = target_application_param.get("/") assert response.status == 200 - assert b'NREUM HEADER' in response.body - assert b'NREUM.info' in response.body + assert b"NREUM HEADER" in response.body + assert b"NREUM.info" in response.body + @asgi_application() async def target_asgi_application_param_on_error(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" response_headers = [ - (b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) try: - raise RuntimeError('ERROR') + raise RuntimeError("ERROR") finally: - add_custom_parameter('key', 'value') + add_custom_attribute("key", "value") -target_application_param_on_error = AsgiTest( - target_asgi_application_param_on_error) + +target_application_param_on_error = AsgiTest(target_asgi_application_param_on_error) _test_html_insertion_param_on_error_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_param_on_error_settings) @validate_transaction_errors(errors=[_runtime_error_name]) -@validate_custom_parameters(required_params=[('key', 'value')]) +@validate_custom_parameters(required_params=[("key", "value")]) def test_html_insertion_param_on_error(): try: - target_application_param_on_error.get('/') + target_application_param_on_error.get("/") except RuntimeError: pass + @asgi_application() async def target_asgi_application_disable_autorum_via_api(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" disable_browser_autorum() - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_disable_autorum_via_api = AsgiTest( - target_asgi_application_disable_autorum_via_api) + +target_application_disable_autorum_via_api = AsgiTest(target_asgi_application_disable_autorum_via_api) _test_html_insertion_disable_autorum_via_api_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_disable_autorum_via_api_settings) + +@override_application_settings(_test_html_insertion_disable_autorum_via_api_settings) def test_html_insertion_disable_autorum_via_api(): - response = target_application_disable_autorum_via_api.get('/') + response = target_application_disable_autorum_via_api.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body + @asgi_application() async def target_asgi_application_manual_rum_insertion(scope, receive, send): - output = b'

RESPONSE

' + output = b"

RESPONSE

" header = get_browser_timing_header() footer = get_browser_timing_footer() @@ -839,36 +898,38 @@ async def target_asgi_application_manual_rum_insertion(scope, receive, send): header = get_browser_timing_header() footer = get_browser_timing_footer() - assert header == '' - assert footer == '' + assert header == "" + assert footer == "" - response_headers = [(b'content-type', b'text/html; charset=utf-8'), - (b'content-length', str(len(output)).encode("utf-8"))] + response_headers = [ + (b"content-type", b"text/html; charset=utf-8"), + (b"content-length", str(len(output)).encode("utf-8")), + ] await send({"type": "http.response.start", "status": 200, "headers": response_headers}) await send({"type": "http.response.body", "body": output}) -target_application_manual_rum_insertion = AsgiTest( - target_asgi_application_manual_rum_insertion) + +target_application_manual_rum_insertion = AsgiTest(target_asgi_application_manual_rum_insertion) _test_html_insertion_manual_rum_insertion_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_manual_rum_insertion_settings) + +@override_application_settings(_test_html_insertion_manual_rum_insertion_settings) def test_html_insertion_manual_rum_insertion(): - response = target_application_manual_rum_insertion.get('/') + response = target_application_manual_rum_insertion.get("/") assert response.status == 200 - assert 'content-type' in response.headers - assert 'content-length' in response.headers + assert "content-type" in response.headers + assert "content-length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert b'NREUM HEADER' not in response.body - assert b'NREUM.info' not in response.body + assert b"NREUM HEADER" not in response.body + assert b"NREUM.info" not in response.body diff --git a/tests/agent_features/test_attribute.py b/tests/agent_features/test_attribute.py index edfffae2f6..9237b2f680 100644 --- a/tests/agent_features/test_attribute.py +++ b/tests/agent_features/test_attribute.py @@ -13,24 +13,31 @@ # limitations under the License. import sys + import pytest import webtest +from testing_support.fixtures import ( + override_application_settings, + validate_agent_attribute_types, + validate_attributes, + validate_attributes_complete, + validate_custom_parameters, +) +from testing_support.sample_applications import fully_featured_app from newrelic.api.background_task import background_task -from newrelic.api.transaction import (add_custom_parameter, - add_custom_parameters) +from newrelic.api.transaction import add_custom_attribute, add_custom_attributes from newrelic.api.wsgi_application import wsgi_application -from newrelic.core.attribute import (truncate, sanitize, Attribute, - CastingFailureException, MAX_64_BIT_INT, _DESTINATIONS_WITH_EVENTS) - +from newrelic.core.attribute import ( + _DESTINATIONS_WITH_EVENTS, + MAX_64_BIT_INT, + Attribute, + CastingFailureException, + sanitize, + truncate, +) from newrelic.packages import six -from testing_support.fixtures import (override_application_settings, - validate_attributes, validate_attributes_complete, - validate_custom_parameters, validate_agent_attribute_types) -from testing_support.sample_applications import fully_featured_app - - # Python 3 lacks longs if sys.version_info >= (3, 0): @@ -43,333 +50,325 @@ @wsgi_application() def target_wsgi_application(environ, start_response): - status = '200 OK' - output = b'Hello World!' + status = "200 OK" + output = b"Hello World!" - path = environ.get('PATH_INFO') - if path == '/user_attribute': - add_custom_parameter('test_key', 'test_value') + path = environ.get("PATH_INFO") + if path == "/user_attribute": + add_custom_attribute("test_key", "test_value") - response_headers = [('Content-Type', 'text/plain; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/plain; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) return [output] -_required_intrinsics = ['trip_id', 'totalTime'] +_required_intrinsics = ["trip_id", "totalTime"] _forgone_intrinsics = [] -@validate_attributes('intrinsic', _required_intrinsics, _forgone_intrinsics) +@validate_attributes("intrinsic", _required_intrinsics, _forgone_intrinsics) def test_intrinsics(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/') - assert response.body == b'Hello World!' - - -_required_agent = ['request.method', 'wsgi.output.seconds', 'response.status', - 'request.headers.host', 'request.headers.accept', 'request.uri', - 'response.headers.contentType', 'response.headers.contentLength'] + response = target_application.get("/") + assert response.body == b"Hello World!" + + +_required_agent = [ + "request.method", + "wsgi.output.seconds", + "response.status", + "request.headers.host", + "request.headers.accept", + "request.uri", + "response.headers.contentType", + "response.headers.contentLength", +] if ThreadUtilization: - _required_agent.append('thread.concurrency') + _required_agent.append("thread.concurrency") _forgone_agent = [] -@validate_attributes('agent', _required_agent, _forgone_agent) +@validate_attributes("agent", _required_agent, _forgone_agent) def test_agent(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/', - extra_environ={'HTTP_ACCEPT': '*/*'}) - assert response.body == b'Hello World!' + response = target_application.get("/", extra_environ={"HTTP_ACCEPT": "*/*"}) + assert response.body == b"Hello World!" _required_user = [] -_forgone_user = ['test_key'] +_forgone_user = ["test_key"] -@validate_attributes('user', _required_user, _forgone_user) +@validate_attributes("user", _required_user, _forgone_user) def test_user_default(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/') - assert response.body == b'Hello World!' + response = target_application.get("/") + assert response.body == b"Hello World!" -_required_user = ['test_key'] +_required_user = ["test_key"] _forgone_user = [] -@validate_attributes('user', _required_user, _forgone_user) +@validate_attributes("user", _required_user, _forgone_user) def test_user_add_attribute(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/user_attribute') - assert response.body == b'Hello World!' + response = target_application.get("/user_attribute") + assert response.body == b"Hello World!" -_settings_legacy_false = {'capture_params': False} +_settings_legacy_false = {"capture_params": False} _required_request_legacy_false = [] -_forgone_request_legacy_false = ['request.parameters.foo'] +_forgone_request_legacy_false = ["request.parameters.foo"] @override_application_settings(_settings_legacy_false) -@validate_attributes('agent', _required_request_legacy_false, - _forgone_request_legacy_false) +@validate_attributes("agent", _required_request_legacy_false, _forgone_request_legacy_false) def test_capture_request_params_legacy_false(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/?foo=bar') - assert response.body == b'Hello World!' + response = target_application.get("/?foo=bar") + assert response.body == b"Hello World!" -_settings_legacy_true = {'capture_params': True} -_required_request_legacy_true = ['request.parameters.foo'] +_settings_legacy_true = {"capture_params": True} +_required_request_legacy_true = ["request.parameters.foo"] _forgone_request_legacy_true = [] @override_application_settings(_settings_legacy_true) -@validate_attributes('agent', _required_request_legacy_true, - _forgone_request_legacy_true) +@validate_attributes("agent", _required_request_legacy_true, _forgone_request_legacy_true) def test_capture_request_params_legacy_true(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/?foo=bar') - assert response.body == b'Hello World!' + response = target_application.get("/?foo=bar") + assert response.body == b"Hello World!" -_required_request_default = ['request.parameters.foo'] +_required_request_default = ["request.parameters.foo"] _forgone_request_default = [] -@validate_attributes('agent', _required_request_default, - _forgone_request_default) +@validate_attributes("agent", _required_request_default, _forgone_request_default) def test_capture_request_params_default(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/?foo=bar') - assert response.body == b'Hello World!' + response = target_application.get("/?foo=bar") + assert response.body == b"Hello World!" _required_display_host_default = [] -_forgone_display_host_default = ['host.displayName'] +_forgone_display_host_default = ["host.displayName"] -@validate_attributes('agent', _required_display_host_default, - _forgone_display_host_default) +@validate_attributes("agent", _required_display_host_default, _forgone_display_host_default) def test_display_host_default(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/') - assert response.body == b'Hello World!' + response = target_application.get("/") + assert response.body == b"Hello World!" -_settings_display_host_custom = {'process_host.display_name': 'CUSTOM NAME'} +_settings_display_host_custom = {"process_host.display_name": "CUSTOM NAME"} -_display_name_attribute = Attribute(name='host.displayName', - value='CUSTOM NAME', destinations=_DESTINATIONS_WITH_EVENTS) +_display_name_attribute = Attribute( + name="host.displayName", value="CUSTOM NAME", destinations=_DESTINATIONS_WITH_EVENTS +) _required_display_host_custom = [_display_name_attribute] _forgone_display_host_custom = [] @override_application_settings(_settings_display_host_custom) -@validate_attributes_complete('agent', _required_display_host_custom, - _forgone_display_host_custom) +@validate_attributes_complete("agent", _required_display_host_custom, _forgone_display_host_custom) def test_display_host_custom(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/') - assert response.body == b'Hello World!' + response = target_application.get("/") + assert response.body == b"Hello World!" # Tests for truncate() + def test_truncate_string(): - s = 'blahblah' + s = "blahblah" result = truncate(s, maxsize=4) assert isinstance(result, six.string_types) - assert result == 'blah' + assert result == "blah" def test_truncate_bytes(): - b = b'foobar' + b = b"foobar" result = truncate(b, maxsize=3) assert isinstance(result, six.binary_type) - assert result == b'foo' + assert result == b"foo" def test_truncate_unicode_snowman(): # '\u2603' is 'SNOWMAN' - u = u'snow\u2603' - assert u.encode('utf-8') == b'snow\xe2\x98\x83' + u = "snow\u2603" + assert u.encode("utf-8") == b"snow\xe2\x98\x83" result = truncate(u, maxsize=5) assert isinstance(result, six.text_type) - assert result == u'snow' + assert result == "snow" def test_truncate_combining_characters(): # '\u0308' is 'COMBINING DIAERESIS' (AKA 'umlaut') - u = u'Zoe\u0308' - assert u.encode('utf-8') == b'Zoe\xcc\x88' + u = "Zoe\u0308" + assert u.encode("utf-8") == b"Zoe\xcc\x88" # truncate will chop off 'COMBINING DIAERESIS', which leaves # 'LATIN SMALL LETTER E' by itself. result = truncate(u, maxsize=3) assert isinstance(result, six.text_type) - assert result == u'Zoe' + assert result == "Zoe" def test_truncate_empty_string(): - s = '' + s = "" result = truncate(s, maxsize=4) assert isinstance(result, six.string_types) - assert result == '' + assert result == "" def test_truncate_empty_bytes(): - b = b'' + b = b"" result = truncate(b, maxsize=3) assert isinstance(result, six.binary_type) - assert result == b'' + assert result == b"" def test_truncate_empty_unicode(): - u = u'' + u = "" result = truncate(u, maxsize=5) assert isinstance(result, six.text_type) - assert result == u'' + assert result == "" # Tests for limits on user attributes -TOO_LONG = '*' * 256 -TRUNCATED = '*' * 255 +TOO_LONG = "*" * 256 +TRUNCATED = "*" * 255 -_required_custom_params = [('key', 'value')] +_required_custom_params = [("key", "value")] _forgone_custom_params = [] @validate_custom_parameters(_required_custom_params, _forgone_custom_params) @background_task() def test_custom_param_ok(): - result = add_custom_parameter('key', 'value') + result = add_custom_attribute("key", "value") assert result @validate_custom_parameters(_required_custom_params, _forgone_custom_params) @background_task() def test_custom_params_ok(): - result = add_custom_parameters([('key', 'value')]) + result = add_custom_attributes([("key", "value")]) assert result _required_custom_params_long_key = [] -_forgone_custom_params_long_key = [(TOO_LONG, 'value')] +_forgone_custom_params_long_key = [(TOO_LONG, "value")] -@validate_custom_parameters(_required_custom_params_long_key, - _forgone_custom_params_long_key) +@validate_custom_parameters(_required_custom_params_long_key, _forgone_custom_params_long_key) @background_task() def test_custom_param_key_too_long(): - result = add_custom_parameter(TOO_LONG, 'value') + result = add_custom_attribute(TOO_LONG, "value") assert not result -@validate_custom_parameters(_required_custom_params_long_key, - _forgone_custom_params_long_key) +@validate_custom_parameters(_required_custom_params_long_key, _forgone_custom_params_long_key) @background_task() def test_custom_params_key_too_long(): - result = add_custom_parameters([(TOO_LONG, 'value')]) + result = add_custom_attributes([(TOO_LONG, "value")]) assert not result -_required_custom_params_long_value = [('key', TRUNCATED)] +_required_custom_params_long_value = [("key", TRUNCATED)] _forgone_custom_params_long_value = [] -@validate_custom_parameters(_required_custom_params_long_value, - _forgone_custom_params_long_value) +@validate_custom_parameters(_required_custom_params_long_value, _forgone_custom_params_long_value) @background_task() def test_custom_param_value_too_long(): - result = add_custom_parameter('key', TOO_LONG) + result = add_custom_attribute("key", TOO_LONG) assert result -@validate_custom_parameters(_required_custom_params_long_value, - _forgone_custom_params_long_value) +@validate_custom_parameters(_required_custom_params_long_value, _forgone_custom_params_long_value) @background_task() def test_custom_params_value_too_long(): - result = add_custom_parameters([('key', TOO_LONG)]) + result = add_custom_attributes([("key", TOO_LONG)]) assert result -_required_custom_params_too_many = [('key-127', 'value')] -_forgone_custom_params_too_many = [('key-128', 'value')] +_required_custom_params_too_many = [("key-127", "value")] +_forgone_custom_params_too_many = [("key-128", "value")] -@validate_custom_parameters(_required_custom_params_too_many, - _forgone_custom_params_too_many) +@validate_custom_parameters(_required_custom_params_too_many, _forgone_custom_params_too_many) @background_task() def test_custom_param_too_many(): for i in range(129): - result = add_custom_parameter('key-%02d' % i, 'value') + result = add_custom_attribute("key-%02d" % i, "value") if i < 128: assert result else: - assert not result # Last one fails + assert not result # Last one fails -@validate_custom_parameters(_required_custom_params_too_many, - _forgone_custom_params_too_many) +@validate_custom_parameters(_required_custom_params_too_many, _forgone_custom_params_too_many) @background_task() def test_custom_params_too_many(): - item_list = [('key-%02d' % i, 'value') for i in range(129)] - result = add_custom_parameters(item_list) + item_list = [("key-%02d" % i, "value") for i in range(129)] + result = add_custom_attributes(item_list) assert not result _required_custom_params_name_not_string = [] -_forgone_custom_params_name_not_string = [(1, 'value')] +_forgone_custom_params_name_not_string = [(1, "value")] -@validate_custom_parameters(_required_custom_params_name_not_string, - _forgone_custom_params_name_not_string) +@validate_custom_parameters(_required_custom_params_name_not_string, _forgone_custom_params_name_not_string) @background_task() def test_custom_param_name_not_string(): - result = add_custom_parameter(1, 'value') + result = add_custom_attribute(1, "value") assert not result -@validate_custom_parameters(_required_custom_params_name_not_string, - _forgone_custom_params_name_not_string) +@validate_custom_parameters(_required_custom_params_name_not_string, _forgone_custom_params_name_not_string) @background_task() def test_custom_params_name_not_string(): - result = add_custom_parameters([(1, 'value')]) + result = add_custom_attributes([(1, "value")]) assert not result TOO_BIG = MAX_64_BIT_INT + 1 _required_custom_params_int_too_big = [] -_forgone_custom_params_int_too_big = [('key', TOO_BIG)] +_forgone_custom_params_int_too_big = [("key", TOO_BIG)] -@validate_custom_parameters(_required_custom_params_int_too_big, - _forgone_custom_params_int_too_big) +@validate_custom_parameters(_required_custom_params_int_too_big, _forgone_custom_params_int_too_big) @background_task() def test_custom_param_int_too_big(): - result = add_custom_parameter('key', TOO_BIG) + result = add_custom_attribute("key", TOO_BIG) assert not result -@validate_custom_parameters(_required_custom_params_int_too_big, - _forgone_custom_params_int_too_big) +@validate_custom_parameters(_required_custom_params_int_too_big, _forgone_custom_params_int_too_big) @background_task() def test_custom_params_int_too_big(): - result = add_custom_parameters([('key', TOO_BIG)]) + result = add_custom_attributes([("key", TOO_BIG)]) assert not result -OK_KEY = '*' * (255 - len('request.parameters.')) -OK_REQUEST_PARAM = 'request.parameters.' + OK_KEY -TOO_LONG_KEY = '*' * (256 - len('request.parameters.')) -TOO_LONG_REQUEST_PARAM = 'request.parameters.' + TOO_LONG_KEY +OK_KEY = "*" * (255 - len("request.parameters.")) +OK_REQUEST_PARAM = "request.parameters." + OK_KEY +TOO_LONG_KEY = "*" * (256 - len("request.parameters.")) +TOO_LONG_REQUEST_PARAM = "request.parameters." + TOO_LONG_KEY assert len(OK_REQUEST_PARAM) == 255 assert len(TOO_LONG_REQUEST_PARAM) == 256 @@ -378,36 +377,33 @@ def test_custom_params_int_too_big(): _forgone_request_key_ok = [] -@validate_attributes('agent', _required_request_key_ok, - _forgone_request_key_ok) +@validate_attributes("agent", _required_request_key_ok, _forgone_request_key_ok) def test_capture_request_params_key_ok(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/?%s=bar' % OK_KEY) - assert response.body == b'Hello World!' + response = target_application.get("/?%s=bar" % OK_KEY) + assert response.body == b"Hello World!" _required_request_key_too_long = [] _forgone_request_key_too_long = [TOO_LONG_REQUEST_PARAM] -@validate_attributes('agent', _required_request_key_too_long, - _forgone_request_key_too_long) +@validate_attributes("agent", _required_request_key_too_long, _forgone_request_key_too_long) def test_capture_request_params_key_too_long(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/?%s=bar' % TOO_LONG_KEY) - assert response.body == b'Hello World!' + response = target_application.get("/?%s=bar" % TOO_LONG_KEY) + assert response.body == b"Hello World!" -_required_request_value_too_long = ['request.parameters.foo'] +_required_request_value_too_long = ["request.parameters.foo"] _forgone_request_value_too_long = [] -@validate_attributes('agent', _required_request_value_too_long, - _forgone_request_value_too_long) +@validate_attributes("agent", _required_request_value_too_long, _forgone_request_value_too_long) def test_capture_request_params_value_too_long(): target_application = webtest.TestApp(target_wsgi_application) - response = target_application.get('/?foo=%s' % TOO_LONG) - assert response.body == b'Hello World!' + response = target_application.get("/?foo=%s" % TOO_LONG) + assert response.body == b"Hello World!" # Test attribute types are according to Agent-Attributes spec. @@ -416,41 +412,48 @@ def test_capture_request_params_value_too_long(): # Types are only defined in the spec for agent attributes, not intrinsics. -agent_attributes = {'request.headers.accept': six.string_types, - 'request.headers.contentLength': int, - 'request.headers.contentType': six.string_types, - 'request.headers.host': six.string_types, - 'request.headers.referer': six.string_types, - 'request.headers.userAgent': six.string_types, - 'request.method': six.string_types, - 'request.parameters.test': six.string_types, - 'response.headers.contentLength': int, - 'response.headers.contentType': six.string_types, - 'response.status': six.string_types} +agent_attributes = { + "request.headers.accept": six.string_types, + "request.headers.contentLength": int, + "request.headers.contentType": six.string_types, + "request.headers.host": six.string_types, + "request.headers.referer": six.string_types, + "request.headers.userAgent": six.string_types, + "request.method": six.string_types, + "request.parameters.test": six.string_types, + "response.headers.contentLength": int, + "response.headers.contentType": six.string_types, + "response.status": six.string_types, +} @validate_agent_attribute_types(agent_attributes) def test_agent_attribute_types(): - test_environ = {'CONTENT_TYPE': 'HTML', 'CONTENT_LENGTH': '100', - 'HTTP_USER_AGENT': 'Firefox', 'HTTP_REFERER': 'somewhere', - 'HTTP_ACCEPT': 'everything'} - fully_featured_application.get('/?test=val', extra_environ=test_environ) + test_environ = { + "CONTENT_TYPE": "HTML", + "CONTENT_LENGTH": "100", + "HTTP_USER_AGENT": "Firefox", + "HTTP_REFERER": "somewhere", + "HTTP_ACCEPT": "everything", + } + fully_featured_application.get("/?test=val", extra_environ=test_environ) # Test sanitize() + def test_sanitize_string(): - s = 'foo' + s = "foo" assert sanitize(s) == s def test_sanitize_bytes(): - b = b'bytes' + b = b"bytes" assert sanitize(b) == b def test_sanitize_unicode(): - u = u'SMILING FACE: \u263a' + u = "SMILING FACE: \u263a" assert sanitize(u) == u @@ -467,22 +470,22 @@ def test_sanitize_int(): def test_sanitize_long(): - l = long(123456) - assert sanitize(l) == l + long_int = long(123456) + assert sanitize(long_int) == long_int def test_sanitize_dict(): - d = {1: 'foo'} + d = {1: "foo"} assert sanitize(d) == "{1: 'foo'}" def test_sanitize_list(): - l = [1, 2, 3, 4] - assert sanitize(l) == '[1, 2, 3, 4]' + list_var = [1, 2, 3, 4] + assert sanitize(list_var) == "[1, 2, 3, 4]" def test_sanitize_tuple(): - t = ('one', 'two', 'three') + t = ("one", "two", "three") assert sanitize(t) == "('one', 'two', 'three')" diff --git a/tests/agent_features/test_attributes_in_action.py b/tests/agent_features/test_attributes_in_action.py index 31c5d625df..f5ee9b2299 100644 --- a/tests/agent_features/test_attributes_in_action.py +++ b/tests/agent_features/test_attributes_in_action.py @@ -33,7 +33,7 @@ from newrelic.api.application import application_instance as application from newrelic.api.message_transaction import message_transaction from newrelic.api.time_trace import notice_error -from newrelic.api.transaction import add_custom_parameter +from newrelic.api.transaction import add_custom_attribute from newrelic.api.wsgi_application import wsgi_application from newrelic.common.object_names import callable_name @@ -132,8 +132,8 @@ def normal_wsgi_application(environ, start_response): output = "header

RESPONSE

" output = output.encode("UTF-8") - add_custom_parameter(USER_ATTRS[0], "test_value") - add_custom_parameter(USER_ATTRS[1], "test_value") + add_custom_attribute(USER_ATTRS[0], "test_value") + add_custom_attribute(USER_ATTRS[1], "test_value") response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) diff --git a/tests/agent_features/test_browser.py b/tests/agent_features/test_browser.py index 5f84920161..5e451be2ed 100644 --- a/tests/agent_features/test_browser.py +++ b/tests/agent_features/test_browser.py @@ -12,47 +12,53 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys -import webtest import json +import sys import six - -from testing_support.fixtures import (override_application_settings, - validate_transaction_errors, validate_custom_parameters) +import webtest +from testing_support.fixtures import ( + override_application_settings, + validate_custom_parameters, + validate_transaction_errors, +) from newrelic.api.application import application_settings -from newrelic.api.transaction import (get_browser_timing_header, - get_browser_timing_footer, add_custom_parameter, - disable_browser_autorum) +from newrelic.api.transaction import ( + add_custom_attribute, + disable_browser_autorum, + get_browser_timing_footer, + get_browser_timing_header, +) from newrelic.api.wsgi_application import wsgi_application from newrelic.common.encoding_utils import deobfuscate -_runtime_error_name = (RuntimeError.__module__ + ':' + RuntimeError.__name__) +_runtime_error_name = RuntimeError.__module__ + ":" + RuntimeError.__name__ + @wsgi_application() def target_wsgi_application_manual_rum(environ, start_response): - status = '200 OK' + status = "200 OK" - text = '%s

RESPONSE

%s' + text = "%s

RESPONSE

%s" - output = (text % (get_browser_timing_header(), - get_browser_timing_footer())).encode('UTF-8') + 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)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) return [output] + target_application_manual_rum = webtest.TestApp(target_wsgi_application_manual_rum) _test_footer_attributes = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "js_agent_loader": "", } + @override_application_settings(_test_footer_attributes) def test_footer_attributes(): settings = application_settings() @@ -66,10 +72,10 @@ def test_footer_attributes(): assert settings.beacon assert settings.error_beacon - token = '0123456789ABCDEF' - headers = { 'Cookie': 'NRAGENT=tk=%s' % token } + token = "0123456789ABCDEF" # nosec + headers = {"Cookie": "NRAGENT=tk=%s" % token} - response = target_application_manual_rum.get('/', headers=headers) + response = target_application_manual_rum.get("/", headers=headers) header = response.html.html.head.script.string content = response.html.html.body.p.string @@ -77,702 +83,731 @@ def test_footer_attributes(): # Validate actual body content. - assert content == 'RESPONSE' + assert content == "RESPONSE" # Validate the insertion of RUM header. - assert header.find('NREUM HEADER') != -1 + assert header.find("NREUM HEADER") != -1 # Now validate the various fields of the footer. The fields are # held by a JSON dictionary. - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) - assert data['licenseKey'] == settings.browser_key - assert data['applicationID'] == settings.application_id + assert data["licenseKey"] == settings.browser_key + assert data["applicationID"] == settings.application_id - assert data['agent'] == settings.js_agent_file - assert data['beacon'] == settings.beacon - assert data['errorBeacon'] == settings.error_beacon + assert data["agent"] == settings.js_agent_file + assert data["beacon"] == settings.beacon + assert data["errorBeacon"] == settings.error_beacon - assert data['applicationTime'] >= 0 - assert data['queueTime'] >= 0 + assert data["applicationTime"] >= 0 + assert data["queueTime"] >= 0 obfuscation_key = settings.license_key[:13] - assert type(data['transactionName']) == type(u'') + assert isinstance(data["transactionName"], str) + # assert type(data["transactionName"]) == type("") + + txn_name = deobfuscate(data["transactionName"], obfuscation_key) - txn_name = deobfuscate(data['transactionName'], obfuscation_key) + assert txn_name == "WebTransaction/Uri/" - assert txn_name == u'WebTransaction/Uri/' + assert "atts" not in data - assert 'atts' not in data _test_rum_ssl_for_http_is_none = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'browser_monitoring.ssl_for_http': None, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "browser_monitoring.ssl_for_http": None, + "js_agent_loader": "", } + @override_application_settings(_test_rum_ssl_for_http_is_none) def test_ssl_for_http_is_none(): settings = application_settings() assert settings.browser_monitoring.ssl_for_http is None - response = target_application_manual_rum.get('/') + response = target_application_manual_rum.get("/") footer = response.html.html.body.script.string - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) + + assert "sslForHttp" not in data - assert 'sslForHttp' not in data _test_rum_ssl_for_http_is_true = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'browser_monitoring.ssl_for_http': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "browser_monitoring.ssl_for_http": True, + "js_agent_loader": "", } + @override_application_settings(_test_rum_ssl_for_http_is_true) def test_ssl_for_http_is_true(): settings = application_settings() assert settings.browser_monitoring.ssl_for_http is True - response = target_application_manual_rum.get('/') + response = target_application_manual_rum.get("/") footer = response.html.html.body.script.string - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) + + assert data["sslForHttp"] is True - assert data['sslForHttp'] is True _test_rum_ssl_for_http_is_false = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': False, - 'browser_monitoring.ssl_for_http': False, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "browser_monitoring.ssl_for_http": False, + "js_agent_loader": "", } + @override_application_settings(_test_rum_ssl_for_http_is_false) def test_ssl_for_http_is_false(): settings = application_settings() assert settings.browser_monitoring.ssl_for_http is False - response = target_application_manual_rum.get('/') + response = target_application_manual_rum.get("/") footer = response.html.html.body.script.string - data = json.loads(footer.split('NREUM.info=')[1]) + data = json.loads(footer.split("NREUM.info=")[1]) + + assert data["sslForHttp"] is False - assert data['sslForHttp'] is False @wsgi_application() def target_wsgi_application_yield_single_no_head(environ, start_response): - status = '200 OK' + status = "200 OK" - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) yield output -target_application_yield_single_no_head = webtest.TestApp( - target_wsgi_application_yield_single_no_head) + +target_application_yield_single_no_head = webtest.TestApp(target_wsgi_application_yield_single_no_head) _test_html_insertion_yield_single_no_head_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_single_no_head_settings) def test_html_insertion_yield_single_no_head(): - response = target_application_yield_single_no_head.get('/', status=200) + response = target_application_yield_single_no_head.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain('NREUM HEADER', 'NREUM.info') + response.mustcontain("NREUM HEADER", "NREUM.info") + @wsgi_application() def target_wsgi_application_yield_multi_no_head(environ, start_response): - status = '200 OK' + status = "200 OK" - output = [ b'', b'

RESPONSE

' ] + output = [b"", b"

RESPONSE

"] - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(b''.join(output))))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(b"".join(output))))] start_response(status, response_headers) for data in output: yield data -target_application_yield_multi_no_head = webtest.TestApp( - target_wsgi_application_yield_multi_no_head) + +target_application_yield_multi_no_head = webtest.TestApp(target_wsgi_application_yield_multi_no_head) _test_html_insertion_yield_multi_no_head_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_multi_no_head_settings) def test_html_insertion_yield_multi_no_head(): - response = target_application_yield_multi_no_head.get('/', status=200) + response = target_application_yield_multi_no_head.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain('NREUM HEADER', 'NREUM.info') + response.mustcontain("NREUM HEADER", "NREUM.info") + @wsgi_application() def target_wsgi_application_unnamed_attachment_header(environ, start_response): - status = '200 OK' + status = "200 OK" - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output))), - ('Content-Disposition', 'attachment')] + response_headers = [ + ("Content-Type", "text/html; charset=utf-8"), + ("Content-Length", str(len(output))), + ("Content-Disposition", "attachment"), + ] start_response(status, response_headers) yield output -target_application_unnamed_attachment_header = webtest.TestApp( - target_wsgi_application_unnamed_attachment_header) + +target_application_unnamed_attachment_header = webtest.TestApp(target_wsgi_application_unnamed_attachment_header) _test_html_insertion_unnamed_attachment_header_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_unnamed_attachment_header_settings) + +@override_application_settings(_test_html_insertion_unnamed_attachment_header_settings) def test_html_insertion_unnamed_attachment_header(): - response = target_application_unnamed_attachment_header.get('/', status=200) + response = target_application_unnamed_attachment_header.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers - assert 'Content-Disposition' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers + assert "Content-Disposition" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) + @wsgi_application() def target_wsgi_application_named_attachment_header(environ, start_response): - status = '200 OK' + status = "200 OK" - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output))), - ('Content-Disposition', 'Attachment; filename="X"')] + response_headers = [ + ("Content-Type", "text/html; charset=utf-8"), + ("Content-Length", str(len(output))), + ("Content-Disposition", 'Attachment; filename="X"'), + ] start_response(status, response_headers) yield output -target_application_named_attachment_header = webtest.TestApp( - target_wsgi_application_named_attachment_header) + +target_application_named_attachment_header = webtest.TestApp(target_wsgi_application_named_attachment_header) _test_html_insertion_named_attachment_header_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_named_attachment_header_settings) + +@override_application_settings(_test_html_insertion_named_attachment_header_settings) def test_html_insertion_named_attachment_header(): - response = target_application_named_attachment_header.get('/', status=200) + response = target_application_named_attachment_header.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers - assert 'Content-Disposition' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers + assert "Content-Disposition" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) + @wsgi_application() def target_wsgi_application_inline_attachment_header(environ, start_response): - status = '200 OK' + status = "200 OK" - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output))), - ('Content-Disposition', 'inline; filename="attachment"')] + response_headers = [ + ("Content-Type", "text/html; charset=utf-8"), + ("Content-Length", str(len(output))), + ("Content-Disposition", 'inline; filename="attachment"'), + ] start_response(status, response_headers) yield output -target_application_inline_attachment_header = webtest.TestApp( - target_wsgi_application_inline_attachment_header) + +target_application_inline_attachment_header = webtest.TestApp(target_wsgi_application_inline_attachment_header) _test_html_insertion_inline_attachment_header_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_inline_attachment_header_settings) + +@override_application_settings(_test_html_insertion_inline_attachment_header_settings) def test_html_insertion_inline_attachment_header(): - response = target_application_inline_attachment_header.get('/', status=200) + response = target_application_inline_attachment_header.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers - assert 'Content-Disposition' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers + assert "Content-Disposition" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain('NREUM HEADER', 'NREUM.info') + response.mustcontain("NREUM HEADER", "NREUM.info") + @wsgi_application() def target_wsgi_application_empty_list(environ, start_response): - status = '200 OK' + status = "200 OK" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', '0')] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", "0")] start_response(status, response_headers) return [] -target_application_empty_list = webtest.TestApp( - target_wsgi_application_empty_list) + +target_application_empty_list = webtest.TestApp(target_wsgi_application_empty_list) _test_html_insertion_empty_list_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_empty_list_settings) + +@override_application_settings(_test_html_insertion_empty_list_settings) def test_html_insertion_empty_list(): - response = target_application_empty_list.get('/', status=200) + response = target_application_empty_list.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) assert len(response.body) == 0 + @wsgi_application() def target_wsgi_application_single_empty_string(environ, start_response): - status = '200 OK' + status = "200 OK" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', '0')] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", "0")] start_response(status, response_headers) - return [''] + return [""] + -target_application_single_empty_string = webtest.TestApp( - target_wsgi_application_single_empty_string) +target_application_single_empty_string = webtest.TestApp(target_wsgi_application_single_empty_string) _test_html_insertion_single_empty_string_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_single_empty_string_settings) + +@override_application_settings(_test_html_insertion_single_empty_string_settings) def test_html_insertion_single_empty_string(): - response = target_application_single_empty_string.get('/', status=200) + response = target_application_single_empty_string.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) assert len(response.body) == 0 + @wsgi_application() def target_wsgi_application_multiple_empty_string(environ, start_response): - status = '200 OK' + status = "200 OK" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', '0')] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", "0")] start_response(status, response_headers) - return ['', ''] + return ["", ""] + -target_application_multiple_empty_string = webtest.TestApp( - target_wsgi_application_multiple_empty_string) +target_application_multiple_empty_string = webtest.TestApp(target_wsgi_application_multiple_empty_string) _test_html_insertion_multiple_empty_string_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_multiple_empty_string_settings) + +@override_application_settings(_test_html_insertion_multiple_empty_string_settings) def test_html_insertion_multiple_empty_string(): - response = target_application_multiple_empty_string.get('/', status=200) + response = target_application_multiple_empty_string.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) assert len(response.body) == 0 + @wsgi_application() def target_wsgi_application_single_large_prelude(environ, start_response): - status = '200 OK' + status = "200 OK" - output = [64*1024*b' ' + b''] + output = [64 * 1024 * b" " + b""] - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(b''.join(output))))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(b"".join(output))))] start_response(status, response_headers) return output -target_application_single_large_prelude = webtest.TestApp( - target_wsgi_application_single_large_prelude) + +target_application_single_large_prelude = webtest.TestApp(target_wsgi_application_single_large_prelude) _test_html_insertion_single_large_prelude_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_single_large_prelude_settings) + +@override_application_settings(_test_html_insertion_single_large_prelude_settings) def test_html_insertion_single_large_prelude(): - response = target_application_single_large_prelude.get('/', status=200) + response = target_application_single_large_prelude.get("/", status=200) # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers + + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + output = [32 * 1024 * b" ", 32 * 1024 * b" ", b""] - output = [32*1024*b' ', 32*1024*b' ', b''] + assert len(response.body) == len(b"".join(output)) - assert len(response.body) == len(b''.join(output)) @wsgi_application() def target_wsgi_application_multi_large_prelude(environ, start_response): - status = '200 OK' + status = "200 OK" - output = [32*1024*b' ', 32*1024*b' ', b''] + output = [32 * 1024 * b" ", 32 * 1024 * b" ", b""] - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(b''.join(output))))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(b"".join(output))))] start_response(status, response_headers) return output -target_application_multi_large_prelude = webtest.TestApp( - target_wsgi_application_multi_large_prelude) + +target_application_multi_large_prelude = webtest.TestApp(target_wsgi_application_multi_large_prelude) _test_html_insertion_multi_large_prelude_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_multi_large_prelude_settings) + +@override_application_settings(_test_html_insertion_multi_large_prelude_settings) def test_html_insertion_multi_large_prelude(): - response = target_application_multi_large_prelude.get('/', status=200) + response = target_application_multi_large_prelude.get("/", status=200) # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers + + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + output = [32 * 1024 * b" ", 32 * 1024 * b" ", b""] - output = [32*1024*b' ', 32*1024*b' ', b''] + assert len(response.body) == len(b"".join(output)) - assert len(response.body) == len(b''.join(output)) @wsgi_application() def target_wsgi_application_yield_before_start(environ, start_response): - status = '200 OK' + status = "200 OK" # Ambiguous whether yield an empty string before calling # start_response() is legal. Various WSGI servers allow it # We have to disable WebTest lint check to get this to run. - yield b'' + yield b"" - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) yield output -target_application_yield_before_start = webtest.TestApp( - target_wsgi_application_yield_before_start, lint=False) + +target_application_yield_before_start = webtest.TestApp(target_wsgi_application_yield_before_start, lint=False) _test_html_insertion_yield_before_start_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_before_start_settings) def test_html_insertion_yield_before_start(): - response = target_application_yield_before_start.get('/', status=200) + response = target_application_yield_before_start.get("/", status=200) # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain('NREUM HEADER', 'NREUM.info') + response.mustcontain("NREUM HEADER", "NREUM.info") + @wsgi_application() def target_wsgi_application_start_yield_start(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) - yield '' + yield "" try: - start_response(status, response_headers) + start_response(status, response_headers) # noqa: F821 except Exception: - start_response('500 Error', response_headers, sys.exc_info()) + start_response("500 Error", response_headers, sys.exc_info()) yield output -target_application_start_yield_start = webtest.TestApp( - target_wsgi_application_start_yield_start) + +target_application_start_yield_start = webtest.TestApp(target_wsgi_application_start_yield_start) _test_html_insertion_start_yield_start_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_start_yield_start_settings) def test_html_insertion_start_yield_start(): - response = target_application_start_yield_start.get('/', status=500) + response = target_application_start_yield_start.get("/", status=500) # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers + + response.mustcontain("NREUM HEADER", "NREUM.info") - response.mustcontain('NREUM HEADER', 'NREUM.info') @wsgi_application() def target_wsgi_application_invalid_content_length(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', 'XXX')] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", "XXX")] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) yield output -target_application_invalid_content_length = webtest.TestApp( - target_wsgi_application_invalid_content_length) + +target_application_invalid_content_length = webtest.TestApp(target_wsgi_application_invalid_content_length) _test_html_insertion_invalid_content_length_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_invalid_content_length_settings) def test_html_insertion_invalid_content_length(): - response = target_application_invalid_content_length.get('/', status=200) + response = target_application_invalid_content_length.get("/", status=200) # This is relying on WebTest not validating the # value of the Content-Length response header # and just passing it through as is. - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers - assert response.headers['Content-Length'] == 'XXX' + assert response.headers["Content-Length"] == "XXX" + + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) @wsgi_application() def target_wsgi_application_content_encoding(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output))), - ('Content-Encoding', 'identity')] + response_headers = [ + ("Content-Type", "text/html; charset=utf-8"), + ("Content-Length", str(len(output))), + ("Content-Encoding", "identity"), + ] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) yield output -target_application_content_encoding = webtest.TestApp( - target_wsgi_application_content_encoding) + +target_application_content_encoding = webtest.TestApp(target_wsgi_application_content_encoding) _test_html_insertion_content_encoding_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_content_encoding_settings) def test_html_insertion_content_encoding(): - response = target_application_content_encoding.get('/', status=200) + response = target_application_content_encoding.get("/", status=200) # Technically 'identity' should not be used in Content-Encoding # but clients will still accept it. Use this fact to disable auto # RUM for this test. Other option is to compress the response # and use 'gzip'. - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers - assert response.headers['Content-Encoding'] == 'identity' + assert response.headers["Content-Encoding"] == "identity" + + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) @wsgi_application() def target_wsgi_application_no_content_type(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Length', str(len(output)))] + response_headers = [("Content-Length", str(len(output)))] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) yield output -target_application_no_content_type = webtest.TestApp( - target_wsgi_application_no_content_type, lint=False) + +target_application_no_content_type = webtest.TestApp(target_wsgi_application_no_content_type, lint=False) _test_html_insertion_no_content_type_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_no_content_type_settings) def test_html_insertion_no_content_type(): - response = target_application_no_content_type.get('/', status=200) + response = target_application_no_content_type.get("/", status=200) + + assert "Content-Type" not in response.headers + assert "Content-Length" in response.headers - assert 'Content-Type' not in response.headers - assert 'Content-Length' in response.headers + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) @wsgi_application() def target_wsgi_application_plain_text(environ, start_response): - output = b'RESPONSE' + output = b"RESPONSE" - response_headers = [('Content-Type', 'text/plain'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/plain"), ("Content-Length", str(len(output)))] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) yield output -target_application_plain_text = webtest.TestApp( - target_wsgi_application_plain_text) + +target_application_plain_text = webtest.TestApp(target_wsgi_application_plain_text) _test_html_insertion_plain_text_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_plain_text_settings) def test_html_insertion_plain_text(): - response = target_application_plain_text.get('/', status=200) + response = target_application_plain_text.get("/", status=200) + + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) @wsgi_application() def target_wsgi_application_write_callback(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html"), ("Content-Length", str(len(output)))] - write = start_response('200 OK', response_headers) + write = start_response("200 OK", response_headers) write(output) return [] -target_application_write_callback = webtest.TestApp( - target_wsgi_application_write_callback) + +target_application_write_callback = webtest.TestApp(target_wsgi_application_write_callback) _test_html_insertion_write_callback_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_write_callback_settings) def test_html_insertion_write_callback(): - response = target_application_write_callback.get('/', status=200) + response = target_application_write_callback.get("/", status=200) + + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) @wsgi_application() def target_wsgi_application_yield_before_write(environ, start_response): - output = [b'', b'

RESPONSE

'] + output = [b"", b"

RESPONSE

"] - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(b''.join(output))))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(b"".join(output))))] - write = start_response('200 OK', response_headers) + write = start_response("200 OK", response_headers) # Technically this is in violation of the WSGI specification # if that write() should always be before yields. @@ -781,172 +816,177 @@ def target_wsgi_application_yield_before_write(environ, start_response): write(output.pop(0)) -target_application_yield_before_write = webtest.TestApp( - target_wsgi_application_yield_before_write) + +target_application_yield_before_write = webtest.TestApp(target_wsgi_application_yield_before_write) _test_html_insertion_yield_before_write_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_yield_before_write_settings) def test_html_insertion_yield_before_write(): - response = target_application_yield_before_write.get('/', status=200) + response = target_application_yield_before_write.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - expected = b'

RESPONSE

' + expected = b"

RESPONSE

" assert response.body == expected + @wsgi_application() def target_wsgi_application_write_before_yield(environ, start_response): - output = [b'', b'

RESPONSE

'] + output = [b"", b"

RESPONSE

"] - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(b''.join(output))))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(b"".join(output))))] - write = start_response('200 OK', response_headers) + write = start_response("200 OK", response_headers) write(output.pop(0)) yield output.pop(0) -target_application_write_before_yield = webtest.TestApp( - target_wsgi_application_write_before_yield) + +target_application_write_before_yield = webtest.TestApp(target_wsgi_application_write_before_yield) _test_html_insertion_write_before_yield_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_write_before_yield_settings) def test_html_insertion_write_before_yield(): - response = target_application_write_before_yield.get('/', status=200) + response = target_application_write_before_yield.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) - expected = b'

RESPONSE

' + expected = b"

RESPONSE

" assert response.body == expected + @wsgi_application() def target_wsgi_application_param_on_close(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) try: yield output finally: - add_custom_parameter('key', 'value') + add_custom_attribute("key", "value") + -target_application_param_on_close = webtest.TestApp( - target_wsgi_application_param_on_close) +target_application_param_on_close = webtest.TestApp(target_wsgi_application_param_on_close) _test_html_insertion_param_on_close_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_param_on_close_settings) -@validate_custom_parameters(required_params=[('key', 'value')]) +@validate_custom_parameters(required_params=[("key", "value")]) def test_html_insertion_param_on_close(): - response = target_application_param_on_close.get('/', status=200) + response = target_application_param_on_close.get("/", status=200) + + response.mustcontain("NREUM HEADER", "NREUM.info") - response.mustcontain('NREUM HEADER', 'NREUM.info') @wsgi_application() def target_wsgi_application_param_on_error(environ, start_response): - output = b'

RESPONSE

' + output = b"

RESPONSE

" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] - start_response('200 OK', response_headers) + start_response("200 OK", response_headers) try: - raise RuntimeError('ERROR') + raise RuntimeError("ERROR") yield output finally: - add_custom_parameter('key', 'value') + add_custom_attribute("key", "value") + -target_application_param_on_error = webtest.TestApp( - target_wsgi_application_param_on_error) +target_application_param_on_error = webtest.TestApp(target_wsgi_application_param_on_error) _test_html_insertion_param_on_error_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } + @override_application_settings(_test_html_insertion_param_on_error_settings) @validate_transaction_errors(errors=[_runtime_error_name]) -@validate_custom_parameters(required_params=[('key', 'value')]) +@validate_custom_parameters(required_params=[("key", "value")]) def test_html_insertion_param_on_error(): try: - response = target_application_param_on_error.get('/', status=500) + response = target_application_param_on_error.get("/", status=500) except RuntimeError: pass + @wsgi_application() def target_wsgi_application_disable_autorum_via_api(environ, start_response): - status = '200 OK' + status = "200 OK" - output = b'

RESPONSE

' + output = b"

RESPONSE

" disable_browser_autorum() - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) yield output -target_application_disable_autorum_via_api = webtest.TestApp( - target_wsgi_application_disable_autorum_via_api) + +target_application_disable_autorum_via_api = webtest.TestApp(target_wsgi_application_disable_autorum_via_api) _test_html_insertion_disable_autorum_via_api_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_disable_autorum_via_api_settings) + +@override_application_settings(_test_html_insertion_disable_autorum_via_api_settings) def test_html_insertion_disable_autorum_via_api(): - response = target_application_disable_autorum_via_api.get('/', status=200) + response = target_application_disable_autorum_via_api.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) + @wsgi_application() def target_wsgi_application_manual_rum_insertion(environ, start_response): - status = '200 OK' + status = "200 OK" - output = b'

RESPONSE

' + output = b"

RESPONSE

" header = get_browser_timing_header() footer = get_browser_timing_footer() @@ -954,34 +994,33 @@ def target_wsgi_application_manual_rum_insertion(environ, start_response): header = get_browser_timing_header() footer = get_browser_timing_footer() - assert header == '' - assert footer == '' + assert header == "" + assert footer == "" - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) yield output -target_application_manual_rum_insertion = webtest.TestApp( - target_wsgi_application_manual_rum_insertion) + +target_application_manual_rum_insertion = webtest.TestApp(target_wsgi_application_manual_rum_insertion) _test_html_insertion_manual_rum_insertion_settings = { - 'browser_monitoring.enabled': True, - 'browser_monitoring.auto_instrument': True, - 'js_agent_loader': u'', + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": True, + "js_agent_loader": "", } -@override_application_settings( - _test_html_insertion_manual_rum_insertion_settings) + +@override_application_settings(_test_html_insertion_manual_rum_insertion_settings) def test_html_insertion_manual_rum_insertion(): - response = target_application_manual_rum_insertion.get('/', status=200) + response = target_application_manual_rum_insertion.get("/", status=200) - assert 'Content-Type' in response.headers - assert 'Content-Length' in response.headers + assert "Content-Type" in response.headers + assert "Content-Length" in response.headers # The 'NREUM HEADER' value comes from our override for the header. # The 'NREUM.info' value comes from the programmatically generated # footer added by the agent. - response.mustcontain(no=['NREUM HEADER', 'NREUM.info']) + response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) diff --git a/tests/agent_features/test_high_security_mode.py b/tests/agent_features/test_high_security_mode.py index 89499d3658..51cd199315 100644 --- a/tests/agent_features/test_high_security_mode.py +++ b/tests/agent_features/test_high_security_mode.py @@ -38,7 +38,7 @@ from newrelic.api.settings import STRIP_EXCEPTION_MESSAGE from newrelic.api.time_trace import notice_error from newrelic.api.transaction import ( - add_custom_parameter, + add_custom_attribute, capture_request_params, current_transaction, record_custom_event, @@ -396,7 +396,7 @@ def test_remote_config_hsm_fixups_server_side_disabled(): @validate_custom_parameters(required_params=[("key", "value")]) @background_task() def test_other_transaction_custom_parameters_hsm_disabled(): - add_custom_parameter("key", "value") + add_custom_attribute("key", "value") @override_application_settings(_test_transaction_settings_hsm_disabled) @@ -404,14 +404,14 @@ def test_other_transaction_custom_parameters_hsm_disabled(): @background_task() def test_other_transaction_multiple_custom_parameters_hsm_disabled(): transaction = current_transaction() - transaction.add_custom_parameters([("key-1", "value-1"), ("key-2", "value-2")]) + transaction.add_custom_attributes([("key-1", "value-1"), ("key-2", "value-2")]) @override_application_settings(_test_transaction_settings_hsm_enabled) @validate_custom_parameters(forgone_params=[("key", "value")]) @background_task() def test_other_transaction_custom_parameters_hsm_enabled(): - add_custom_parameter("key", "value") + add_custom_attribute("key", "value") @override_application_settings(_test_transaction_settings_hsm_enabled) @@ -419,7 +419,7 @@ def test_other_transaction_custom_parameters_hsm_enabled(): @background_task() def test_other_transaction_multiple_custom_parameters_hsm_enabled(): transaction = current_transaction() - transaction.add_custom_parameters([("key-1", "value-1"), ("key-2", "value-2")]) + transaction.add_custom_attributes([("key-1", "value-1"), ("key-2", "value-2")]) class TestException(Exception): @@ -434,7 +434,7 @@ class TestException(Exception): @validate_custom_parameters(required_params=[("key-1", "value-1")]) @background_task() def test_other_transaction_error_parameters_hsm_disabled(): - add_custom_parameter("key-1", "value-1") + add_custom_attribute("key-1", "value-1") try: raise TestException("test message") except Exception: @@ -448,7 +448,7 @@ def test_other_transaction_error_parameters_hsm_disabled(): @validate_custom_parameters(forgone_params=[("key-1", "value-1")]) @background_task() def test_other_transaction_error_parameters_hsm_enabled(): - add_custom_parameter("key-1", "value-1") + add_custom_attribute("key-1", "value-1") try: raise TestException("test message") except Exception: diff --git a/tests/agent_features/test_span_events.py b/tests/agent_features/test_span_events.py index 0024c1b8b8..f87e10fcb9 100644 --- a/tests/agent_features/test_span_events.py +++ b/tests/agent_features/test_span_events.py @@ -12,15 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest import sys -from newrelic.api.transaction import current_transaction -from newrelic.api.time_trace import (current_trace, - add_custom_span_attribute, notice_error) -from newrelic.api.background_task import background_task -from newrelic.common.object_names import callable_name +import pytest +from testing_support.fixtures import ( + dt_enabled, + function_not_called, + override_application_settings, + validate_transaction_event_attributes, + validate_transaction_metrics, + validate_tt_segment_params, +) +from testing_support.validators.validate_span_events import validate_span_events +from newrelic.api.background_task import background_task from newrelic.api.database_trace import DatabaseTrace from newrelic.api.datastore_trace import DatastoreTrace from newrelic.api.external_trace import ExternalTrace @@ -28,81 +33,73 @@ from newrelic.api.memcache_trace import MemcacheTrace from newrelic.api.message_trace import MessageTrace from newrelic.api.solr_trace import SolrTrace - -from testing_support.fixtures import (override_application_settings, - function_not_called, validate_tt_segment_params, - validate_transaction_metrics, dt_enabled, - validate_transaction_event_attributes) -from testing_support.validators.validate_span_events import ( - validate_span_events) +from newrelic.api.time_trace import ( + add_custom_span_attribute, + current_trace, + notice_error, +) +from newrelic.api.transaction import current_transaction +from newrelic.common.object_names import callable_name ERROR = ValueError("whoops") ERROR_NAME = callable_name(ERROR) -@pytest.mark.parametrize('dt_enabled', (True, False)) -@pytest.mark.parametrize('span_events_enabled', (True, False)) -@pytest.mark.parametrize('txn_sampled', (True, False)) +@pytest.mark.parametrize("dt_enabled", (True, False)) +@pytest.mark.parametrize("span_events_enabled", (True, False)) +@pytest.mark.parametrize("txn_sampled", (True, False)) def test_span_events(dt_enabled, span_events_enabled, txn_sampled): - guid = 'dbb536c53b749e0b' - sentinel_guid = '0687e0c371ea2c4e' - function_guid = '482439c52de807ee' - transaction_name = 'OtherTransaction/Function/transaction' + guid = "dbb536c53b749e0b" + sentinel_guid = "0687e0c371ea2c4e" + function_guid = "482439c52de807ee" + transaction_name = "OtherTransaction/Function/transaction" priority = 0.5 - @function_trace(name='child') + @function_trace(name="child") def child(): pass - @function_trace(name='function') + @function_trace(name="function") def function(): current_trace().guid = function_guid child() - _settings = { - 'distributed_tracing.enabled': dt_enabled, - 'span_events.enabled': span_events_enabled - } + _settings = {"distributed_tracing.enabled": dt_enabled, "span_events.enabled": span_events_enabled} count = 0 if dt_enabled and span_events_enabled and txn_sampled: count = 1 exact_intrinsics_common = { - 'type': 'Span', - 'transactionId': guid, - 'sampled': txn_sampled, - 'priority': priority, - 'category': 'generic', + "type": "Span", + "transactionId": guid, + "sampled": txn_sampled, + "priority": priority, + "category": "generic", } - expected_intrinsics = ('timestamp', 'duration') + expected_intrinsics = ("timestamp", "duration") exact_intrinsics_root = exact_intrinsics_common.copy() - exact_intrinsics_root['name'] = 'Function/transaction' - exact_intrinsics_root['transaction.name'] = transaction_name - exact_intrinsics_root['nr.entryPoint'] = True + exact_intrinsics_root["name"] = "Function/transaction" + exact_intrinsics_root["transaction.name"] = transaction_name + exact_intrinsics_root["nr.entryPoint"] = True exact_intrinsics_function = exact_intrinsics_common.copy() - exact_intrinsics_function['name'] = 'Function/function' - exact_intrinsics_function['parentId'] = sentinel_guid + exact_intrinsics_function["name"] = "Function/function" + exact_intrinsics_function["parentId"] = sentinel_guid exact_intrinsics_child = exact_intrinsics_common.copy() - exact_intrinsics_child['name'] = 'Function/child' - exact_intrinsics_child['parentId'] = function_guid - - @validate_span_events(count=count, - expected_intrinsics=['nr.entryPoint']) - @validate_span_events(count=count, - exact_intrinsics=exact_intrinsics_root, - expected_intrinsics=expected_intrinsics) - @validate_span_events(count=count, - exact_intrinsics=exact_intrinsics_function, - expected_intrinsics=expected_intrinsics) - @validate_span_events(count=count, - exact_intrinsics=exact_intrinsics_child, - expected_intrinsics=expected_intrinsics) + exact_intrinsics_child["name"] = "Function/child" + exact_intrinsics_child["parentId"] = function_guid + + @validate_span_events(count=count, expected_intrinsics=["nr.entryPoint"]) + @validate_span_events(count=count, exact_intrinsics=exact_intrinsics_root, expected_intrinsics=expected_intrinsics) + @validate_span_events( + count=count, exact_intrinsics=exact_intrinsics_function, expected_intrinsics=expected_intrinsics + ) + @validate_span_events(count=count, exact_intrinsics=exact_intrinsics_child, expected_intrinsics=expected_intrinsics) @override_application_settings(_settings) - @background_task(name='transaction') + @background_task(name="transaction") def _test(): # Force intrinsics txn = current_transaction() @@ -116,22 +113,27 @@ def _test(): _test() -@pytest.mark.parametrize('trace_type,args', ( - (DatabaseTrace, ('select * from foo', )), - (DatastoreTrace, ('db_product', 'db_target', 'db_operation')), - (ExternalTrace, ('lib', 'url')), - (FunctionTrace, ('name', )), - (MemcacheTrace, ('command', )), - (MessageTrace, ('lib', 'operation', 'dst_type', 'dst_name')), - (SolrTrace, ('lib', 'command')), -)) +@pytest.mark.parametrize( + "trace_type,args", + ( + (DatabaseTrace, ("select * from foo",)), + (DatastoreTrace, ("db_product", "db_target", "db_operation")), + (ExternalTrace, ("lib", "url")), + (FunctionTrace, ("name",)), + (MemcacheTrace, ("command",)), + (MessageTrace, ("lib", "operation", "dst_type", "dst_name")), + (SolrTrace, ("lib", "command")), + ), +) def test_each_span_type(trace_type, args): @validate_span_events(count=2) - @override_application_settings({ - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, - }) - @background_task(name='test_each_span_type') + @override_application_settings( + { + "distributed_tracing.enabled": True, + "span_events.enabled": True, + } + ) + @background_task(name="test_each_span_type") def _test(): transaction = current_transaction() @@ -143,37 +145,37 @@ def _test(): _test() -@pytest.mark.parametrize('sql,sql_format,expected', ( - pytest.param( - 'a' * 2001, - 'raw', - ''.join(['a'] * 1997 + ['...']), - id='truncate'), - pytest.param( - 'a' * 2000, - 'raw', - ''.join(['a'] * 2000), - id='no_truncate'), - pytest.param( - 'select * from %s' % ''.join(['?'] * 2000), - 'obfuscated', - 'select * from %s...' % ( - ''.join(['?'] * (2000 - len('select * from ') - 3))), - id='truncate_obfuscated'), - pytest.param('select 1', 'off', ''), - pytest.param('select 1', 'raw', 'select 1'), - pytest.param('select 1', 'obfuscated', 'select ?'), -)) +@pytest.mark.parametrize( + "sql,sql_format,expected", + ( + pytest.param("a" * 2001, "raw", "".join(["a"] * 1997 + ["..."]), id="truncate"), + pytest.param("a" * 2000, "raw", "".join(["a"] * 2000), id="no_truncate"), + pytest.param( + "select * from %s" % "".join(["?"] * 2000), + "obfuscated", + "select * from %s..." % ("".join(["?"] * (2000 - len("select * from ") - 3))), + id="truncate_obfuscated", + ), + pytest.param("select 1", "off", ""), + pytest.param("select 1", "raw", "select 1"), + pytest.param("select 1", "obfuscated", "select ?"), + ), +) def test_database_db_statement_format(sql, sql_format, expected): - @validate_span_events(count=1, exact_agents={ - 'db.statement': expected, - }) - @override_application_settings({ - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, - 'transaction_tracer.record_sql': sql_format, - }) - @background_task(name='test_database_db_statement_format') + @validate_span_events( + count=1, + exact_agents={ + "db.statement": expected, + }, + ) + @override_application_settings( + { + "distributed_tracing.enabled": True, + "span_events.enabled": True, + "transaction_tracer.record_sql": sql_format, + } + ) + @background_task(name="test_database_db_statement_format") def _test(): transaction = current_transaction() transaction._sampled = True @@ -186,115 +188,130 @@ def _test(): @validate_span_events( count=1, - exact_intrinsics={'category': 'datastore'}, - unexpected_agents=['db.statement'], + exact_intrinsics={"category": "datastore"}, + unexpected_agents=["db.statement"], +) +@override_application_settings( + { + "distributed_tracing.enabled": True, + "span_events.enabled": True, + "span_events.attributes.exclude": ["db.statement"], + } ) -@override_application_settings({ - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, - 'span_events.attributes.exclude': ['db.statement'], -}) -@background_task(name='test_database_db_statement_exclude') +@background_task(name="test_database_db_statement_exclude") def test_database_db_statement_exclude(): transaction = current_transaction() transaction._sampled = True - with DatabaseTrace('select 1'): + with DatabaseTrace("select 1"): pass -@pytest.mark.parametrize('trace_type,args,attrs', ( - (DatastoreTrace, ('db_product', 'db_target', 'db_operation'), {"db.collection": "db_target", "db.operation": "db_operation"}), - (DatabaseTrace, ("select 1 from db_table",), {"db.collection": "db_table", "db.statement": "select ? from db_table"}), -)) +@pytest.mark.parametrize( + "trace_type,args,attrs", + ( + ( + DatastoreTrace, + ("db_product", "db_target", "db_operation"), + {"db.collection": "db_target", "db.operation": "db_operation"}, + ), + ( + DatabaseTrace, + ("select 1 from db_table",), + {"db.collection": "db_table", "db.statement": "select ? from db_table"}, + ), + ), +) def test_datastore_database_trace_attrs(trace_type, args, attrs): @validate_span_events( count=1, exact_agents=attrs, ) - @override_application_settings({ - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, - }) - @background_task(name='test_database_db_statement_exclude') + @override_application_settings( + { + "distributed_tracing.enabled": True, + "span_events.enabled": True, + } + ) + @background_task(name="test_database_db_statement_exclude") def test(): transaction = current_transaction() transaction._sampled = True with trace_type(*args): pass - + test() -@pytest.mark.parametrize('exclude_url', (True, False)) +@pytest.mark.parametrize("exclude_url", (True, False)) def test_external_spans(exclude_url): override_settings = { - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, + "distributed_tracing.enabled": True, + "span_events.enabled": True, } if exclude_url: - override_settings['span_events.attributes.exclude'] = ['http.url'] + override_settings["span_events.attributes.exclude"] = ["http.url"] exact_agents = {} - unexpected_agents = ['http.url'] + unexpected_agents = ["http.url"] else: - exact_agents = {'http.url': 'http://example.com/foo'} + exact_agents = {"http.url": "http://example.com/foo"} unexpected_agents = [] @validate_span_events( count=1, exact_intrinsics={ - 'name': 'External/example.com/library/get', - 'type': 'Span', - 'sampled': True, - - 'category': 'http', - 'span.kind': 'client', - 'component': 'library', - 'http.method': 'get', + "name": "External/example.com/library/get", + "type": "Span", + "sampled": True, + "category": "http", + "span.kind": "client", + "component": "library", + "http.method": "get", }, exact_agents=exact_agents, unexpected_agents=unexpected_agents, - expected_intrinsics=('priority',), + expected_intrinsics=("priority",), ) @override_application_settings(override_settings) - @background_task(name='test_external_spans') + @background_task(name="test_external_spans") def _test(): transaction = current_transaction() transaction._sampled = True - with ExternalTrace( - library='library', - url='http://example.com/foo?secret=123', - method='get'): + with ExternalTrace(library="library", url="http://example.com/foo?secret=123", method="get"): pass _test() -@pytest.mark.parametrize('kwarg_override,attr_override', ( - ({'url': 'a' * 256}, {'http.url': 'a' * 255}), - ({'library': 'a' * 256}, {'component': 'a' * 255}), - ({'method': 'a' * 256}, {'http.method': 'a' * 255}), -)) -@override_application_settings({ - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, -}) +@pytest.mark.parametrize( + "kwarg_override,attr_override", + ( + ({"url": "a" * 256}, {"http.url": "a" * 255}), + ({"library": "a" * 256}, {"component": "a" * 255}), + ({"method": "a" * 256}, {"http.method": "a" * 255}), + ), +) +@override_application_settings( + { + "distributed_tracing.enabled": True, + "span_events.enabled": True, + } +) def test_external_span_limits(kwarg_override, attr_override): exact_intrinsics = { - 'type': 'Span', - 'sampled': True, - - 'category': 'http', - 'span.kind': 'client', - 'component': 'library', - 'http.method': 'get', + "type": "Span", + "sampled": True, + "category": "http", + "span.kind": "client", + "component": "library", + "http.method": "get", } exact_agents = { - 'http.url': 'http://example.com/foo', + "http.url": "http://example.com/foo", } for attr_name, attr_value in attr_override.items(): if attr_name in exact_agents: @@ -303,9 +320,9 @@ def test_external_span_limits(kwarg_override, attr_override): exact_intrinsics[attr_name] = attr_value kwargs = { - 'library': 'library', - 'url': 'http://example.com/foo?secret=123', - 'method': 'get', + "library": "library", + "url": "http://example.com/foo?secret=123", + "method": "get", } kwargs.update(kwarg_override) @@ -313,9 +330,9 @@ def test_external_span_limits(kwarg_override, attr_override): count=1, exact_intrinsics=exact_intrinsics, exact_agents=exact_agents, - expected_intrinsics=('priority',), + expected_intrinsics=("priority",), ) - @background_task(name='test_external_spans') + @background_task(name="test_external_spans") def _test(): transaction = current_transaction() transaction._sampled = True @@ -326,32 +343,34 @@ def _test(): _test() -@pytest.mark.parametrize('kwarg_override,attribute_override', ( - ({'host': 'a' * 256}, - {'peer.hostname': 'a' * 255, 'peer.address': 'a' * 255}), - ({'port_path_or_id': 'a' * 256, 'host': 'a'}, - {'peer.hostname': 'a', 'peer.address': 'a:' + 'a' * 253}), - ({'database_name': 'a' * 256}, {'db.instance': 'a' * 255}), -)) -@override_application_settings({ - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, -}) +@pytest.mark.parametrize( + "kwarg_override,attribute_override", + ( + ({"host": "a" * 256}, {"peer.hostname": "a" * 255, "peer.address": "a" * 255}), + ({"port_path_or_id": "a" * 256, "host": "a"}, {"peer.hostname": "a", "peer.address": "a:" + "a" * 253}), + ({"database_name": "a" * 256}, {"db.instance": "a" * 255}), + ), +) +@override_application_settings( + { + "distributed_tracing.enabled": True, + "span_events.enabled": True, + } +) def test_datastore_span_limits(kwarg_override, attribute_override): exact_intrinsics = { - 'type': 'Span', - 'sampled': True, - - 'category': 'datastore', - 'span.kind': 'client', - 'component': 'library', + "type": "Span", + "sampled": True, + "category": "datastore", + "span.kind": "client", + "component": "library", } exact_agents = { - 'db.instance': 'db', - 'peer.hostname': 'foo', - 'peer.address': 'foo:1234', + "db.instance": "db", + "peer.hostname": "foo", + "peer.address": "foo:1234", } for k, v in attribute_override.items(): @@ -361,22 +380,22 @@ def test_datastore_span_limits(kwarg_override, attribute_override): exact_intrinsics[k] = v kwargs = { - 'product': 'library', - 'target': 'table', - 'operation': 'operation', - 'host': 'foo', - 'port_path_or_id': 1234, - 'database_name': 'db', + "product": "library", + "target": "table", + "operation": "operation", + "host": "foo", + "port_path_or_id": 1234, + "database_name": "db", } kwargs.update(kwarg_override) @validate_span_events( count=1, exact_intrinsics=exact_intrinsics, - expected_intrinsics=('priority',), + expected_intrinsics=("priority",), exact_agents=exact_agents, ) - @background_task(name='test_external_spans') + @background_task(name="test_external_spans") def _test(): transaction = current_transaction() transaction._sampled = True @@ -387,10 +406,9 @@ def _test(): _test() -@pytest.mark.parametrize('collect_span_events', (False, True)) -@pytest.mark.parametrize('span_events_enabled', (False, True)) -def test_collect_span_events_override(collect_span_events, - span_events_enabled): +@pytest.mark.parametrize("collect_span_events", (False, True)) +@pytest.mark.parametrize("span_events_enabled", (False, True)) +def test_collect_span_events_override(collect_span_events, span_events_enabled): if collect_span_events and span_events_enabled: spans_expected = True @@ -400,63 +418,64 @@ def test_collect_span_events_override(collect_span_events, span_count = 2 if spans_expected else 0 @validate_span_events(count=span_count) - @override_application_settings({ - 'transaction_tracer.enabled': False, - 'distributed_tracing.enabled': True, - 'span_events.enabled': span_events_enabled, - 'collect_span_events': collect_span_events - }) - @background_task(name='test_collect_span_events_override') + @override_application_settings( + { + "transaction_tracer.enabled": False, + "distributed_tracing.enabled": True, + "span_events.enabled": span_events_enabled, + "collect_span_events": collect_span_events, + } + ) + @background_task(name="test_collect_span_events_override") def _test(): transaction = current_transaction() transaction._sampled = True - with FunctionTrace('span_generator'): + with FunctionTrace("span_generator"): pass if not spans_expected: - _test = function_not_called( - 'newrelic.core.attribute', - 'resolve_agent_attributes')(_test) + _test = function_not_called("newrelic.core.attribute", "resolve_agent_attributes")(_test) _test() -@pytest.mark.parametrize('include_attribues', (True, False)) +@pytest.mark.parametrize("include_attribues", (True, False)) def test_span_event_agent_attributes(include_attribues): override_settings = { - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, + "distributed_tracing.enabled": True, + "span_events.enabled": True, } if include_attribues: count = 1 - override_settings['attributes.include'] = ['*'] + override_settings["attributes.include"] = ["*"] else: count = 0 @override_application_settings(override_settings) + @validate_span_events(count=count, expected_agents=["webfrontend.queue.seconds"]) @validate_span_events( - count=count, expected_agents=['webfrontend.queue.seconds']) - @validate_span_events( - count=count, - exact_agents={'trace1_a': 'foobar', 'trace1_b': 'barbaz'}, - unexpected_agents=['trace2_a', 'trace2_b']) + count=count, + exact_agents={"trace1_a": "foobar", "trace1_b": "barbaz"}, + unexpected_agents=["trace2_a", "trace2_b"], + ) @validate_span_events( - count=count, - exact_agents={'trace2_a': 'foobar', 'trace2_b': 'barbaz'}, - unexpected_agents=['trace1_a', 'trace1_b']) - @background_task(name='test_span_event_agent_attributes') + count=count, + exact_agents={"trace2_a": "foobar", "trace2_b": "barbaz"}, + unexpected_agents=["trace1_a", "trace1_b"], + ) + @background_task(name="test_span_event_agent_attributes") def _test(): transaction = current_transaction() transaction.queue_start = 1.0 transaction._sampled = True - with FunctionTrace('trace1') as trace_1: - trace_1._add_agent_attribute('trace1_a', 'foobar') - trace_1._add_agent_attribute('trace1_b', 'barbaz') - with FunctionTrace('trace2') as trace_2: - trace_2._add_agent_attribute('trace2_a', 'foobar') - trace_2._add_agent_attribute('trace2_b', 'barbaz') + with FunctionTrace("trace1") as trace_1: + trace_1._add_agent_attribute("trace1_a", "foobar") + trace_1._add_agent_attribute("trace1_b", "barbaz") + with FunctionTrace("trace2") as trace_2: + trace_2._add_agent_attribute("trace2_a", "foobar") + trace_2._add_agent_attribute("trace2_b", "barbaz") _test() @@ -469,31 +488,36 @@ def __exit__(self, *args): pass -@pytest.mark.parametrize('trace_type,args', ( - (DatabaseTrace, ('select * from foo', )), - (DatastoreTrace, ('db_product', 'db_target', 'db_operation')), - (ExternalTrace, ('lib', 'url')), - (FunctionTrace, ('name', )), - (MemcacheTrace, ('command', )), - (MessageTrace, ('lib', 'operation', 'dst_type', 'dst_name')), - (SolrTrace, ('lib', 'command')), - (FakeTrace, ()), -)) -@pytest.mark.parametrize('exclude_attributes', (True, False)) +@pytest.mark.parametrize( + "trace_type,args", + ( + (DatabaseTrace, ("select * from foo",)), + (DatastoreTrace, ("db_product", "db_target", "db_operation")), + (ExternalTrace, ("lib", "url")), + (FunctionTrace, ("name",)), + (MemcacheTrace, ("command",)), + (MessageTrace, ("lib", "operation", "dst_type", "dst_name")), + (SolrTrace, ("lib", "command")), + (FakeTrace, ()), + ), +) +@pytest.mark.parametrize("exclude_attributes", (True, False)) def test_span_event_user_attributes(trace_type, args, exclude_attributes): _settings = { - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, + "distributed_tracing.enabled": True, + "span_events.enabled": True, } - forgone_params = ['invalid_value', ] - expected_params = {'trace1_a': 'foobar', 'trace1_b': 'barbaz'} + forgone_params = [ + "invalid_value", + ] + expected_params = {"trace1_a": "foobar", "trace1_b": "barbaz"} # We expect user_attributes to be included by default if exclude_attributes: count = 0 - _settings['attributes.exclude'] = ['*'] - forgone_params.extend(('trace1_a', 'trace1_b')) + _settings["attributes.exclude"] = ["*"] + forgone_params.extend(("trace1_a", "trace1_b")) expected_trace_params = {} else: expected_trace_params = expected_params @@ -503,44 +527,44 @@ def test_span_event_user_attributes(trace_type, args, exclude_attributes): @validate_span_events( count=count, exact_users=expected_params, - unexpected_users=forgone_params,) - @validate_tt_segment_params(exact_params=expected_trace_params, - forgone_params=forgone_params) - @background_task(name='test_span_event_user_attributes') + unexpected_users=forgone_params, + ) + @validate_tt_segment_params(exact_params=expected_trace_params, forgone_params=forgone_params) + @background_task(name="test_span_event_user_attributes") def _test(): transaction = current_transaction() transaction._sampled = True with trace_type(*args): - add_custom_span_attribute('trace1_a', 'foobar') - add_custom_span_attribute('trace1_b', 'barbaz') - add_custom_span_attribute('invalid_value', sys.maxsize + 1) + add_custom_span_attribute("trace1_a", "foobar") + add_custom_span_attribute("trace1_b", "barbaz") + add_custom_span_attribute("invalid_value", sys.maxsize + 1) _test() -@validate_span_events(count=1, exact_users={'foo': 'b'}) +@validate_span_events(count=1, exact_users={"foo": "b"}) @dt_enabled -@background_task(name='test_span_user_attribute_overrides_transaction_attribute') +@background_task(name="test_span_user_attribute_overrides_transaction_attribute") def test_span_user_attribute_overrides_transaction_attribute(): transaction = current_transaction() - transaction.add_custom_parameter('foo', 'a') - add_custom_span_attribute('foo', 'b') - transaction.add_custom_parameter('foo', 'c') + transaction.add_custom_attribute("foo", "a") + add_custom_span_attribute("foo", "b") + transaction.add_custom_attribute("foo", "c") -@override_application_settings({'attributes.include': '*'}) -@validate_span_events(count=1, exact_agents={'foo': 'b'}) +@override_application_settings({"attributes.include": "*"}) +@validate_span_events(count=1, exact_agents={"foo": "b"}) @dt_enabled -@background_task(name='test_span_agent_attribute_overrides_transaction_attribute') +@background_task(name="test_span_agent_attribute_overrides_transaction_attribute") def test_span_agent_attribute_overrides_transaction_attribute(): transaction = current_transaction() trace = current_trace() - transaction._add_agent_attribute('foo', 'a') - trace._add_agent_attribute('foo', 'b') - transaction._add_agent_attribute('foo', 'c') + transaction._add_agent_attribute("foo", "a") + trace._add_agent_attribute("foo", "b") + transaction._add_agent_attribute("foo", "c") def test_span_custom_attribute_limit(): @@ -555,71 +579,69 @@ def test_span_custom_attribute_limit(): for i in range(128): if i < 64: - span_custom_attrs.append('span_attr%i' % i) - txn_custom_attrs.append('txn_attr%i' % i) + span_custom_attrs.append("span_attr%i" % i) + txn_custom_attrs.append("txn_attr%i" % i) unexpected_txn_attrs.extend(span_custom_attrs) span_custom_attrs.extend(txn_custom_attrs[:64]) - expected_txn_attrs = {'user': txn_custom_attrs, 'agent': [], - 'intrinsic': []} - expected_absent_txn_attrs = {'agent': [], - 'user': unexpected_txn_attrs, - 'intrinsic': []} - - @override_application_settings({'attributes.include': '*'}) - @validate_transaction_event_attributes(expected_txn_attrs, - expected_absent_txn_attrs) - @validate_span_events(count=1, - expected_users=span_custom_attrs, - unexpected_users=txn_custom_attrs[64:]) + expected_txn_attrs = {"user": txn_custom_attrs, "agent": [], "intrinsic": []} + expected_absent_txn_attrs = {"agent": [], "user": unexpected_txn_attrs, "intrinsic": []} + + @override_application_settings({"attributes.include": "*"}) + @validate_transaction_event_attributes(expected_txn_attrs, expected_absent_txn_attrs) + @validate_span_events(count=1, expected_users=span_custom_attrs, unexpected_users=txn_custom_attrs[64:]) @dt_enabled - @background_task(name='test_span_attribute_limit') + @background_task(name="test_span_attribute_limit") def _test(): transaction = current_transaction() for i in range(128): - transaction.add_custom_parameter('txn_attr%i' % i, 'txnValue') + transaction.add_custom_attribute("txn_attr%i" % i, "txnValue") if i < 64: - add_custom_span_attribute('span_attr%i' % i, 'spanValue') + add_custom_span_attribute("span_attr%i" % i, "spanValue") + _test() _span_event_metrics = [("Supportability/SpanEvent/Errors/Dropped", None)] -@pytest.mark.parametrize('trace_type,args', ( - (DatabaseTrace, ('select * from foo', )), - (DatastoreTrace, ('db_product', 'db_target', 'db_operation')), - (ExternalTrace, ('lib', 'url')), - (FunctionTrace, ('name', )), - (MemcacheTrace, ('command', )), - (MessageTrace, ('lib', 'operation', 'dst_type', 'dst_name')), - (SolrTrace, ('lib', 'command')), - (FakeTrace, ()), -)) +@pytest.mark.parametrize( + "trace_type,args", + ( + (DatabaseTrace, ("select * from foo",)), + (DatastoreTrace, ("db_product", "db_target", "db_operation")), + (ExternalTrace, ("lib", "url")), + (FunctionTrace, ("name",)), + (MemcacheTrace, ("command",)), + (MessageTrace, ("lib", "operation", "dst_type", "dst_name")), + (SolrTrace, ("lib", "command")), + (FakeTrace, ()), + ), +) def test_span_event_error_attributes_notice_error(trace_type, args): _settings = { - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, + "distributed_tracing.enabled": True, + "span_events.enabled": True, } error = ValueError("whoops") exact_agents = { - 'error.class': callable_name(error), - 'error.message': 'whoops', + "error.class": callable_name(error), + "error.message": "whoops", } @override_application_settings(_settings) @validate_transaction_metrics( - 'test_span_event_error_attributes_notice_error', - background_task=True, - rollup_metrics=_span_event_metrics) + "test_span_event_error_attributes_notice_error", background_task=True, rollup_metrics=_span_event_metrics + ) @validate_span_events( count=1, - exact_agents=exact_agents,) - @background_task(name='test_span_event_error_attributes_notice_error') + exact_agents=exact_agents, + ) + @background_task(name="test_span_event_error_attributes_notice_error") def _test(): transaction = current_transaction() transaction._sampled = True @@ -633,36 +655,39 @@ def _test(): _test() -@pytest.mark.parametrize('trace_type,args', ( - (DatabaseTrace, ('select * from foo', )), - (DatastoreTrace, ('db_product', 'db_target', 'db_operation')), - (ExternalTrace, ('lib', 'url')), - (FunctionTrace, ('name', )), - (MemcacheTrace, ('command', )), - (MessageTrace, ('lib', 'operation', 'dst_type', 'dst_name')), - (SolrTrace, ('lib', 'command')), -)) +@pytest.mark.parametrize( + "trace_type,args", + ( + (DatabaseTrace, ("select * from foo",)), + (DatastoreTrace, ("db_product", "db_target", "db_operation")), + (ExternalTrace, ("lib", "url")), + (FunctionTrace, ("name",)), + (MemcacheTrace, ("command",)), + (MessageTrace, ("lib", "operation", "dst_type", "dst_name")), + (SolrTrace, ("lib", "command")), + ), +) def test_span_event_error_attributes_observed(trace_type, args): error = ValueError("whoops") exact_agents = { - 'error.class': callable_name(error), - 'error.message': 'whoops', + "error.class": callable_name(error), + "error.message": "whoops", } # Verify errors are not recorded since notice_error is not called - rollups = [('Errors/all', None)] + _span_event_metrics + rollups = [("Errors/all", None)] + _span_event_metrics @dt_enabled @validate_transaction_metrics( - 'test_span_event_error_attributes_observed', - background_task=True, - rollup_metrics=rollups) + "test_span_event_error_attributes_observed", background_task=True, rollup_metrics=rollups + ) @validate_span_events( count=1, - exact_agents=exact_agents,) - @background_task(name='test_span_event_error_attributes_observed') + exact_agents=exact_agents, + ) + @background_task(name="test_span_event_error_attributes_observed") def _test(): try: with trace_type(*args): @@ -673,20 +698,22 @@ def _test(): _test() -@pytest.mark.parametrize('trace_type,args', ( - (DatabaseTrace, ('select * from foo', )), - (DatastoreTrace, ('db_product', 'db_target', 'db_operation')), - (ExternalTrace, ('lib', 'url')), - (FunctionTrace, ('name', )), - (MemcacheTrace, ('command', )), - (MessageTrace, ('lib', 'operation', 'dst_type', 'dst_name')), - (SolrTrace, ('lib', 'command')), - (FakeTrace, ()), -)) +@pytest.mark.parametrize( + "trace_type,args", + ( + (DatabaseTrace, ("select * from foo",)), + (DatastoreTrace, ("db_product", "db_target", "db_operation")), + (ExternalTrace, ("lib", "url")), + (FunctionTrace, ("name",)), + (MemcacheTrace, ("command",)), + (MessageTrace, ("lib", "operation", "dst_type", "dst_name")), + (SolrTrace, ("lib", "command")), + (FakeTrace, ()), + ), +) @dt_enabled -@validate_span_events(count=1, - exact_agents={'error.class': ERROR_NAME, 'error.message': 'whoops'}) -@background_task(name='test_span_event_notice_error_overrides_observed') +@validate_span_events(count=1, exact_agents={"error.class": ERROR_NAME, "error.message": "whoops"}) +@background_task(name="test_span_event_notice_error_overrides_observed") def test_span_event_notice_error_overrides_observed(trace_type, args): try: with trace_type(*args): @@ -699,21 +726,24 @@ def test_span_event_notice_error_overrides_observed(trace_type, args): pass -@pytest.mark.parametrize('trace_type,args', ( - (DatabaseTrace, ('select * from foo', )), - (DatastoreTrace, ('db_product', 'db_target', 'db_operation')), - (ExternalTrace, ('lib', 'url')), - (FunctionTrace, ('name', )), - (MemcacheTrace, ('command', )), - (MessageTrace, ('lib', 'operation', 'dst_type', 'dst_name')), - (SolrTrace, ('lib', 'command')), - (FakeTrace, ()), -)) -@override_application_settings({'error_collector.enabled': False}) -@validate_span_events(count=0, expected_agents=['error.class']) -@validate_span_events(count=0, expected_agents=['error.message']) +@pytest.mark.parametrize( + "trace_type,args", + ( + (DatabaseTrace, ("select * from foo",)), + (DatastoreTrace, ("db_product", "db_target", "db_operation")), + (ExternalTrace, ("lib", "url")), + (FunctionTrace, ("name",)), + (MemcacheTrace, ("command",)), + (MessageTrace, ("lib", "operation", "dst_type", "dst_name")), + (SolrTrace, ("lib", "command")), + (FakeTrace, ()), + ), +) +@override_application_settings({"error_collector.enabled": False}) +@validate_span_events(count=0, expected_agents=["error.class"]) +@validate_span_events(count=0, expected_agents=["error.message"]) @dt_enabled -@background_task(name='test_span_event_errors_disabled') +@background_task(name="test_span_event_errors_disabled") def test_span_event_errors_disabled(trace_type, args): with trace_type(*args): try: @@ -725,32 +755,34 @@ def test_span_event_errors_disabled(trace_type, args): _metrics = [("Supportability/SpanEvent/Errors/Dropped", 2)] -@pytest.mark.parametrize('trace_type,args', ( - (FunctionTrace, ('name', )), - (FakeTrace, ()), -)) +@pytest.mark.parametrize( + "trace_type,args", + ( + (FunctionTrace, ("name",)), + (FakeTrace, ()), + ), +) def test_span_event_multiple_errors(trace_type, args): _settings = { - 'distributed_tracing.enabled': True, - 'span_events.enabled': True, + "distributed_tracing.enabled": True, + "span_events.enabled": True, } error = ValueError("whoops") exact_agents = { - 'error.class': callable_name(error), - 'error.message': 'whoops', + "error.class": callable_name(error), + "error.message": "whoops", "error.expected": False, } @override_application_settings(_settings) @validate_span_events( count=1, - exact_agents=exact_agents,) - @validate_transaction_metrics("test_span_event_multiple_errors", - background_task=True, - rollup_metrics=_metrics) - @background_task(name='test_span_event_multiple_errors') + exact_agents=exact_agents, + ) + @validate_transaction_metrics("test_span_event_multiple_errors", background_task=True, rollup_metrics=_metrics) + @background_task(name="test_span_event_multiple_errors") def _test(): transaction = current_transaction() transaction._sampled = True diff --git a/tests/cross_agent/test_rum_client_config.py b/tests/cross_agent/test_rum_client_config.py index d60cff7773..c2a4a465f9 100644 --- a/tests/cross_agent/test_rum_client_config.py +++ b/tests/cross_agent/test_rum_client_config.py @@ -14,94 +14,118 @@ import json import os + import pytest import webtest +from testing_support.fixtures import override_application_settings -from newrelic.api.transaction import (set_transaction_name, - add_custom_parameter, get_browser_timing_footer) +from newrelic.api.transaction import ( + add_custom_attribute, + get_browser_timing_footer, + set_transaction_name, +) from newrelic.api.wsgi_application import wsgi_application -from testing_support.fixtures import override_application_settings - def _load_tests(): - fixture = os.path.join(os.curdir, 'fixtures', 'rum_client_config.json') - with open(fixture, 'r') as fh: + fixture = os.path.join(os.curdir, "fixtures", "rum_client_config.json") + with open(fixture, "r") as fh: js = fh.read() return json.loads(js) -fields = ['testname', 'apptime_milliseconds', 'queuetime_milliseconds', - 'browser_monitoring.attributes.enabled', 'transaction_name', - 'license_key', 'connect_reply', 'user_attributes', 'expected'] + +fields = [ + "testname", + "apptime_milliseconds", + "queuetime_milliseconds", + "browser_monitoring.attributes.enabled", + "transaction_name", + "license_key", + "connect_reply", + "user_attributes", + "expected", +] # Replace . as not a valid character in python argument names -field_names = ','.join([f.replace('.', '_') for f in fields]) +field_names = ",".join([f.replace(".", "_") for f in fields]) + def _parametrize_test(test): return tuple([test.get(f, None) for f in fields]) + _rum_tests = [_parametrize_test(t) for t in _load_tests()] + @wsgi_application() def target_wsgi_application(environ, start_response): - status = '200 OK' + status = "200 OK" - txn_name = environ.get('txn_name') - set_transaction_name(txn_name, group='') + txn_name = environ.get("txn_name") + set_transaction_name(txn_name, group="") - user_attrs = json.loads(environ.get('user_attrs')) + user_attrs = json.loads(environ.get("user_attrs")) for key, value in user_attrs.items(): - add_custom_parameter(key, value) + add_custom_attribute(key, value) - text = '%s

RESPONSE

' + text = "%s

RESPONSE

" - output = (text % get_browser_timing_footer()).encode('UTF-8') + output = (text % get_browser_timing_footer()).encode("UTF-8") - response_headers = [('Content-Type', 'text/html; charset=utf-8'), - ('Content-Length', str(len(output)))] + response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] start_response(status, response_headers) return [output] + target_application = webtest.TestApp(target_wsgi_application) + @pytest.mark.parametrize(field_names, _rum_tests) -def test_browser_montioring(testname, apptime_milliseconds, queuetime_milliseconds, - browser_monitoring_attributes_enabled, transaction_name, - license_key, connect_reply, user_attributes, expected): +def test_browser_montioring( + testname, + apptime_milliseconds, + queuetime_milliseconds, + browser_monitoring_attributes_enabled, + transaction_name, + license_key, + connect_reply, + user_attributes, + expected, +): settings = { - 'browser_monitoring.attributes.enabled': browser_monitoring_attributes_enabled, - 'license_key': license_key, - 'js_agent_loader': u'', - } + "browser_monitoring.attributes.enabled": browser_monitoring_attributes_enabled, + "license_key": license_key, + "js_agent_loader": "", + } settings.update(connect_reply) @override_application_settings(settings) def run_browser_data_test(): - response = target_application.get('/', - extra_environ={'txn_name': str(transaction_name), - 'user_attrs': json.dumps(user_attributes)}) + response = target_application.get( + "/", extra_environ={"txn_name": str(transaction_name), "user_attrs": json.dumps(user_attributes)} + ) # We actually put the "footer" in the header, the first script is the # agent "header", the second one is where the data lives, hence the [1]. - footer = response.html.html.head.find_all('script')[1] - footer_data = json.loads(footer.string.split('NREUM.info=')[1]) + footer = response.html.html.head.find_all("script")[1] + footer_data = json.loads(footer.string.split("NREUM.info=")[1]) # Not feasible to test the time metric values in testing - expected.pop('queueTime') - expected.pop('applicationTime') - assert footer_data['applicationTime'] >= 0 - assert footer_data['queueTime'] >= 0 + expected.pop("queueTime") + expected.pop("applicationTime") + assert footer_data["applicationTime"] >= 0 + assert footer_data["queueTime"] >= 0 # Python always prepends stuff to the transaction name, so this # doesn't match the obscured value. - expected.pop('transactionName') + expected.pop("transactionName") # Check that all other values are correct @@ -112,7 +136,7 @@ def run_browser_data_test(): # don't omit it, so we need to special case 'atts' when we compare # to 'expected'. - if key == 'atts' and value == '': + if key == "atts" and value == "": assert key not in footer_data else: assert footer_data[key] == value diff --git a/tests/testing_support/sample_applications.py b/tests/testing_support/sample_applications.py index 0d5f03b8b3..74d5e6dbec 100644 --- a/tests/testing_support/sample_applications.py +++ b/tests/testing_support/sample_applications.py @@ -17,127 +17,132 @@ try: from urllib2 import urlopen # Py2.X except ImportError: - from urllib.request import urlopen # Py3.X + from urllib.request import urlopen # Py3.X import sqlite3 as db from newrelic.api.time_trace import notice_error -from newrelic.api.transaction import (add_custom_parameter, - get_browser_timing_header, get_browser_timing_footer, - record_custom_event) +from newrelic.api.transaction import ( + add_custom_attribute, + get_browser_timing_footer, + get_browser_timing_header, + record_custom_event, +) from newrelic.api.wsgi_application import wsgi_application _logger = logging.getLogger(__name__) _custom_parameters = { - 'user' : 'user-name', - 'account' : 'account-name', - 'product' : 'product-name', - 'bytes' : b'bytes-value', - 'string' : 'string-value', - 'unicode' : u'unicode-value', - 'integer' : 1, - 'float' : 1.0, - 'invalid-utf8' : b'\xe2', - 'multibyte-utf8' : b'\xe2\x88\x9a', - 'multibyte-unicode' : b'\xe2\x88\x9a'.decode('utf-8'), - 'list' : [], - 'tuple' : (), - 'dict' : {}, + "user": "user-name", + "account": "account-name", + "product": "product-name", + "bytes": b"bytes-value", + "string": "string-value", + "unicode": "unicode-value", + "integer": 1, + "float": 1.0, + "invalid-utf8": b"\xe2", + "multibyte-utf8": b"\xe2\x88\x9a", + "multibyte-unicode": b"\xe2\x88\x9a".decode("utf-8"), + "list": [], + "tuple": (), + "dict": {}, } -_err_param = { - 'err-param' : 'value' -} +_err_param = {"err-param": "value"} + def user_attributes_added(): """Expected values when the custom parameters in this file are added as user attributes """ user_attributes = _custom_parameters.copy() - user_attributes['list'] = '[]' - user_attributes['tuple'] = '()' - user_attributes['dict'] = '{}' + user_attributes["list"] = "[]" + user_attributes["tuple"] = "()" + user_attributes["dict"] = "{}" return user_attributes + def error_user_params_added(): return _err_param.copy() + @wsgi_application() def fully_featured_app(environ, start_response): - status = '200 OK' + status = "200 OK" - path = environ.get('PATH_INFO') - use_user_attrs = environ.get('record_attributes', 'TRUE') == 'TRUE' + path = environ.get("PATH_INFO") + use_user_attrs = environ.get("record_attributes", "TRUE") == "TRUE" - environ['wsgi.input'].read() - environ['wsgi.input'].readline() - environ['wsgi.input'].readlines() + environ["wsgi.input"].read() + environ["wsgi.input"].readline() + environ["wsgi.input"].readlines() if use_user_attrs: for attr, val in _custom_parameters.items(): - add_custom_parameter(attr, val) + add_custom_attribute(attr, val) - if 'db' in environ and int(environ['db']) > 0: + if "db" in environ and int(environ["db"]) > 0: connection = db.connect(":memory:") - for i in range(int(environ['db']) - 1): + for i in range(int(environ["db"]) - 1): connection.execute("create table test_db%d (a, b, c)" % i) - if 'external' in environ: - for i in range(int(environ['external'])): - r = urlopen('http://www.python.org') + if "external" in environ: + for i in range(int(environ["external"])): + r = urlopen("http://www.python.org") # nosec r.read(10) - if 'err_message' in environ: - n_errors = int(environ.get('n_errors', 1)) + if "err_message" in environ: + n_errors = int(environ.get("n_errors", 1)) for i in range(n_errors): try: # append number to stats engine to get unique errors, so they # don't immediately get filtered out. - raise ValueError(environ['err_message'] + str(i)) + raise ValueError(environ["err_message"] + str(i)) except ValueError: if use_user_attrs: notice_error(attributes=_err_param) else: notice_error() - text = '%s

RESPONSE

%s' + text = "%s

RESPONSE

%s" - output = (text % (get_browser_timing_header(), - get_browser_timing_footer())).encode('UTF-8') + 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)))] + response_headers = [("Content-type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))] write = start_response(status, response_headers) - write(b'') + write(b"") return [output] + @wsgi_application() def simple_exceptional_app(environ, start_response): - start_response('500 :(',[]) + start_response("500 :(", []) + + raise ValueError("Transaction had bad value") - raise ValueError('Transaction had bad value') @wsgi_application() def simple_app(environ, start_response): - status = '200 OK' + status = "200 OK" _logger.info("Starting response") start_response(status, response_headers=[]) return [] + @wsgi_application() def simple_custom_event_app(environ, start_response): - params = {'snowman': u'\u2603', 'foo': 'bar'} - record_custom_event('SimpleAppEvent', params) + params = {"snowman": "\u2603", "foo": "bar"} + record_custom_event("SimpleAppEvent", params) - start_response(status='200 OK', response_headers=[]) + start_response(status="200 OK", response_headers=[]) return [] diff --git a/tests/testing_support/sample_asgi_applications.py b/tests/testing_support/sample_asgi_applications.py index 53bf40d334..9118883b09 100644 --- a/tests/testing_support/sample_asgi_applications.py +++ b/tests/testing_support/sample_asgi_applications.py @@ -15,7 +15,7 @@ from newrelic.api.asgi_application import ASGIApplicationWrapper from newrelic.api.time_trace import notice_error from newrelic.api.transaction import ( - add_custom_parameter, + add_custom_attribute, current_transaction, ignore_transaction, ) @@ -101,8 +101,8 @@ async def __call__(self, scope, receive, send): @ASGIApplicationWrapper async def normal_asgi_application(scope, receive, send): output = b"header

RESPONSE

" - add_custom_parameter("puppies", "test_value") - add_custom_parameter("sunshine", "test_value") + add_custom_attribute("puppies", "test_value") + add_custom_attribute("sunshine", "test_value") response_headers = [ (b"content-type", b"text/html; charset=utf-8"), From 8690fe3a02bee4ee381cabd50292b3ca5f3b44ab Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Wed, 12 Oct 2022 12:42:07 -0700 Subject: [PATCH 2/5] Fix unicode tests and some pylint errors --- newrelic/api/transaction.py | 8 +- newrelic/core/attribute.py | 158 ++++++++++------------ tests/agent_features/test_asgi_browser.py | 4 +- tests/agent_features/test_attribute.py | 9 +- tests/agent_features/test_browser.py | 4 +- 5 files changed, 89 insertions(+), 94 deletions(-) diff --git a/newrelic/api/transaction.py b/newrelic/api/transaction.py index c0e07cea7c..d828535b98 100644 --- a/newrelic/api/transaction.py +++ b/newrelic/api/transaction.py @@ -29,7 +29,6 @@ import newrelic.core.error_node import newrelic.core.root_node import newrelic.core.transaction_node -import newrelic.packages.six as six from newrelic.api.application import application_instance from newrelic.api.time_trace import TimeTrace, get_linking_metadata from newrelic.common.encoding_utils import ( @@ -71,6 +70,7 @@ TraceCacheNoActiveTraceError, trace_cache, ) +from newrelic.packages import six _logger = logging.getLogger(__name__) @@ -120,7 +120,7 @@ def complete_root(self): self.exited = True @staticmethod - def complete_trace(): + def complete_trace(): # pylint: disable=arguments-differ pass @property @@ -832,7 +832,7 @@ def trace_intrinsics(self): # Add in special CPU time value for UI to display CPU burn. - # XXX Disable cpu time value for CPU burn as was + # TODO: Disable cpu time value for CPU burn as was # previously reporting incorrect value and we need to # fix it, at least on Linux to report just the CPU time # for the executing thread. @@ -1571,7 +1571,7 @@ def _create_error_node(self, settings, fullname, message, expected, custom_param source=source, ) - # TODO Errors are recorded in time order. If + # TODO: Errors are recorded in time order. If # there are two exceptions of same type and # different message, the UI displays the first # one. In the PHP agent it was recording the diff --git a/newrelic/core/attribute.py b/newrelic/core/attribute.py index 4c26739396..c5f19e4c06 100644 --- a/newrelic/core/attribute.py +++ b/newrelic/core/attribute.py @@ -13,20 +13,21 @@ # limitations under the License. import logging - from collections import namedtuple +from newrelic.core.attribute_filter import ( + DST_ALL, + DST_ERROR_COLLECTOR, + DST_SPAN_EVENTS, + DST_TRANSACTION_EVENTS, + DST_TRANSACTION_SEGMENTS, + DST_TRANSACTION_TRACER, +) from newrelic.packages import six -from newrelic.core.attribute_filter import (DST_ALL, DST_ERROR_COLLECTOR, - DST_TRANSACTION_TRACER, DST_TRANSACTION_EVENTS, DST_SPAN_EVENTS, - DST_TRANSACTION_SEGMENTS) - - _logger = logging.getLogger(__name__) -_Attribute = namedtuple('_Attribute', - ['name', 'value', 'destinations']) +_Attribute = namedtuple("_Attribute", ["name", "value", "destinations"]) # The following destinations are created here, never changed, and only # used in create_agent_attributes. It is placed at the module level here @@ -34,61 +35,59 @@ # All agent attributes go to transaction traces and error traces by default. -_DESTINATIONS = (DST_ERROR_COLLECTOR | - DST_TRANSACTION_TRACER | - DST_TRANSACTION_SEGMENTS) -_DESTINATIONS_WITH_EVENTS = (_DESTINATIONS | - DST_TRANSACTION_EVENTS | - DST_SPAN_EVENTS) +_DESTINATIONS = DST_ERROR_COLLECTOR | DST_TRANSACTION_TRACER | DST_TRANSACTION_SEGMENTS +_DESTINATIONS_WITH_EVENTS = _DESTINATIONS | DST_TRANSACTION_EVENTS | DST_SPAN_EVENTS # The following subset goes to transaction events by default. -_TRANSACTION_EVENT_DEFAULT_ATTRIBUTES = set(( - 'host.displayName', - 'request.method', - 'request.headers.contentType', - 'request.headers.contentLength', - 'request.uri', - 'response.status', - 'request.headers.accept', - 'response.headers.contentLength', - 'response.headers.contentType', - 'request.headers.host', - 'request.headers.userAgent', - 'message.queueName', - 'message.routingKey', - 'http.url', - 'http.statusCode', - 'aws.requestId', - 'aws.operation', - 'aws.lambda.arn', - 'aws.lambda.coldStart', - 'aws.lambda.eventSource.arn', +_TRANSACTION_EVENT_DEFAULT_ATTRIBUTES = set( + ( + "host.displayName", + "request.method", + "request.headers.contentType", + "request.headers.contentLength", + "request.uri", + "response.status", + "request.headers.accept", + "response.headers.contentLength", + "response.headers.contentType", + "request.headers.host", + "request.headers.userAgent", + "message.queueName", + "message.routingKey", + "http.url", + "http.statusCode", + "aws.requestId", + "aws.operation", + "aws.lambda.arn", + "aws.lambda.coldStart", + "aws.lambda.eventSource.arn", "db.collection", - 'db.instance', - 'db.operation', - 'db.statement', - 'error.class', - 'error.message', - 'error.expected', - 'peer.hostname', - 'peer.address', - 'graphql.field.name', - 'graphql.field.parentType', - 'graphql.field.path', - 'graphql.field.returnType', - 'graphql.operation.name', - 'graphql.operation.type', - 'graphql.operation.query', + "db.instance", + "db.operation", + "db.statement", + "error.class", + "error.message", + "error.expected", + "peer.hostname", + "peer.address", + "graphql.field.name", + "graphql.field.parentType", + "graphql.field.path", + "graphql.field.returnType", + "graphql.operation.name", + "graphql.operation.type", + "graphql.operation.query", "code.filepath", "code.function", "code.lineno", "code.namespace", -)) + ) +) MAX_NUM_USER_ATTRIBUTES = 128 MAX_ATTRIBUTE_LENGTH = 255 -MAX_64_BIT_INT = 2 ** 63 - 1 +MAX_64_BIT_INT = 2**63 - 1 MAX_LOG_MESSAGE_LENGTH = 32768 @@ -109,10 +108,8 @@ class CastingFailureException(Exception): class Attribute(_Attribute): - def __repr__(self): - return "Attribute(name=%r, value=%r, destinations=%r)" % ( - self.name, self.value, bin(self.destinations)) + return "Attribute(name=%r, value=%r, destinations=%r)" % (self.name, self.value, bin(self.destinations)) def create_attributes(attr_dict, destinations, attribute_filter): @@ -142,8 +139,7 @@ def create_agent_attributes(attr_dict, attribute_filter): return attributes -def resolve_user_attributes( - attr_dict, attribute_filter, target_destination, attr_class=dict): +def resolve_user_attributes(attr_dict, attribute_filter, target_destination, attr_class=dict): u_attrs = attr_class() for attr_name, attr_value in attr_dict.items(): @@ -158,8 +154,7 @@ def resolve_user_attributes( return u_attrs -def resolve_agent_attributes( - attr_dict, attribute_filter, target_destination, attr_class=dict): +def resolve_agent_attributes(attr_dict, attribute_filter, target_destination, attr_class=dict): a_attrs = attr_class() for attr_name, attr_value in attr_dict.items(): @@ -182,10 +177,9 @@ def create_user_attributes(attr_dict, attribute_filter): return create_attributes(attr_dict, destinations, attribute_filter) -def truncate( - text, maxsize=MAX_ATTRIBUTE_LENGTH, encoding='utf-8', ending=None): +def truncate(text, maxsize=MAX_ATTRIBUTE_LENGTH, encoding="utf-8", ending=None): - # Truncate text so that it's byte representation + # Truncate text so that its byte representation # is no longer than maxsize bytes. # If text is unicode (Python 2 or 3), return unicode. @@ -198,21 +192,21 @@ def truncate( ending = ending and ending.encode(encoding) if ending and truncated != text: - truncated = truncated[:-len(ending)] + ending + truncated = truncated[: -len(ending)] + ending return truncated -def _truncate_unicode(u, maxsize, encoding='utf-8'): +def _truncate_unicode(u, maxsize, encoding="utf-8"): encoded = u.encode(encoding)[:maxsize] - return encoded.decode(encoding, 'ignore') + return encoded.decode(encoding, "ignore") def _truncate_bytes(s, maxsize): return s[:maxsize] -def check_name_length(name, max_length=MAX_ATTRIBUTE_LENGTH, encoding='utf-8'): +def check_name_length(name, max_length=MAX_ATTRIBUTE_LENGTH, encoding="utf-8"): trunc_name = truncate(name, max_length, encoding) if name != trunc_name: raise NameTooLongException() @@ -228,8 +222,7 @@ def check_max_int(value, max_int=MAX_64_BIT_INT): raise IntTooLargeException() -def process_user_attribute( - name, value, max_length=MAX_ATTRIBUTE_LENGTH, ending=None): +def process_user_attribute(name, value, max_length=MAX_ATTRIBUTE_LENGTH, ending=None): # Perform all necessary checks on a potential attribute. # @@ -250,23 +243,19 @@ def process_user_attribute( value = sanitize(value) except NameIsNotStringException: - _logger.debug('Attribute name must be a string. Dropping ' - 'attribute: %r=%r', name, value) + _logger.debug("Attribute name must be a string. Dropping " "attribute: %r=%r", name, value) return FAILED_RESULT except NameTooLongException: - _logger.debug('Attribute name exceeds maximum length. Dropping ' - 'attribute: %r=%r', name, value) + _logger.debug("Attribute name exceeds maximum length. Dropping " "attribute: %r=%r", name, value) return FAILED_RESULT except IntTooLargeException: - _logger.debug('Attribute value exceeds maximum integer value. ' - 'Dropping attribute: %r=%r', name, value) + _logger.debug("Attribute value exceeds maximum integer value. " "Dropping attribute: %r=%r", name, value) return FAILED_RESULT except CastingFailureException: - _logger.debug('Attribute value cannot be cast to a string. ' - 'Dropping attribute: %r=%r', name, value) + _logger.debug("Attribute value cannot be cast to a string. " "Dropping attribute: %r=%r", name, value) return FAILED_RESULT else: @@ -278,9 +267,12 @@ def process_user_attribute( if isinstance(value, valid_types_text): trunc_value = truncate(value, maxsize=max_length, ending=ending) if value != trunc_value: - _logger.debug('Attribute value exceeds maximum length ' - '(%r bytes). Truncating value: %r=%r.', - max_length, name, trunc_value) + _logger.debug( + "Attribute value exceeds maximum length " "(%r bytes). Truncating value: %r=%r.", + max_length, + name, + trunc_value, + ) value = trunc_value @@ -294,8 +286,7 @@ def sanitize(value): # # Raise CastingFailureException, if str(value) somehow fails. - valid_value_types = (six.text_type, six.binary_type, bool, float, - six.integer_types) + valid_value_types = (six.text_type, six.binary_type, bool, float, six.integer_types) if not isinstance(value, valid_value_types): original = value @@ -305,7 +296,8 @@ def sanitize(value): except Exception: raise CastingFailureException() else: - _logger.debug('Attribute value is of type: %r. Casting %r to ' - 'string: %s', type(original), original, value) + _logger.debug( + "Attribute value is of type: %r. Casting %r to " "string: %s", type(original), original, value + ) return value diff --git a/tests/agent_features/test_asgi_browser.py b/tests/agent_features/test_asgi_browser.py index 9cce245e41..a1c3daeb74 100644 --- a/tests/agent_features/test_asgi_browser.py +++ b/tests/agent_features/test_asgi_browser.py @@ -109,8 +109,8 @@ def test_footer_attributes(): obfuscation_key = settings.license_key[:13] - assert isinstance(data["transactionName"], str) - # assert type(data["transactionName"]) == type("") + type_transaction_data = unicode if six.PY2 else str # noqa: F821 + assert isinstance(data["transactionName"], type_transaction_data) txn_name = deobfuscate(data["transactionName"], obfuscation_key) diff --git a/tests/agent_features/test_attribute.py b/tests/agent_features/test_attribute.py index 9237b2f680..ab6f778ddb 100644 --- a/tests/agent_features/test_attribute.py +++ b/tests/agent_features/test_attribute.py @@ -203,7 +203,8 @@ def test_truncate_bytes(): def test_truncate_unicode_snowman(): # '\u2603' is 'SNOWMAN' - u = "snow\u2603" + # decode("unicode-escape") is used to get Py2 unicode + u = "snow\u2603".decode("unicode-escape") if six.PY2 else "snow\u2603" assert u.encode("utf-8") == b"snow\xe2\x98\x83" result = truncate(u, maxsize=5) assert isinstance(result, six.text_type) @@ -212,7 +213,8 @@ def test_truncate_unicode_snowman(): def test_truncate_combining_characters(): # '\u0308' is 'COMBINING DIAERESIS' (AKA 'umlaut') - u = "Zoe\u0308" + # decode("unicode-escape") is used to get Py2 unicode + u = "Zoe\u0308".decode("unicode-escape") if six.PY2 else "Zoe\u0308" assert u.encode("utf-8") == b"Zoe\xcc\x88" # truncate will chop off 'COMBINING DIAERESIS', which leaves @@ -238,7 +240,8 @@ def test_truncate_empty_bytes(): def test_truncate_empty_unicode(): - u = "" + # decode("unicode-escape") is used to get Py2 unicode + u = "".decode("unicode-escape") if six.PY2 else "" result = truncate(u, maxsize=5) assert isinstance(result, six.text_type) assert result == "" diff --git a/tests/agent_features/test_browser.py b/tests/agent_features/test_browser.py index 5e451be2ed..b5ca867d5e 100644 --- a/tests/agent_features/test_browser.py +++ b/tests/agent_features/test_browser.py @@ -106,8 +106,8 @@ def test_footer_attributes(): obfuscation_key = settings.license_key[:13] - assert isinstance(data["transactionName"], str) - # assert type(data["transactionName"]) == type("") + type_transaction_data = unicode if six.PY2 else str # noqa: F821 + assert isinstance(data["transactionName"], type_transaction_data) txn_name = deobfuscate(data["transactionName"], obfuscation_key) From 807ec1c5c40fe421300ccdcd6fedd81f288dce2c Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Wed, 12 Oct 2022 13:44:29 -0700 Subject: [PATCH 3/5] Fix more pylint errors --- newrelic/admin/validate_config.py | 2 +- newrelic/api/transaction.py | 15 ++++++++------- newrelic/core/attribute.py | 14 ++++++-------- tests/agent_features/test_span_events.py | 9 +++++---- tests/testing_support/sample_asgi_applications.py | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/newrelic/admin/validate_config.py b/newrelic/admin/validate_config.py index ac25b715e1..52b73110f1 100644 --- a/newrelic/admin/validate_config.py +++ b/newrelic/admin/validate_config.py @@ -212,7 +212,7 @@ def validate_config(args): if not _application.active: _logger.error( - "Unable to register application for test, " "connection could not be established within %s seconds.", + "Unable to register application for test, connection could not be established within %s seconds.", _timeout, ) return diff --git a/newrelic/api/transaction.py b/newrelic/api/transaction.py index d828535b98..6177d401cd 100644 --- a/newrelic/api/transaction.py +++ b/newrelic/api/transaction.py @@ -26,7 +26,6 @@ from collections import OrderedDict import newrelic.core.database_node -import newrelic.core.error_node import newrelic.core.root_node import newrelic.core.transaction_node from newrelic.api.application import application_instance @@ -61,6 +60,7 @@ ) from newrelic.core.config import DEFAULT_RESERVOIR_SIZE, LOG_EVENT_RESERVOIR_SIZE from newrelic.core.custom_event import create_custom_event +from newrelic.core.error_node import ErrorNode from newrelic.core.log_event_node import LogEventNode from newrelic.core.stack_trace import exception_stack from newrelic.core.stats_engine import CustomMetrics, SampledDataSet @@ -1558,7 +1558,7 @@ def _create_error_node(self, settings, fullname, message, expected, custom_param if error.type == fullname and error.message == message: return - node = newrelic.core.error_node.ErrorNode( + node = ErrorNode( timestamp=time.time(), type=fullname, message=message, @@ -1609,7 +1609,8 @@ def _process_node(self, node): node.node_count = self._trace_node_count self.total_time += node.exclusive - if type(node) is newrelic.core.database_node.DatabaseNode: + # if type(node) is newrelic.core.database_node.DatabaseNode: + if isinstance(newrelic.core.database_node.DatabaseNode, ErrorNode): settings = self._settings if not settings: return @@ -1675,7 +1676,7 @@ def add_custom_attributes(self, items): def add_custom_parameter(self, name, value): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + ("The add_custom_parameter API has been deprecated. Please use the add_custom_attribute API."), DeprecationWarning, ) return self.add_custom_attribute(name, value) @@ -1683,7 +1684,7 @@ def add_custom_parameter(self, name, value): def add_custom_parameters(self, items): # Deprecation warning warnings.warn( - ("The add_custom_parameters API has been deprecated. " "Please use the add_custom_attributes API."), + ("The add_custom_parameters API has been deprecated. Please use the add_custom_attributes API."), DeprecationWarning, ) return self.add_custom_attributes(items) @@ -1779,7 +1780,7 @@ def add_custom_attributes(items): def add_custom_parameter(key, value): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + ("The add_custom_parameter API has been deprecated. Please use the add_custom_attribute API."), DeprecationWarning, ) return add_custom_attribute(key, value) @@ -1788,7 +1789,7 @@ def add_custom_parameter(key, value): def add_custom_parameters(items): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + ("The add_custom_parameter API has been deprecated. Please use the add_custom_attribute API."), DeprecationWarning, ) return add_custom_attributes(items) diff --git a/newrelic/core/attribute.py b/newrelic/core/attribute.py index c5f19e4c06..e72ab071ec 100644 --- a/newrelic/core/attribute.py +++ b/newrelic/core/attribute.py @@ -243,19 +243,19 @@ def process_user_attribute(name, value, max_length=MAX_ATTRIBUTE_LENGTH, ending= value = sanitize(value) except NameIsNotStringException: - _logger.debug("Attribute name must be a string. Dropping " "attribute: %r=%r", name, value) + _logger.debug("Attribute name must be a string. Dropping attribute: %r=%r", name, value) return FAILED_RESULT except NameTooLongException: - _logger.debug("Attribute name exceeds maximum length. Dropping " "attribute: %r=%r", name, value) + _logger.debug("Attribute name exceeds maximum length. Dropping attribute: %r=%r", name, value) return FAILED_RESULT except IntTooLargeException: - _logger.debug("Attribute value exceeds maximum integer value. " "Dropping attribute: %r=%r", name, value) + _logger.debug("Attribute value exceeds maximum integer value. Dropping attribute: %r=%r", name, value) return FAILED_RESULT except CastingFailureException: - _logger.debug("Attribute value cannot be cast to a string. " "Dropping attribute: %r=%r", name, value) + _logger.debug("Attribute value cannot be cast to a string. Dropping attribute: %r=%r", name, value) return FAILED_RESULT else: @@ -268,7 +268,7 @@ def process_user_attribute(name, value, max_length=MAX_ATTRIBUTE_LENGTH, ending= trunc_value = truncate(value, maxsize=max_length, ending=ending) if value != trunc_value: _logger.debug( - "Attribute value exceeds maximum length " "(%r bytes). Truncating value: %r=%r.", + "Attribute value exceeds maximum length (%r bytes). Truncating value: %r=%r.", max_length, name, trunc_value, @@ -296,8 +296,6 @@ def sanitize(value): except Exception: raise CastingFailureException() else: - _logger.debug( - "Attribute value is of type: %r. Casting %r to " "string: %s", type(original), original, value - ) + _logger.debug("Attribute value is of type: %r. Casting %r to string: %s", type(original), original, value) return value diff --git a/tests/agent_features/test_span_events.py b/tests/agent_features/test_span_events.py index 4656131696..3faa73a0c8 100644 --- a/tests/agent_features/test_span_events.py +++ b/tests/agent_features/test_span_events.py @@ -413,10 +413,11 @@ def _test(): @pytest.mark.parametrize("span_events_enabled", (False, True)) def test_collect_span_events_override(collect_span_events, span_events_enabled): - if collect_span_events and span_events_enabled: - spans_expected = True - else: - spans_expected = False + # if collect_span_events and span_events_enabled: + # spans_expected = True + # else: + # spans_expected = False + spans_expected = collect_span_events and span_events_enabled span_count = 2 if spans_expected else 0 diff --git a/tests/testing_support/sample_asgi_applications.py b/tests/testing_support/sample_asgi_applications.py index 9118883b09..62cd5330c3 100644 --- a/tests/testing_support/sample_asgi_applications.py +++ b/tests/testing_support/sample_asgi_applications.py @@ -34,7 +34,7 @@ async def __call__(self, receive, send): if self.scope["path"] == "/exc": raise ValueError("whoopsies") - elif self.scope["path"] == "/ignored": + if self.scope["path"] == "/ignored": ignore_transaction() await send({"type": "http.response.start", "status": 200}) @@ -57,7 +57,7 @@ async def simple_app_v3_raw(scope, receive, send): if scope["path"] == "/exc": raise ValueError("whoopsies") - elif scope["path"] == "/ignored": + if scope["path"] == "/ignored": ignore_transaction() await send({"type": "http.response.start", "status": 200}) From 66b1b1e8846b919d1ba8563b1e9346205465ebf6 Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Wed, 12 Oct 2022 16:42:13 -0700 Subject: [PATCH 4/5] Revert "Fix more pylint errors" This reverts commit 807ec1c5c40fe421300ccdcd6fedd81f288dce2c. --- newrelic/admin/validate_config.py | 2 +- newrelic/api/transaction.py | 15 +++++++-------- newrelic/core/attribute.py | 14 ++++++++------ tests/agent_features/test_span_events.py | 9 ++++----- tests/testing_support/sample_asgi_applications.py | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/newrelic/admin/validate_config.py b/newrelic/admin/validate_config.py index 52b73110f1..ac25b715e1 100644 --- a/newrelic/admin/validate_config.py +++ b/newrelic/admin/validate_config.py @@ -212,7 +212,7 @@ def validate_config(args): if not _application.active: _logger.error( - "Unable to register application for test, connection could not be established within %s seconds.", + "Unable to register application for test, " "connection could not be established within %s seconds.", _timeout, ) return diff --git a/newrelic/api/transaction.py b/newrelic/api/transaction.py index 6177d401cd..d828535b98 100644 --- a/newrelic/api/transaction.py +++ b/newrelic/api/transaction.py @@ -26,6 +26,7 @@ from collections import OrderedDict import newrelic.core.database_node +import newrelic.core.error_node import newrelic.core.root_node import newrelic.core.transaction_node from newrelic.api.application import application_instance @@ -60,7 +61,6 @@ ) from newrelic.core.config import DEFAULT_RESERVOIR_SIZE, LOG_EVENT_RESERVOIR_SIZE from newrelic.core.custom_event import create_custom_event -from newrelic.core.error_node import ErrorNode from newrelic.core.log_event_node import LogEventNode from newrelic.core.stack_trace import exception_stack from newrelic.core.stats_engine import CustomMetrics, SampledDataSet @@ -1558,7 +1558,7 @@ def _create_error_node(self, settings, fullname, message, expected, custom_param if error.type == fullname and error.message == message: return - node = ErrorNode( + node = newrelic.core.error_node.ErrorNode( timestamp=time.time(), type=fullname, message=message, @@ -1609,8 +1609,7 @@ def _process_node(self, node): node.node_count = self._trace_node_count self.total_time += node.exclusive - # if type(node) is newrelic.core.database_node.DatabaseNode: - if isinstance(newrelic.core.database_node.DatabaseNode, ErrorNode): + if type(node) is newrelic.core.database_node.DatabaseNode: settings = self._settings if not settings: return @@ -1676,7 +1675,7 @@ def add_custom_attributes(self, items): def add_custom_parameter(self, name, value): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. Please use the add_custom_attribute API."), + ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), DeprecationWarning, ) return self.add_custom_attribute(name, value) @@ -1684,7 +1683,7 @@ def add_custom_parameter(self, name, value): def add_custom_parameters(self, items): # Deprecation warning warnings.warn( - ("The add_custom_parameters API has been deprecated. Please use the add_custom_attributes API."), + ("The add_custom_parameters API has been deprecated. " "Please use the add_custom_attributes API."), DeprecationWarning, ) return self.add_custom_attributes(items) @@ -1780,7 +1779,7 @@ def add_custom_attributes(items): def add_custom_parameter(key, value): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. Please use the add_custom_attribute API."), + ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), DeprecationWarning, ) return add_custom_attribute(key, value) @@ -1789,7 +1788,7 @@ def add_custom_parameter(key, value): def add_custom_parameters(items): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. Please use the add_custom_attribute API."), + ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), DeprecationWarning, ) return add_custom_attributes(items) diff --git a/newrelic/core/attribute.py b/newrelic/core/attribute.py index e72ab071ec..c5f19e4c06 100644 --- a/newrelic/core/attribute.py +++ b/newrelic/core/attribute.py @@ -243,19 +243,19 @@ def process_user_attribute(name, value, max_length=MAX_ATTRIBUTE_LENGTH, ending= value = sanitize(value) except NameIsNotStringException: - _logger.debug("Attribute name must be a string. Dropping attribute: %r=%r", name, value) + _logger.debug("Attribute name must be a string. Dropping " "attribute: %r=%r", name, value) return FAILED_RESULT except NameTooLongException: - _logger.debug("Attribute name exceeds maximum length. Dropping attribute: %r=%r", name, value) + _logger.debug("Attribute name exceeds maximum length. Dropping " "attribute: %r=%r", name, value) return FAILED_RESULT except IntTooLargeException: - _logger.debug("Attribute value exceeds maximum integer value. Dropping attribute: %r=%r", name, value) + _logger.debug("Attribute value exceeds maximum integer value. " "Dropping attribute: %r=%r", name, value) return FAILED_RESULT except CastingFailureException: - _logger.debug("Attribute value cannot be cast to a string. Dropping attribute: %r=%r", name, value) + _logger.debug("Attribute value cannot be cast to a string. " "Dropping attribute: %r=%r", name, value) return FAILED_RESULT else: @@ -268,7 +268,7 @@ def process_user_attribute(name, value, max_length=MAX_ATTRIBUTE_LENGTH, ending= trunc_value = truncate(value, maxsize=max_length, ending=ending) if value != trunc_value: _logger.debug( - "Attribute value exceeds maximum length (%r bytes). Truncating value: %r=%r.", + "Attribute value exceeds maximum length " "(%r bytes). Truncating value: %r=%r.", max_length, name, trunc_value, @@ -296,6 +296,8 @@ def sanitize(value): except Exception: raise CastingFailureException() else: - _logger.debug("Attribute value is of type: %r. Casting %r to string: %s", type(original), original, value) + _logger.debug( + "Attribute value is of type: %r. Casting %r to " "string: %s", type(original), original, value + ) return value diff --git a/tests/agent_features/test_span_events.py b/tests/agent_features/test_span_events.py index 3faa73a0c8..4656131696 100644 --- a/tests/agent_features/test_span_events.py +++ b/tests/agent_features/test_span_events.py @@ -413,11 +413,10 @@ def _test(): @pytest.mark.parametrize("span_events_enabled", (False, True)) def test_collect_span_events_override(collect_span_events, span_events_enabled): - # if collect_span_events and span_events_enabled: - # spans_expected = True - # else: - # spans_expected = False - spans_expected = collect_span_events and span_events_enabled + if collect_span_events and span_events_enabled: + spans_expected = True + else: + spans_expected = False span_count = 2 if spans_expected else 0 diff --git a/tests/testing_support/sample_asgi_applications.py b/tests/testing_support/sample_asgi_applications.py index 62cd5330c3..9118883b09 100644 --- a/tests/testing_support/sample_asgi_applications.py +++ b/tests/testing_support/sample_asgi_applications.py @@ -34,7 +34,7 @@ async def __call__(self, receive, send): if self.scope["path"] == "/exc": raise ValueError("whoopsies") - if self.scope["path"] == "/ignored": + elif self.scope["path"] == "/ignored": ignore_transaction() await send({"type": "http.response.start", "status": 200}) @@ -57,7 +57,7 @@ async def simple_app_v3_raw(scope, receive, send): if scope["path"] == "/exc": raise ValueError("whoopsies") - if scope["path"] == "/ignored": + elif scope["path"] == "/ignored": ignore_transaction() await send({"type": "http.response.start", "status": 200}) From 086cb59d3102bee57858c564355b02d8200b3dd7 Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Mon, 17 Oct 2022 10:11:41 -0700 Subject: [PATCH 5/5] Edit deprecation message in add_custom_parameters --- newrelic/api/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newrelic/api/transaction.py b/newrelic/api/transaction.py index d828535b98..0f712bcb88 100644 --- a/newrelic/api/transaction.py +++ b/newrelic/api/transaction.py @@ -1788,7 +1788,7 @@ def add_custom_parameter(key, value): def add_custom_parameters(items): # Deprecation warning warnings.warn( - ("The add_custom_parameter API has been deprecated. " "Please use the add_custom_attribute API."), + ("The add_custom_parameters API has been deprecated. " "Please use the add_custom_attributes API."), DeprecationWarning, ) return add_custom_attributes(items)