From 5c21db1299469e4a070d19cd55cafc20b33e4d35 Mon Sep 17 00:00:00 2001 From: Tim McCormack Date: Wed, 26 Jun 2024 15:56:02 -0400 Subject: [PATCH] feat: Prevent agent from talking to New Relic (but preserve instrumentation) (#1) If the Django setting `EDX_NEWRELIC_NO_REPORT` is present and enabled, the agent will not talk to New Relic's servers and will instead use a set of previously captured responses from our sandbox account. Instrumentation (tracing, etc.) will still be in place, but the data will be discarded rather than being reported. See https://github.com/edx/edx-arch-experiments/issues/692 --- newrelic/core/agent_protocol.py | 139 ++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/newrelic/core/agent_protocol.py b/newrelic/core/agent_protocol.py index 9ac42e4050..7465979143 100644 --- a/newrelic/core/agent_protocol.py +++ b/newrelic/core/agent_protocol.py @@ -50,6 +50,131 @@ _logger = logging.getLogger(__name__) +# {return_value: ...} wrapper has been removed from each of these +FAKE_AGENT_RESPONSE = { + 'preconnect': {"redirect_host": "collector.newrelic.com"}, + 'connect': json_decode(r""" +{ + "agent_run_id": "xxx1", + "data_report_period": 60, + "application_id": "xxx10", + "browser_key": "xxx2", + "beacon": "bam.nr-data.net", + "apdex_t": 0.5, + "url_rules": [ + { + "ignore": false, + "match_expression": ".*\\.(ace|arj|ini|txt|udl|plist|css|gif|ico|jpe?g|js|png|swf|woff|caf|aiff|m4v|mpe?g|mp3|mp4|mov)$", + "replacement": "/*.\\1", + "eval_order": 1000, + "terminate_chain": true, + "replace_all": false, + "each_segment": false + }, + { + "ignore": false, + "match_expression": "^[0-9][0-9a-f_,.-]*$", + "replacement": "*", + "eval_order": 1001, + "terminate_chain": false, + "replace_all": false, + "each_segment": true + }, + { + "ignore": false, + "match_expression": "^(.*)/[0-9][0-9a-f_,-]*\\.([0-9a-z][0-9a-z]*)$", + "replacement": "\\1/.*\\2", + "eval_order": 1002, + "terminate_chain": false, + "replace_all": false, + "each_segment": false + } + ], + "data_methods": { + "error_event_data": { + "report_period_in_seconds": 60, + "max_samples_stored": 10000 + }, + "span_event_data": { + "report_period_in_seconds": 60, + "max_samples_stored": 10000 + }, + "custom_event_data": { + "report_period_in_seconds": 60, + "max_samples_stored": 10000 + }, + "analytic_event_data": { + "report_period_in_seconds": 60, + "max_samples_stored": 10000 + } + }, + "event_harvest_config": { + "report_period_ms": 5000, + "harvest_limits": { + "error_event_data": 8, + "log_event_data": 0, + "analytic_event_data": 100, + "custom_event_data": 300 + } + }, + "span_event_harvest_config": { + "report_period_ms": 60000, + "harvest_limit": 2000 + }, + "transaction_naming_scheme": "legacy", + "product_level": 0, + "max_payload_size_in_bytes": 1000000, + "sampling_rate": 0, + "entity_guid": "xxx3", + "collect_error_events": true, + "collect_analytics_events": true, + "collect_span_events": true, + "collect_errors": true, + "collect_traces": true, + "sampling_target_period_in_seconds": 60, + "sampling_target": 10, + "primary_application_id": "xxx6", + "account_id": "xxx7", + "messages": [], + "cross_process_id": "xxx4", + "encoding_key": "xxx5", + "trusted_account_ids": [], + "trusted_account_key": "xxx8", + "request_headers_map": {}, + "browser_monitoring.distributed_tracing.enabled": true, + "js_agent_file": "", + "episodes_url": "https://js-agent.newrelic.com/nr-100.js", + "error_beacon": "bam.nr-data.net", + "browser_monitoring.loader_version": "1.260.1", + "browser_monitoring.loader": "spa", + "js_agent_loader_version": "bogus-loader-sorry", + "feature_flags": [], + "episodes_file": "js-agent.newrelic.com/nr-100.js", + "browser_monitoring.privacy.cookies_enabled": true, + "js_agent_loader": "xxx9" +} +"""), + 'agent_settings': None, + + # From data_collector.py + 'agent_command_results': {}, + 'analytic_event_data': {}, + 'custom_event_data': {}, + 'dimensional_metric_data': {}, + 'error_data': {}, + 'error_event_data': {}, + 'get_agent_commands': {}, + 'log_event_data': {}, + 'metric_data': {}, + 'ml_event_data': {}, + 'profile_data': {}, + 'shutdown': {}, + 'span_event_data': {}, + 'sql_trace_data': {}, + 'transaction_sample_data': {}, +} + + class AgentProtocol(object): VERSION = 17 @@ -158,6 +283,12 @@ class AgentProtocol(object): } def __init__(self, settings, host=None, client_cls=ApplicationModeClient): + try: + from django.conf import settings as django_settings + self._no_report = bool(getattr(django_settings, "EDX_NEWRELIC_NO_REPORT", False)) + except ImportError: + self._no_report = False + if settings.audit_log_file: audit_log_fp = open(settings.audit_log_file, "a") else: @@ -225,6 +356,14 @@ def send( payload=(), path="/agent_listener/invoke_raw_method", ): + if self._no_report: + if method in FAKE_AGENT_RESPONSE: + return FAKE_AGENT_RESPONSE[method] + else: + _logger.warning(f"NR agent wanted to send unrecognized {method=} -- denied, and will ignore future instances.") + FAKE_AGENT_RESPONSE[method] = {} + return {} + params, headers, payload = self._to_http(method, payload) try: