newrelic_rpm 3.9.4.245 → 3.9.5.251
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +57 -0
- data/Guardfile +1 -0
- data/lib/new_relic/agent/agent.rb +3 -3
- data/lib/new_relic/agent/audit_logger.rb +5 -2
- data/lib/new_relic/agent/configuration/default_source.rb +11 -5
- data/lib/new_relic/agent/error_collector.rb +14 -1
- data/lib/new_relic/agent/hostname.rb +22 -1
- data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +8 -2
- data/lib/new_relic/agent/instrumentation/queue_time.rb +9 -6
- data/lib/new_relic/agent/method_tracer.rb +51 -172
- data/lib/new_relic/agent/method_tracer_helpers.rb +90 -0
- data/lib/new_relic/agent/new_relic_service.rb +33 -11
- data/lib/new_relic/agent/new_relic_service/encoders.rb +9 -5
- data/lib/new_relic/agent/request_sampler.rb +20 -12
- data/lib/new_relic/agent/rules_engine.rb +31 -78
- data/lib/new_relic/agent/rules_engine/replacement_rule.rb +76 -0
- data/lib/new_relic/agent/rules_engine/segment_terms_rule.rb +48 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +1 -1
- data/lib/new_relic/agent/sql_sampler.rb +39 -10
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +1 -0
- data/lib/new_relic/agent/stats_engine/stats_hash.rb +7 -13
- data/lib/new_relic/agent/system_info.rb +96 -10
- data/lib/new_relic/agent/threading/agent_thread.rb +4 -1
- data/lib/new_relic/agent/threading/backtrace_node.rb +67 -57
- data/lib/new_relic/agent/threading/thread_profile.rb +30 -15
- data/lib/new_relic/agent/transaction.rb +11 -4
- data/lib/new_relic/environment_report.rb +21 -20
- data/lib/new_relic/version.rb +1 -1
- data/test/agent_helper.rb +12 -0
- data/test/fixtures/cross_agent_tests/README.md +1 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_1core_1logical.txt +3 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_1core_2logical.txt +14 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_2core_2logical.txt +14 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_4core_4logical.txt +28 -0
- data/test/fixtures/{proc_cpuinfo.txt → cross_agent_tests/proc_cpuinfo/2pack_12core_24logical.txt} +0 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_20core_40logical.txt +999 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_2core_2logical.txt +51 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_2core_4logical.txt +28 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_4core_4logical.txt +28 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/4pack_4core_4logical.txt +103 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/8pack_8core_8logical.txt +199 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/README.md +24 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/Xpack_Xcore_2logical.txt +43 -0
- data/test/fixtures/cross_agent_tests/transaction_segment_terms.json +101 -0
- data/test/multiverse/lib/multiverse/suite.rb +1 -1
- data/test/multiverse/suites/agent_only/agent_run_id_handling_test.rb +40 -0
- data/test/multiverse/suites/agent_only/labels_test.rb +9 -14
- data/test/multiverse/suites/agent_only/marshaling_test.rb +4 -6
- data/test/multiverse/suites/agent_only/rename_rule_test.rb +41 -4
- data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +11 -3
- data/test/multiverse/suites/config_file_loading/config_file_loading_test.rb +8 -8
- data/test/multiverse/suites/rack/example_app.rb +20 -0
- data/test/multiverse/suites/rack/http_response_code_test.rb +51 -0
- data/test/multiverse/suites/sidekiq/Envfile +13 -6
- data/test/multiverse/suites/sidekiq/sidekiq_server.rb +4 -3
- data/test/new_relic/agent/audit_logger_test.rb +27 -0
- data/test/new_relic/agent/error_collector_test.rb +26 -5
- data/test/new_relic/agent/hostname_test.rb +66 -14
- data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +8 -12
- data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +7 -45
- data/test/new_relic/agent/method_tracer_test.rb +52 -1
- data/test/new_relic/agent/new_relic_service_test.rb +76 -0
- data/test/new_relic/agent/request_sampler_test.rb +7 -0
- data/test/new_relic/agent/rules_engine_test.rb +87 -56
- data/test/new_relic/agent/sql_sampler_test.rb +50 -14
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +2 -2
- data/test/new_relic/agent/stats_engine/samplers_test.rb +1 -1
- data/test/new_relic/agent/{stats_hash_test.rb → stats_engine/stats_hash_test.rb} +1 -38
- data/test/new_relic/agent/system_info_test.rb +45 -0
- data/test/new_relic/agent/threading/agent_thread_test.rb +30 -0
- data/test/new_relic/agent/threading/backtrace_node_test.rb +27 -44
- data/test/new_relic/agent/threading/thread_profile_test.rb +35 -14
- data/test/new_relic/agent/transaction_test.rb +13 -10
- data/test/new_relic/environment_report_test.rb +7 -6
- data/test/new_relic/fake_collector.rb +10 -6
- data/test/new_relic/multiverse_helpers.rb +4 -11
- data/test/new_relic/rack/agent_hooks_test.rb +1 -1
- data/test/performance/lib/performance/baseline_compare_reporter.rb +24 -7
- data/test/performance/lib/performance/result.rb +3 -1
- data/test/performance/lib/performance/runner.rb +10 -0
- data/test/performance/lib/performance/timer.rb +6 -10
- data/test/performance/script/runner +18 -1
- data/test/performance/suites/queue_time.rb +21 -0
- data/test/performance/suites/stats_hash.rb +34 -0
- data/test/performance/suites/thread_profiling.rb +26 -0
- metadata +25 -4
- metadata.gz.sig +0 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module MethodTracerHelpers
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# helper for logging errors to the newrelic_agent.log
|
11
|
+
# properly. Logs the error at error level
|
12
|
+
def log_errors(code_area)
|
13
|
+
yield
|
14
|
+
rescue => e
|
15
|
+
::NewRelic::Agent.logger.error("Caught exception in #{code_area}.", e)
|
16
|
+
end
|
17
|
+
|
18
|
+
def trace_execution_scoped_header(state, t0)
|
19
|
+
log_errors(:trace_execution_scoped_header) do
|
20
|
+
stack = state.traced_method_stack
|
21
|
+
stack.push_frame(state, :method_tracer, t0)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def record_metrics(state, first_name, other_names, duration, exclusive, options)
|
26
|
+
record_scoped_metric = options.has_key?(:scoped_metric) ? options[:scoped_metric] : true
|
27
|
+
stat_engine = NewRelic::Agent.instance.stats_engine
|
28
|
+
if record_scoped_metric
|
29
|
+
stat_engine.record_scoped_and_unscoped_metrics(state, first_name, other_names, duration, exclusive)
|
30
|
+
else
|
31
|
+
metrics = [first_name].concat(other_names)
|
32
|
+
stat_engine.record_unscoped_metrics(state, metrics, duration, exclusive)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def trace_execution_scoped_footer(state, t0, first_name, metric_names, expected_frame, options, t1=Time.now.to_f)
|
37
|
+
log_errors(:trace_method_execution_footer) do
|
38
|
+
if expected_frame
|
39
|
+
stack = state.traced_method_stack
|
40
|
+
create_metrics = options.has_key?(:metric) ? options[:metric] : true
|
41
|
+
frame = stack.pop_frame(state, expected_frame, first_name, t1, create_metrics)
|
42
|
+
if create_metrics
|
43
|
+
duration = t1 - t0
|
44
|
+
exclusive = duration - frame.children_time
|
45
|
+
|
46
|
+
if duration < 1_000_000_000 # roughly 31 years
|
47
|
+
if duration < 0
|
48
|
+
::NewRelic::Agent.logger.log_once(:warn, "metric_duration_negative:#{first_name}",
|
49
|
+
"Metric #{first_name} has negative duration: #{duration} s")
|
50
|
+
end
|
51
|
+
|
52
|
+
if exclusive < 0
|
53
|
+
::NewRelic::Agent.logger.log_once(:warn, "metric_exclusive_negative:#{first_name}",
|
54
|
+
"Metric #{first_name} has negative exclusive time: duration = #{duration} s, child_time = #{frame.children_time}")
|
55
|
+
end
|
56
|
+
|
57
|
+
record_metrics(state, first_name, metric_names, duration, exclusive, options)
|
58
|
+
else
|
59
|
+
::NewRelic::Agent.logger.log_once(:warn, "too_huge_metric:#{first_name}",
|
60
|
+
"Ignoring metric #{first_name} with unacceptably large duration: #{duration} s")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def trace_execution_scoped(metric_names, options={}) #THREAD_LOCAL_ACCESS
|
68
|
+
state = NewRelic::Agent::TransactionState.tl_get
|
69
|
+
return yield unless state.is_execution_traced?
|
70
|
+
|
71
|
+
metric_names = Array(metric_names)
|
72
|
+
first_name = metric_names.shift
|
73
|
+
return yield unless first_name
|
74
|
+
|
75
|
+
additional_metrics_callback = options[:additional_metrics_callback]
|
76
|
+
start_time = Time.now.to_f
|
77
|
+
expected_scope = trace_execution_scoped_header(state, start_time)
|
78
|
+
|
79
|
+
begin
|
80
|
+
result = yield
|
81
|
+
metric_names += Array(additional_metrics_callback.call) if additional_metrics_callback
|
82
|
+
result
|
83
|
+
ensure
|
84
|
+
trace_execution_scoped_footer(state, start_time, first_name, metric_names, expected_scope, options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -124,22 +124,26 @@ module NewRelic
|
|
124
124
|
metric_data_array = build_metric_data_array(stats_hash)
|
125
125
|
result = invoke_remote(
|
126
126
|
:metric_data,
|
127
|
-
[@agent_id, timeslice_start.to_f, timeslice_end.to_f, metric_data_array]
|
127
|
+
[@agent_id, timeslice_start.to_f, timeslice_end.to_f, metric_data_array],
|
128
|
+
:item_count => metric_data_array.size
|
128
129
|
)
|
129
130
|
fill_metric_id_cache(result)
|
130
131
|
result
|
131
132
|
end
|
132
133
|
|
133
134
|
def error_data(unsent_errors)
|
134
|
-
invoke_remote(:error_data, [@agent_id, unsent_errors]
|
135
|
+
invoke_remote(:error_data, [@agent_id, unsent_errors],
|
136
|
+
:item_count => unsent_errors.size)
|
135
137
|
end
|
136
138
|
|
137
139
|
def transaction_sample_data(traces)
|
138
|
-
invoke_remote(:transaction_sample_data, [@agent_id, traces]
|
140
|
+
invoke_remote(:transaction_sample_data, [@agent_id, traces],
|
141
|
+
:item_count => traces.size)
|
139
142
|
end
|
140
143
|
|
141
144
|
def sql_trace_data(sql_traces)
|
142
|
-
invoke_remote(:sql_trace_data, [sql_traces]
|
145
|
+
invoke_remote(:sql_trace_data, [sql_traces],
|
146
|
+
:item_count => sql_traces.size)
|
143
147
|
end
|
144
148
|
|
145
149
|
def profile_data(profile)
|
@@ -160,7 +164,8 @@ module NewRelic
|
|
160
164
|
|
161
165
|
# Send fine-grained analytic data to the collector.
|
162
166
|
def analytic_event_data(data)
|
163
|
-
invoke_remote(:analytic_event_data, [@agent_id, data]
|
167
|
+
invoke_remote(:analytic_event_data, [@agent_id, data],
|
168
|
+
:item_count => data.size)
|
164
169
|
end
|
165
170
|
|
166
171
|
# We do not compress if content is smaller than 64kb. There are
|
@@ -332,7 +337,10 @@ module NewRelic
|
|
332
337
|
:collector => @collector)
|
333
338
|
@marshaller.load(decompress_response(response))
|
334
339
|
ensure
|
335
|
-
|
340
|
+
record_timing_supportability_metrics(method, start_ts, serialize_finish_ts)
|
341
|
+
if size
|
342
|
+
record_size_supportability_metrics(method, size, options[:item_count])
|
343
|
+
end
|
336
344
|
end
|
337
345
|
|
338
346
|
def handle_serialization_error(method, e)
|
@@ -344,7 +352,7 @@ module NewRelic
|
|
344
352
|
raise error
|
345
353
|
end
|
346
354
|
|
347
|
-
def
|
355
|
+
def record_timing_supportability_metrics(method, start_ts, serialize_finish_ts)
|
348
356
|
serialize_time = serialize_finish_ts && (serialize_finish_ts - start_ts)
|
349
357
|
duration = (Time.now - start_ts).to_f
|
350
358
|
NewRelic::Agent.record_metric("Supportability/invoke_remote", duration)
|
@@ -353,10 +361,24 @@ module NewRelic
|
|
353
361
|
NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize", serialize_time)
|
354
362
|
NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize/#{method.to_s}", serialize_time)
|
355
363
|
end
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
364
|
+
end
|
365
|
+
|
366
|
+
# For these metrics, we use the following fields:
|
367
|
+
# call_count => number of times this remote method was invoked
|
368
|
+
# total_call_time => total size in bytes of payloads across all invocations
|
369
|
+
# total_exclusive_time => total size in items (e.g. unique metrics, traces, events, etc) across all invocations
|
370
|
+
#
|
371
|
+
# The last field doesn't make sense for all methods (e.g. get_agent_commands),
|
372
|
+
# so we omit it for those methods that don't really take collections
|
373
|
+
# of items as arguments.
|
374
|
+
def record_size_supportability_metrics(method, size_bytes, item_count)
|
375
|
+
metrics = [
|
376
|
+
"Supportability/invoke_remote_size",
|
377
|
+
"Supportability/invoke_remote_size/#{method.to_s}"
|
378
|
+
]
|
379
|
+
# we may not have an item count, in which case, just record 0 for the exclusive time
|
380
|
+
item_count ||= 0
|
381
|
+
NewRelic::Agent.agent.stats_engine.tl_record_unscoped_metrics(metrics, size_bytes, item_count)
|
360
382
|
end
|
361
383
|
|
362
384
|
# Raises an UnrecoverableServerException if the post_string is longer
|
@@ -10,21 +10,25 @@ module NewRelic
|
|
10
10
|
class NewRelicService
|
11
11
|
module Encoders
|
12
12
|
module Identity
|
13
|
-
def self.encode(data)
|
13
|
+
def self.encode(data, opts=nil)
|
14
14
|
data
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
module Compressed
|
19
|
-
def self.encode(data)
|
19
|
+
def self.encode(data, opts=nil)
|
20
20
|
Zlib::Deflate.deflate(data, Zlib::DEFAULT_COMPRESSION)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
module Base64CompressedJSON
|
25
|
-
def self.encode(data)
|
26
|
-
|
27
|
-
|
25
|
+
def self.encode(data, opts={})
|
26
|
+
normalize_encodings = if opts[:skip_normalization]
|
27
|
+
false
|
28
|
+
else
|
29
|
+
Agent.config[:normalize_json_string_encodings]
|
30
|
+
end
|
31
|
+
json = ::NewRelic::JSONWrapper.dump(data, :normalize => normalize_encodings)
|
28
32
|
Base64.encode64(Compressed.encode(json))
|
29
33
|
end
|
30
34
|
end
|
@@ -13,20 +13,21 @@ class NewRelic::Agent::RequestSampler
|
|
13
13
|
MonitorMixin
|
14
14
|
|
15
15
|
# The type field of the sample
|
16
|
-
SAMPLE_TYPE = 'Transaction'
|
16
|
+
SAMPLE_TYPE = 'Transaction'.freeze
|
17
17
|
|
18
18
|
# Strings for static keys of the sample structure
|
19
|
-
TYPE_KEY = 'type'
|
20
|
-
TIMESTAMP_KEY = 'timestamp'
|
21
|
-
NAME_KEY = 'name'
|
22
|
-
DURATION_KEY = 'duration'
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
19
|
+
TYPE_KEY = 'type'.freeze
|
20
|
+
TIMESTAMP_KEY = 'timestamp'.freeze
|
21
|
+
NAME_KEY = 'name'.freeze
|
22
|
+
DURATION_KEY = 'duration'.freeze
|
23
|
+
HTTP_RESPONSE_CODE_KEY = 'httpResponseCode'.freeze
|
24
|
+
GUID_KEY = 'nr.guid'.freeze
|
25
|
+
REFERRING_TRANSACTION_GUID_KEY = 'nr.referringTransactionGuid'.freeze
|
26
|
+
CAT_TRIP_ID_KEY = 'nr.tripId'.freeze
|
27
|
+
CAT_PATH_HASH_KEY = 'nr.pathHash'.freeze
|
28
|
+
CAT_REFERRING_PATH_HASH_KEY = 'nr.referringPathHash'.freeze
|
29
|
+
CAT_ALTERNATE_PATH_HASHES_KEY = 'nr.alternatePathHashes'.freeze
|
30
|
+
APDEX_PERF_ZONE_KEY = 'nr.apdexPerfZone'.freeze
|
30
31
|
|
31
32
|
def initialize( event_listener )
|
32
33
|
super()
|
@@ -185,10 +186,17 @@ class NewRelic::Agent::RequestSampler
|
|
185
186
|
optionally_append(CAT_PATH_HASH_KEY, :cat_path_hash, sample, payload)
|
186
187
|
optionally_append(CAT_REFERRING_PATH_HASH_KEY, :cat_referring_path_hash, sample, payload)
|
187
188
|
optionally_append(APDEX_PERF_ZONE_KEY, :apdex_perf_zone, sample, payload)
|
189
|
+
append_http_response_code(sample, payload)
|
188
190
|
append_cat_alternate_path_hashes(sample, payload)
|
189
191
|
sample
|
190
192
|
end
|
191
193
|
|
194
|
+
def append_http_response_code(sample, payload)
|
195
|
+
unless NewRelic::Agent.config[:disable_middleware_instrumentation]
|
196
|
+
optionally_append(HTTP_RESPONSE_CODE_KEY, :http_response_code, sample, payload)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
192
200
|
def append_cat_alternate_path_hashes(sample, payload)
|
193
201
|
if payload.include?(:cat_alternate_path_hashes)
|
194
202
|
sample[CAT_ALTERNATE_PATH_HASHES_KEY] = payload[:cat_alternate_path_hashes].sort.join(',')
|
@@ -2,103 +2,56 @@
|
|
2
2
|
# This file is distributed under New Relic's license terms.
|
3
3
|
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
4
|
|
5
|
+
require 'new_relic/agent/rules_engine/replacement_rule'
|
6
|
+
require 'new_relic/agent/rules_engine/segment_terms_rule'
|
7
|
+
|
5
8
|
module NewRelic
|
6
9
|
module Agent
|
7
10
|
class RulesEngine
|
11
|
+
SEGMENT_SEPARATOR = '/'.freeze
|
12
|
+
LEADING_SLASH_REGEX = %r{^/}.freeze
|
13
|
+
|
8
14
|
include Enumerable
|
9
15
|
extend Forwardable
|
10
16
|
|
11
17
|
def_delegators :@rules, :size, :inspect, :each, :clear
|
12
18
|
|
13
|
-
def self.
|
14
|
-
|
19
|
+
def self.create_metric_rules(connect_response)
|
20
|
+
specs = connect_response['metric_name_rules'] || []
|
21
|
+
rules = specs.map { |spec| ReplacementRule.new(spec) }
|
15
22
|
self.new(rules)
|
16
23
|
end
|
17
24
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def << rule
|
23
|
-
@rules << rule
|
24
|
-
@rules.sort!
|
25
|
-
@rules
|
26
|
-
end
|
27
|
-
|
28
|
-
def rename(original_string)
|
29
|
-
@rules.inject(original_string) do |string,rule|
|
30
|
-
if rule.each_segment
|
31
|
-
segments = string.split('/')
|
32
|
-
|
33
|
-
# If string looks like '/foo/bar', we want to
|
34
|
-
# ignore the empty string preceding the first slash.
|
35
|
-
add_preceding_slash = false
|
36
|
-
if segments[0] == ''
|
37
|
-
segments.shift
|
38
|
-
add_preceding_slash = true
|
39
|
-
end
|
25
|
+
def self.create_transaction_rules(connect_response)
|
26
|
+
txn_name_specs = connect_response['transaction_name_rules'] || []
|
27
|
+
segment_rule_specs = connect_response['transaction_segment_terms'] || []
|
40
28
|
|
41
|
-
|
42
|
-
|
43
|
-
result = '/'+result if add_preceding_slash
|
44
|
-
else
|
45
|
-
result, matched = rule.apply(string)
|
46
|
-
end
|
29
|
+
txn_name_rules = txn_name_specs.map { |s| ReplacementRule.new(s) }
|
30
|
+
segment_rules = segment_rule_specs.map { |s| SegmentTermsRule.new(s) }
|
47
31
|
|
48
|
-
|
49
|
-
result
|
50
|
-
end
|
32
|
+
self.new(txn_name_rules, segment_rules)
|
51
33
|
end
|
52
34
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def initialize(options)
|
58
|
-
if !options['match_expression']
|
59
|
-
raise ArgumentError.new('missing required match_expression')
|
60
|
-
end
|
61
|
-
if !options['replacement'] && !options['ignore']
|
62
|
-
raise ArgumentError.new('must specify replacement when ignore is false')
|
63
|
-
end
|
64
|
-
|
65
|
-
@match_expression = Regexp.new(options['match_expression'], Regexp::IGNORECASE)
|
66
|
-
@replacement = options['replacement']
|
67
|
-
@ignore = options['ignore'] || false
|
68
|
-
@eval_order = options['eval_order'] || 0
|
69
|
-
@replace_all = options['replace_all'] || false
|
70
|
-
@each_segment = options['each_segment'] || false
|
71
|
-
@terminate_chain = options['terminate_chain'] || false
|
72
|
-
end
|
73
|
-
|
74
|
-
def apply(string)
|
75
|
-
if @ignore
|
76
|
-
if string.match @match_expression
|
77
|
-
[nil, true]
|
78
|
-
else
|
79
|
-
[string, false]
|
80
|
-
end
|
81
|
-
else
|
82
|
-
method = @replace_all ? :gsub : :sub
|
83
|
-
result = string.send(method, @match_expression, @replacement)
|
84
|
-
match_found = ($~ != nil)
|
85
|
-
[result, match_found]
|
86
|
-
end
|
87
|
-
end
|
35
|
+
def initialize(rules=[], segment_term_rules=[])
|
36
|
+
@rules = rules.sort
|
37
|
+
@segment_term_rules = segment_term_rules
|
38
|
+
end
|
88
39
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
str_result
|
40
|
+
def apply_rules(rules, string)
|
41
|
+
rules.each do |rule|
|
42
|
+
if rule.matches?(string)
|
43
|
+
string = rule.apply(string)
|
44
|
+
break if rule.terminal?
|
95
45
|
end
|
96
|
-
[result, matched]
|
97
46
|
end
|
47
|
+
string
|
48
|
+
end
|
98
49
|
|
99
|
-
|
100
|
-
|
101
|
-
|
50
|
+
def rename(original_string)
|
51
|
+
renamed = apply_rules(@rules, original_string)
|
52
|
+
return nil unless renamed
|
53
|
+
renamed = apply_rules(@segment_term_rules, renamed)
|
54
|
+
renamed
|
102
55
|
end
|
103
56
|
end
|
104
57
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
class RulesEngine
|
8
|
+
class ReplacementRule
|
9
|
+
attr_reader(:terminate_chain, :each_segment, :ignore, :replace_all, :eval_order,
|
10
|
+
:match_expression, :replacement)
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
if !options['match_expression']
|
14
|
+
raise ArgumentError.new('missing required match_expression')
|
15
|
+
end
|
16
|
+
if !options['replacement'] && !options['ignore']
|
17
|
+
raise ArgumentError.new('must specify replacement when ignore is false')
|
18
|
+
end
|
19
|
+
|
20
|
+
@match_expression = Regexp.new(options['match_expression'], Regexp::IGNORECASE)
|
21
|
+
@replacement = options['replacement']
|
22
|
+
@ignore = options['ignore'] || false
|
23
|
+
@eval_order = options['eval_order'] || 0
|
24
|
+
@replace_all = options['replace_all'] || false
|
25
|
+
@each_segment = options['each_segment'] || false
|
26
|
+
@terminate_chain = options['terminate_chain'] || false
|
27
|
+
end
|
28
|
+
|
29
|
+
def terminal?
|
30
|
+
@terminate_chain || @ignore
|
31
|
+
end
|
32
|
+
|
33
|
+
def matches?(string)
|
34
|
+
if @each_segment
|
35
|
+
string.split(SEGMENT_SEPARATOR).any? do |segment|
|
36
|
+
segment.match(@match_expression)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
string.match @match_expression
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def apply(string)
|
44
|
+
if @ignore
|
45
|
+
nil
|
46
|
+
elsif @each_segment
|
47
|
+
apply_to_each_segment(string)
|
48
|
+
else
|
49
|
+
apply_replacement(string)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def apply_replacement(string)
|
54
|
+
method = @replace_all ? :gsub : :sub
|
55
|
+
string.send(method, @match_expression, @replacement)
|
56
|
+
end
|
57
|
+
|
58
|
+
def apply_to_each_segment(string)
|
59
|
+
string = string.dup
|
60
|
+
leading_slash = string.slice!(LEADING_SLASH_REGEX)
|
61
|
+
segments = string.split(SEGMENT_SEPARATOR)
|
62
|
+
|
63
|
+
segments.map! do |segment|
|
64
|
+
apply_replacement(segment)
|
65
|
+
end
|
66
|
+
|
67
|
+
"#{leading_slash}#{segments.join(SEGMENT_SEPARATOR)}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def <=>(other)
|
71
|
+
eval_order <=> other.eval_order
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|