newrelic-infinite_tracing 6.11.0.365

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc6db54323c4ae07d8db4e91bf7fc71b39290662585d0b65ac55cf60e5f91a4a
4
+ data.tar.gz: 928754820a8b4bea71bcb7bc1095058f2dba37bf0d53f7088642fbc67a0027f6
5
+ SHA512:
6
+ metadata.gz: 12826887a68e0df4c28f2d7050909318a018f1ed8701b410b3a13a7750de45ee6375952ff8eaf46a56cbb5aac9f1ac5154b1ca14c7f1f38169c0219247bbfdd3
7
+ data.tar.gz: ea57b78c49205cc3b35af034d4de6f6ccb9d74b902aa2806f521307af9fa10e3fbd06eb5f5a9b1afbfe9f74107733947d870c6e5f240e8547465247e33ea88a3
@@ -0,0 +1,2 @@
1
+ LICENSE
2
+ CONTRIBUTING.md
@@ -0,0 +1,6 @@
1
+ # New Relic Infinite Tracing for Ruby Agent Release Notes #
2
+
3
+ ## v6.11.0
4
+
5
+ * Initial Release!
6
+ * Implements gRPC protocol for communicating with Trace Observers
data/Gemfile ADDED
@@ -0,0 +1,12 @@
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
+ # frozen_string_literal: true
5
+
6
+ source 'https://rubygems.org'
7
+
8
+ gemspec name: "newrelic-infinite_tracing"
9
+
10
+ group :development do
11
+ gem 'newrelic_rpm', path: '..'
12
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'rake/testtask'
3
+ require "#{File.dirname(__FILE__)}/tasks/all.rb"
4
+
5
+ task :default => :test
6
+
7
+ task :console do
8
+ require 'pry'
9
+ require 'infinite_tracing'
10
+ ARGV.clear
11
+ Pry.start
12
+ end
13
+
14
+ Rake::TestTask.new do |t|
15
+ ROOT = File.join File.dirname(__FILE__)
16
+ $LOAD_PATH << ROOT
17
+
18
+ file_pattern = "#{ROOT}/**/*_test.rb"
19
+
20
+ t.libs << "#{ROOT}/test"
21
+ t.libs << "#{ROOT}/lib"
22
+ t.pattern = Array(file_pattern).join(",")
23
+ t.verbose = ENV["VERBOSE"]
24
+ end
@@ -0,0 +1,40 @@
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
+ # frozen_string_literal: true
5
+
6
+ require 'uri'
7
+
8
+ require 'newrelic_rpm'
9
+
10
+ NewRelic::Agent.logger.debug "Detected New Relic Infinite Tracing Gem"
11
+
12
+ require 'infinite_tracing/version'
13
+ require 'infinite_tracing/config'
14
+
15
+ DependencyDetection.defer do
16
+ named :infinite_tracing
17
+
18
+ depends_on do
19
+ NewRelic::Agent::InfiniteTracing::Config.should_load?
20
+ end
21
+
22
+ executes do
23
+ NewRelic::Agent.logger.debug "Loading New Relic Infinite Tracing Library"
24
+
25
+ require 'infinite_tracing/proto'
26
+
27
+ require 'infinite_tracing/constants'
28
+ require 'infinite_tracing/worker'
29
+ require 'infinite_tracing/record_status_handler'
30
+
31
+ require 'infinite_tracing/transformer'
32
+ require 'infinite_tracing/streaming_buffer'
33
+ require 'infinite_tracing/suspended_streaming_buffer'
34
+ require 'infinite_tracing/channel'
35
+ require 'infinite_tracing/connection'
36
+ require 'infinite_tracing/client'
37
+
38
+ require 'infinite_tracing/agent_integrations'
39
+ end
40
+ end
@@ -0,0 +1,20 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic::Agent
7
+ module InfiniteTracing
8
+ if Config.enabled? || Config.test_framework?
9
+ NewRelic::Agent.logger.debug "Integrating Infinite Tracer with Agent"
10
+
11
+ require_relative 'agent_integrations/agent'
12
+ require_relative 'agent_integrations/segment'
13
+ require_relative 'agent_integrations/datastore_segment'
14
+ require_relative 'agent_integrations/external_request_segment'
15
+
16
+ else
17
+ NewRelic::Agent.logger.debug "Skipped Integrating Infinite Tracer with Agent"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic::Agent
7
+ NewRelic::Agent.logger.debug "Installing Infinite Tracer in Agent"
8
+
9
+ Agent.class_eval do
10
+
11
+ def new_infinite_tracer
12
+ # We must start streaming in a thread or we block/deadlock the
13
+ # entire start up process for the Agent.
14
+ InfiniteTracing::Client.new.tap do |client|
15
+ @infinite_tracer_thread = InfiniteTracing::Worker.new(:infinite_tracer) do
16
+ NewRelic::Agent.logger.debug "Opening Infinite Tracer Stream with gRPC server"
17
+ client.start_streaming
18
+ end
19
+ end
20
+ end
21
+
22
+ def close_infinite_tracer
23
+ return unless @infinite_tracer_thread
24
+ @infinite_tracer_thread.join
25
+ @infinite_tracer_thread.stop
26
+ @infinite_tracer_thread = nil
27
+ end
28
+
29
+ def infinite_tracer
30
+ @infinite_tracer ||= new_infinite_tracer
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic
7
+ module Agent
8
+ class Transaction
9
+ class DatastoreSegment
10
+ def record_span_event
11
+ tracer = ::NewRelic::Agent.agent.infinite_tracer
12
+ tracer << Proc.new { SpanEventPrimitive.for_datastore_segment self }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic
7
+ module Agent
8
+ class Transaction
9
+ class ExternalRequestSegment
10
+ def record_span_event
11
+ tracer = ::NewRelic::Agent.agent.infinite_tracer
12
+ tracer << Proc.new { SpanEventPrimitive.for_external_request_segment self }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic
7
+ module Agent
8
+ class Transaction
9
+ class Segment
10
+
11
+ def segment_complete
12
+ record_span_event
13
+ end
14
+
15
+ def record_span_event
16
+ tracer = ::NewRelic::Agent.agent.infinite_tracer
17
+ tracer << Proc.new { SpanEventPrimitive.for_segment self }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic::Agent
7
+ module InfiniteTracing
8
+ class Channel
9
+
10
+ def stub
11
+ NewRelic::Agent.logger.debug "Infinite Tracer Opening Channel to #{host_and_port}"
12
+
13
+ Com::Newrelic::Trace::V1::IngestService::Stub.new \
14
+ host_and_port,
15
+ credentials,
16
+ channel_override: channel
17
+ end
18
+
19
+ def channel
20
+ GRPC::Core::Channel.new(host_and_port, settings, credentials)
21
+ end
22
+
23
+ def credentials
24
+ if Config.local?
25
+ :this_channel_is_insecure
26
+ else
27
+ # Uses system configured certificates by default
28
+ GRPC::Core::ChannelCredentials.new
29
+ end
30
+ end
31
+
32
+ def host_and_port
33
+ Config.trace_observer_host_and_port
34
+ end
35
+
36
+ def settings
37
+ {
38
+ 'grpc.minimal_stack' => 1,
39
+ 'grpc.enable_deadline_checking' => 0,
40
+ }
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,138 @@
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
+ # frozen_string_literal: true
5
+
6
+ # The Client class manages the streaming buffer with respect to the gRPC Connection.
7
+ #
8
+ # Restarting the client will cause a new connection to the gRPC server.
9
+ # When the client is restarted, a new streaming buffer is started and contents of old
10
+ # buffer are transferred to the new buffer.
11
+ #
12
+ # Suspending the client will prevent the client from attempting to reconnect to the
13
+ # gRPC server, but will still continue to record the span events `seen` metric.
14
+ module NewRelic::Agent
15
+ module InfiniteTracing
16
+ class Client
17
+ include Constants
18
+
19
+ def initialize
20
+ @suspended = false
21
+ @response_handler = nil
22
+ @lock = Mutex.new
23
+ end
24
+
25
+ def << segment
26
+ buffer << segment
27
+ end
28
+
29
+ # Transfers spans in streaming buffer from previous
30
+ # client (if any) and returns self (so we chain the call)
31
+ def transfer previous_client
32
+ return self unless previous_client
33
+ previous_client.buffer.transfer buffer
34
+ self
35
+ end
36
+
37
+ # provides the correct streaming buffer instance based on whether the
38
+ # client is currently suspended.
39
+ def new_streaming_buffer
40
+ buffer_class = suspended? ? SuspendedStreamingBuffer : StreamingBuffer
41
+ buffer_class.new Config.span_events_queue_size
42
+ end
43
+
44
+ def buffer
45
+ @buffer ||= new_streaming_buffer
46
+ end
47
+
48
+ def flush
49
+ buffer.flush_queue
50
+ end
51
+
52
+ # Turns camelcase base class name into upper snake case version of the name.
53
+ def formatted_class_name class_name
54
+ class_name = class_name.split(":")[-1]
55
+ formatted_class_name = (class_name.gsub!(/(.)([A-Z])/,'\1_\2') || class_name).upcase
56
+ end
57
+
58
+ # Literal codes are all mapped to unique class names, so we can deduce the
59
+ # name of the error to report in the metric from the error's class name.
60
+ def grpc_error_metric_name error
61
+ GRPC_ERROR_NAME_METRIC % formatted_class_name(error.class.name)
62
+ end
63
+
64
+ # Reports AND logs general response metric along with a more specific error metric
65
+ def record_error_metrics_and_log error
66
+ NewRelic::Agent.record_metric RESPONSE_ERROR_METRIC, 0.0
67
+ if error.is_a? GRPC::BadStatus
68
+ NewRelic::Agent.record_metric grpc_error_metric_name(error), 0.0
69
+ else
70
+ NewRelic::Agent.record_metric GRPC_OTHER_ERROR_METRIC, 0.0
71
+ end
72
+ NewRelic::Agent.logger.warn "gRPC response error received.", error
73
+ end
74
+
75
+ def handle_error error
76
+ record_error_metrics_and_log error
77
+
78
+ case error
79
+ when GRPC::Unavailable then restart
80
+ when GRPC::Unimplemented then suspend
81
+ else
82
+ # Set exponential backoff to false so we'll reconnect at periodic (15 second) intervals instead
83
+ start_streaming false
84
+ end
85
+ end
86
+
87
+ def suspended?
88
+ @suspended
89
+ end
90
+
91
+ # Places the client into suspended state whereby client will no longer attempt to
92
+ # reconnect to the gRPC server nor will it attempt to send span events henceforth.
93
+ # The Suspended Streaming Buffer will be installed in this state.
94
+ def suspend
95
+ return if suspended?
96
+ @lock.synchronize do
97
+ @suspended = true
98
+ @buffer = new_streaming_buffer
99
+ NewRelic::Agent.logger.warn "The Trace Observer host signaled to suspend streaming span events. " \
100
+ "No more span events will be sent during this session."
101
+ end
102
+ end
103
+
104
+ def restart
105
+ @lock.synchronize do
106
+ Connection.reset
107
+ old_buffer = @buffer
108
+ @buffer = new_streaming_buffer
109
+ old_buffer.transfer @buffer
110
+ end
111
+ start_streaming
112
+ end
113
+
114
+ def stop
115
+ return unless @response_handler
116
+ @lock.synchronize do
117
+ @response_handler.stop
118
+ @response_handler = nil
119
+ end
120
+ end
121
+
122
+ def start_streaming exponential_backoff=true
123
+ return if suspended?
124
+ @lock.synchronize { @response_handler = record_spans exponential_backoff }
125
+ end
126
+
127
+ def record_spans exponential_backoff
128
+ RecordStatusHandler.new self, Connection.record_spans(self, buffer.enumerator, exponential_backoff)
129
+ end
130
+
131
+ def record_span_batches exponential_backoff
132
+ RecordStatusHandler.new self, Connection.record_span_batches(self, buffer.batch_enumerator, exponential_backoff)
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,115 @@
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
+ # frozen_string_literal: true
5
+
6
+ module NewRelic::Agent
7
+ module InfiniteTracing
8
+ module Config
9
+ extend self
10
+
11
+ TRACE_OBSERVER_NOT_CONFIGURED_ERROR = "Trace Observer host not configured!"
12
+
13
+ # We only want to load the infinite tracing gem's files when
14
+ # a) we're inside test framework and running tests
15
+ # b) the trace observer host is configured
16
+ #
17
+ def should_load?
18
+ test_framework? || trace_observer_configured?
19
+ end
20
+
21
+ # Infinite Tracing support is enabled when the following conditions are true:
22
+ # a) Distributed tracing is enabled in the agent, AND
23
+ # b) Span events are enabled in the agent, both by client side configuration
24
+ # AND the collect_span_events connect response field, AND
25
+ # c) A Trace Observer host is configured by setting infinite_tracing.trace_observer.host.
26
+ def enabled?
27
+ distributed_tracing_enabled? &&
28
+ span_events_enabled? &&
29
+ trace_observer_configured?
30
+ end
31
+
32
+ # Distributed Tracing must be enabled for Infinite Tracing
33
+ def distributed_tracing_enabled?
34
+ NewRelic::Agent.config[:'distributed_tracing.enabled']
35
+ end
36
+
37
+ # Span Events must be enabled for Infinite Tracing
38
+ def span_events_enabled?
39
+ NewRelic::Agent.config[:'span_events.enabled']
40
+ end
41
+
42
+ # running locally is akin to communicating with the gRPC server with an
43
+ # unencrypted channel. Generally, this is _not_ allowed by the agent
44
+ # in normal use-cases. The only known use-case for this is when
45
+ # streaming under TEST conditions.
46
+ def local?
47
+ test_framework?
48
+ end
49
+
50
+ # removes the scheme and port from a host entry.
51
+ def without_scheme_or_port url
52
+ url.gsub(%r{^https?://|:\d+$}, '')
53
+ end
54
+
55
+ def trace_observer_host
56
+ without_scheme_or_port NewRelic::Agent.config[:'infinite_tracing.trace_observer.host']
57
+ end
58
+
59
+ # If the port is declared on the host entry, it overrides the port entry because otherwise
60
+ # we'd need to figure out if user supplied the port or if the default source config set
61
+ # the port. To help with debugging configuration issues, we log whenever the port entry
62
+ # is overriden by the presence of the port on the host entry.
63
+ def port_from_host_entry
64
+ port_str = NewRelic::Agent.config[:'infinite_tracing.trace_observer.host'].scan(%r{:(\d+)$}).flatten
65
+ if port = (port_str[0] and port_str[0].to_i)
66
+ NewRelic::Agent.logger.warn(":'infinite_tracing.trace_observer.port' is ignored if present because :'infinite_tracing.trace_observer.host' specifies the port")
67
+ return port
68
+ end
69
+ end
70
+
71
+ # This is the port the trace observer is listening on. It can be supplied as a suffix
72
+ # on the host entry or via the separate port entry.
73
+ def trace_observer_port
74
+ port_from_host_entry || NewRelic::Agent.config[:'infinite_tracing.trace_observer.port']
75
+ end
76
+
77
+ # The scheme is based on whether the Trace Observer is running locally or remotely.
78
+ # Remote unsecure (unencypted) streaming is disallowed!
79
+ def trace_observer_scheme
80
+ local? ? NewRelic::HTTP : NewRelic::HTTPS
81
+ end
82
+
83
+ # The uniform resource identifier of the Trace Observer host constructed from all the parts.
84
+ def trace_observer_uri
85
+ if trace_observer_configured?
86
+ URI("#{trace_observer_scheme}://#{trace_observer_host_and_port}")
87
+ else
88
+ NewRelic::Agent.logger.error TRACE_OBSERVER_NOT_CONFIGURED_ERROR
89
+ raise TRACE_OBSERVER_NOT_CONFIGURED_ERROR
90
+ end
91
+ end
92
+
93
+ # returns host and port together expressed as +hostname:port+ string.
94
+ def trace_observer_host_and_port
95
+ "#{trace_observer_host}:#{trace_observer_port}"
96
+ end
97
+
98
+ # The maximum number of span events the Streaming Buffer can hold when buffering
99
+ # to stream across the gRPC channel.
100
+ def span_events_queue_size
101
+ NewRelic::Agent.config[:'span_events.queue_size']
102
+ end
103
+
104
+ # Returns TRUE if we're running in a test environment
105
+ def test_framework?
106
+ NewRelic::Agent.config[:framework] == :test
107
+ end
108
+
109
+ # Infinite Tracing is configured when a non empty string is set as the host
110
+ def trace_observer_configured?
111
+ trace_observer_host != NewRelic::EMPTY_STR
112
+ end
113
+ end
114
+ end
115
+ end