Permalink
Browse files

Merge pull request #1615 from ruby-agent/dev

Ruby Agent 4.6.0 RC 1
  • Loading branch information...
2 parents 2bc79e1 + 7f46ab4 commit 35cb1eea0d9301d30bc5cd7aad63f8a332cdad1a Dana Scheider committed with GitHub Enterprise Oct 28, 2017
Showing with 2,938 additions and 1,042 deletions.
  1. +2 −0 .yardopts
  2. +8 −0 CHANGELOG.md
  3. +1 −0 lib/new_relic/agent.rb
  4. +19 −12 lib/new_relic/agent/agent.rb
  5. +7 −0 lib/new_relic/agent/configuration/default_source.rb
  6. +29 −0 lib/new_relic/agent/distributed_trace_monitor.rb
  7. +223 −0 lib/new_relic/agent/distributed_trace_payload.rb
  8. +72 −0 lib/new_relic/agent/distributed_trace_priority_sampled_buffer.rb
  9. +137 −0 lib/new_relic/agent/external.rb
  10. +31 −0 lib/new_relic/agent/http_clients/abstract_request.rb
  11. +3 −1 lib/new_relic/agent/http_clients/curb_wrappers.rb
  12. +3 −1 lib/new_relic/agent/http_clients/excon_wrappers.rb
  13. +3 −1 lib/new_relic/agent/http_clients/http_rb_wrappers.rb
  14. +3 −1 lib/new_relic/agent/http_clients/httpclient_wrappers.rb
  15. +3 −1 lib/new_relic/agent/http_clients/net_http_wrappers.rb
  16. +3 −1 lib/new_relic/agent/http_clients/typhoeus_wrappers.rb
  17. +2 −2 lib/new_relic/agent/sql_sampler.rb
  18. +59 −0 lib/new_relic/agent/throughput_monitor.rb
  19. +59 −15 lib/new_relic/agent/transaction.rb
  20. +17 −5 lib/new_relic/agent/transaction/abstract_segment.rb
  21. +121 −0 lib/new_relic/agent/transaction/distributed_tracing.rb
  22. +183 −19 lib/new_relic/agent/transaction/external_request_segment.rb
  23. +2 −3 lib/new_relic/agent/transaction/segment.rb
  24. +14 −5 lib/new_relic/agent/transaction/trace.rb
  25. +58 −0 lib/new_relic/agent/transaction/trace_builder.rb
  26. +34 −32 lib/new_relic/agent/transaction/trace_node.rb
  27. +4 −9 lib/new_relic/agent/transaction/tracing.rb
  28. +18 −0 lib/new_relic/agent/transaction_error_primitive.rb
  29. +14 −0 lib/new_relic/agent/transaction_event_aggregator.rb
  30. +14 −0 lib/new_relic/agent/transaction_event_primitive.rb
  31. +9 −1 lib/new_relic/agent/transaction_event_recorder.rb
  32. +0 −138 lib/new_relic/agent/transaction_sample_builder.rb
  33. +12 −66 lib/new_relic/agent/transaction_sampler.rb
  34. +1 −3 lib/new_relic/agent/transaction_state.rb
  35. +5 −0 lib/new_relic/supportability_helper.rb
  36. +1 −1 lib/new_relic/version.rb
  37. +1 −1 newrelic.yml
  38. +1 −1 newrelic_rpm.gemspec
  39. +11 −3 test/agent_helper.rb
  40. +3 −3 test/multiverse/suites/active_record/active_record_test.rb
  41. +1 −1 test/multiverse/suites/agent_only/marshaling_test.rb
  42. +1 −1 test/multiverse/suites/agent_only/set_transaction_name_test.rb
  43. +39 −0 test/multiverse/suites/agent_only/throughput_monitor_test.rb
  44. +6 −5 test/multiverse/suites/curb/curb_test.rb
  45. +5 −5 test/multiverse/suites/excon/excon_test.rb
  46. +3 −3 test/multiverse/suites/httpclient/httpclient_test.rb
  47. +10 −12 test/multiverse/suites/mongo/helpers/mongo_operation_tests.rb
  48. +9 −11 test/multiverse/suites/mongo/mongo2_instrumentation_test.rb
  49. +1 −1 test/multiverse/suites/rails/action_cable_test.rb
  50. +1 −1 test/multiverse/suites/rails/gc_instrumentation_test.rb
  51. +2 −2 test/multiverse/suites/rails/ignore_test.rb
  52. +10 −10 test/multiverse/suites/rails/view_instrumentation_test.rb
  53. +11 −11 test/multiverse/suites/redis/redis_instrumentation_test.rb
  54. +1 −1 test/multiverse/suites/sequel/sequel_helpers.rb
  55. +6 −6 test/multiverse/suites/typhoeus/typhoeus_test.rb
  56. +1 −1 test/new_relic/agent/datastores_test.rb
  57. +49 −0 test/new_relic/agent/distributed_trace_monitor_test.rb
  58. +172 −0 test/new_relic/agent/distributed_trace_payload_test.rb
  59. +66 −0 test/new_relic/agent/distributed_trace_priority_sampled_buffer_test.rb
  60. +195 −0 test/new_relic/agent/external_test.rb
  61. +4 −4 test/new_relic/agent/instrumentation/action_cable_subscriber_test.rb
  62. +7 −7 test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb
  63. +4 −7 test/new_relic/agent/instrumentation/action_view_subscriber_test.rb
  64. +2 −3 test/new_relic/agent/instrumentation/active_record_subscriber_test.rb
  65. +8 −5 test/new_relic/agent/messaging_test.rb
  66. +15 −44 test/new_relic/agent/method_tracer_test.rb
  67. +0 −30 test/new_relic/agent/mock_scope_listener.rb
  68. +3 −6 test/new_relic/agent/sql_sampler_test.rb
  69. +46 −0 test/new_relic/agent/supportability_test.rb
  70. +25 −0 test/new_relic/agent/throughput_monitor_test.rb
  71. +18 −4 test/new_relic/agent/transaction/datastore_segment_test.rb
  72. +363 −0 test/new_relic/agent/transaction/distributed_tracing_test.rb
  73. +276 −2 test/new_relic/agent/transaction/external_request_segment_test.rb
  74. +95 −0 test/new_relic/agent/transaction/trace_builder_test.rb
  75. +59 −77 test/new_relic/agent/transaction/trace_node_test.rb
  76. +11 −11 test/new_relic/agent/transaction/trace_test.rb
  77. +108 −1 test/new_relic/agent/transaction/tracing_test.rb
  78. +31 −1 test/new_relic/agent/transaction_event_aggregator_test.rb
  79. +26 −0 test/new_relic/agent/transaction_event_recorder_test.rb
  80. +0 −215 test/new_relic/agent/transaction_sample_builder_test.rb
  81. +10 −204 test/new_relic/agent/transaction_sampler_test.rb
  82. +0 −1 test/new_relic/agent/transaction_state_test.rb
  83. +24 −1 test/new_relic/agent/transaction_test.rb
  84. +24 −23 test/new_relic/http_client_test_cases.rb
View
@@ -2,6 +2,7 @@
--api public
lib/new_relic/agent.rb
lib/new_relic/agent/method_tracer.rb
+lib/new_relic/agent/external.rb
lib/new_relic/agent/instrumentation/controller_instrumentation.rb
lib/new_relic/agent/instrumentation/rack.rb
lib/new_relic/agent/instrumentation/metric_frame.rb
@@ -15,6 +16,7 @@ lib/new_relic/rack/agent_hooks.rb
lib/new_relic/rack/browser_monitoring.rb
lib/new_relic/rack/error_collector.rb
lib/new_relic/rack.rb
+lib/new_relic/agent/transaction/external_request_segment.rb
-
LICENSE
CHANGELOG
View
@@ -1,5 +1,13 @@
# New Relic Ruby Agent Release Notes #
+## v4.6.0 ##
+
+ * Public API for External Requests
+
+ The agent now has public API for instrumenting external requests and linking
+ up transactions via cross application tracing. See the [API Guide](https://docs.newrelic.com/docs/agents/ruby-agent/customization/ruby-agent-api-guide#externals)
+ for more details this new functionality.
+
## v4.5.0 ##
* Send synthetics headers even when CAT disabled
View
@@ -54,6 +54,7 @@ module Agent
require 'new_relic/agent/rules_engine'
require 'new_relic/agent/http_clients/uri_util'
require 'new_relic/agent/system_info'
+ require 'new_relic/agent/external'
require 'new_relic/agent/instrumentation/controller_instrumentation'
@@ -19,6 +19,7 @@
require 'new_relic/agent/commands/agent_command_router'
require 'new_relic/agent/event_listener'
require 'new_relic/agent/cross_app_monitor'
+require 'new_relic/agent/distributed_trace_monitor'
require 'new_relic/agent/synthetics_monitor'
require 'new_relic/agent/synthetics_event_buffer'
require 'new_relic/agent/transaction_event_recorder'
@@ -29,6 +30,7 @@
require 'new_relic/agent/utilization_data'
require 'new_relic/environment_report'
require 'new_relic/agent/attribute_filter'
+require 'new_relic/agent/throughput_monitor'
module NewRelic
module Agent
@@ -48,18 +50,20 @@ def initialize
@service = NewRelicService.new
- @events = NewRelic::Agent::EventListener.new
- @stats_engine = NewRelic::Agent::StatsEngine.new
- @transaction_sampler = NewRelic::Agent::TransactionSampler.new
- @sql_sampler = NewRelic::Agent::SqlSampler.new
- @agent_command_router = NewRelic::Agent::Commands::AgentCommandRouter.new(@events)
- @cross_app_monitor = NewRelic::Agent::CrossAppMonitor.new(@events)
- @synthetics_monitor = NewRelic::Agent::SyntheticsMonitor.new(@events)
- @error_collector = NewRelic::Agent::ErrorCollector.new
- @transaction_rules = NewRelic::Agent::RulesEngine.new
- @harvest_samplers = NewRelic::Agent::SamplerCollection.new(@events)
- @monotonic_gc_profiler = NewRelic::Agent::VM::MonotonicGCProfiler.new
- @javascript_instrumentor = NewRelic::Agent::JavascriptInstrumentor.new(@events)
+ @events = NewRelic::Agent::EventListener.new
+ @stats_engine = NewRelic::Agent::StatsEngine.new
+ @transaction_sampler = NewRelic::Agent::TransactionSampler.new
+ @sql_sampler = NewRelic::Agent::SqlSampler.new
+ @agent_command_router = NewRelic::Agent::Commands::AgentCommandRouter.new(@events)
+ @cross_app_monitor = NewRelic::Agent::CrossAppMonitor.new(@events)
+ @distributed_trace_monitor = NewRelic::Agent::DistributedTraceMonitor.new(@events)
+ @synthetics_monitor = NewRelic::Agent::SyntheticsMonitor.new(@events)
+ @error_collector = NewRelic::Agent::ErrorCollector.new
+ @transaction_rules = NewRelic::Agent::RulesEngine.new
+ @harvest_samplers = NewRelic::Agent::SamplerCollection.new(@events)
+ @monotonic_gc_profiler = NewRelic::Agent::VM::MonotonicGCProfiler.new
+ @javascript_instrumentor = NewRelic::Agent::JavascriptInstrumentor.new(@events)
+ @throughput_monitor = NewRelic::Agent::ThroughputMonitor.new
@harvester = NewRelic::Agent::Harvester.new(@events)
@after_fork_lock = Mutex.new
@@ -140,6 +144,7 @@ module InstanceMethods
attr_reader :custom_event_aggregator
attr_reader :transaction_event_recorder
attr_reader :attribute_filter
+ attr_reader :throughput_monitor
def transaction_event_aggregator
@transaction_event_recorder.transaction_event_aggregator
@@ -543,6 +548,7 @@ def drop_buffered_data
@transaction_event_recorder.drop_buffered_data
@custom_event_aggregator.reset!
@sql_sampler.reset!
+
if Agent.config[:clear_transaction_state_after_fork]
TransactionState.tl_clear
end
@@ -1148,6 +1154,7 @@ def transmit_data
harvest_and_send_for_agent_commands
end
ensure
+ throughput_monitor.reset!
NewRelic::Agent::Database.close_connections
duration = (Time.now - now).to_f
NewRelic::Agent.record_metric('Supportability/Harvest', duration)
@@ -1639,6 +1639,13 @@ def self.convert_to_constant_list(raw_value)
:type => Boolean,
:allowed_from_server => false,
:description => 'If <code>true</code>, the agent will clear <code>TransactionState</code> in <code>Agent.drop_buffered_data</code>.'
+ },
+ :'distributed_tracing.enabled' => {
+ :default => false,
+ :public => false,
+ :type => Boolean,
+ :allowed_from_server => false,
+ :description => 'If <code>true</code> enables experimental distributed tracing feature.'
}
}.freeze
end
@@ -0,0 +1,29 @@
+# encoding: utf-8
+# This file is distributed under New Relic's license terms.
+# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
+
+require 'new_relic/agent/inbound_request_monitor'
+require 'new_relic/agent/cross_app_tracing'
+
+module NewRelic
+ module Agent
+ class DistributedTraceMonitor < InboundRequestMonitor
+ NEWRELIC_TRACE_KEY = 'HTTP_X_NEWRELIC_TRACE'.freeze
+ HTTP_TRANSPORT_TYPE = 'HTTP'.freeze
+
+ def on_finished_configuring(events)
+ return unless NewRelic::Agent.config[:'distributed_tracing.enabled']
+ events.subscribe(:before_call, &method(:on_before_call))
+ end
+
+ def on_before_call(request)
+ return unless CrossAppTracing.cross_app_enabled?
+ return unless payload = request[NEWRELIC_TRACE_KEY]
+
+ state = NewRelic::Agent::TransactionState.tl_get
+ txn = state.current_transaction
+ txn.accept_distributed_trace_payload HTTP_TRANSPORT_TYPE, payload
+ end
+ end
+ end
+end
@@ -0,0 +1,223 @@
+# encoding: utf-8
+# This file is distributed under New Relic's license terms.
+# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
+require 'json'
+require 'base64'
+
+module NewRelic
+ module Agent
+ class DistributedTracePayload
+ VERSION =[0, 0].freeze
+ CALLER_TYPE = "App".freeze
+ POUND = '#'.freeze
+
+ # Key names for serialization
+ VERSION_KEY = 'v'.freeze
+ DATA_KEY = 'd'.freeze
+ CALLER_TYPE_KEY = 'ty'.freeze
+ CALLER_ACCOUNT_KEY = 'ac'.freeze
+ CALLER_APP_KEY = 'ap'.freeze
+ ID_KEY = 'id'.freeze
+ TRIP_ID_KEY = 'tr'.freeze
+ SAMPLED_KEY = 'sa'.freeze
+ PARENT_IDS_KEY = 'pa'.freeze
+ DEPTH_KEY = 'de'.freeze
+ ORDER_KEY = 'or'.freeze
+ TIMESTAMP_KEY = 'ti'.freeze
+ HOST_KEY = 'ho'.freeze
+ SYNTHETICS_KEY = 'sy'.freeze
+ SYNTHETICS_RESOURCE_KEY = 'r'.freeze
+ SYNTHETICS_JOB_KEY = 'j'.freeze
+ SYNTHETICS_MONITOR_KEY = 'm'.freeze
+
+ # Intrinsic Keys
+ CALLER_TYPE_INTRINSIC_KEY = "caller.type".freeze
+ CALLER_APP_INTRINSIC_KEY = "caller.app".freeze
+ CALLER_ACCOUNT_ID_INTRINSIC_KEY = "caller.account".freeze
+ CALLER_TRANSPORT_TYPE_INTRINSIC_KEY = "caller.transportType".freeze
+ CALLER_TRANSPORT_DURATION_INTRINSIC_KEY = "caller.transportDuration".freeze
+ CALLER_HOST_INTRINSIC_KEY = "caller.host".freeze
+ DEPTH_INTRINSIC_KEY = "nr.depth".freeze
+ ORDER_INTRINSIC_KEY = "nr.order".freeze
+ GUID_INTRINSIC_KEY = "nr.guid".freeze
+ REFERRING_TRANSACTION_GUID_INTRINSIC_KEY = "nr.referringTransactionGuid".freeze
+ TRIP_ID_INTRINSIC_KEY = "nr.tripId".freeze
+ PARENT_IDS_INTRINSIC_KEY = "nr.parentIds".freeze
+ COMMA = ",".freeze
+
+ INTRINSIC_KEYS = [
+ CALLER_TYPE_INTRINSIC_KEY,
+ CALLER_APP_INTRINSIC_KEY,
+ CALLER_ACCOUNT_ID_INTRINSIC_KEY,
+ CALLER_TRANSPORT_TYPE_INTRINSIC_KEY,
+ CALLER_TRANSPORT_DURATION_INTRINSIC_KEY,
+ CALLER_HOST_INTRINSIC_KEY,
+ DEPTH_INTRINSIC_KEY,
+ ORDER_INTRINSIC_KEY,
+ GUID_INTRINSIC_KEY,
+ REFERRING_TRANSACTION_GUID_INTRINSIC_KEY,
+ TRIP_ID_INTRINSIC_KEY,
+ PARENT_IDS_INTRINSIC_KEY
+ ].freeze
+
+ class << self
+ def for_transaction transaction, uri=nil
+ payload = new
+ return payload unless connected?
+
+ payload.version = VERSION
+ payload.caller_type = CALLER_TYPE
+
+ # We should not rely on the xp_id being formulated this way, but we have
+ # seen nil account ids coming down in staging for some accounts
+ account_id, fallback_app_id = Agent.config[:cross_process_id].split(POUND)
+ payload.caller_account_id = account_id
+
+ payload.caller_app_id = if Agent.config[:application_id].empty?
+ fallback_app_id
+ else
+ Agent.config[:application_id]
+ end
+
+ payload.timestamp = (Time.now.to_f * 1000).round
+ payload.id = transaction.guid
+ payload.trip_id = transaction.distributed_tracing_trip_id
+ payload.sampled = transaction.sampled?
+ payload.parent_ids = transaction.parent_ids
+ payload.depth = transaction.depth + 1
+ payload.order = transaction.order
+ payload.host = uri.host if uri
+
+ if transaction.synthetics_payload
+ payload.synthetics_resource = transaction.synthetics_payload[2]
+ payload.synthetics_job = transaction.synthetics_payload[3]
+ payload.synthetics_monitor = transaction.synthetics_payload[4]
+ end
+
+ payload
+ end
+
+ def from_json serialized_payload
+ raw_payload = JSON.parse serialized_payload
+ payload_data = raw_payload[DATA_KEY]
+
+ payload = new
+ payload.version = raw_payload[VERSION_KEY]
+ payload.caller_type = payload_data[CALLER_TYPE_KEY]
+ payload.caller_account_id = payload_data[CALLER_ACCOUNT_KEY]
+ payload.caller_app_id = payload_data[CALLER_APP_KEY]
+ payload.timestamp = payload_data[TIMESTAMP_KEY]
+ payload.id = payload_data[ID_KEY]
+ payload.trip_id = payload_data[TRIP_ID_KEY]
+ payload.sampled = payload_data[SAMPLED_KEY]
+ payload.parent_ids = payload_data[PARENT_IDS_KEY]
+ payload.depth = payload_data[DEPTH_KEY]
+ payload.order = payload_data[ORDER_KEY]
+ payload.host = payload_data[HOST_KEY]
+
+ if payload_synthetics = payload_data[SYNTHETICS_KEY]
+ payload.synthetics_resource = payload_synthetics[SYNTHETICS_RESOURCE_KEY]
+ payload.synthetics_job = payload_synthetics[SYNTHETICS_JOB_KEY]
+ payload.synthetics_monitor = payload_synthetics[SYNTHETICS_MONITOR_KEY]
+ end
+
+ payload
+ end
+
+ def from_http_safe http_safe_payload
+ decoded_payload = Base64.strict_decode64 http_safe_payload
+ from_json decoded_payload
+ end
+
+ #assigns intrinsics for the first distributed trace in a trip
+ def assign_initial_intrinsics transaction, payload
+ payload[TRIP_ID_INTRINSIC_KEY] = transaction.distributed_tracing_trip_id
+ payload[DEPTH_INTRINSIC_KEY] = transaction.depth
+ payload[PARENT_IDS_INTRINSIC_KEY] = transaction.parent_ids
+ end
+
+ private
+
+ # We use the presence of the cross_process_id in the config to tell if we
+ # have connected yet.
+ def connected?
+ !!Agent.config[:'cross_process_id']
+ end
+ end
+
+ attr_accessor :version,
+ :caller_type,
+ :caller_transport_type,
+ :caller_account_id,
+ :caller_app_id,
+ :id,
+ :trip_id,
+ :sampled,
+ :parent_ids,
+ :synthetics_resource,
+ :synthetics_job,
+ :synthetics_monitor,
+ :order,
+ :depth,
+ :timestamp,
+ :host
+
+ alias_method :sampled?, :sampled
+
+ def synthetics?
+ !!(synthetics_resource || synthetics_job || synthetics_monitor)
+ end
+
+ def to_json
+ result = {
+ VERSION_KEY => version
+ }
+
+ result[DATA_KEY] = {
+ CALLER_TYPE_KEY => caller_type,
+ CALLER_ACCOUNT_KEY => caller_account_id,
+ CALLER_APP_KEY => caller_app_id,
+ ID_KEY => id,
+ TRIP_ID_KEY => trip_id,
+ SAMPLED_KEY => sampled,
+ PARENT_IDS_KEY => parent_ids,
+ DEPTH_KEY => depth,
+ ORDER_KEY => order,
+ HOST_KEY => host,
+ TIMESTAMP_KEY => timestamp,
+ }
+
+ if synthetics?
+ result[DATA_KEY][SYNTHETICS_KEY] = {
+ SYNTHETICS_RESOURCE_KEY => synthetics_resource,
+ SYNTHETICS_JOB_KEY => synthetics_job,
+ SYNTHETICS_MONITOR_KEY => synthetics_monitor
+ }
+ end
+
+ JSON.dump(result)
+ end
+
+ alias_method :text, :to_json
+
+ def http_safe
+ Base64.strict_encode64 to_json
+ end
+
+ def assign_intrinsics transaction, payload
+ payload[CALLER_TYPE_INTRINSIC_KEY] = caller_type
+ payload[CALLER_APP_INTRINSIC_KEY] = caller_app_id
+ payload[CALLER_ACCOUNT_ID_INTRINSIC_KEY] = caller_account_id
+ payload[CALLER_TRANSPORT_TYPE_INTRINSIC_KEY] = caller_transport_type
+ payload[CALLER_TRANSPORT_DURATION_INTRINSIC_KEY] = transaction.transport_duration
+ payload[CALLER_HOST_INTRINSIC_KEY] = host
+ payload[DEPTH_INTRINSIC_KEY] = depth
+ payload[ORDER_INTRINSIC_KEY] = order
+ payload[GUID_INTRINSIC_KEY] = transaction.guid
+ payload[REFERRING_TRANSACTION_GUID_INTRINSIC_KEY] = id
+ payload[TRIP_ID_INTRINSIC_KEY] = trip_id
+ payload[PARENT_IDS_INTRINSIC_KEY] = parent_ids.join COMMA if parent_ids
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 35cb1ee

Please sign in to comment.