newrelic_rpm 9.7.1 → 9.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -1
- data/README.md +1 -1
- data/lib/new_relic/agent/agent.rb +4 -1
- data/lib/new_relic/agent/agent_helpers/connect.rb +10 -8
- data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/startup.rb +2 -1
- data/lib/new_relic/agent/agent_logger.rb +2 -1
- data/lib/new_relic/agent/configuration/default_source.rb +67 -3
- data/lib/new_relic/agent/configuration/environment_source.rb +9 -1
- data/lib/new_relic/agent/configuration/high_security_source.rb +1 -0
- data/lib/new_relic/agent/configuration/manager.rb +28 -8
- data/lib/new_relic/agent/configuration/security_policy_source.rb +11 -0
- data/lib/new_relic/agent/configuration/yaml_source.rb +2 -0
- data/lib/new_relic/agent/connect/request_builder.rb +1 -1
- data/lib/new_relic/agent/custom_event_aggregator.rb +4 -4
- data/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +1 -5
- data/lib/new_relic/agent/error_collector.rb +2 -0
- data/lib/new_relic/agent/harvester.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/instrumentation.rb +7 -3
- data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -0
- data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +6 -1
- data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +6 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/chain.rb +36 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/instrumentation.rb +196 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/prepend.rb +20 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai.rb +35 -0
- data/lib/new_relic/agent/llm/chat_completion_message.rb +25 -0
- data/lib/new_relic/agent/llm/chat_completion_summary.rb +66 -0
- data/lib/new_relic/agent/llm/embedding.rb +60 -0
- data/lib/new_relic/agent/llm/llm_event.rb +95 -0
- data/lib/new_relic/agent/llm/response_headers.rb +80 -0
- data/lib/new_relic/agent/llm.rb +49 -0
- data/lib/new_relic/agent/log_event_aggregator.rb +1 -16
- data/lib/new_relic/agent/new_relic_service.rb +12 -2
- data/lib/new_relic/agent/serverless_handler.rb +171 -0
- data/lib/new_relic/agent/threading/agent_thread.rb +1 -2
- data/lib/new_relic/agent/tracer.rb +5 -5
- data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
- data/lib/new_relic/agent/transaction/tracing.rb +2 -2
- data/lib/new_relic/agent/transaction_error_primitive.rb +23 -19
- data/lib/new_relic/agent.rb +102 -8
- data/lib/new_relic/constants.rb +2 -0
- data/lib/new_relic/control/instance_methods.rb +7 -0
- data/lib/new_relic/local_environment.rb +13 -6
- data/lib/new_relic/rack/browser_monitoring.rb +8 -4
- data/lib/new_relic/supportability_helper.rb +2 -0
- data/lib/new_relic/thread_local_storage.rb +31 -0
- data/lib/new_relic/version.rb +2 -2
- data/lib/tasks/config.rake +2 -1
- data/newrelic.yml +27 -1
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad2c3da566c3fda3369af14dd1d25b82a582c3db88c910474634b1b8169648fc
|
4
|
+
data.tar.gz: 1ebe2637f6cef4b3b3e70afb157b91516cc6bf37511f1acb1f3ec5e1caf917e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d86c1de9e30e3952b7d5dbec62845693795e6c3b0273b5f34a19a0a550e208314774d3da08246c26c8e903657c36b71b91ae170911869f3e801879e8c8ee4e05
|
7
|
+
data.tar.gz: bb5732481d984d24366be27b1186e35b753b7dff7613774b8f4f50749e516db6556edd93b1017e656535cf83ef306e31199e44b03d677ea8c249f472f7f30222
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,51 @@
|
|
1
1
|
# New Relic Ruby Agent Release Notes
|
2
2
|
|
3
|
+
## v9.9.0
|
4
|
+
|
5
|
+
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.
|
6
|
+
|
7
|
+
- **Feature: Serverless Mode for AWS Lambda**
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
11
|
+
- **Feature: Add support for Elasticsearch 8.13.0**
|
12
|
+
|
13
|
+
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.
|
14
|
+
|
15
|
+
- **Bugfix: Add 'request.temperature' to ruby-openai chat completion summaries**
|
16
|
+
|
17
|
+
Previously, the agent was not reporting the `request.temperature` attribute on `LlmChatCompletionSummary` events through ruby-openai instrumentation. We are now reporting this attribute.
|
18
|
+
|
19
|
+
## v9.8.0
|
20
|
+
|
21
|
+
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.
|
22
|
+
|
23
|
+
- **Feature: Add instrumentation for ruby-openai**
|
24
|
+
|
25
|
+
Instrumentation has been added for the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem, supporting versions 3.4.0 and higher [(PR#2442)](https://github.com/newrelic/newrelic-ruby-agent/pull/2442). While ruby-openai instrumentation is enabled by default, the configuration option `ai_monitoring.enabled` is disabled by default and controls all AI monitoring. `ai_monitoring.enabled` must be set to `true` in order to receive ruby-openai instrumentation. High-Security Mode must be disabled in order to receive AI monitoring.
|
26
|
+
|
27
|
+
Calls to embedding and chat completion endpoints are automatically traced. These events can be enhanced with the introduction of two new APIs. Custom attributes can also be added to LLM events using the API `NewRelic::Agent.add_custom_attributes`, but they must be prefixed with `llm.`. For example, `NewRelic::Agent.add_custom_attributes({'llm.user_id': user_id})`.
|
28
|
+
|
29
|
+
- **Feature: Add AI monitoring APIs**
|
30
|
+
|
31
|
+
This version introduces two new APIs that allow users to record additional information on LLM events:
|
32
|
+
* `NewRelic::Agent.record_llm_feedback_event` - Records user feedback events.
|
33
|
+
* `NewRelic::Agent.set_llm_token_count_callback` - Sets a callback proc for calculating `token_count` attributes for embedding and chat completion message events.
|
34
|
+
|
35
|
+
Visit [RubyDoc](https://rubydoc.info/github/newrelic/newrelic-ruby-agent/) for more information on each of these APIs.
|
36
|
+
|
37
|
+
- **Feature: Store tracer state on thread-level**
|
38
|
+
|
39
|
+
A new configuration option, `thread_local_tracer_state`, stores New Relic's tracer state on the thread-level, as opposed to the default fiber-level storage. This configuration is turned off by default. Our thanks go to community member [@markiz](https://github.com/markiz) who contributed the idea, code, configuration option, and tests for this new feature! [PR#2475](https://github.com/newrelic/newrelic-ruby-agent/pull/2475).
|
40
|
+
|
41
|
+
- **Bugfix: Harden the browser agent insertion logic**
|
42
|
+
|
43
|
+
With [Issue#2462](https://github.com/newrelic/newrelic-ruby-agent/issues/2462), community member [@miry](https://github.com/miry) explained that it was possible for an HTTP response headers hash to have symbols for values. Not only would these symbols prevent the inclusion of the New Relic browser agent tag in the response body, but more importantly they would cause an exception that would bubble up to the monitored web application itself. With [PR#2465](https://github.com/newrelic/newrelic-ruby-agent/pull/2465) symbol based values are now supported and all other potential future exceptions are now handled. Additionally, the refactor to support symbols has been shown through benchmarking to give the processing of string and mixed type hashes a slight speed boost too.
|
44
|
+
|
45
|
+
- **Bugfix: Prevent Exception in Active Support Broadcast logger instrumentation**
|
46
|
+
|
47
|
+
Previously, in certain situations the agent could cause an exception to be raised when attempting to interact with a broadcast log event. This has been fixed. Thanks to [@nathan-appere](https://github.com/nathan-appere) for reporting this issue and providing a fix! [PR#2510](https://github.com/newrelic/newrelic-ruby-agent/pull/2510)
|
48
|
+
|
3
49
|
|
4
50
|
## v9.7.1
|
5
51
|
|
@@ -19,7 +65,7 @@ Version 9.7.0 introduces ViewComponent instrumentation, changes the endpoint use
|
|
19
65
|
|
20
66
|
- **Feature: ViewComponent instrumentation**
|
21
67
|
|
22
|
-
[ViewComponent](https://viewcomponent.org/) is a now an instrumented library. [PR#2367](https://github.com/newrelic/newrelic-ruby-agent/pull/2367)
|
68
|
+
[ViewComponent](https://viewcomponent.org/) is a now an instrumented library. [PR#2367](https://github.com/newrelic/newrelic-ruby-agent/pull/2367)
|
23
69
|
|
24
70
|
- **Feature: Use root path to access Elasticsearch cluster name**
|
25
71
|
|
data/README.md
CHANGED
@@ -119,7 +119,7 @@ If you have any questions, or to execute our corporate CLA (required if your con
|
|
119
119
|
|
120
120
|
As noted in our [security policy](https://github.com/newrelic/newrelic-ruby-agent/security/policy), New Relic is committed to the privacy and security of our customers and their data. We believe that providing coordinated disclosure by security researchers and engaging with the security community are important means to achieve our security goals.
|
121
121
|
|
122
|
-
If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through [
|
122
|
+
If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through [our bug bounty program](https://docs.newrelic.com/docs/security/security-privacy/information-security/report-security-vulnerabilities/).
|
123
123
|
|
124
124
|
If you would like to contribute to this project, please review [these guidelines](https://github.com/newrelic/newrelic-ruby-agent/blob/main/CONTRIBUTING.md).
|
125
125
|
|
@@ -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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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)
|
@@ -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
|
-
|
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
|
@@ -360,6 +375,26 @@ module NewRelic
|
|
360
375
|
- a.third.event
|
361
376
|
DESCRIPTION
|
362
377
|
},
|
378
|
+
:'ai_monitoring.enabled' => {
|
379
|
+
:default => false,
|
380
|
+
:public => true,
|
381
|
+
:type => Boolean,
|
382
|
+
:allowed_from_server => false,
|
383
|
+
:description => 'If `false`, all LLM instrumentation (OpenAI only for now) will be disabled and no metrics, events, or spans will be sent. AI Monitoring is automatically disabled if `high_security` mode is enabled.'
|
384
|
+
},
|
385
|
+
:'ai_monitoring.record_content.enabled' => {
|
386
|
+
:default => true,
|
387
|
+
:public => true,
|
388
|
+
:type => Boolean,
|
389
|
+
:allowed_from_server => false,
|
390
|
+
:description => <<~DESCRIPTION
|
391
|
+
If `false`, LLM instrumentation (OpenAI only for now) will not capture input and output content on specific LLM events.
|
392
|
+
|
393
|
+
The excluded attributes include:
|
394
|
+
* `content` from LlmChatCompletionMessage events
|
395
|
+
* `input` from LlmEmbedding events
|
396
|
+
DESCRIPTION
|
397
|
+
},
|
363
398
|
# this is only set via server side config
|
364
399
|
:apdex_t => {
|
365
400
|
:default => 0.5,
|
@@ -554,6 +589,13 @@ module NewRelic
|
|
554
589
|
:allowed_from_server => false,
|
555
590
|
:description => 'When set to `true`, forces a synchronous connection to the New Relic [collector](/docs/using-new-relic/welcome-new-relic/get-started/glossary/#collector) during application startup. For very short-lived processes, this helps ensure the New Relic agent has time to report.'
|
556
591
|
},
|
592
|
+
:thread_local_tracer_state => {
|
593
|
+
:default => false,
|
594
|
+
:public => true,
|
595
|
+
:type => Boolean,
|
596
|
+
:allowed_from_server => false,
|
597
|
+
:description => 'If `true`, tracer state storage is thread-local, otherwise, fiber-local'
|
598
|
+
},
|
557
599
|
:timeout => {
|
558
600
|
:default => 2 * 60, # 2 minutes
|
559
601
|
:public => true,
|
@@ -720,7 +762,7 @@ module NewRelic
|
|
720
762
|
:public => true,
|
721
763
|
:type => Integer,
|
722
764
|
:allowed_from_server => false,
|
723
|
-
:description => 'Defines the maximum number of frames in an error backtrace. Backtraces over this amount are truncated
|
765
|
+
:description => 'Defines the maximum number of frames in an error backtrace. Backtraces over this amount are truncated in the middle, preserving the beginning and the end of the stack trace.'
|
724
766
|
},
|
725
767
|
:'error_collector.max_event_samples_stored' => {
|
726
768
|
:default => 100,
|
@@ -773,6 +815,7 @@ module NewRelic
|
|
773
815
|
:public => true,
|
774
816
|
:type => String,
|
775
817
|
:allowed_from_server => false,
|
818
|
+
:allowlist => %w[debug info warn error fatal unknown DEBUG INFO WARN ERROR FATAL UNKNOWN],
|
776
819
|
:description => <<~DESCRIPTION
|
777
820
|
Sets the minimum level a log event must have to be forwarded to New Relic.
|
778
821
|
|
@@ -1570,6 +1613,15 @@ module NewRelic
|
|
1570
1613
|
:allowed_from_server => false,
|
1571
1614
|
:description => 'Controls auto-instrumentation of `Net::HTTP` at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
|
1572
1615
|
},
|
1616
|
+
:'instrumentation.ruby_openai' => {
|
1617
|
+
:default => 'auto',
|
1618
|
+
:documentation_default => 'auto',
|
1619
|
+
:public => true,
|
1620
|
+
:type => String,
|
1621
|
+
:dynamic_name => true,
|
1622
|
+
:allowed_from_server => false,
|
1623
|
+
:description => 'Controls auto-instrumentation of the ruby-openai gem at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
|
1624
|
+
},
|
1573
1625
|
:'instrumentation.puma_rack' => {
|
1574
1626
|
:default => value_of(:'instrumentation.rack'),
|
1575
1627
|
:documentation_default => 'auto',
|
@@ -1808,6 +1860,17 @@ module NewRelic
|
|
1808
1860
|
:transform => DefaultSource.method(:convert_to_regexp_list),
|
1809
1861
|
: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).'
|
1810
1862
|
},
|
1863
|
+
# Serverless
|
1864
|
+
:'serverless_mode.enabled' => {
|
1865
|
+
:default => false,
|
1866
|
+
:public => true,
|
1867
|
+
:type => Boolean,
|
1868
|
+
:allowed_from_server => false,
|
1869
|
+
:transform => proc { |bool| NewRelic::Agent::ServerlessHandler.env_var_set? || bool },
|
1870
|
+
:description => 'If `true`, the agent will operate in a streamlined mode suitable for use with short-lived ' \
|
1871
|
+
'serverless functions. NOTE: Only AWS Lambda functions are supported currently and this ' \
|
1872
|
+
"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."
|
1873
|
+
},
|
1811
1874
|
# Sidekiq
|
1812
1875
|
:'sidekiq.args.include' => {
|
1813
1876
|
default: NewRelic::EMPTY_ARRAY,
|
@@ -2216,6 +2279,7 @@ module NewRelic
|
|
2216
2279
|
:public => true,
|
2217
2280
|
:type => Symbol,
|
2218
2281
|
:allowed_from_server => false,
|
2282
|
+
:allowlist => %i[none low medium high],
|
2219
2283
|
:external => :infinite_tracing,
|
2220
2284
|
:description => <<~DESC
|
2221
2285
|
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
|
-
|
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
|
@@ -34,6 +34,7 @@ module NewRelic
|
|
34
34
|
def initialize
|
35
35
|
reset_to_defaults
|
36
36
|
@callbacks = Hash.new { |hash, key| hash[key] = [] }
|
37
|
+
@lock = Mutex.new
|
37
38
|
end
|
38
39
|
|
39
40
|
def add_config_for_testing(source, level = 0)
|
@@ -137,7 +138,11 @@ module NewRelic
|
|
137
138
|
end
|
138
139
|
|
139
140
|
def evaluate_and_apply_transformations(key, value)
|
140
|
-
|
141
|
+
evaluated = evaluate_procs(value)
|
142
|
+
default = enforce_allowlist(key, evaluated)
|
143
|
+
return default if default
|
144
|
+
|
145
|
+
apply_transformations(key, evaluated)
|
141
146
|
end
|
142
147
|
|
143
148
|
def apply_transformations(key, value)
|
@@ -145,7 +150,7 @@ module NewRelic
|
|
145
150
|
begin
|
146
151
|
transform.call(value)
|
147
152
|
rescue => e
|
148
|
-
|
153
|
+
NewRelic::Agent.logger.error("Error applying transformation for #{key}, pre-transform value was: #{value}.", e)
|
149
154
|
raise e
|
150
155
|
end
|
151
156
|
else
|
@@ -153,8 +158,21 @@ module NewRelic
|
|
153
158
|
end
|
154
159
|
end
|
155
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
|
+
|
156
170
|
def transform_from_default(key)
|
157
|
-
|
171
|
+
default_source.transform_for(key)
|
172
|
+
end
|
173
|
+
|
174
|
+
def default_source
|
175
|
+
NewRelic::Agent::Configuration::DefaultSource
|
158
176
|
end
|
159
177
|
|
160
178
|
def register_callback(key, &proc)
|
@@ -213,7 +231,7 @@ module NewRelic
|
|
213
231
|
begin
|
214
232
|
thawed_layer[k] = instance_eval(&v) if v.respond_to?(:call)
|
215
233
|
rescue => e
|
216
|
-
|
234
|
+
NewRelic::Agent.logger.debug("#{e.class.name} : #{e.message} - when accessing config key #{k}")
|
217
235
|
thawed_layer[k] = nil
|
218
236
|
end
|
219
237
|
thawed_layer.delete(:config)
|
@@ -364,9 +382,11 @@ module NewRelic
|
|
364
382
|
def reset_cache
|
365
383
|
return new_cache unless defined?(@cache) && @cache
|
366
384
|
|
367
|
-
|
368
|
-
|
369
|
-
|
385
|
+
@lock.synchronize do
|
386
|
+
preserved = @cache.dup.select { |_k, v| DEPENDENCY_DETECTION_VALUES.include?(v) }
|
387
|
+
new_cache
|
388
|
+
preserved.each { |k, v| @cache[k] = v }
|
389
|
+
end
|
370
390
|
|
371
391
|
@cache
|
372
392
|
end
|
@@ -380,7 +400,7 @@ module NewRelic
|
|
380
400
|
# is expensive enough that we don't want to do it unless we're
|
381
401
|
# actually going to be logging the message based on our current log
|
382
402
|
# level, so use a `do` block.
|
383
|
-
|
403
|
+
NewRelic::Agent.logger.debug do
|
384
404
|
hash = flattened.delete_if { |k, _h| DEFAULTS.fetch(k, {}).fetch(:exclude_from_reported_settings, false) }
|
385
405
|
"Updating config (#{direction}) from #{source.class}. Results: #{hash.inspect}"
|
386
406
|
end
|
@@ -7,6 +7,8 @@ require 'new_relic/agent/configuration/dotted_hash'
|
|
7
7
|
module NewRelic
|
8
8
|
module Agent
|
9
9
|
module Configuration
|
10
|
+
# The Language Security Policy Source gives customers the ability to
|
11
|
+
# configure high security mode settings.
|
10
12
|
class SecurityPolicySource < DottedHash
|
11
13
|
class << self
|
12
14
|
def enabled?(option)
|
@@ -147,6 +149,15 @@ module NewRelic
|
|
147
149
|
permitted_fn: nil
|
148
150
|
}
|
149
151
|
],
|
152
|
+
'ai_monitoring' => [
|
153
|
+
{
|
154
|
+
option: :'ai_monitoring.enabled',
|
155
|
+
supported: true,
|
156
|
+
enabled_fn: method(:enabled?),
|
157
|
+
disabled_value: false,
|
158
|
+
permitted_fn: nil
|
159
|
+
}
|
160
|
+
],
|
150
161
|
'allow_raw_exception_messages' => [
|
151
162
|
{
|
152
163
|
option: :'strip_exception_messages.enabled',
|
@@ -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 =>
|
27
|
+
:language => LANGUAGE,
|
28
28
|
:labels => Agent.config.parsed_labels,
|
29
29
|
:agent_version => NewRelic::VERSION::STRING,
|
30
30
|
:environment => @environment_report,
|
@@ -52,11 +52,11 @@ module NewRelic
|
|
52
52
|
{TYPE => type,
|
53
53
|
TIMESTAMP => Process.clock_gettime(Process::CLOCK_REALTIME).to_i,
|
54
54
|
PRIORITY => priority},
|
55
|
-
create_custom_event_attributes(attributes)
|
55
|
+
create_custom_event_attributes(type, attributes)
|
56
56
|
]
|
57
57
|
end
|
58
58
|
|
59
|
-
def create_custom_event_attributes(attributes)
|
59
|
+
def create_custom_event_attributes(type, attributes)
|
60
60
|
result = AttributeProcessing.flatten_and_coerce(attributes)
|
61
61
|
|
62
62
|
if result.size > MAX_ATTRIBUTE_COUNT
|
@@ -70,9 +70,9 @@ module NewRelic
|
|
70
70
|
key = key[0, MAX_NAME_SIZE]
|
71
71
|
end
|
72
72
|
|
73
|
-
# value is limited to 4095
|
73
|
+
# value is limited to 4095 except for LLM content-related events
|
74
74
|
if val.is_a?(String) && val.length > MAX_ATTRIBUTE_SIZE
|
75
|
-
val = val[0, MAX_ATTRIBUTE_SIZE]
|
75
|
+
val = val[0, MAX_ATTRIBUTE_SIZE] unless NewRelic::Agent::LLM.exempt_event_attribute?(type, key)
|
76
76
|
end
|
77
77
|
|
78
78
|
new_result[key] = val
|
@@ -35,7 +35,7 @@ module NewRelic
|
|
35
35
|
|
36
36
|
class << self
|
37
37
|
def for_transaction(transaction)
|
38
|
-
return nil unless connected?
|
38
|
+
return nil unless Agent.instance.connected?
|
39
39
|
|
40
40
|
payload = new
|
41
41
|
payload.version = VERSION
|
@@ -101,10 +101,6 @@ module NewRelic
|
|
101
101
|
transaction.current_segment.guid
|
102
102
|
end
|
103
103
|
end
|
104
|
-
|
105
|
-
def connected?
|
106
|
-
Agent.instance.connected?
|
107
|
-
end
|
108
104
|
end
|
109
105
|
|
110
106
|
attr_accessor :version,
|
@@ -216,6 +216,8 @@ module NewRelic
|
|
216
216
|
def notice_segment_error(segment, exception, options = {})
|
217
217
|
return if skip_notice_error?(exception)
|
218
218
|
|
219
|
+
options.merge!(segment.llm_event.error_attributes(exception)) if segment.llm_event
|
220
|
+
|
219
221
|
segment.set_noticed_error(create_noticed_error(exception, options))
|
220
222
|
exception
|
221
223
|
rescue => e
|
@@ -5,9 +5,13 @@
|
|
5
5
|
module NewRelic::Agent::Instrumentation
|
6
6
|
module ActiveSupportBroadcastLogger
|
7
7
|
def record_one_broadcast_with_new_relic(*args)
|
8
|
-
broadcasts[1..-1]
|
9
|
-
|
10
|
-
|
8
|
+
if broadcasts && broadcasts[1..-1]
|
9
|
+
broadcasts[1..-1].each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, true) }
|
10
|
+
yield
|
11
|
+
broadcasts.each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, false) }
|
12
|
+
else
|
13
|
+
NewRelic::Agent.logger.error('Error recording broadcasted logger')
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
@@ -10,7 +10,11 @@ module NewRelic::Agent::Instrumentation
|
|
10
10
|
OPERATION = 'perform_request'
|
11
11
|
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
|
12
12
|
|
13
|
-
|
13
|
+
# We need the positional arguments `params` and `body`
|
14
|
+
# to capture the nosql statement
|
15
|
+
# *args protects the instrumented method if new arguments are added to
|
16
|
+
# perform_request
|
17
|
+
def perform_request_with_tracing(_method, _path, params = {}, body = nil, _headers = nil, *_args)
|
14
18
|
return yield unless NewRelic::Agent::Tracer.tracing_enabled?
|
15
19
|
|
16
20
|
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
|
@@ -22,6 +26,7 @@ module NewRelic::Agent::Instrumentation
|
|
22
26
|
port_path_or_id: nr_hosts[:port],
|
23
27
|
database_name: nr_cluster_name
|
24
28
|
)
|
29
|
+
|
25
30
|
begin
|
26
31
|
NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
|
27
32
|
ensure
|
@@ -32,7 +32,13 @@ module NewRelic
|
|
32
32
|
end
|
33
33
|
|
34
34
|
wrapped_response = NewRelic::Agent::HTTPClients::NetHTTPResponse.new(response)
|
35
|
+
|
36
|
+
if NewRelic::Agent::LLM.openai_parent?(segment)
|
37
|
+
NewRelic::Agent::LLM.populate_openai_response_headers(wrapped_response, segment.parent)
|
38
|
+
end
|
39
|
+
|
35
40
|
segment.process_response_headers(wrapped_response)
|
41
|
+
|
36
42
|
response
|
37
43
|
ensure
|
38
44
|
segment&.finish
|
@@ -0,0 +1,36 @@
|
|
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::Agent::Instrumentation
|
6
|
+
module OpenAI::Chain
|
7
|
+
def self.instrument!
|
8
|
+
::OpenAI::Client.class_eval do
|
9
|
+
include NewRelic::Agent::Instrumentation::OpenAI
|
10
|
+
|
11
|
+
alias_method(:json_post_without_new_relic, :json_post)
|
12
|
+
|
13
|
+
# In versions 4.0.0+ json_post is an instance method
|
14
|
+
# defined in the OpenAI::HTTP module, included by the
|
15
|
+
# OpenAI::Client class
|
16
|
+
def json_post(**kwargs)
|
17
|
+
json_post_with_new_relic(**kwargs) do
|
18
|
+
json_post_without_new_relic(**kwargs)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# In versions below 4.0.0 json_post is a class method
|
23
|
+
# on OpenAI::Client
|
24
|
+
class << self
|
25
|
+
alias_method(:json_post_without_new_relic, :json_post)
|
26
|
+
|
27
|
+
def json_post(**kwargs)
|
28
|
+
json_post_with_new_relic(**kwargs) do
|
29
|
+
json_post_without_new_relic(**kwargs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|