newrelic-infinite_tracing 6.11.0.365

Sign up to get free protection for your applications and to get access to all the features.
@@ -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