newrelic_rpm 9.8.0 → 9.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -2
  3. data/README.md +3 -0
  4. data/Rakefile +1 -1
  5. data/lib/bootstrap.rb +105 -0
  6. data/lib/new_relic/agent/agent.rb +4 -1
  7. data/lib/new_relic/agent/agent_helpers/connect.rb +10 -8
  8. data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -1
  9. data/lib/new_relic/agent/agent_helpers/startup.rb +2 -1
  10. data/lib/new_relic/agent/agent_logger.rb +2 -1
  11. data/lib/new_relic/agent/aws.rb +56 -0
  12. data/lib/new_relic/agent/configuration/default_source.rb +65 -15
  13. data/lib/new_relic/agent/configuration/environment_source.rb +9 -1
  14. data/lib/new_relic/agent/configuration/manager.rb +22 -5
  15. data/lib/new_relic/agent/configuration/yaml_source.rb +2 -0
  16. data/lib/new_relic/agent/connect/request_builder.rb +1 -1
  17. data/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +1 -5
  18. data/lib/new_relic/agent/error_collector.rb +23 -0
  19. data/lib/new_relic/agent/harvester.rb +1 -1
  20. data/lib/new_relic/agent/instrumentation/dynamodb/chain.rb +27 -0
  21. data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +58 -0
  22. data/lib/new_relic/agent/instrumentation/dynamodb/prepend.rb +19 -0
  23. data/lib/new_relic/agent/instrumentation/dynamodb.rb +25 -0
  24. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +6 -1
  25. data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +0 -1
  26. data/lib/new_relic/agent/instrumentation/ruby_openai/instrumentation.rb +1 -2
  27. data/lib/new_relic/agent/log_event_aggregator.rb +1 -16
  28. data/lib/new_relic/agent/new_relic_service.rb +12 -2
  29. data/lib/new_relic/agent/serverless_handler.rb +171 -0
  30. data/lib/new_relic/agent/span_event_primitive.rb +4 -8
  31. data/lib/new_relic/agent/transaction/external_request_segment.rb +0 -10
  32. data/lib/new_relic/agent/transaction.rb +2 -6
  33. data/lib/new_relic/agent/transaction_error_primitive.rb +23 -19
  34. data/lib/new_relic/agent.rb +12 -8
  35. data/lib/new_relic/constants.rb +2 -0
  36. data/lib/new_relic/control/instance_methods.rb +7 -0
  37. data/lib/new_relic/local_environment.rb +13 -6
  38. data/lib/new_relic/rack/browser_monitoring.rb +9 -1
  39. data/lib/new_relic/version.rb +1 -1
  40. data/lib/tasks/config.rake +5 -3
  41. data/lib/tasks/helpers/config.html.erb +3 -2
  42. data/lib/tasks/helpers/format.rb +1 -1
  43. data/newrelic.yml +15 -1
  44. data/test/agent_helper.rb +3 -1
  45. metadata +10 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fac159d955f3aaf1b926d68335c05f8bbea9fa3089bed68bee11f9bb50243361
4
- data.tar.gz: 783c82413e45080741cbc9fd9fe0488b59cede5b64d359af46cf524d871b7763
3
+ metadata.gz: d3793bb498826b000ad9e00e7d57de0e95988c4ba213e341a6a8c18f90df772e
4
+ data.tar.gz: 6cb26b15c2faace4a7e1fd6775a3f69f965beaad10f395e0fb618cb411dbacfe
5
5
  SHA512:
6
- metadata.gz: ae3deb3425be91f81d30fd76f6af95ec2ff573f2941e17474cbc96cadd70baa7eda534fcb8886ed039b71eba202d81ef9098600e307dcdf27919f84612eea15d
7
- data.tar.gz: 4000a2bd73ea4eee6853878630d777226c39af0b46aafa8946bc167495728dc754136731162e660e6fbc895d2df1ca12709e3d2e60be4df8ae5f09035e072cfe
6
+ metadata.gz: acb8dd0c767f40796fa1afb9cc26c0a57a9bdd83a4cbf550589a094f5cf48da32caf1d4c0f10e154633b754086bd62aa17d393bffa37eee5a0128e72c3bd6160
7
+ data.tar.gz: 6ce06fe8f1506405b7696089bf366e1aa0ccd60ab0d5034b22db87ac3c4523bd9d8d4dc4a6b568515cd516cd5c64babf853cdfed4af1839fd45cbed0450994f5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # New Relic Ruby Agent Release Notes
2
2
 
3
+ ## v9.10.0
4
+
5
+ Version 9.10.0 introduces instrumentation for DynamoDB, adds a new feature to automatically apply nonces from the Rails content security policy, fixes a bug that would cause an expected error to negatively impact a transaction's Apdex, and fixes the agent's autostart logic so that by default `rails runner` and `rails db` commands will not cause the agent to start.
6
+
7
+ - **Feature: Add instrumentation for DynamoDB**
8
+
9
+ The agent has added instrumentation for the aws-sdk-dynamodb gem. The agent will now record datastore spans for DynamoDB client calls made with the aws-sdk-dynamodb gem. [PR#2642](https://github.com/newrelic/newrelic-ruby-agent/pull/2642)
10
+
11
+ - **Feature: Automatically apply nonces from the Rails content security policy**
12
+
13
+ To auto-inject browser monitoring with the New Relic Ruby agent, you either need to set your content security policy to 'unsafe-inline' or provide a nonce. Previously, the only way to provide a nonce was by using the [`NewRelic::Agent.browser_timing_header`](https://rubydoc.info/gems/newrelic_rpm/NewRelic/Agent#browser_timing_header-instance_method) API. Now, when a Rails application uses [the content security policy configuration to add a nonce](https://guides.rubyonrails.org/security.html#adding-a-nonce), the nonce will be automatically applied to the browser agent. A new configuration option, [`browser_monitoring.content_security_policy_nonce`](https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration/#browser_monitoring-content_security_policy_nonce), toggles this feature. It is on by default. Thank you [@baldarn](https://github.com/baldarn) for submitting this feature! [PR#2544](https://github.com/newrelic/newrelic-ruby-agent/pull/2544)
14
+
15
+ - **Bugfix: Expected errors related to HTTP status code, class, and message won't impact Apdex**
16
+
17
+ The agent is supposed to prevent observed application errors from negatively impacting Apdex if the errors are either ignored or expected. There are two ways for the agent to expect an error: via the `notice_error` API receiving an `expected: true` argument or via matches made against user-configured lists for expected HTTP status codes (`:'error_collector.expected_status_codes'`), expected error classes (`:'error_collector.expected_classes'`), or expected error messages (`:'error_collector.expected_messages'`). Previously, only errors expected via the `notice_error` API were correctly prevented from impacting Apdex. Expected errors set by configuration incorrectly impacted Apdex. This behavior has been fixed and now both types of expected errors will correctly not impact Apdex. Thanks very much to [@florianpilz](https://github.com/florianpilz) for bringing this issue to our attention. [PR#2619](https://github.com/newrelic/newrelic-ruby-agent/pull/2619)
18
+
19
+ - **Bugfix: Do not start the agent automatically when `rails runner` or `rails db` commands are ran**
20
+
21
+ [PR#2239](https://github.com/newrelic/newrelic-ruby-agent/pull/2239) taught the agent how to recognize `bin/rails` based contexts that it should not automatically start up in. But `bin/rails runner` and `bin/rails db` commands would still see the agent start automatically. Those 2 contexts will now no longer see the agent start automatically. Thank you to [@jdelStrother](https://github.com/jdelStrother) for both bringing the `bin/rails` context to our attention and for letting us know about the `bin/rails runner` and `bin/rails db` outliers that still needed fixing. [PR#2623](https://github.com/newrelic/newrelic-ruby-agent/pull/2623)
22
+
23
+ Older agent versions that are still supported by New Relic can update to the new list of denylisted constants by having the following line added to the `newrelic.yml` configuration file:
24
+
25
+ ```yaml
26
+ autostart.denylisted_constants: "Rails::Command::ConsoleCommand,Rails::Command::CredentialsCommand,Rails::Command::Db::System::ChangeCommand,Rails::Command::DbConsoleCommand,Rails::Command::DestroyCommand,Rails::Command::DevCommand,Rails::Command::EncryptedCommand,Rails::Command::GenerateCommand,Rails::Command::InitializersCommand,Rails::Command::NotesCommand,Rails::Command::RakeCommand,Rails::Command::RoutesCommand,Rails::Command::RunnerCommand,Rails::Command::SecretsCommand,Rails::Console,Rails::DBConsole"
27
+ ```
28
+
29
+ ## v9.9.0
30
+
31
+ Version 9.9.0 introduces support for AWS Lambda serverless function observability, adds support for Elasticsearch 8.13.0, and adds the 'request.temperature' attribute to chat completion summaries in ruby-openai instrumentation.
32
+
33
+ - **Feature: Serverless Mode for AWS Lambda**
34
+
35
+ The Ruby agent is now capable of operating in a quick and light serverless mode suitable for observing AWS Lambda function invocations. For serverless use, the agent is delivered by a New Relic Lambda [layer](https://github.com/newrelic/newrelic-lambda-layers) that can be associated with a Lambda function. All reported data will appear in New Relic's dedicated serverless UI views. Only AWS based Lambda functions are supported for now, though support for other cloud hosted serverless offerings may be added in future depending on Ruby customer demand. The serverless functionality is only intended for use with the official New Relic Ruby layers for Lambda. Any existing workflows that involve the manual use of the Ruby agent in an AWS Lambda context without a New Relic layer should not be impacted.
36
+
37
+ For more details, see our [getting started guide](https://docs.newrelic.com/docs/serverless-function-monitoring/aws-lambda-monitoring/get-started/monitoring-aws-lambda-serverless-monitoring/).
38
+
39
+ - **Feature: Add support for Elasticsearch 8.13.0**
40
+
41
+ Elasticsearch 8.13.0 increased the number of arguments used in the method the agent instruments, `Elastic::Transport::Client#perform_request`. Now, the agent supports a variable number of arguments for the instrumented method to prevent future `ArgumentError`s.
42
+
43
+ - **Bugfix: Add 'request.temperature' to ruby-openai chat completion summaries**
44
+
45
+ Previously, the agent was not reporting the `request.temperature` attribute on `LlmChatCompletionSummary` events through ruby-openai instrumentation. We are now reporting this attribute.
46
+
3
47
  ## v9.8.0
4
48
 
5
49
  Version 9.8.0 introduces instrumentation for ruby-openai, adds the option to store tracer state on the thread-level, hardens the browser agent insertion logic to better proactively anticipate errors, and prevents excpetions from being raised in the Active Support Broadcast logger instrumentation.
@@ -15,7 +59,7 @@ Version 9.8.0 introduces instrumentation for ruby-openai, adds the option to sto
15
59
  This version introduces two new APIs that allow users to record additional information on LLM events:
16
60
  * `NewRelic::Agent.record_llm_feedback_event` - Records user feedback events.
17
61
  * `NewRelic::Agent.set_llm_token_count_callback` - Sets a callback proc for calculating `token_count` attributes for embedding and chat completion message events.
18
-
62
+
19
63
  Visit [RubyDoc](https://rubydoc.info/github/newrelic/newrelic-ruby-agent/) for more information on each of these APIs.
20
64
 
21
65
  - **Feature: Store tracer state on thread-level**
@@ -49,7 +93,7 @@ Version 9.7.0 introduces ViewComponent instrumentation, changes the endpoint use
49
93
 
50
94
  - **Feature: ViewComponent instrumentation**
51
95
 
52
- [ViewComponent](https://viewcomponent.org/) is a now an instrumented library. [PR#2367](https://github.com/newrelic/newrelic-ruby-agent/pull/2367)
96
+ [ViewComponent](https://viewcomponent.org/) is a now an instrumented library. [PR#2367](https://github.com/newrelic/newrelic-ruby-agent/pull/2367)
53
97
 
54
98
  - **Feature: Use root path to access Elasticsearch cluster name**
55
99
 
data/README.md CHANGED
@@ -23,6 +23,9 @@ can be found on [our docs site](http://docs.newrelic.com/docs/ruby/supported-fra
23
23
  You can also monitor non-web applications. Refer to the "Other
24
24
  Environments" section below.
25
25
 
26
+ We offer an AWS Lambda layer for instrumenting your serverless Ruby functions.
27
+ Details can be found on our [getting started guide](https://docs.newrelic.com/docs/serverless-function-monitoring/aws-lambda-monitoring/get-started/monitoring-aws-lambda-serverless-monitoring/).
28
+
26
29
  ## Installing and Using
27
30
 
28
31
  The latest released gem for the Ruby agent can be found at [RubyGems.org](https://rubygems.org/gems/newrelic_rpm)
data/Rakefile CHANGED
@@ -134,5 +134,5 @@ task :console do
134
134
  require 'pry' if ENV['ENABLE_PRY']
135
135
  require 'newrelic_rpm'
136
136
  ARGV.clear
137
- Pry.start
137
+ ENV['ENABLE_PRY'] ? Pry.start : binding.irb # rubocop:disable Lint/Debugger
138
138
  end
data/lib/bootstrap.rb ADDED
@@ -0,0 +1,105 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ # This file is designed to bootstrap a `Bundler.require`-based Ruby app (such as
6
+ # a Ruby on Rails app) so the app can be instrumented and observed by the
7
+ # New Relic Ruby agent without the agent being added to the app as a dependency.
8
+ # NOTE: introducing the agent into your application via bootstrap is in beta.
9
+ # Use at your own risk.
10
+ #
11
+ # Given a production-ready Ruby app that optionally has a pre-packaged "frozen"
12
+ # or "deployment"–gem bundle, the New Relic Ruby agent can be introduced
13
+ # to the app without modifying the app and keeping all of the app's content
14
+ # read-only.
15
+ #
16
+ # Prerequisites:
17
+ # - Ruby (tested v2.4+)
18
+ # - Bundler (included with Ruby, tested v1.17+)
19
+ #
20
+ # Instructions:
21
+ # - First, make sure the New Relic Ruby agent exists on disk. For these
22
+ # instructions, we'll assume the agent exists at `/newrelic`.
23
+ # - The agent can be downloaded as the "newrelic_rpm" gem from RubyGems.org
24
+ # and unpacked with "gem unpack"
25
+ # - The agent can be cloned from the New Relic public GitHub repo:
26
+ # https://github.com/newrelic/newrelic-ruby-agent
27
+ # - Next, use the "RUBYOPT" environment variable to require ("-r") this
28
+ # file (note that the ".rb" extension is dropped):
29
+ # ```
30
+ # export RUBYOPT="-r /newrelic/lib/bootstrap"
31
+ # ```
32
+ # - Add your New Relic license key as an environment variable.
33
+ # ```
34
+ # export NEW_RELIC_LICENSE_KEY=1a2b3c4d5e67f8g9h0i
35
+ # ```
36
+ # - Launch an existing Ruby app as usual. For a Ruby on Rails app, this might
37
+ # involve running `bin/rails server`.
38
+ # - In the Ruby app's directory, look for and inspect
39
+ # `log/newrelic_agent.log`. If this file exists and there are no "WARN" or
40
+ # "ERROR" entries within it, then the agent was successfully introduced to
41
+ # the Ruby application.
42
+
43
+ module NRBundlerPatch
44
+ NR_AGENT_GEM = 'newrelic_rpm'
45
+
46
+ def require(*_groups)
47
+ super
48
+
49
+ require_newrelic
50
+ end
51
+
52
+ def require_newrelic
53
+ lib = File.dirname(__FILE__)
54
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
55
+ Kernel.require NR_AGENT_GEM
56
+ end
57
+ end
58
+
59
+ class NRBundlerPatcher
60
+ BUNDLER = 'bundler'
61
+ RUBYOPT = 'RUBYOPT'
62
+
63
+ def self.patch
64
+ check_for_require
65
+ check_for_rubyopt
66
+ check_for_bundler
67
+ Bundler::Runtime.prepend(NRBundlerPatch)
68
+ end
69
+
70
+ private
71
+
72
+ def self.check_for_require
73
+ warn_and_exit "#{__FILE__} is meant to be required, not invoked directly" if $PROGRAM_NAME == __FILE__
74
+ end
75
+
76
+ def self.check_for_rubyopt
77
+ unless ENV[RUBYOPT].to_s.match?("-r #{__FILE__.rpartition('.').first}")
78
+ warn_and_exit "#{__FILE__} is meant to be required via the RUBYOPT env var"
79
+ end
80
+ end
81
+
82
+ def self.check_for_bundler
83
+ require_bundler
84
+
85
+ warn_and_exit 'Required Ruby Bundler class Bundler::Runtime not defined!' unless defined?(Bundler::Runtime)
86
+
87
+ unless Bundler::Runtime.method_defined?(:require)
88
+ warn_and_exit "The active Ruby Bundler instance doesn't offer Bundler::Runtime#require"
89
+ end
90
+ end
91
+
92
+ def self.require_bundler
93
+ require BUNDLER
94
+ rescue LoadError => e
95
+ warn_and_exit "Required Ruby library '#{BUNDLER}' could not be required - #{e}"
96
+ end
97
+
98
+ def self.warn_and_exit(msg)
99
+ warn "New Relic entrypoint at #{__FILE__} encountered an issue:\n\t#{msg}"
100
+
101
+ exit 1
102
+ end
103
+ end
104
+
105
+ NRBundlerPatcher.patch
@@ -34,6 +34,7 @@ require 'new_relic/agent/utilization_data'
34
34
  require 'new_relic/environment_report'
35
35
  require 'new_relic/agent/attribute_filter'
36
36
  require 'new_relic/agent/adaptive_sampler'
37
+ require 'new_relic/agent/serverless_handler'
37
38
  require 'new_relic/agent/connect/request_builder'
38
39
  require 'new_relic/agent/connect/response_handler'
39
40
 
@@ -96,6 +97,7 @@ module NewRelic
96
97
  @monotonic_gc_profiler = VM::MonotonicGCProfiler.new
97
98
  @adaptive_sampler = AdaptiveSampler.new(Agent.config[:sampling_target],
98
99
  Agent.config[:sampling_target_period_in_seconds])
100
+ @serverless_handler = ServerlessHandler.new
99
101
  end
100
102
 
101
103
  def init_event_handlers
@@ -172,6 +174,7 @@ module NewRelic
172
174
  attr_reader :transaction_event_recorder
173
175
  attr_reader :attribute_filter
174
176
  attr_reader :adaptive_sampler
177
+ attr_reader :serverless_handler
175
178
 
176
179
  def transaction_event_aggregator
177
180
  @transaction_event_recorder.transaction_event_aggregator
@@ -307,7 +310,7 @@ module NewRelic
307
310
  @stats_engine = StatsEngine.new
308
311
  end
309
312
 
310
- def flush_pipe_data
313
+ def flush_pipe_data # used only by resque
311
314
  if connected? && @service.is_a?(PipeService)
312
315
  transmit_data_types
313
316
  end
@@ -27,9 +27,13 @@ module NewRelic
27
27
  @connect_state == :disconnected
28
28
  end
29
29
 
30
- # Don't connect if we're already connected, or if we tried to connect
31
- # and were rejected with prejudice because of a license issue, unless
32
- # we're forced to by force_reconnect.
30
+ def serverless?
31
+ Agent.config[:'serverless_mode.enabled']
32
+ end
33
+
34
+ # Don't connect if we're already connected, if we're in serverless mode,
35
+ # or if we tried to connect and were rejected with prejudice because of
36
+ # a license issue, unless we're forced to by force_reconnect.
33
37
  def should_connect?(force = false)
34
38
  force || (!connected? && !disconnected?)
35
39
  end
@@ -62,10 +66,8 @@ module NewRelic
62
66
  # no longer try to connect to the server, saving the
63
67
  # application and the server load
64
68
  def handle_license_error(error)
65
- ::NewRelic::Agent.logger.error( \
66
- error.message, \
67
- 'Visit NewRelic.com to obtain a valid license key, or to upgrade your account.'
68
- )
69
+ ::NewRelic::Agent.logger.error(error.message,
70
+ 'Visit newrelic.com to obtain a valid license key, or to upgrade your account.')
69
71
  disconnect
70
72
  end
71
73
 
@@ -94,7 +96,7 @@ module NewRelic
94
96
  # connects, then configures the agent using the response from
95
97
  # the connect service
96
98
  def connect_to_server
97
- request_builder = ::NewRelic::Agent::Connect::RequestBuilder.new( \
99
+ request_builder = ::NewRelic::Agent::Connect::RequestBuilder.new(
98
100
  @service,
99
101
  Agent.config,
100
102
  event_harvest_config,
@@ -128,7 +128,7 @@ module NewRelic
128
128
  catch_errors do
129
129
  NewRelic::Agent.disable_all_tracing do
130
130
  connect(connection_options)
131
- if connected?
131
+ if NewRelic::Agent.instance.connected?
132
132
  create_and_run_event_loop
133
133
  # never reaches here unless there is a problem or
134
134
  # the agent is exiting
@@ -124,6 +124,8 @@ module NewRelic
124
124
  # Warn the user if they have configured their agent not to
125
125
  # send data, that way we can see this clearly in the log file
126
126
  def monitoring?
127
+ return false if Agent.config[:'serverless_mode.enabled']
128
+
127
129
  if Agent.config[:monitor_mode]
128
130
  true
129
131
  else
@@ -146,7 +148,6 @@ module NewRelic
146
148
  end
147
149
  end
148
150
 
149
- # A correct license key exists and is of the proper length
150
151
  def has_correct_license_key?
151
152
  has_license_key? && correct_license_length
152
153
  end
@@ -132,7 +132,8 @@ module NewRelic
132
132
  end
133
133
 
134
134
  def wants_stdout?
135
- ::NewRelic::Agent.config[:log_file_path].casecmp(NewRelic::STANDARD_OUT) == 0
135
+ ::NewRelic::Agent.config[:log_file_path].casecmp(NewRelic::STANDARD_OUT) == 0 ||
136
+ ::NewRelic::Agent.config[:'serverless_mode.enabled']
136
137
  end
137
138
 
138
139
  def find_or_create_file_path(path_setting, root)
@@ -0,0 +1,56 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module Aws
8
+ CHARACTERS = %w[A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7].freeze
9
+ HEX_MASK = '7fffffffff80'
10
+
11
+ def self.create_arn(service, resource, config)
12
+ region = config.region
13
+ account_id = NewRelic::Agent::Aws.convert_access_key_to_account_id(config.credentials.access_key_id)
14
+
15
+ "arn:aws:#{service}:#{region}:#{account_id}:#{resource}"
16
+ rescue => e
17
+ NewRelic::Agent.logger.warn("Failed to create ARN: #{e}")
18
+ end
19
+
20
+ def self.convert_access_key_to_account_id(access_key)
21
+ decoded_key = Integer(decode_to_hex(access_key[4..-1]), 16)
22
+ mask = Integer(HEX_MASK, 16)
23
+ (decoded_key & mask) >> 7
24
+ end
25
+
26
+ def self.decode_to_hex(access_key)
27
+ bytes = access_key.delete('=').each_char.map { |c| CHARACTERS.index(c) }
28
+
29
+ bytes.each_slice(8).map do |section|
30
+ convert_section(section)
31
+ end.flatten[0...6].join
32
+ end
33
+
34
+ def self.convert_section(section)
35
+ buffer = 0
36
+ section.each do |chunk|
37
+ buffer = (buffer << 5) + chunk
38
+ end
39
+
40
+ chunk_count = (section.length * 5.0 / 8.0).floor
41
+
42
+ if section.length < 8
43
+ buffer >>= (5 - (chunk_count * 8)) % 5
44
+ end
45
+
46
+ decoded = []
47
+ chunk_count.times do |i|
48
+ shift = 8 * (chunk_count - 1 - i)
49
+ decoded << ((buffer >> shift) & 255).to_s(16)
50
+ end
51
+
52
+ decoded
53
+ end
54
+ end
55
+ end
56
+ end
@@ -52,9 +52,24 @@ module NewRelic
52
52
  result
53
53
  end
54
54
 
55
+ def self.default_settings(key)
56
+ ::NewRelic::Agent::Configuration::DEFAULTS[key]
57
+ end
58
+
59
+ def self.value_from_defaults(key, subkey)
60
+ default_settings(key)&.send(:[], subkey)
61
+ end
62
+
63
+ def self.allowlist_for(key)
64
+ value_from_defaults(key, :allowlist)
65
+ end
66
+
67
+ def self.default_for(key)
68
+ value_from_defaults(key, :default)
69
+ end
70
+
55
71
  def self.transform_for(key)
56
- default_settings = ::NewRelic::Agent::Configuration::DEFAULTS[key]
57
- default_settings[:transform] if default_settings
72
+ value_from_defaults(key, :transform)
58
73
  end
59
74
 
60
75
  def self.config_search_paths # rubocop:disable Metrics/AbcSize
@@ -375,9 +390,11 @@ module NewRelic
375
390
  :description => <<~DESCRIPTION
376
391
  If `false`, LLM instrumentation (OpenAI only for now) will not capture input and output content on specific LLM events.
377
392
 
378
- The excluded attributes include:
379
- * `content` from LlmChatCompletionMessage events
380
- * `input` from LlmEmbedding events
393
+ The excluded attributes include:
394
+ * `content` from LlmChatCompletionMessage events
395
+ * `input` from LlmEmbedding events
396
+
397
+ This is an optional security setting to prevent recording sensitive data sent to and received from your LLMs.
381
398
  DESCRIPTION
382
399
  },
383
400
  # this is only set via server side config
@@ -462,12 +479,9 @@ module NewRelic
462
479
  :public => true,
463
480
  :type => Boolean,
464
481
  :allowed_from_server => false,
465
- :description => 'Forces the exit handler that sends all cached data to collector ' \
466
- 'before shutting down to be installed regardless of detecting scenarios where it generally should not be. ' \
467
- 'Known use-case for this option is where Sinatra is running as an embedded service within another framework ' \
468
- 'and the agent is detecting the Sinatra app and skipping the `at_exit` handler as a result. Sinatra classically ' \
469
- 'runs the entire application in an `at_exit` block and would otherwise misbehave if the agent\'s `at_exit` handler ' \
470
- 'was also installed in those circumstances. Note: `send_data_on_exit` should also be set to `true` in tandem with this setting.'
482
+ :description => <<~DESC
483
+ Forces the exit handler that sends all cached data to collector before shutting down to be installed regardless of detecting scenarios where it generally should not be. Known use-case for this option is where Sinatra is running as an embedded service within another framework and the agent is detecting the Sinatra app and skipping the `at_exit` handler as a result. Sinatra classically runs the entire application in an `at_exit` block and would otherwise misbehave if the Agent's `at_exit` handler was also installed in those circumstances. Note: `send_data_on_exit` should also be set to `true` in tandem with this setting."
484
+ DESC
471
485
  },
472
486
  :high_security => {
473
487
  :default => false,
@@ -765,6 +779,15 @@ module NewRelic
765
779
  :allowed_from_server => true,
766
780
  :description => 'If `true`, enables [auto-injection](/docs/browser/new-relic-browser/installation-configuration/adding-apps-new-relic-browser#select-apm-app) of the JavaScript header for page load timing (sometimes referred to as real user monitoring or RUM).'
767
781
  },
782
+ # CSP nonce
783
+ :'browser_monitoring.content_security_policy_nonce' => {
784
+ :default => value_of(:'rum.enabled'),
785
+ :documentation_default => false,
786
+ :public => true,
787
+ :type => Boolean,
788
+ :allowed_from_server => false,
789
+ :description => 'If `true`, enables auto-injection of [Content Security Policy Nonce](https://content-security-policy.com/nonce/) in browser monitoring scripts. For now, auto-injection only works with Rails 5.2+.'
790
+ },
768
791
  # Transaction events
769
792
  :'transaction_events.enabled' => {
770
793
  :default => true,
@@ -800,6 +823,7 @@ module NewRelic
800
823
  :public => true,
801
824
  :type => String,
802
825
  :allowed_from_server => false,
826
+ :allowlist => %w[debug info warn error fatal unknown DEBUG INFO WARN ERROR FATAL UNKNOWN],
803
827
  :description => <<~DESCRIPTION
804
828
  Sets the minimum level a log event must have to be forwarded to New Relic.
805
829
 
@@ -1053,7 +1077,9 @@ module NewRelic
1053
1077
  Rails::Command::GenerateCommand
1054
1078
  Rails::Command::InitializersCommand
1055
1079
  Rails::Command::NotesCommand
1080
+ Rails::Command::RakeCommand
1056
1081
  Rails::Command::RoutesCommand
1082
+ Rails::Command::RunnerCommand
1057
1083
  Rails::Command::SecretsCommand
1058
1084
  Rails::Console
1059
1085
  Rails::DBConsole].join(','),
@@ -1424,6 +1450,14 @@ module NewRelic
1424
1450
  :allowed_from_server => false,
1425
1451
  :description => 'Controls auto-instrumentation of bunny at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
1426
1452
  },
1453
+ :'instrumentation.dynamodb' => {
1454
+ :default => 'auto',
1455
+ :public => true,
1456
+ :type => String,
1457
+ :dynamic_name => true,
1458
+ :allowed_from_server => false,
1459
+ :description => 'Controls auto-instrumentation of the aws-sdk-dynamodb library at start-up. May be one of `auto`, `prepend`, `chain`, `disabled`.'
1460
+ },
1427
1461
  :'instrumentation.fiber' => {
1428
1462
  :default => 'auto',
1429
1463
  :public => true,
@@ -1472,7 +1506,7 @@ module NewRelic
1472
1506
  :type => String,
1473
1507
  :dynamic_name => true,
1474
1508
  :allowed_from_server => false,
1475
- :description => 'Controls auto-instrumentation of ethon at start up. May be one of [auto|prepend|chain|disabled]'
1509
+ :description => 'Controls auto-instrumentation of ethon at start up. May be one of `auto`, `prepend`, `chain`, `disabled`'
1476
1510
  },
1477
1511
  :'instrumentation.excon' => {
1478
1512
  :default => 'enabled',
@@ -1542,7 +1576,7 @@ module NewRelic
1542
1576
  :type => String,
1543
1577
  :dynamic_name => true,
1544
1578
  :allowed_from_server => false,
1545
- :description => 'Controls auto-instrumentation of httpx at start up. May be one of [auto|prepend|chain|disabled]'
1579
+ :description => 'Controls auto-instrumentation of httpx at start up. May be one of `auto`, `prepend`, `chain`, `disabled`'
1546
1580
  },
1547
1581
  :'instrumentation.logger' => {
1548
1582
  :default => instrumentation_value_from_boolean(:'application_logging.enabled'),
@@ -1604,7 +1638,7 @@ module NewRelic
1604
1638
  :type => String,
1605
1639
  :dynamic_name => true,
1606
1640
  :allowed_from_server => false,
1607
- :description => 'Controls auto-instrumentation of the ruby-openai gem at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
1641
+ :description => 'Controls auto-instrumentation of the ruby-openai gem at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`. Defaults to `disabled` in high security mode.'
1608
1642
  },
1609
1643
  :'instrumentation.puma_rack' => {
1610
1644
  :default => value_of(:'instrumentation.rack'),
@@ -1844,6 +1878,17 @@ module NewRelic
1844
1878
  :transform => DefaultSource.method(:convert_to_regexp_list),
1845
1879
  :description => 'Define transactions you want the agent to ignore, by specifying a list of patterns matching the URI you want to ignore. For more detail, see [the docs on ignoring specific transactions](/docs/agents/ruby-agent/api-guides/ignoring-specific-transactions/#config-ignoring).'
1846
1880
  },
1881
+ # Serverless
1882
+ :'serverless_mode.enabled' => {
1883
+ :default => false,
1884
+ :public => true,
1885
+ :type => Boolean,
1886
+ :allowed_from_server => false,
1887
+ :transform => proc { |bool| NewRelic::Agent::ServerlessHandler.env_var_set? || bool },
1888
+ :description => 'If `true`, the agent will operate in a streamlined mode suitable for use with short-lived ' \
1889
+ 'serverless functions. NOTE: Only AWS Lambda functions are supported currently and this ' \
1890
+ "option is not intended for use without [New Relic's Ruby Lambda layer](https://docs.newrelic.com/docs/serverless-function-monitoring/aws-lambda-monitoring/get-started/monitoring-aws-lambda-serverless-monitoring/) offering."
1891
+ },
1847
1892
  # Sidekiq
1848
1893
  :'sidekiq.args.include' => {
1849
1894
  default: NewRelic::EMPTY_ARRAY,
@@ -1936,7 +1981,11 @@ module NewRelic
1936
1981
  :public => true,
1937
1982
  :type => Integer,
1938
1983
  :allowed_from_server => true,
1939
- :description => 'Defines the maximum number of span events reported from a single harvest. Any Integer between `1` and `10000` is valid.'
1984
+ :description => <<~DESC
1985
+ * Defines the maximum number of span events reported from a single harvest. Any Integer between `1` and `10000` is valid.'
1986
+ * When configuring the agent for [AI monitoring](/docs/ai-monitoring/intro-to-ai-monitoring), set to max value `10000`.\
1987
+ This ensures that the agent captures the maximum amount of distributed traces.
1988
+ DESC
1940
1989
  },
1941
1990
  # Strip exception messages
1942
1991
  :'strip_exception_messages.enabled' => {
@@ -2252,6 +2301,7 @@ module NewRelic
2252
2301
  :public => true,
2253
2302
  :type => Symbol,
2254
2303
  :allowed_from_server => false,
2304
+ :allowlist => %i[none low medium high],
2255
2305
  :external => :infinite_tracing,
2256
2306
  :description => <<~DESC
2257
2307
  Configure the compression level for data sent to the trace observer.
@@ -99,7 +99,7 @@ module NewRelic
99
99
  elsif !value.nil?
100
100
  self[config_key] = true
101
101
  end
102
- else
102
+ elsif !serverless?
103
103
  ::NewRelic::Agent.logger.info("#{environment_key} does not have a corresponding configuration setting (#{config_key} does not exist).")
104
104
  ::NewRelic::Agent.logger.info('Run `rake newrelic:config:docs` or visit https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration to see a list of available configuration settings.')
105
105
  self[config_key] = value
@@ -114,6 +114,14 @@ module NewRelic
114
114
  def collect_new_relic_environment_variable_keys
115
115
  ENV.keys.select { |key| key.match(SUPPORTED_PREFIXES) }
116
116
  end
117
+
118
+ # we can't rely on the :'serverless_mode.enabled' config parameter being
119
+ # set yet to signify serverless mode given that we're in the midst of
120
+ # building the config but we can always rely on the env var being set
121
+ # by the Lambda layer
122
+ def serverless?
123
+ NewRelic::Agent::ServerlessHandler.env_var_set?
124
+ end
117
125
  end
118
126
  end
119
127
  end
@@ -138,7 +138,11 @@ module NewRelic
138
138
  end
139
139
 
140
140
  def evaluate_and_apply_transformations(key, value)
141
- apply_transformations(key, evaluate_procs(value))
141
+ evaluated = evaluate_procs(value)
142
+ default = enforce_allowlist(key, evaluated)
143
+ return default if default
144
+
145
+ apply_transformations(key, evaluated)
142
146
  end
143
147
 
144
148
  def apply_transformations(key, value)
@@ -146,7 +150,7 @@ module NewRelic
146
150
  begin
147
151
  transform.call(value)
148
152
  rescue => e
149
- ::NewRelic::Agent.logger.error("Error applying transformation for #{key}, pre-transform value was: #{value}.", e)
153
+ NewRelic::Agent.logger.error("Error applying transformation for #{key}, pre-transform value was: #{value}.", e)
150
154
  raise e
151
155
  end
152
156
  else
@@ -154,8 +158,21 @@ module NewRelic
154
158
  end
155
159
  end
156
160
 
161
+ def enforce_allowlist(key, value)
162
+ return unless allowlist = default_source.allowlist_for(key)
163
+ return if allowlist.include?(value)
164
+
165
+ default = default_source.default_for(key)
166
+ NewRelic::Agent.logger.warn "Invalid value '#{value}' for #{key}, applying default value of '#{default}'"
167
+ default
168
+ end
169
+
157
170
  def transform_from_default(key)
158
- ::NewRelic::Agent::Configuration::DefaultSource.transform_for(key)
171
+ default_source.transform_for(key)
172
+ end
173
+
174
+ def default_source
175
+ NewRelic::Agent::Configuration::DefaultSource
159
176
  end
160
177
 
161
178
  def register_callback(key, &proc)
@@ -214,7 +231,7 @@ module NewRelic
214
231
  begin
215
232
  thawed_layer[k] = instance_eval(&v) if v.respond_to?(:call)
216
233
  rescue => e
217
- ::NewRelic::Agent.logger.debug("#{e.class.name} : #{e.message} - when accessing config key #{k}")
234
+ NewRelic::Agent.logger.debug("#{e.class.name} : #{e.message} - when accessing config key #{k}")
218
235
  thawed_layer[k] = nil
219
236
  end
220
237
  thawed_layer.delete(:config)
@@ -383,7 +400,7 @@ module NewRelic
383
400
  # is expensive enough that we don't want to do it unless we're
384
401
  # actually going to be logging the message based on our current log
385
402
  # level, so use a `do` block.
386
- ::NewRelic::Agent.logger.debug do
403
+ NewRelic::Agent.logger.debug do
387
404
  hash = flattened.delete_if { |k, _h| DEFAULTS.fetch(k, {}).fetch(:exclude_from_reported_settings, false) }
388
405
  "Updating config (#{direction}) from #{source.class}. Results: #{hash.inspect}"
389
406
  end
@@ -53,6 +53,8 @@ module NewRelic
53
53
  protected
54
54
 
55
55
  def validate_config_file_path(path)
56
+ return if NewRelic::Agent.config[:'serverless_mode.enabled']
57
+
56
58
  expanded_path = File.expand_path(path)
57
59
 
58
60
  if path.empty? || !File.exist?(expanded_path)
@@ -24,7 +24,7 @@ module NewRelic
24
24
  :host => local_host,
25
25
  :display_host => Agent.config[:'process_host.display_name'],
26
26
  :app_name => Agent.config[:app_name],
27
- :language => 'ruby',
27
+ :language => LANGUAGE,
28
28
  :labels => Agent.config.parsed_labels,
29
29
  :agent_version => NewRelic::VERSION::STRING,
30
30
  :environment => @environment_report,