newrelic_rpm 3.9.6.257 → 3.9.7.266

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +45 -0
  3. data/lib/new_relic/agent.rb +0 -1
  4. data/lib/new_relic/agent/agent.rb +61 -22
  5. data/lib/new_relic/agent/agent_logger.rb +11 -9
  6. data/lib/new_relic/agent/configuration/default_source.rb +25 -2
  7. data/lib/new_relic/agent/cross_app_monitor.rb +15 -53
  8. data/lib/new_relic/agent/cross_app_tracing.rb +11 -3
  9. data/lib/new_relic/agent/custom_event_aggregator.rb +98 -0
  10. data/lib/new_relic/agent/event_buffer.rb +84 -0
  11. data/lib/new_relic/agent/harvester.rb +4 -15
  12. data/lib/new_relic/agent/hostname.rb +1 -9
  13. data/lib/new_relic/agent/inbound_request_monitor.rb +41 -0
  14. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +2 -2
  15. data/lib/new_relic/agent/instrumentation/queue_time.rb +0 -5
  16. data/lib/new_relic/agent/instrumentation/resque.rb +4 -9
  17. data/lib/new_relic/agent/javascript_instrumentor.rb +0 -12
  18. data/lib/new_relic/agent/method_tracer_helpers.rb +3 -1
  19. data/lib/new_relic/agent/new_relic_service.rb +1 -1
  20. data/lib/new_relic/agent/sampled_buffer.rb +13 -41
  21. data/lib/new_relic/agent/samplers/delayed_job_sampler.rb +52 -24
  22. data/lib/new_relic/agent/sized_buffer.rb +23 -0
  23. data/lib/new_relic/agent/synthetics_monitor.rb +50 -0
  24. data/lib/new_relic/agent/system_info.rb +28 -0
  25. data/lib/new_relic/agent/transaction.rb +78 -9
  26. data/lib/new_relic/agent/transaction/synthetics_sample_buffer.rb +25 -0
  27. data/lib/new_relic/agent/{request_sampler.rb → transaction_event_aggregator.rb} +53 -19
  28. data/lib/new_relic/agent/transaction_sample_builder.rb +3 -1
  29. data/lib/new_relic/agent/transaction_sampler.rb +10 -0
  30. data/lib/new_relic/agent/transaction_state.rb +1 -9
  31. data/lib/new_relic/coerce.rb +8 -2
  32. data/lib/new_relic/control.rb +0 -1
  33. data/lib/new_relic/local_environment.rb +12 -8
  34. data/lib/new_relic/metric_spec.rb +11 -2
  35. data/lib/new_relic/rack/developer_mode.rb +9 -1
  36. data/lib/new_relic/transaction_sample.rb +4 -10
  37. data/lib/new_relic/version.rb +1 -1
  38. data/newrelic_rpm.gemspec +1 -0
  39. data/test/agent_helper.rb +15 -2
  40. data/test/environments/norails/Gemfile +1 -0
  41. data/test/environments/rails21/Gemfile +1 -0
  42. data/test/environments/rails22/Gemfile +1 -0
  43. data/test/environments/rails23/Gemfile +1 -0
  44. data/test/environments/rails30/Gemfile +1 -0
  45. data/test/environments/rails31/Gemfile +1 -0
  46. data/test/environments/rails32/Gemfile +2 -1
  47. data/test/environments/rails40/Gemfile +2 -1
  48. data/test/environments/rails41/Gemfile +3 -1
  49. data/test/environments/rails42/Gemfile +3 -1
  50. data/test/fixtures/cross_agent_tests/README.md +6 -2
  51. data/test/fixtures/cross_agent_tests/docker_container_id/README.md +6 -0
  52. data/test/fixtures/cross_agent_tests/docker_container_id/cases.json +22 -0
  53. data/test/fixtures/cross_agent_tests/docker_container_id/docker-0.9.1.txt +10 -0
  54. data/test/fixtures/cross_agent_tests/docker_container_id/docker-1.0.0.txt +10 -0
  55. data/test/fixtures/cross_agent_tests/docker_container_id/docker-1.3.txt +9 -0
  56. data/test/fixtures/cross_agent_tests/docker_container_id/empty.txt +0 -0
  57. data/test/fixtures/cross_agent_tests/docker_container_id/lxc-containers-without-docker.txt +10 -0
  58. data/test/fixtures/cross_agent_tests/proc_meminfo/README.md +7 -0
  59. data/test/fixtures/cross_agent_tests/proc_meminfo/meminfo_4096MB.txt +47 -0
  60. data/test/fixtures/cross_agent_tests/rum_client_config.json +0 -80
  61. data/test/fixtures/cross_agent_tests/synthetics/synthetics.json +317 -0
  62. data/test/multiverse/lib/multiverse/runner.rb +1 -1
  63. data/test/multiverse/lib/multiverse/suite.rb +1 -0
  64. data/test/multiverse/suites/active_record/Envfile +42 -8
  65. data/test/multiverse/suites/active_record/ar_method_aliasing.rb +8 -4
  66. data/test/multiverse/suites/agent_only/audit_log_test.rb +0 -2
  67. data/test/multiverse/suites/agent_only/collector_exception_handling_test.rb +1 -2
  68. data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +5 -5
  69. data/test/multiverse/suites/agent_only/encoding_handling_test.rb +2 -0
  70. data/test/multiverse/suites/agent_only/harvest_timestamps_test.rb +8 -0
  71. data/test/multiverse/suites/agent_only/synthetics_test.rb +139 -0
  72. data/test/multiverse/suites/delayed_job/Envfile +79 -0
  73. data/test/multiverse/suites/delayed_job/before_suite.rb +33 -0
  74. data/test/multiverse/suites/delayed_job/config/newrelic.yml +18 -0
  75. data/test/multiverse/suites/delayed_job/delayed_job_sampler_test.rb +131 -0
  76. data/test/multiverse/suites/delayed_job/unsupported_backend_test.rb +24 -0
  77. data/test/multiverse/suites/high_security/high_security_test.rb +3 -10
  78. data/test/multiverse/suites/rails/Envfile +4 -4
  79. data/test/multiverse/suites/rails/request_statistics_test.rb +4 -4
  80. data/test/new_relic/agent/agent_test.rb +32 -4
  81. data/test/new_relic/agent/configuration/default_source_test.rb +6 -0
  82. data/test/new_relic/agent/cross_app_monitor_test.rb +23 -29
  83. data/test/new_relic/agent/custom_event_aggregator_test.rb +88 -0
  84. data/test/new_relic/agent/event_buffer_test_cases.rb +152 -0
  85. data/test/new_relic/agent/harvester_test.rb +5 -25
  86. data/test/new_relic/agent/hostname_test.rb +1 -1
  87. data/test/new_relic/agent/inbound_request_monitor_test.rb +49 -0
  88. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +9 -9
  89. data/test/new_relic/agent/instrumentation/queue_time_test.rb +0 -19
  90. data/test/new_relic/agent/javascript_instrumentor_test.rb +0 -45
  91. data/test/new_relic/agent/new_relic_service_test.rb +17 -12
  92. data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -4
  93. data/test/new_relic/agent/sampled_buffer_test.rb +73 -145
  94. data/test/new_relic/agent/sized_buffer_test.rb +29 -0
  95. data/test/new_relic/agent/synthetics_monitor_test.rb +96 -0
  96. data/test/new_relic/agent/system_info_test.rb +19 -3
  97. data/test/new_relic/agent/transaction/developer_mode_sample_buffer_test.rb +14 -8
  98. data/test/new_relic/agent/transaction/synthetics_sample_buffer_test.rb +38 -0
  99. data/test/new_relic/agent/{request_sampler_test.rb → transaction_event_aggregator_test.rb} +55 -4
  100. data/test/new_relic/agent/transaction_sampler_test.rb +33 -11
  101. data/test/new_relic/agent/transaction_state_test.rb +0 -1
  102. data/test/new_relic/agent/transaction_test.rb +121 -19
  103. data/test/new_relic/coerce_test.rb +38 -3
  104. data/test/new_relic/control_test.rb +1 -0
  105. data/test/new_relic/fake_collector.rb +6 -2
  106. data/test/new_relic/fake_external_server.rb +2 -6
  107. data/test/new_relic/fake_server.rb +46 -29
  108. data/test/new_relic/http_client_test_cases.rb +71 -2
  109. data/test/new_relic/local_environment_test.rb +78 -0
  110. data/test/new_relic/metric_parser/metric_parser_test.rb +2 -0
  111. data/test/new_relic/metric_spec_test.rb +43 -0
  112. data/test/new_relic/multiverse_helpers.rb +11 -18
  113. data/test/new_relic/rack/browser_monitoring_test.rb +1 -18
  114. data/test/new_relic/rack/developer_mode_test.rb +1 -1
  115. data/test/new_relic/transaction_sample_test.rb +14 -4
  116. data/test/performance/suites/rack_middleware.rb +3 -1
  117. data/test/test_helper.rb +33 -0
  118. metadata +48 -7
  119. metadata.gz.sig +0 -0
  120. data/lib/new_relic/agent/browser_token.rb +0 -41
  121. data/test/multiverse/suites/agent_only/before_suite.rb +0 -7
  122. data/test/new_relic/agent/browser_token_test.rb +0 -56
@@ -18,9 +18,16 @@ module NewRelic
18
18
  class DelayedJobSampler < NewRelic::Agent::Sampler
19
19
  named :delayed_job
20
20
 
21
+ # DelayedJob supports multiple backends, only some of which we can
22
+ # handle. Check whether we think we've got what we need here.
23
+ def self.supported_backend?
24
+ ::Delayed::Worker.backend.to_s == "Delayed::Backend::ActiveRecord::Job"
25
+ end
26
+
21
27
  def initialize
22
- raise Unsupported, "DJ instrumentation disabled" if Agent.config[:disable_dj]
23
- raise Unsupported, "No DJ worker present" unless NewRelic::DelayedJobInjection.worker_name
28
+ raise Unsupported, "DJ queue sampler disabled" if Agent.config[:disable_dj]
29
+ raise Unsupported, "DJ queue sampling unsupported with backend '#{::Delayed::Worker.backend}'" unless self.class.supported_backend?
30
+ raise Unsupported, "No DJ worker present. Skipping DJ queue sampler" unless NewRelic::DelayedJobInjection.worker_name
24
31
  end
25
32
 
26
33
  def record_failed_jobs(value)
@@ -31,51 +38,72 @@ module NewRelic
31
38
  NewRelic::Agent.record_metric("Workers/DelayedJob/locked_jobs", value)
32
39
  end
33
40
 
41
+ FAILED_QUERY = 'failed_at is not NULL'.freeze
42
+ LOCKED_QUERY = 'locked_by is not NULL'.freeze
43
+
34
44
  def failed_jobs
35
- Delayed::Job.count(:conditions => 'failed_at is not NULL')
45
+ count(FAILED_QUERY)
36
46
  end
37
47
 
38
48
  def locked_jobs
39
- Delayed::Job.count(:conditions => 'locked_by is not NULL')
49
+ count(LOCKED_QUERY)
50
+ end
51
+
52
+ def count(query)
53
+ if ::ActiveRecord::VERSION::MAJOR.to_i < 4
54
+ ::Delayed::Job.count(query)
55
+ else
56
+ ::Delayed::Job.where(query).count
57
+ end
40
58
  end
41
59
 
42
60
  def self.supported_on_this_platform?
43
- defined?(Delayed::Job)
61
+ defined?(::Delayed::Job)
44
62
  end
45
63
 
46
64
  def poll
47
65
  record_failed_jobs(failed_jobs)
48
66
  record_locked_jobs(locked_jobs)
49
-
50
- if @queue
51
- record_queue_length_across_dimension('queue')
52
- else
53
- record_queue_length_across_dimension('priority')
54
- end
67
+ record_queue_length_metrics
55
68
  end
56
69
 
57
70
  private
58
71
 
59
- def record_queue_length_across_dimension(column)
72
+ def record_queue_length_metrics
73
+ counts = []
74
+ counts << record_counts_by("queue", "name") if ::Delayed::Job.instance_methods.include?(:queue)
75
+ counts << record_counts_by("priority")
76
+
77
+ all_metric = "Workers/DelayedJob/queue_length/all"
78
+ NewRelic::Agent.record_metric(all_metric, counts.max)
79
+ end
80
+
81
+ QUEUE_QUERY_CONDITION = 'run_at <= ? and failed_at is NULL'.freeze
82
+
83
+ def record_counts_by(column_name, metric_segment = column_name)
60
84
  all_count = 0
61
- Delayed::Job.count(:group => column, :conditions => ['run_at < ? and failed_at is NULL', Time.now]).each do | column_val, count |
85
+ queue_counts(column_name).each do |column_val, count|
62
86
  all_count += count
63
- metric = "Workers/DelayedJob/queue_length/#{column == 'queue' ? 'name' : column}/#{column_val}"
87
+ column_val = "default" if column_val.nil? || column_val == ""
88
+ metric = "Workers/DelayedJob/queue_length/#{metric_segment}/#{column_val}"
64
89
  NewRelic::Agent.record_metric(metric, count)
65
90
  end
66
- all_metric = "Workers/DelayedJob/queue_length/all"
67
- NewRelic::Agent.record_metric(all_metric, all_count)
91
+ all_count
68
92
  end
69
93
 
70
- # Figure out if we get the queues.
71
- def setup
72
- return unless @queue.nil?
73
- @setup = true
74
- columns = Delayed::Job.columns
75
- columns.each do | c |
76
- @queue = true if c.name.to_s == 'priority'
94
+ def queue_counts(column_name)
95
+ now = ::Delayed::Job.db_time_now
96
+ # There is not an ActiveRecord syntax for what we're trying to do
97
+ # here that's valid on 2.x through 4.1, so split it up.
98
+ result = if ::ActiveRecord::VERSION::MAJOR.to_i < 4
99
+ ::Delayed::Job.count(:group => column_name,
100
+ :conditions => [QUEUE_QUERY_CONDITION, now])
101
+ else
102
+ ::Delayed::Job.where(QUEUE_QUERY_CONDITION, now).
103
+ group(column_name).
104
+ count
77
105
  end
78
- @queue ||= false
106
+ result.to_a
79
107
  end
80
108
  end
81
109
  end
@@ -0,0 +1,23 @@
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
+ require 'new_relic/agent/event_buffer'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ class SizedBuffer < EventBuffer
10
+
11
+ def append_event(x)
12
+ if @items.size < @capacity
13
+ @items << x
14
+ return x
15
+ else
16
+ return nil
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,50 @@
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
+ require 'new_relic/agent/inbound_request_monitor'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ class SyntheticsMonitor < InboundRequestMonitor
10
+ SYNTHETICS_HEADER_KEY = 'HTTP_X_NEWRELIC_SYNTHETICS'.freeze
11
+
12
+ SUPPORTED_VERSION = 1
13
+ EXPECTED_PAYLOAD_LENGTH = 5
14
+
15
+ def on_finished_configuring(events)
16
+ events.subscribe(:before_call, &method(:on_before_call))
17
+ end
18
+
19
+ def on_before_call(request) #THREAD_LOCAL_ACCESS
20
+ encoded_header = request[SYNTHETICS_HEADER_KEY]
21
+ return unless encoded_header
22
+
23
+ incoming_payload = deserialize_header(encoded_header, SYNTHETICS_HEADER_KEY)
24
+
25
+ return unless incoming_payload &&
26
+ is_valid_payload?(incoming_payload) &&
27
+ is_supported_version?(incoming_payload) &&
28
+ is_trusted?(incoming_payload)
29
+
30
+ state = NewRelic::Agent::TransactionState.tl_get
31
+ txn = state.current_transaction
32
+ txn.raw_synthetics_header = encoded_header
33
+ txn.synthetics_payload = incoming_payload
34
+ end
35
+
36
+ def is_supported_version?(incoming_payload)
37
+ incoming_payload.first == SUPPORTED_VERSION
38
+ end
39
+
40
+ def is_trusted?(incoming_payload)
41
+ account_id = incoming_payload[1]
42
+ NewRelic::Agent.config[:trusted_account_ids].include?(account_id)
43
+ end
44
+
45
+ def is_valid_payload?(incoming_payload)
46
+ incoming_payload.length == EXPECTED_PAYLOAD_LENGTH
47
+ end
48
+ end
49
+ end
50
+ end
@@ -124,6 +124,34 @@ module NewRelic
124
124
  proc_try_read('/proc/version')
125
125
  end
126
126
 
127
+ def self.docker_container_id
128
+ return unless ruby_os_identifier =~ /linux/
129
+
130
+ cgroup_info = proc_try_read('/proc/self/cgroup')
131
+ return unless cgroup_info
132
+
133
+ parse_docker_container_id(cgroup_info)
134
+ end
135
+
136
+ def self.parse_docker_container_id(cgroup_info)
137
+ cpu_cgroup = parse_cgroup_ids(cgroup_info)['cpu']
138
+ return unless cpu_cgroup && cpu_cgroup =~ %r{^/docker/(.*)}
139
+ return $1
140
+ end
141
+
142
+ def self.parse_cgroup_ids(cgroup_info)
143
+ cgroup_ids = {}
144
+
145
+ cgroup_info.split("\n").each do |line|
146
+ parts = line.split(':')
147
+ next unless parts.size == 3
148
+ _, type, cgroup_id = parts
149
+ cgroup_ids[type] = cgroup_id
150
+ end
151
+
152
+ cgroup_ids
153
+ end
154
+
127
155
  # A File.read against /(proc|sysfs)/* can hang with some older Linuxes.
128
156
  # See https://bugzilla.redhat.com/show_bug.cgi?id=604887, RUBY-736, and
129
157
  # https://github.com/opscode/ohai/commit/518d56a6cb7d021b47ed3d691ecf7fba7f74a6a7
@@ -63,6 +63,9 @@ module NewRelic
63
63
  # Populated with the trace sample once this transaction is completed.
64
64
  attr_reader :transaction_trace
65
65
 
66
+ # Fields for tracking synthetics requests
67
+ attr_accessor :raw_synthetics_header, :synthetics_payload
68
+
66
69
  # Return the currently active transaction, or nil.
67
70
  def self.tl_current
68
71
  TransactionState.tl_get.current_transaction
@@ -357,9 +360,24 @@ module NewRelic
357
360
  transaction_sampler.ignore_transaction(state)
358
361
  end
359
362
 
363
+ WEB_SUMMARY_METRIC = 'HttpDispatcher'.freeze
364
+ OTHER_SUMMARY_METRIC = 'OtherTransaction/all'.freeze
365
+
360
366
  def summary_metrics
361
- metric_parser = NewRelic::MetricParser::MetricParser.for_metric_named(@frozen_name)
362
- metric_parser.summary_metrics
367
+ if @frozen_name.start_with?(CONTROLLER_PREFIX)
368
+ [WEB_SUMMARY_METRIC]
369
+ else
370
+ background_summary_metrics
371
+ end
372
+ end
373
+
374
+ def background_summary_metrics
375
+ segments = @frozen_name.split('/')
376
+ if segments.size > 2
377
+ ["OtherTransaction/#{segments[1]}/all", OTHER_SUMMARY_METRIC]
378
+ else
379
+ []
380
+ end
363
381
  end
364
382
 
365
383
  def needs_middleware_summary_metrics?(name)
@@ -402,6 +420,7 @@ module NewRelic
402
420
  end
403
421
 
404
422
  def user_defined_rules_ignore?
423
+ return unless uri
405
424
  return if (rules = NewRelic::Agent.config[:"rules.ignore_url_regexes"]).empty?
406
425
 
407
426
  parsed = NewRelic::Agent::HTTPClients::URIUtil.parse_url(uri)
@@ -422,7 +441,7 @@ module NewRelic
422
441
 
423
442
  record_summary_metrics(outermost_segment_name, end_time)
424
443
  record_apdex(state, end_time) unless ignore_apdex?
425
- NewRelic::Agent::Instrumentation::QueueTime.record_frontend_metrics(apdex_start, start_time) if queue_time > 0.0
444
+ record_queue_time
426
445
 
427
446
  record_exceptions
428
447
  merge_metrics
@@ -451,6 +470,7 @@ module NewRelic
451
470
  }
452
471
  append_cat_info(state, duration, payload)
453
472
  append_apdex_perf_zone(duration, payload)
473
+ append_synthetics_to(state, payload)
454
474
  append_referring_transaction_guid_to(state, payload)
455
475
  append_http_response_code(payload)
456
476
 
@@ -462,8 +482,7 @@ module NewRelic
462
482
  end
463
483
 
464
484
  def include_guid?(state, duration)
465
- state.is_cross_app? ||
466
- (state.request_token && duration > apdex_t)
485
+ state.is_cross_app? || is_synthetics_request?
467
486
  end
468
487
 
469
488
  def cat_trip_id(state)
@@ -489,6 +508,35 @@ module NewRelic
489
508
  NewRelic::Agent.instance.cross_app_monitor.client_referring_transaction_path_hash(state)
490
509
  end
491
510
 
511
+ def is_synthetics_request?
512
+ synthetics_payload != nil && raw_synthetics_header != nil
513
+ end
514
+
515
+ def synthetics_version
516
+ info = synthetics_payload or return nil
517
+ info[0]
518
+ end
519
+
520
+ def synthetics_account_id
521
+ info = synthetics_payload or return nil
522
+ info[1]
523
+ end
524
+
525
+ def synthetics_resource_id
526
+ info = synthetics_payload or return nil
527
+ info[2]
528
+ end
529
+
530
+ def synthetics_job_id
531
+ info = synthetics_payload or return nil
532
+ info[3]
533
+ end
534
+
535
+ def synthetics_monitor_id
536
+ info = synthetics_payload or return nil
537
+ info[4]
538
+ end
539
+
492
540
  APDEX_S = 'S'.freeze
493
541
  APDEX_T = 'T'.freeze
494
542
  APDEX_F = 'F'.freeze
@@ -528,6 +576,14 @@ module NewRelic
528
576
  end
529
577
  end
530
578
 
579
+ def append_synthetics_to(state, payload)
580
+ return unless is_synthetics_request?
581
+
582
+ payload[:synthetics_resource_id] = synthetics_resource_id
583
+ payload[:synthetics_job_id] = synthetics_job_id
584
+ payload[:synthetics_monitor_id] = synthetics_monitor_id
585
+ end
586
+
531
587
  def append_referring_transaction_guid_to(state, payload)
532
588
  referring_guid = NewRelic::Agent.instance.cross_app_monitor.client_referring_transaction_guid(state)
533
589
  if referring_guid
@@ -595,6 +651,23 @@ module NewRelic
595
651
  end
596
652
  end
597
653
 
654
+ QUEUE_TIME_METRIC = 'WebFrontend/QueueTime'.freeze
655
+
656
+ def queue_time
657
+ @apdex_start ? @start_time - @apdex_start : 0
658
+ end
659
+
660
+ def record_queue_time
661
+ value = queue_time
662
+ if value > 0.0
663
+ if value < MethodTracerHelpers::MAX_ALLOWED_METRIC_DURATION
664
+ @metrics.record_unscoped(QUEUE_TIME_METRIC, value)
665
+ else
666
+ ::NewRelic::Agent.logger.log_once(:warn, :too_high_queue_time, "Not recording unreasonably large queue time of #{value} s")
667
+ end
668
+ end
669
+ end
670
+
598
671
  APDEX_METRIC = 'Apdex'.freeze
599
672
 
600
673
  def had_error?
@@ -669,10 +742,6 @@ module NewRelic
669
742
  alias_method :user_attributes, :custom_parameters
670
743
  alias_method :set_user_attributes, :add_custom_parameters
671
744
 
672
- def queue_time
673
- @apdex_start ? @start_time - @apdex_start : 0
674
- end
675
-
676
745
  # Returns truthy if the current in-progress transaction is considered a
677
746
  # a web transaction (as opposed to, e.g., a background transaction).
678
747
  #
@@ -0,0 +1,25 @@
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
+ require 'new_relic/agent/transaction/transaction_sample_buffer'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ class Transaction
10
+ class SyntheticsSampleBuffer < TransactionSampleBuffer
11
+ def capacity
12
+ NewRelic::Agent.config[:'synthetics.traces_limit']
13
+ end
14
+
15
+ def allow_sample?(sample)
16
+ sample.synthetics_resource_id != nil
17
+ end
18
+
19
+ def truncate_samples
20
+ @samples.slice!(max_capacity..-1)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -8,7 +8,7 @@ require 'monitor'
8
8
  require 'newrelic_rpm' unless defined?( NewRelic )
9
9
  require 'new_relic/agent' unless defined?( NewRelic::Agent )
10
10
 
11
- class NewRelic::Agent::RequestSampler
11
+ class NewRelic::Agent::TransactionEventAggregator
12
12
  include NewRelic::Coerce,
13
13
  MonitorMixin
14
14
 
@@ -28,14 +28,19 @@ class NewRelic::Agent::RequestSampler
28
28
  CAT_REFERRING_PATH_HASH_KEY = 'nr.referringPathHash'.freeze
29
29
  CAT_ALTERNATE_PATH_HASHES_KEY = 'nr.alternatePathHashes'.freeze
30
30
  APDEX_PERF_ZONE_KEY = 'nr.apdexPerfZone'.freeze
31
+ SYNTHETICS_RESOURCE_ID_KEY = "nr.syntheticsResourceId".freeze
32
+ SYNTHETICS_JOB_ID_KEY = "nr.syntheticsJobId".freeze
33
+ SYNTHETICS_MONITOR_ID_KEY = "nr.syntheticsMonitorId".freeze
31
34
 
32
35
  def initialize( event_listener )
33
36
  super()
34
37
 
35
38
  @enabled = false
36
- @samples = ::NewRelic::Agent::SampledBuffer.new(NewRelic::Agent.config[:'analytics_events.max_samples_stored'])
37
39
  @notified_full = false
38
40
 
41
+ @samples = ::NewRelic::Agent::SampledBuffer.new(NewRelic::Agent.config[:'analytics_events.max_samples_stored'])
42
+ @synthetics_samples = ::NewRelic::Agent::SizedBuffer.new(NewRelic::Agent.config[:'synthetics.events_limit'])
43
+
39
44
  event_listener.subscribe( :transaction_finished, &method(:on_transaction_finished) )
40
45
  self.register_config_callbacks
41
46
  end
@@ -47,31 +52,35 @@ class NewRelic::Agent::RequestSampler
47
52
 
48
53
  # Fetch a copy of the sampler's gathered samples. (Synchronized)
49
54
  def samples
50
- return self.synchronize { @samples.to_a }
55
+ return self.synchronize { @samples.to_a.concat(@synthetics_samples.to_a) }
51
56
  end
52
57
 
53
58
  def reset!
54
- NewRelic::Agent.logger.debug "Resetting RequestSampler"
55
-
56
- sample_count, request_count = 0
59
+ sample_count, request_count, synthetics_dropped = 0
57
60
  old_samples = nil
58
61
 
59
62
  self.synchronize do
60
63
  sample_count = @samples.size
61
- request_count = @samples.seen
62
- old_samples = @samples.to_a
63
- @samples.reset
64
+ request_count = @samples.num_seen
65
+
66
+ synthetics_dropped = @synthetics_samples.num_dropped
67
+
68
+ old_samples = @samples.to_a + @synthetics_samples.to_a
69
+ @samples.reset!
70
+ @synthetics_samples.reset!
71
+
64
72
  @notified_full = false
65
73
  end
66
74
 
67
- [old_samples, sample_count, request_count]
75
+ [old_samples, sample_count, request_count, synthetics_dropped]
68
76
  end
69
77
 
70
78
  # Clear any existing samples, reset the last sample time, and return the
71
79
  # previous set of samples. (Synchronized)
72
80
  def harvest!
73
- old_samples, sample_count, request_count = reset!
81
+ old_samples, sample_count, request_count, synthetics_dropped = reset!
74
82
  record_sampling_rate(request_count, sample_count) if @enabled
83
+ record_dropped_synthetics(synthetics_dropped)
75
84
  old_samples
76
85
  end
77
86
 
@@ -79,7 +88,7 @@ class NewRelic::Agent::RequestSampler
79
88
  # transmission to the collector. (Synchronized)
80
89
  def merge!(old_samples)
81
90
  self.synchronize do
82
- old_samples.each { |s| @samples.append(s) }
91
+ old_samples.each { |s| append_event(s) }
83
92
  end
84
93
  end
85
94
 
@@ -96,15 +105,28 @@ class NewRelic::Agent::RequestSampler
96
105
  ])
97
106
 
98
107
  engine = NewRelic::Agent.instance.stats_engine
99
- engine.tl_record_supportability_metric_count("RequestSampler/requests", request_count)
100
- engine.tl_record_supportability_metric_count("RequestSampler/samples", sample_count)
108
+ engine.tl_record_supportability_metric_count("TransactionEventAggregator/requests", request_count)
109
+ engine.tl_record_supportability_metric_count("TransactionEventAggregator/samples", sample_count)
110
+ end
111
+
112
+ def record_dropped_synthetics(synthetics_dropped)
113
+ return unless synthetics_dropped > 0
114
+
115
+ NewRelic::Agent.logger.debug("Synthetics transaction event limit (#{@samples.capacity}) reached. Further synthetics events this harvest period dropped.")
116
+
117
+ engine = NewRelic::Agent.instance.stats_engine
118
+ engine.tl_record_supportability_metric_count("TransactionEventAggregator/synthetics_events_dropped", synthetics_dropped)
101
119
  end
102
120
 
103
121
  def register_config_callbacks
104
122
  NewRelic::Agent.config.register_callback(:'analytics_events.max_samples_stored') do |max_samples|
105
- NewRelic::Agent.logger.debug "RequestSampler max_samples set to #{max_samples}"
123
+ NewRelic::Agent.logger.debug "TransactionEventAggregator max_samples set to #{max_samples}"
106
124
  self.synchronize { @samples.capacity = max_samples }
107
- self.reset!
125
+ end
126
+
127
+ NewRelic::Agent.config.register_callback(:'synthetics.events_limit') do |max_samples|
128
+ NewRelic::Agent.logger.debug "TransactionEventAggregator limit for synthetics events set to #{max_samples}"
129
+ self.synchronize { @synthetics_samples.capacity = max_samples }
108
130
  end
109
131
 
110
132
  NewRelic::Agent.config.register_callback(:'analytics_events.enabled') do |enabled|
@@ -113,7 +135,7 @@ class NewRelic::Agent::RequestSampler
113
135
  end
114
136
 
115
137
  def notify_full
116
- NewRelic::Agent.logger.debug "Request Sampler capacity of #{@samples.capacity} reached, beginning sampling"
138
+ NewRelic::Agent.logger.debug "Transaction event capacity of #{@samples.capacity} reached, beginning sampling"
117
139
  @notified_full = true
118
140
  end
119
141
 
@@ -124,8 +146,17 @@ class NewRelic::Agent::RequestSampler
124
146
  main_event = create_main_event(payload)
125
147
  custom_params = create_custom_parameters(payload)
126
148
 
127
- is_full = self.synchronize { @samples.append([main_event, custom_params]) }
128
- notify_full if is_full && !@notified_full
149
+ self.synchronize { append_event([main_event, custom_params]) }
150
+ notify_full if !@notified_full && @samples.full?
151
+ end
152
+
153
+ def append_event(event)
154
+ main_event, _ = event
155
+ if main_event.include?(SYNTHETICS_RESOURCE_ID_KEY)
156
+ @synthetics_samples.append(event)
157
+ else
158
+ @samples.append(event)
159
+ end
129
160
  end
130
161
 
131
162
  def self.map_metric(metric_name, to_add={})
@@ -186,6 +217,9 @@ class NewRelic::Agent::RequestSampler
186
217
  optionally_append(CAT_PATH_HASH_KEY, :cat_path_hash, sample, payload)
187
218
  optionally_append(CAT_REFERRING_PATH_HASH_KEY, :cat_referring_path_hash, sample, payload)
188
219
  optionally_append(APDEX_PERF_ZONE_KEY, :apdex_perf_zone, sample, payload)
220
+ optionally_append(SYNTHETICS_RESOURCE_ID_KEY, :synthetics_resource_id, sample, payload)
221
+ optionally_append(SYNTHETICS_JOB_ID_KEY, :synthetics_job_id, sample, payload)
222
+ optionally_append(SYNTHETICS_MONITOR_ID_KEY, :synthetics_monitor_id, sample, payload)
189
223
  append_http_response_code(sample, payload)
190
224
  append_cat_alternate_path_hashes(sample, payload)
191
225
  sample