diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4d56452170..eeb36af84a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -9,10 +9,11 @@ # Offense count: 422 # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes. Metrics/AbcSize: - Max: 54 + Max: 43 Exclude: - - test/**/* - infinite_tracing/test/**/* + - lib/new_relic/cli/commands/deployments.rb + - test/**/* # This cop supports safe autocorrection (--autocorrect). Minitest/AssertInDelta: diff --git a/lib/new_relic/agent/agent_helpers/start_worker_thread.rb b/lib/new_relic/agent/agent_helpers/start_worker_thread.rb index b0c85c80e3..e8c3abba90 100644 --- a/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +++ b/lib/new_relic/agent/agent_helpers/start_worker_thread.rb @@ -60,36 +60,13 @@ def interval_for(event_type) end def create_and_run_event_loop - data_harvest = :"#{Agent.config[:data_report_period]}_second_harvest" - event_harvest = :"#{Agent.config[:event_report_period]}_second_harvest" - @event_loop = create_event_loop + data_harvest = :"#{Agent.config[:data_report_period]}_second_harvest" @event_loop.on(data_harvest) do transmit_data end - - @event_loop.on(interval_for(TRANSACTION_EVENT_DATA)) do - transmit_analytic_event_data - end - @event_loop.on(interval_for(CUSTOM_EVENT_DATA)) do - transmit_custom_event_data - end - @event_loop.on(interval_for(ERROR_EVENT_DATA)) do - transmit_error_event_data - end - @event_loop.on(interval_for(SPAN_EVENT_DATA)) do - transmit_span_event_data - end - @event_loop.on(interval_for(LOG_EVENT_DATA)) do - transmit_log_event_data - end - - @event_loop.on(:reset_log_once_keys) do - ::NewRelic::Agent.logger.clear_already_logged - end - @event_loop.fire_every(Agent.config[:data_report_period], data_harvest) - @event_loop.fire_every(Agent.config[:event_report_period], event_harvest) - @event_loop.fire_every(LOG_ONCE_KEYS_RESET_PERIOD, :reset_log_once_keys) + establish_interval_transmissions + establish_fire_everies(data_harvest) @event_loop.run end @@ -161,6 +138,37 @@ def deferred_work!(connection_options) end end end + + private + + def establish_interval_transmissions + @event_loop.on(interval_for(TRANSACTION_EVENT_DATA)) do + transmit_analytic_event_data + end + @event_loop.on(interval_for(CUSTOM_EVENT_DATA)) do + transmit_custom_event_data + end + @event_loop.on(interval_for(ERROR_EVENT_DATA)) do + transmit_error_event_data + end + @event_loop.on(interval_for(SPAN_EVENT_DATA)) do + transmit_span_event_data + end + @event_loop.on(interval_for(LOG_EVENT_DATA)) do + transmit_log_event_data + end + end + + def establish_fire_everies(data_harvest) + @event_loop.on(:reset_log_once_keys) do + ::NewRelic::Agent.logger.clear_already_logged + end + + event_harvest = :"#{Agent.config[:event_report_period]}_second_harvest" + @event_loop.fire_every(Agent.config[:data_report_period], data_harvest) + @event_loop.fire_every(Agent.config[:event_report_period], event_harvest) + @event_loop.fire_every(LOG_ONCE_KEYS_RESET_PERIOD, :reset_log_once_keys) + end end end end diff --git a/lib/new_relic/agent/configuration/default_source.rb b/lib/new_relic/agent/configuration/default_source.rb index 67fe5de7ef..c6bdbaf3e8 100644 --- a/lib/new_relic/agent/configuration/default_source.rb +++ b/lib/new_relic/agent/configuration/default_source.rb @@ -80,35 +80,35 @@ def self.transform_for(key) default_settings[:transform] if default_settings end - def self.config_search_paths + def self.config_search_paths # rubocop:disable Metrics/AbcSize proc { - paths = [ - File.join("config", "newrelic.yml"), - File.join("newrelic.yml"), - File.join("config", "newrelic.yml.erb"), - File.join("newrelic.yml.erb") - ] + yaml = 'newrelic.yml' + config_yaml = File.join('config', yaml) + erb = 'newrelic.yml.erb' + config_erb = File.join('config', erb) + + paths = [config_yaml, yaml, config_erb, erb] if NewRelic::Control.instance.root - paths << File.join(NewRelic::Control.instance.root, "config", "newrelic.yml") - paths << File.join(NewRelic::Control.instance.root, "newrelic.yml") - paths << File.join(NewRelic::Control.instance.root, "config", "newrelic.yml.erb") - paths << File.join(NewRelic::Control.instance.root, "newrelic.yml.erb") + paths << File.join(NewRelic::Control.instance.root, config_yaml) + paths << File.join(NewRelic::Control.instance.root, yaml) + paths << File.join(NewRelic::Control.instance.root, config_erb) + paths << File.join(NewRelic::Control.instance.root, erb) end if ENV['HOME'] - paths << File.join(ENV['HOME'], ".newrelic", "newrelic.yml") - paths << File.join(ENV['HOME'], "newrelic.yml") - paths << File.join(ENV['HOME'], ".newrelic", "newrelic.yml.erb") - paths << File.join(ENV['HOME'], "newrelic.yml.erb") + paths << File.join(ENV['HOME'], '.newrelic', yaml) + paths << File.join(ENV['HOME'], yaml) + paths << File.join(ENV['HOME'], '.newrelic', erb) + paths << File.join(ENV['HOME'], erb) end # If we're packaged for warbler, we can tell from GEM_HOME # the following line needs else branch coverage - if ENV["GEM_HOME"] && ENV["GEM_HOME"].end_with?(".jar!") # rubocop:disable Style/SafeNavigation - app_name = File.basename(ENV["GEM_HOME"], ".jar!") - paths << File.join(ENV["GEM_HOME"], app_name, "config", "newrelic.yml") - paths << File.join(ENV["GEM_HOME"], app_name, "config", "newrelic.yml.erb") + if ENV['GEM_HOME'] && ENV['GEM_HOME'].end_with?(".jar!") # rubocop:disable Style/SafeNavigation + app_name = File.basename(ENV['GEM_HOME'], '.jar!') + paths << File.join(ENV['GEM_HOME'], app_name, config_yaml) + paths << File.join(ENV['GEM_HOME'], app_name, config_erb) end paths diff --git a/lib/new_relic/agent/transaction.rb b/lib/new_relic/agent/transaction.rb index 3a36f8a6ec..df685ce87d 100644 --- a/lib/new_relic/agent/transaction.rb +++ b/lib/new_relic/agent/transaction.rb @@ -734,15 +734,19 @@ def merge_metrics def record_exceptions error_recorded = false @exceptions.each do |exception, options| - options[:uri] ||= request_path if request_path - options[:port] = request_port if request_port - options[:metric] = best_name - options[:attributes] = @attributes - - span_id = options.delete(:span_id) - error_recorded = !!agent.error_collector.notice_error(exception, options, span_id) || error_recorded + error_recorded = record_exception(exception, options, error_recorded) end - payload[:error] = error_recorded if payload + payload&.[]=(:error, error_recorded) + end + + def record_exception(exception, options, error_recorded) + options[:uri] ||= request_path if request_path + options[:port] = request_port if request_port + options[:metric] = best_name + options[:attributes] = @attributes + + span_id = options.delete(:span_id) + !!agent.error_collector.notice_error(exception, options, span_id) || error_recorded end # Do not call this. Invoke the class method instead. diff --git a/lib/new_relic/cli/commands/deployments.rb b/lib/new_relic/cli/commands/deployments.rb index 3f7c4d927c..74409a627a 100644 --- a/lib/new_relic/cli/commands/deployments.rb +++ b/lib/new_relic/cli/commands/deployments.rb @@ -163,24 +163,24 @@ def set_params_v2(request) def options OptionParser.new(%Q(Usage: #{$0} #{self.class.command} [OPTIONS] ["description"] ), 40) do |o| o.separator("OPTIONS:") - o.on("-a", "--appname=NAME", String, - "Set the application name.", - "Default is app_name setting in newrelic.yml. Available only when using API v1.") { |e| @appname = e } - o.on("-i", "--appid=ID", String, - "Set the application ID", - "If not provided, will connect to the New Relic collector to get it") { |i| @application_id = i } - o.on("-e", "--environment=name", String, - "Override the (RAILS|RUBY|RACK)_ENV setting", + o.on('-a', '--appname=NAME', String, + 'Set the application name.', + 'Default is app_name setting in newrelic.yml. Available only when using API v1.') { |e| @appname = e } + o.on('-i', '--appid=ID', String, + 'Set the application ID', + 'If not provided, will connect to the New Relic collector to get it') { |i| @application_id = i } + o.on('-e', '--environment=name', String, + 'Override the (RAILS|RUBY|RACK)_ENV setting', "currently: #{control.env}") { |e| @environment = e } - o.on("-u", "--user=USER", String, - "Specify the user deploying, for information only", + o.on('-u', '--user=USER', String, + 'Specify the user deploying, for information only', "Default: #{@user || ''}") { |u| @user = u } - o.on("-r", "--revision=REV", String, - "Specify the revision being deployed. Required when using New Relic REST API v2") { |r| @revision = r } - o.on("-l", "--license-key=KEY", String, - "Specify the license key of the account for the app being deployed") { |l| @license_key = l } - o.on("-c", "--changes", - "Read in a change log from the standard input") { @changelog = STDIN.read } + o.on('-r', '--revision=REV', String, + 'Specify the revision being deployed. Required when using New Relic REST API v2') { |r| @revision = r } + o.on('-l', '--license-key=KEY', String, + 'Specify the license key of the account for the app being deployed') { |l| @license_key = l } + o.on('-c', '--changes', + 'Read in a change log from the standard input') { @changelog = STDIN.read } yield(o) if block_given? end end diff --git a/lib/new_relic/control/frameworks/rails.rb b/lib/new_relic/control/frameworks/rails.rb index 9705f7a46c..aaa9137b37 100644 --- a/lib/new_relic/control/frameworks/rails.rb +++ b/lib/new_relic/control/frameworks/rails.rb @@ -44,26 +44,38 @@ def rails_config # find a config and use that. def init_config(options = {}) @config = options[:config] - # Install the dependency detection, - if rails_config && ::Rails.configuration.respond_to?(:after_initialize) - rails_config.after_initialize do - # This will insure we load all the instrumentation as late as possible. If the agent - # is not enabled, it will load a limited amount of instrumentation. - DependencyDetection.detect! - end + install_dependency_detection + install_browser_monitoring_and_agent_hooks + rescue => e + ::NewRelic::Agent.logger.error('Failure during init_config for Rails. Is Rails required in a non-Rails ' \ + 'app? Set NEW_RELIC_FRAMEWORK=ruby to avoid this message. The Ruby agent ' \ + 'will continue running, but Rails-specific features may be missing. ' \ + "#{e.class} - #{e.message}") + end + + def install_dependency_detection + return unless rails_config && ::Rails.configuration.respond_to?(:after_initialize) + + rails_config.after_initialize do + # This will insure we load all the instrumentation as late as + # possible. If the agent is not enabled, it will load a limited + # amount of instrumentation. + DependencyDetection.detect! end + end + + def install_browser_monitoring_and_agent_hooks + return unless rails_config + if !Agent.config[:agent_enabled] - # Might not be running if it does not think mongrel, thin, passenger, etc - # is running, if it thinks it's a rake task, or if the agent_enabled is false. - ::NewRelic::Agent.logger.info("New Relic Agent not running.") + # Might not be running if it does not think mongrel, thin, + # passenger, etc. is running, if it thinks it's a rake task, or + # if the agent_enabled is false. + ::NewRelic::Agent.logger.info('New Relic Agent not running. Skipping browser monitoring and agent hooks.') else install_browser_monitoring(rails_config) install_agent_hooks(rails_config) end - rescue => e - ::NewRelic::Agent.logger.error("Failure during init_config for Rails. Is Rails required in a non-Rails app? Set NEW_RELIC_FRAMEWORK=ruby to avoid this message.", - "The Ruby agent will continue running, but Rails-specific features may be missing.", - e) end def install_agent_hooks(config) diff --git a/lib/new_relic/rack/browser_monitoring.rb b/lib/new_relic/rack/browser_monitoring.rb index 6badc3be90..c34d823324 100644 --- a/lib/new_relic/rack/browser_monitoring.rb +++ b/lib/new_relic/rack/browser_monitoring.rb @@ -40,7 +40,7 @@ def traced_call(env) js_to_inject = NewRelic::Agent.browser_timing_header if (js_to_inject != NewRelic::EMPTY_STR) && should_instrument?(env, status, headers) - response_string = autoinstrument_source(response, headers, js_to_inject) + response_string = autoinstrument_source(response, js_to_inject) if headers.key?(CONTENT_LENGTH) content_length = response_string ? response_string.bytesize : 0 headers[CONTENT_LENGTH] = content_length.to_s @@ -69,11 +69,17 @@ def should_instrument?(env, status, headers) private - def autoinstrument_source(response, headers, js_to_inject) + def autoinstrument_source(response, js_to_inject) source = gather_source(response) close_old_response(response) - return nil unless source + return unless source + modify_source(source, js_to_inject) + rescue => e + NewRelic::Agent.logger.debug("Skipping RUM instrumentation on exception: #{e.class} - #{e.message}") + end + + def modify_source(source, js_to_inject) # Only scan the first 50k (roughly) then give up. beginning_of_source = source[0..SCAN_LIMIT] meta_tag_positions = find_meta_tag_positions(beginning_of_source) @@ -81,18 +87,15 @@ def autoinstrument_source(response, headers, js_to_inject) if insertion_index = find_insertion_index(meta_tag_positions, beginning_of_source, body_start) source = source_injection(source, insertion_index, js_to_inject) else - NewRelic::Agent.logger.debug("Skipping RUM instrumentation. Could not properly determine location to inject script.") + NewRelic::Agent.logger.debug('Skipping RUM instrumentation. Could not properly determine location to ' \ + 'inject script.') end else msg = "Skipping RUM instrumentation. Unable to find tag in first #{SCAN_LIMIT} bytes of document." NewRelic::Agent.logger.log_once(:warn, :rum_insertion_failure, msg) NewRelic::Agent.logger.debug(msg) end - source - rescue => e - NewRelic::Agent.logger.debug("Skipping RUM instrumentation on exception.", e) - nil end def html?(headers)