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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +12 -0
- data/Rakefile +24 -0
- data/lib/infinite_tracing.rb +40 -0
- data/lib/infinite_tracing/agent_integrations.rb +20 -0
- data/lib/infinite_tracing/agent_integrations/agent.rb +33 -0
- data/lib/infinite_tracing/agent_integrations/datastore_segment.rb +17 -0
- data/lib/infinite_tracing/agent_integrations/external_request_segment.rb +17 -0
- data/lib/infinite_tracing/agent_integrations/segment.rb +22 -0
- data/lib/infinite_tracing/channel.rb +45 -0
- data/lib/infinite_tracing/client.rb +138 -0
- data/lib/infinite_tracing/config.rb +115 -0
- data/lib/infinite_tracing/connection.rb +171 -0
- data/lib/infinite_tracing/constants.rb +20 -0
- data/lib/infinite_tracing/proto.rb +18 -0
- data/lib/infinite_tracing/proto/infinite_tracing_pb.rb +44 -0
- data/lib/infinite_tracing/proto/infinite_tracing_services_pb.rb +42 -0
- data/lib/infinite_tracing/record_status_handler.rb +49 -0
- data/lib/infinite_tracing/streaming_buffer.rb +162 -0
- data/lib/infinite_tracing/suspended_streaming_buffer.rb +39 -0
- data/lib/infinite_tracing/transformer.rb +54 -0
- data/lib/infinite_tracing/version.rb +12 -0
- data/lib/infinite_tracing/worker.rb +74 -0
- data/lib/new_relic/infinite_tracing.rb +40 -0
- data/lib/newrelic/infinite_tracing.rb +8 -0
- data/lib/proto/infinite_tracing.proto +42 -0
- data/newrelic-infinite_tracing.gemspec +91 -0
- data/tasks/all.rb +8 -0
- data/tasks/proto.rake +51 -0
- metadata +290 -0
@@ -0,0 +1,171 @@
|
|
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 connection class manages the channel and connection to the gRPC server.
|
7
|
+
#
|
8
|
+
# Calls to the gRPC server are blocked until the agent connects to the collector
|
9
|
+
# and obtains a license_key and agent_run_token from the server side configuration.
|
10
|
+
#
|
11
|
+
# If the agent is instructed to reconnect by the collector, that event triggers
|
12
|
+
# server_source_configuration_added, which this connection is subscribed to and will
|
13
|
+
# also notify the client to restart and re-establish its bi-directional streaming
|
14
|
+
# with the gRPC server.
|
15
|
+
#
|
16
|
+
# NOTE: Connection is implemented as a Singleton and it also only ever expects *one*
|
17
|
+
# client instance by design.
|
18
|
+
module NewRelic::Agent
|
19
|
+
module InfiniteTracing
|
20
|
+
class Connection
|
21
|
+
|
22
|
+
|
23
|
+
# listens for server side configurations added to the agent. When a new config is
|
24
|
+
# added, we have a new agent run token and need to restart the client's RPC stream
|
25
|
+
# with the new metadata information.
|
26
|
+
NewRelic::Agent.agent.events.subscribe(:server_source_configuration_added) do
|
27
|
+
begin
|
28
|
+
Connection.instance.notify_agent_started
|
29
|
+
rescue => error
|
30
|
+
NewRelic::Agent.logger.error \
|
31
|
+
"Error during notify :server_source_configuration_added event",
|
32
|
+
error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
|
38
|
+
def instance
|
39
|
+
@@instance ||= new
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset
|
43
|
+
@@instance = new
|
44
|
+
end
|
45
|
+
|
46
|
+
# RPC calls will pass the calling client instance in. We track this
|
47
|
+
# so we're able to signal the client to restart when connectivity to the
|
48
|
+
# server is disrupted.
|
49
|
+
def record_spans client, enumerator, exponential_backoff
|
50
|
+
instance.record_spans client, enumerator, exponential_backoff
|
51
|
+
end
|
52
|
+
|
53
|
+
# RPC calls will pass the calling client instance in. We track this
|
54
|
+
# so we're able to signal the client to restart when connectivity to the
|
55
|
+
# server is disrupted.
|
56
|
+
def record_span_batches client, enumerator, exponential_backoff
|
57
|
+
instance.record_span_batch client, enumerator, exponential_backoff
|
58
|
+
end
|
59
|
+
|
60
|
+
def metadata
|
61
|
+
instance.metadata
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# We attempt to connect and record spans with reconnection backoff in order to deal with
|
66
|
+
# unavailable errors coming from the stub being created and record_span call
|
67
|
+
def record_spans client, enumerator, exponential_backoff
|
68
|
+
@active_clients[client] = client
|
69
|
+
with_reconnection_backoff(exponential_backoff) { rpc.record_span enumerator, metadata: metadata }
|
70
|
+
end
|
71
|
+
|
72
|
+
# RPC calls will pass the calling client instance in. We track this
|
73
|
+
# so we're able to signal the client to restart when connectivity to the
|
74
|
+
# server is disrupted.
|
75
|
+
def record_span_batches client, enumerator, exponential_backoff
|
76
|
+
@active_clients[client] = client
|
77
|
+
with_reconnection_backoff(exponential_backoff) { rpc.record_span_batch enumerator, metadata: metadata }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Acquires the new channel stub for the RPC calls.
|
81
|
+
# We attempt to connect and record spans with reconnection backoff in order to deal with
|
82
|
+
# unavailable errors coming from the stub being created and record_span call
|
83
|
+
def rpc
|
84
|
+
@rpc ||= Channel.new.stub
|
85
|
+
end
|
86
|
+
|
87
|
+
# The metadata for the RPC calls is a blocking call waiting for the Agent to
|
88
|
+
# connect and receive the server side configuration, which contains the license_key
|
89
|
+
# as well as the agent_id (agent_run_token).
|
90
|
+
def metadata
|
91
|
+
return @metadata if @metadata
|
92
|
+
|
93
|
+
@lock.synchronize do
|
94
|
+
@agent_started.wait(@lock) if !@agent_connected
|
95
|
+
|
96
|
+
@metadata = {
|
97
|
+
"license_key" => license_key,
|
98
|
+
"agent_run_token" => agent_id
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Initializes rpc so we can get a Channel and Stub (connect to gRPC server)
|
104
|
+
# Initializes metadata so we use newest values in establishing channel
|
105
|
+
# Sets the agent_connected flag and signals the agent started so any
|
106
|
+
# waiting locks (rpc calls ahead of the agent connecting) can proceed.
|
107
|
+
def notify_agent_started
|
108
|
+
@lock.synchronize do
|
109
|
+
@rpc = nil
|
110
|
+
@metadata = nil
|
111
|
+
@agent_connected = true
|
112
|
+
@agent_started.signal
|
113
|
+
end
|
114
|
+
@active_clients.each_value(&:restart)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# prepares the connection to wait for the agent to connect and have an
|
120
|
+
# agent_run_token ready for metadata on rpc calls.
|
121
|
+
def initialize
|
122
|
+
@active_clients = {}
|
123
|
+
@rpc = nil
|
124
|
+
@metadata = nil
|
125
|
+
@connection_attempts = 0
|
126
|
+
@agent_connected = NewRelic::Agent.agent.connected?
|
127
|
+
@agent_started = ConditionVariable.new
|
128
|
+
@lock = Mutex.new
|
129
|
+
end
|
130
|
+
|
131
|
+
# The agent run token, which is only available after a server source configuration has
|
132
|
+
# been added to the agent's config stack.
|
133
|
+
def agent_id
|
134
|
+
NewRelic::Agent.agent.service.agent_id.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
def license_key
|
138
|
+
NewRelic::Agent.config[:license_key]
|
139
|
+
end
|
140
|
+
|
141
|
+
# Continues retrying the connection at backoff intervals until a successful connection is made
|
142
|
+
def with_reconnection_backoff exponential_backoff=true, &block
|
143
|
+
@connection_attempts = 0
|
144
|
+
begin
|
145
|
+
yield
|
146
|
+
rescue => exception
|
147
|
+
retry_connection_period = retry_connection_period(exponential_backoff)
|
148
|
+
::NewRelic::Agent.logger.error "Error establishing connection with infinite tracing service:", exception
|
149
|
+
::NewRelic::Agent.logger.info "Will re-attempt infinte tracing connection in #{retry_connection_period} seconds"
|
150
|
+
sleep retry_connection_period
|
151
|
+
note_connect_failure
|
152
|
+
retry
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def retry_connection_period exponential_backoff=true
|
157
|
+
if exponential_backoff
|
158
|
+
NewRelic::CONNECT_RETRY_PERIODS[@connection_attempts] || NewRelic::MAX_RETRY_PERIOD
|
159
|
+
else
|
160
|
+
NewRelic::MIN_RETRY_PERIOD
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# broken out to help for testing
|
165
|
+
def note_connect_failure
|
166
|
+
@connection_attempts += 1
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
171
|
+
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
|
+
module Constants
|
9
|
+
SUPPORTABILITY_PREFIX = "Supportability/InfiniteTracing/Span"
|
10
|
+
|
11
|
+
SPANS_SEEN_METRIC = "#{SUPPORTABILITY_PREFIX}/Seen"
|
12
|
+
SPANS_SENT_METRIC = "#{SUPPORTABILITY_PREFIX}/Sent"
|
13
|
+
QUEUE_DUMPED_METRIC = "#{SUPPORTABILITY_PREFIX}/AgentQueueDumped"
|
14
|
+
RESPONSE_ERROR_METRIC = "#{SUPPORTABILITY_PREFIX}/Response/Error"
|
15
|
+
|
16
|
+
GRPC_ERROR_NAME_METRIC = "#{SUPPORTABILITY_PREFIX}/gRPC/%s"
|
17
|
+
GRPC_OTHER_ERROR_METRIC = GRPC_ERROR_NAME_METRIC % "Other"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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 'grpc'
|
7
|
+
require 'google/protobuf'
|
8
|
+
|
9
|
+
require_relative 'proto/infinite_tracing_pb'
|
10
|
+
require_relative 'proto/infinite_tracing_services_pb'
|
11
|
+
|
12
|
+
# Mapping gRPC namespaced classes into New Relic's
|
13
|
+
module NewRelic::Agent::InfiniteTracing
|
14
|
+
Span = Com::Newrelic::Trace::V1::Span
|
15
|
+
SpanBatch = Com::Newrelic::Trace::V1::SpanBatch
|
16
|
+
AttributeValue = Com::Newrelic::Trace::V1::AttributeValue
|
17
|
+
RecordStatus = Com::Newrelic::Trace::V1::RecordStatus
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
7
|
+
# source: infinite_tracing.proto
|
8
|
+
|
9
|
+
|
10
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
11
|
+
add_message "com.newrelic.trace.v1.SpanBatch" do
|
12
|
+
repeated :spans, :message, 1, "com.newrelic.trace.v1.Span"
|
13
|
+
end
|
14
|
+
add_message "com.newrelic.trace.v1.Span" do
|
15
|
+
optional :trace_id, :string, 1
|
16
|
+
map :intrinsics, :string, :message, 2, "com.newrelic.trace.v1.AttributeValue"
|
17
|
+
map :user_attributes, :string, :message, 3, "com.newrelic.trace.v1.AttributeValue"
|
18
|
+
map :agent_attributes, :string, :message, 4, "com.newrelic.trace.v1.AttributeValue"
|
19
|
+
end
|
20
|
+
add_message "com.newrelic.trace.v1.AttributeValue" do
|
21
|
+
oneof :value do
|
22
|
+
optional :string_value, :string, 1
|
23
|
+
optional :bool_value, :bool, 2
|
24
|
+
optional :int_value, :int64, 3
|
25
|
+
optional :double_value, :double, 4
|
26
|
+
end
|
27
|
+
end
|
28
|
+
add_message "com.newrelic.trace.v1.RecordStatus" do
|
29
|
+
optional :messages_seen, :uint64, 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Com
|
34
|
+
module Newrelic
|
35
|
+
module Trace
|
36
|
+
module V1
|
37
|
+
SpanBatch = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.newrelic.trace.v1.SpanBatch").msgclass
|
38
|
+
Span = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.newrelic.trace.v1.Span").msgclass
|
39
|
+
AttributeValue = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.newrelic.trace.v1.AttributeValue").msgclass
|
40
|
+
RecordStatus = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.newrelic.trace.v1.RecordStatus").msgclass
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
7
|
+
# Source: infinite_tracing.proto for package 'com.newrelic.trace.v1'
|
8
|
+
# Original file comments:
|
9
|
+
# encoding: utf-8
|
10
|
+
# This file is distributed under New Relic's license terms.
|
11
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
12
|
+
#
|
13
|
+
|
14
|
+
|
15
|
+
module Com
|
16
|
+
module Newrelic
|
17
|
+
module Trace
|
18
|
+
module V1
|
19
|
+
module IngestService
|
20
|
+
class Service
|
21
|
+
|
22
|
+
include GRPC::GenericService
|
23
|
+
|
24
|
+
self.marshal_class_method = :encode
|
25
|
+
self.unmarshal_class_method = :decode
|
26
|
+
self.service_name = 'com.newrelic.trace.v1.IngestService'
|
27
|
+
|
28
|
+
# Accepts a stream of Span messages, and returns an irregular stream of
|
29
|
+
# RecordStatus messages.
|
30
|
+
rpc :RecordSpan, stream(Span), stream(RecordStatus)
|
31
|
+
# Accepts a stream of SpanBatch messages, and returns an irregular
|
32
|
+
# stream of RecordStatus messages. This endpoint can be used to improve
|
33
|
+
# throughput when Span messages are small
|
34
|
+
rpc :RecordSpanBatch, stream(SpanBatch), stream(RecordStatus)
|
35
|
+
end
|
36
|
+
|
37
|
+
Stub = Service.rpc_stub_class
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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 RecordStatusHandler
|
9
|
+
def initialize client, enumerator
|
10
|
+
@client = client
|
11
|
+
@enumerator = enumerator
|
12
|
+
@messages_seen = nil
|
13
|
+
@lock = Mutex.new
|
14
|
+
@lock.synchronize { @worker = start_handler }
|
15
|
+
end
|
16
|
+
|
17
|
+
def messages_seen
|
18
|
+
@messages_seen ? @messages_seen.messages_seen : 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def start_handler
|
22
|
+
Worker.new self.class.name do
|
23
|
+
begin
|
24
|
+
@enumerator.each do |response|
|
25
|
+
break if response.nil? || response.is_a?(Exception)
|
26
|
+
@lock.synchronize do
|
27
|
+
@messages_seen = response
|
28
|
+
NewRelic::Agent.logger.debug "gRPC Infinite Tracer Observer saw #{messages_seen} messages"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue => error
|
32
|
+
@lock.synchronize { @client.handle_error error }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue => error
|
36
|
+
NewRelic::Agent.logger.error "gRPC Worker Error", error
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop
|
40
|
+
return if @worker.nil?
|
41
|
+
@lock.synchronize do
|
42
|
+
NewRelic::Agent.logger.debug "gRPC Stopping Response Handler"
|
43
|
+
@worker.stop
|
44
|
+
@worker = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,162 @@
|
|
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 StreamingBuffer class provides an Enumerator to the standard Ruby Queue
|
7
|
+
# class. The enumerator is blocking while the queue is empty.
|
8
|
+
module NewRelic::Agent
|
9
|
+
module InfiniteTracing
|
10
|
+
|
11
|
+
BATCH_SIZE = 100
|
12
|
+
|
13
|
+
class StreamingBuffer
|
14
|
+
include Constants
|
15
|
+
include Enumerable
|
16
|
+
extend Forwardable
|
17
|
+
def_delegators :@queue, :empty?, :num_waiting, :push
|
18
|
+
|
19
|
+
DEFAULT_QUEUE_SIZE = 10_000
|
20
|
+
FLUSH_DELAY = 0.005
|
21
|
+
MAX_FLUSH_WAIT = 3 # three seconds
|
22
|
+
|
23
|
+
attr_reader :queue
|
24
|
+
|
25
|
+
def initialize max_size = DEFAULT_QUEUE_SIZE
|
26
|
+
@max_size = max_size
|
27
|
+
@lock = Mutex.new
|
28
|
+
@queue = Queue.new
|
29
|
+
@batch = Array.new
|
30
|
+
end
|
31
|
+
|
32
|
+
# Dumps the contents of this streaming buffer onto
|
33
|
+
# the given buffer and closes the queue
|
34
|
+
def transfer new_buffer
|
35
|
+
@lock.synchronize do
|
36
|
+
until @queue.empty? do new_buffer.push @queue.pop end
|
37
|
+
@queue.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Pushes the segment given onto the queue.
|
42
|
+
#
|
43
|
+
# If the queue is at capacity, it is dumped and a
|
44
|
+
# supportability metric is recorded for the event.
|
45
|
+
#
|
46
|
+
# When a restart signal is received, the queue is
|
47
|
+
# locked with a mutex, blocking the push until
|
48
|
+
# the queue has restarted.
|
49
|
+
def << segment
|
50
|
+
@lock.synchronize do
|
51
|
+
clear_queue if @queue.size >= @max_size
|
52
|
+
NewRelic::Agent.increment_metric SPANS_SEEN_METRIC
|
53
|
+
@queue.push segment
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Drops all segments from the queue and records a
|
58
|
+
# supportability metric for the event.
|
59
|
+
def clear_queue
|
60
|
+
@queue.clear
|
61
|
+
NewRelic::Agent.increment_metric QUEUE_DUMPED_METRIC
|
62
|
+
end
|
63
|
+
|
64
|
+
# # Waits for the queue to be fully consumed or for the
|
65
|
+
# # waiting consumers to release.
|
66
|
+
# def flush_queue
|
67
|
+
# @queue.num_waiting.times { @queue.push nil }
|
68
|
+
# close_queue
|
69
|
+
# until @queue.empty? do sleep(FLUSH_DELAY) end
|
70
|
+
# end
|
71
|
+
|
72
|
+
# Waits for the queue to be fully consumed or for the
|
73
|
+
# waiting consumers to release.
|
74
|
+
def flush_queue
|
75
|
+
@queue.num_waiting.times { @queue.push nil }
|
76
|
+
close_queue
|
77
|
+
|
78
|
+
# Logs if we're throwing away spans because nothing's
|
79
|
+
# waiting to take them off the queue.
|
80
|
+
if @queue.num_waiting == 0 && !@queue.empty?
|
81
|
+
NewRelic::Agent.logger.warn "Discarding #{@queue.size} segments on Streaming Buffer"
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
# Only wait a short while for queue to flush
|
86
|
+
cutoff = Time.now + MAX_FLUSH_WAIT
|
87
|
+
until @queue.empty? || Time.now >= cutoff do sleep(FLUSH_DELAY) end
|
88
|
+
end
|
89
|
+
|
90
|
+
def close_queue
|
91
|
+
@lock.synchronize { @queue.close }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the blocking enumerator that will pop
|
95
|
+
# items off the queue while any items are present
|
96
|
+
# If +nil+ is popped, the queue is closing.
|
97
|
+
#
|
98
|
+
# The segment is transformed into a serializable
|
99
|
+
# span here so processing is taking place within
|
100
|
+
# the gRPC call's thread rather than in the main
|
101
|
+
# application thread.
|
102
|
+
def enumerator
|
103
|
+
return enum_for(:enumerator) unless block_given?
|
104
|
+
loop do
|
105
|
+
if segment = @queue.pop(false)
|
106
|
+
NewRelic::Agent.increment_metric SPANS_SENT_METRIC
|
107
|
+
yield transform(segment)
|
108
|
+
|
109
|
+
else
|
110
|
+
raise ClosedQueueError
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the blocking enumerator that will pop
|
116
|
+
# items off the queue while any items are present
|
117
|
+
#
|
118
|
+
# yielding is deferred until batch_size spans is
|
119
|
+
# reached.
|
120
|
+
#
|
121
|
+
# If +nil+ is popped, the queue is closing. A
|
122
|
+
# final yield on non-empty batch is fired.
|
123
|
+
#
|
124
|
+
# The segment is transformed into a serializable
|
125
|
+
# span here so processing is taking place within
|
126
|
+
# the gRPC call's thread rather than in the main
|
127
|
+
# application thread.
|
128
|
+
def batch_enumerator
|
129
|
+
return enum_for(:enumerator) unless block_given?
|
130
|
+
loop do
|
131
|
+
if proc_or_segment = @queue.pop(false)
|
132
|
+
NewRelic::Agent.increment_metric SPANS_SENT_METRIC
|
133
|
+
@batch << transform(proc_or_segment)
|
134
|
+
if @batch.size >= BATCH_SIZE
|
135
|
+
yield SpanBatch.new(spans: @batch)
|
136
|
+
@batch.clear
|
137
|
+
end
|
138
|
+
|
139
|
+
else
|
140
|
+
yield SpanBatch.new(spans: @batch) unless @batch.empty?
|
141
|
+
raise ClosedQueueError
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def span_event proc_or_segment
|
149
|
+
if proc_or_segment.is_a?(Proc)
|
150
|
+
proc_or_segment.call
|
151
|
+
else
|
152
|
+
SpanEventPrimitive.for_segment(proc_or_segment)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def transform proc_or_segment
|
157
|
+
Span.new Transformer.transform(span_event proc_or_segment)
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|