freddy-jruby 1.0.0 → 1.3.2
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 +4 -4
- data/.travis.yml +2 -2
- data/Gemfile +1 -0
- data/README.md +35 -0
- data/freddy.gemspec +2 -1
- data/lib/freddy.rb +11 -3
- data/lib/freddy/consumers.rb +0 -13
- data/lib/freddy/consumers/respond_to_consumer.rb +17 -4
- data/lib/freddy/consumers/response_consumer.rb +1 -8
- data/lib/freddy/consumers/tap_into_consumer.rb +17 -3
- data/lib/freddy/delivery.rb +21 -0
- data/lib/freddy/payload.rb +5 -2
- data/lib/freddy/producers.rb +0 -8
- data/lib/freddy/producers/reply_producer.rb +3 -2
- data/lib/freddy/producers/send_and_forget_producer.rb +20 -2
- data/lib/freddy/producers/send_and_wait_response_producer.rb +18 -5
- data/lib/freddy/trace_carrier.rb +26 -0
- data/spec/freddy/consumers/respond_to_consumer_spec.rb +0 -1
- data/spec/freddy/trace_carrier_spec.rb +56 -0
- data/spec/integration/logging_spec.rb +89 -32
- data/spec/integration/tracing_spec.rb +133 -0
- data/spec/spec_helper.rb +16 -0
- metadata +22 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ef47a37bd23d9834cd4d639e0ea0455f5a2c5ad
|
4
|
+
data.tar.gz: 7a778b7dfa7e473a6ea27d848e67df2c662cf249
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fe89c5b20d04b4315937406452315f44516ae6ca1b250b7b11362e1ec5edfcb83ed7ffc0e654dab23735caf37490b311ecd87430f8fc4c9e2bbbb9b85c9a72d
|
7
|
+
data.tar.gz: 9e79d8a97a65c7f7077b90e9577a39ec0238edcff0afb0d97e82c3c99fac304faf154fc822fee1d3042d4600c1a825c69bca9dab998ec295383dee7bddabce89
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -135,6 +135,41 @@ The following operations are supported:
|
|
135
135
|
responder_handler.shutdown
|
136
136
|
```
|
137
137
|
|
138
|
+
## Request Tracing
|
139
|
+
|
140
|
+
Freddy supports [OpenTracing API|https://github.com/opentracing/opentracing-ruby]. You must set a global tracer which then freddy will use:
|
141
|
+
```ruby
|
142
|
+
OpenTracing.global_tracing = MyTracerImplementation.new(...)
|
143
|
+
```
|
144
|
+
|
145
|
+
For example you can use [Logasm::Tracer](https://github.com/salemove/logasm-tracer) which will log all incoming and outgoing requests with trace ID, parent ID and span ID.
|
146
|
+
|
147
|
+
Current trace can be accessed through a thread-local variable `Freddy.trace`. Calling `deliver` or `deliver_with_response` will pass trace context to down-stream services.
|
148
|
+
|
149
|
+
Accessing trace information when using Logasm::Tracer implementation:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
freddy1 = Freddy.build
|
153
|
+
freddy1.respond_do('service1') do |payload, msg_handler|
|
154
|
+
puts "Trace id: #{Freddy.trace.context.trace_id}"
|
155
|
+
puts "Span id: #{Freddy.trace.context.span_id}"
|
156
|
+
|
157
|
+
freddy1.deliver('service2', {})
|
158
|
+
end
|
159
|
+
|
160
|
+
freddy2 = Freddy.build
|
161
|
+
freddy2.tap_into('service2') do |payload|
|
162
|
+
puts "Has same trace_id as the request in service1: #{Freddy.trace.context.trace_id}"
|
163
|
+
puts "Has service1 request span id as parent id: #{Freddy.trace.context.parent_id}"
|
164
|
+
puts "Has its own generated span id: #{Freddy.trace.context.span_id}"
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
In case you already have an ongoing OpenTracing span (e.g. provided by REST API) then you can pass the trace information to Freddy by doing:
|
169
|
+
```ruby
|
170
|
+
Freddy.trace = trace_span
|
171
|
+
```
|
172
|
+
The `trace_span` must implement OpenTracing::Span interface.
|
138
173
|
|
139
174
|
## Notes about concurrency
|
140
175
|
|
data/freddy.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
else
|
9
9
|
spec.name = "freddy"
|
10
10
|
end
|
11
|
-
spec.version = '1.
|
11
|
+
spec.version = '1.3.2'
|
12
12
|
spec.authors = ["Salemove TechMovers"]
|
13
13
|
spec.email = ["techmovers@salemove.com"]
|
14
14
|
spec.description = %q{Messaging API}
|
@@ -32,4 +32,5 @@ Gem::Specification.new do |spec|
|
|
32
32
|
end
|
33
33
|
|
34
34
|
spec.add_dependency "thread", "~> 0.1"
|
35
|
+
spec.add_dependency "opentracing", "~> 0.3"
|
35
36
|
end
|
data/lib/freddy.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'thread/pool'
|
3
3
|
require 'securerandom'
|
4
|
+
require 'opentracing'
|
4
5
|
|
5
6
|
Dir[File.dirname(__FILE__) + '/freddy/*.rb'].each(&method(:require))
|
6
7
|
|
@@ -25,11 +26,20 @@ class Freddy
|
|
25
26
|
# @example
|
26
27
|
# Freddy.build(Logger.new(STDOUT), user: 'thumper', pass: 'howdy')
|
27
28
|
def self.build(logger = Logger.new(STDOUT), max_concurrency: DEFAULT_MAX_CONCURRENCY, **config)
|
28
|
-
|
29
|
+
OpenTracing.global_tracer ||= OpenTracing::Tracer.new
|
29
30
|
|
31
|
+
connection = Adapters.determine.connect(config)
|
30
32
|
new(connection, logger, max_concurrency)
|
31
33
|
end
|
32
34
|
|
35
|
+
def self.trace
|
36
|
+
Thread.current[:freddy_trace]
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.trace=(trace)
|
40
|
+
Thread.current[:freddy_trace] = trace
|
41
|
+
end
|
42
|
+
|
33
43
|
def initialize(connection, logger, max_concurrency)
|
34
44
|
@connection = connection
|
35
45
|
@logger = logger
|
@@ -76,7 +86,6 @@ class Freddy
|
|
76
86
|
handler_adapter_factory = MessageHandlerAdapters::Factory.new(producer)
|
77
87
|
|
78
88
|
Consumers::RespondToConsumer.consume(
|
79
|
-
logger: @logger,
|
80
89
|
thread_pool: Thread.pool(@prefetch_buffer_size),
|
81
90
|
destination: destination,
|
82
91
|
channel: channel,
|
@@ -111,7 +120,6 @@ class Freddy
|
|
111
120
|
@logger.debug "Tapping into messages that match #{pattern}"
|
112
121
|
|
113
122
|
Consumers::TapIntoConsumer.consume(
|
114
|
-
logger: @logger,
|
115
123
|
thread_pool: Thread.pool(@prefetch_buffer_size),
|
116
124
|
pattern: pattern,
|
117
125
|
channel: @connection.create_channel(prefetch: @prefetch_buffer_size),
|
data/lib/freddy/consumers.rb
CHANGED
@@ -1,14 +1 @@
|
|
1
|
-
class Freddy
|
2
|
-
module Consumers
|
3
|
-
def self.log_receive_event(logger, queue_name, delivery)
|
4
|
-
logger.debug(
|
5
|
-
message: 'Received message',
|
6
|
-
queue: queue_name,
|
7
|
-
payload: delivery.payload,
|
8
|
-
correlation_id: delivery.correlation_id
|
9
|
-
)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
1
|
Dir[File.dirname(__FILE__) + '/consumers/*.rb'].each(&method(:require))
|
@@ -5,8 +5,7 @@ class Freddy
|
|
5
5
|
new(*attrs).consume(&block)
|
6
6
|
end
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@logger = logger
|
8
|
+
def initialize(thread_pool:, destination:, channel:, handler_adapter_factory:)
|
10
9
|
@consume_thread_pool = thread_pool
|
11
10
|
@destination = destination
|
12
11
|
@channel = channel
|
@@ -15,8 +14,6 @@ class Freddy
|
|
15
14
|
|
16
15
|
def consume(&block)
|
17
16
|
consumer = consume_from_destination do |delivery|
|
18
|
-
Consumers.log_receive_event(@logger, @destination, delivery)
|
19
|
-
|
20
17
|
adapter = @handler_adapter_factory.for(delivery)
|
21
18
|
|
22
19
|
msg_handler = MessageHandler.new(adapter, delivery)
|
@@ -37,9 +34,25 @@ class Freddy
|
|
37
34
|
def process_message(delivery, &block)
|
38
35
|
@consume_thread_pool.process do
|
39
36
|
begin
|
37
|
+
Freddy.trace = delivery.build_trace("freddy:respond:#{@destination}",
|
38
|
+
tags: {
|
39
|
+
'peer.address': "#{@destination}:#{delivery.payload[:type]}",
|
40
|
+
'component': 'freddy',
|
41
|
+
'span.kind': 'server' # RPC
|
42
|
+
}
|
43
|
+
)
|
44
|
+
Freddy.trace.log(
|
45
|
+
event: 'Received message through respond_to',
|
46
|
+
queue: @destination,
|
47
|
+
payload: delivery.payload,
|
48
|
+
correlation_id: delivery.correlation_id
|
49
|
+
)
|
50
|
+
|
40
51
|
block.call(delivery)
|
41
52
|
ensure
|
42
53
|
@channel.acknowledge(delivery.tag, false)
|
54
|
+
Freddy.trace.finish
|
55
|
+
Freddy.trace = nil
|
43
56
|
end
|
44
57
|
end
|
45
58
|
end
|
@@ -8,16 +8,9 @@ class Freddy
|
|
8
8
|
def consume(channel, queue, &block)
|
9
9
|
@logger.debug "Consuming messages on #{queue.name}"
|
10
10
|
queue.subscribe do |delivery|
|
11
|
-
|
11
|
+
block.call(delivery)
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def process_message(channel, queue, delivery, &block)
|
18
|
-
Consumers.log_receive_event(@logger, queue.name, delivery)
|
19
|
-
block.call(delivery)
|
20
|
-
end
|
21
14
|
end
|
22
15
|
end
|
23
16
|
end
|
@@ -5,8 +5,7 @@ class Freddy
|
|
5
5
|
new(*attrs).consume(&block)
|
6
6
|
end
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@logger = logger
|
8
|
+
def initialize(thread_pool:, pattern:, channel:, options:)
|
10
9
|
@consume_thread_pool = thread_pool
|
11
10
|
@pattern = pattern
|
12
11
|
@channel = channel
|
@@ -43,10 +42,25 @@ class Freddy
|
|
43
42
|
def process_message(queue, delivery, &block)
|
44
43
|
@consume_thread_pool.process do
|
45
44
|
begin
|
46
|
-
|
45
|
+
Freddy.trace = delivery.build_trace("freddy:observe:#{@pattern}",
|
46
|
+
tags: {
|
47
|
+
'message_bus.destination': @pattern,
|
48
|
+
'component': 'freddy',
|
49
|
+
'span.kind': 'consumer' # Message Bus
|
50
|
+
},
|
51
|
+
force_follows_from: true
|
52
|
+
)
|
53
|
+
Freddy.trace.log(
|
54
|
+
event: 'Received message through tap_into',
|
55
|
+
payload: delivery.payload,
|
56
|
+
correlation_id: delivery.correlation_id
|
57
|
+
)
|
58
|
+
|
47
59
|
block.call delivery.payload, delivery.routing_key
|
48
60
|
ensure
|
49
61
|
@channel.acknowledge(delivery.tag, false)
|
62
|
+
Freddy.trace.finish
|
63
|
+
Freddy.trace = nil
|
50
64
|
end
|
51
65
|
end
|
52
66
|
end
|
data/lib/freddy/delivery.rb
CHANGED
@@ -20,5 +20,26 @@ class Freddy
|
|
20
20
|
def reply_to
|
21
21
|
@metadata.reply_to
|
22
22
|
end
|
23
|
+
|
24
|
+
def build_trace(operation_name, tags: {}, force_follows_from: false)
|
25
|
+
carrier = TraceCarrier.new(@metadata)
|
26
|
+
parent =
|
27
|
+
if expecting_response? && !force_follows_from
|
28
|
+
OpenTracing.global_tracer.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creating a child span when the message sender is expecting a response.
|
34
|
+
# Otherwise creating a new trace because the OpenTracing client does not
|
35
|
+
# support FollowsFrom yet.
|
36
|
+
OpenTracing.start_span(operation_name, child_of: parent, tags: tags)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def expecting_response?
|
42
|
+
type == 'request'
|
43
|
+
end
|
23
44
|
end
|
24
45
|
end
|
data/lib/freddy/payload.rb
CHANGED
@@ -22,12 +22,15 @@ class Freddy
|
|
22
22
|
end
|
23
23
|
|
24
24
|
class OjAdapter
|
25
|
+
PARSE_OPTIONS = { symbol_keys: true }
|
26
|
+
DUMP_OPTIONS = { mode: :compat, time_format: :xmlschema, second_precision: 6 }
|
27
|
+
|
25
28
|
def self.parse(payload)
|
26
|
-
Oj.strict_load(payload,
|
29
|
+
Oj.strict_load(payload, PARSE_OPTIONS)
|
27
30
|
end
|
28
31
|
|
29
32
|
def self.dump(payload)
|
30
|
-
Oj.dump(payload,
|
33
|
+
Oj.dump(payload, DUMP_OPTIONS)
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
data/lib/freddy/producers.rb
CHANGED
@@ -9,10 +9,11 @@ class Freddy
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def produce(destination, payload, properties)
|
12
|
-
|
12
|
+
Freddy.trace.log event: 'Sending response', queue: destination, payload: payload
|
13
13
|
|
14
14
|
properties = properties.merge(
|
15
|
-
routing_key: destination,
|
15
|
+
routing_key: destination,
|
16
|
+
content_type: CONTENT_TYPE
|
16
17
|
)
|
17
18
|
|
18
19
|
@exchange.publish Payload.dump(payload), properties
|
@@ -10,15 +10,33 @@ class Freddy
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def produce(destination, payload, properties)
|
13
|
-
|
13
|
+
span = OpenTracing.start_span("freddy:notify:#{destination}",
|
14
|
+
child_of: Freddy.trace,
|
15
|
+
tags: {
|
16
|
+
'message_bus.destination': destination,
|
17
|
+
'component': 'freddy',
|
18
|
+
'span.kind': 'producer' # Message Bus
|
19
|
+
}
|
20
|
+
)
|
21
|
+
span.log event: 'Sending message', payload: payload
|
14
22
|
|
15
|
-
properties = properties.merge(
|
23
|
+
properties = properties.merge(
|
24
|
+
routing_key: destination,
|
25
|
+
content_type: CONTENT_TYPE
|
26
|
+
)
|
27
|
+
OpenTracing.global_tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, TraceCarrier.new(properties))
|
16
28
|
json_payload = Payload.dump(payload)
|
17
29
|
|
18
30
|
# Connection adapters handle thread safety for #publish themselves. No
|
19
31
|
# need to lock these.
|
20
32
|
@topic_exchange.publish json_payload, properties.dup
|
21
33
|
@exchange.publish json_payload, properties.dup
|
34
|
+
ensure
|
35
|
+
# We don't know how many listeners there are and we do not know when
|
36
|
+
# this message gets processed. Instead we close the span immediately.
|
37
|
+
# Listeners should use FollowsFrom to add trace information.
|
38
|
+
# https://github.com/opentracing/specification/blob/master/specification.md
|
39
|
+
span.finish
|
22
40
|
end
|
23
41
|
end
|
24
42
|
end
|
@@ -23,14 +23,24 @@ class Freddy
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def produce(destination, payload, timeout_in_seconds:, delete_on_timeout:, **properties)
|
26
|
+
span = OpenTracing.start_span("freddy:request:#{destination}",
|
27
|
+
child_of: Freddy.trace,
|
28
|
+
tags: {
|
29
|
+
'component': 'freddy',
|
30
|
+
'span.kind': 'client', # RPC
|
31
|
+
'payload.type': payload[:type]
|
32
|
+
}
|
33
|
+
)
|
34
|
+
|
26
35
|
correlation_id = SecureRandom.uuid
|
27
36
|
|
28
37
|
container = SyncResponseContainer.new(
|
29
|
-
on_timeout(correlation_id, destination, timeout_in_seconds)
|
38
|
+
on_timeout(correlation_id, destination, timeout_in_seconds, span)
|
30
39
|
)
|
31
40
|
|
32
41
|
@request_manager.store(correlation_id,
|
33
42
|
callback: container,
|
43
|
+
trace: span,
|
34
44
|
destination: destination
|
35
45
|
)
|
36
46
|
|
@@ -43,11 +53,11 @@ class Freddy
|
|
43
53
|
correlation_id: correlation_id, reply_to: @response_queue.name,
|
44
54
|
mandatory: true, type: 'request'
|
45
55
|
)
|
56
|
+
OpenTracing.global_tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, TraceCarrier.new(properties))
|
46
57
|
json_payload = Payload.dump(payload)
|
47
58
|
|
48
|
-
|
49
|
-
|
50
|
-
queue: destination,
|
59
|
+
span.log(
|
60
|
+
event: 'Publishing request',
|
51
61
|
payload: payload,
|
52
62
|
response_queue: @response_queue.name,
|
53
63
|
correlation_id: correlation_id
|
@@ -77,14 +87,17 @@ class Freddy
|
|
77
87
|
@logger.debug "Got response for request to #{request[:destination]} "\
|
78
88
|
"with correlation_id #{delivery.correlation_id}"
|
79
89
|
request[:callback].call(delivery.payload, delivery)
|
90
|
+
ensure
|
91
|
+
request[:trace].finish
|
80
92
|
end
|
81
93
|
|
82
|
-
def on_timeout(correlation_id, destination, timeout_in_seconds)
|
94
|
+
def on_timeout(correlation_id, destination, timeout_in_seconds, trace)
|
83
95
|
Proc.new do
|
84
96
|
@logger.warn "Request timed out waiting response from #{destination}"\
|
85
97
|
", correlation id #{correlation_id}"
|
86
98
|
|
87
99
|
@request_manager.delete(correlation_id)
|
100
|
+
trace.finish
|
88
101
|
end
|
89
102
|
end
|
90
103
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Freddy
|
2
|
+
# Carrier for rabbitmq following OpenTracing API
|
3
|
+
# See https://github.com/opentracing/opentracing-ruby/blob/master/lib/opentracing/carrier.rb
|
4
|
+
class TraceCarrier
|
5
|
+
def initialize(properties)
|
6
|
+
@properties = properties
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
@properties.headers && @properties.headers["x-trace-#{key}"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
@properties[:headers] ||= {}
|
15
|
+
@properties[:headers]["x-trace-#{key}"] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&block)
|
19
|
+
Hash[
|
20
|
+
(@properties.headers || {})
|
21
|
+
.select {|key, _| key =~ /^x-trace/}
|
22
|
+
.map {|key, value| [key.sub(/x-trace-/, ''), value]}
|
23
|
+
].each(&block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Freddy::TraceCarrier do
|
4
|
+
subject(:carrier) { described_class.new(properties) }
|
5
|
+
|
6
|
+
context 'when adding trace information' do
|
7
|
+
let(:properties) { {x: 'y'} }
|
8
|
+
let(:key_name) { 'some-key' }
|
9
|
+
let(:key_value) { 'some-key' }
|
10
|
+
|
11
|
+
it 'adds a header with x-trace- prefix' do
|
12
|
+
carrier[key_name] = key_value
|
13
|
+
expect(properties[:headers]["x-trace-#{key_name}"]).to eq(key_value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when extracting trace information' do
|
18
|
+
let(:key_name) { 'some-key' }
|
19
|
+
let(:serialized_key_name) { "x-trace-#{key_name}" }
|
20
|
+
let(:key_value) { 'some-key' }
|
21
|
+
|
22
|
+
let(:properties) do
|
23
|
+
double(headers: {serialized_key_name => key_value})
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'extracts a header with x-trace- prefix' do
|
27
|
+
expect(carrier[key_name]).to eq(key_value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#each' do
|
32
|
+
context 'when headers are present' do
|
33
|
+
let(:properties) do
|
34
|
+
double(
|
35
|
+
headers: {
|
36
|
+
"x-trace-key1" => "value1",
|
37
|
+
"x-trace-key2" => "value2",
|
38
|
+
"other-key" => "value3"
|
39
|
+
}
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'iterates over keys starting with x-trace- prefix' do
|
44
|
+
expect(carrier.each.count).to eq(2)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when no headers' do
|
49
|
+
let(:properties) { double(headers: nil) }
|
50
|
+
|
51
|
+
it 'iterates over an empty list' do
|
52
|
+
expect(carrier.each.count).to eq(0)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,44 +1,101 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'logasm/tracer'
|
2
3
|
|
3
|
-
describe 'Logging' do
|
4
|
-
let(:
|
5
|
-
let(:
|
4
|
+
describe 'Logging with Logasm::Tracer' do
|
5
|
+
let(:logger) { ArrayLogger.new }
|
6
|
+
let(:tracer) { Logasm::Tracer.new(logger) }
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
before { OpenTracing.global_tracer = tracer }
|
9
|
+
after { OpenTracing.global_tracer = nil }
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
context 'when receiving an untraced request' do
|
12
|
+
let(:freddy) { Freddy.build(spy, config) }
|
13
|
+
let(:destination) { random_destination }
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
before do
|
16
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
17
|
+
sleep 0.1 # emulate some processing
|
18
|
+
msg_handler.success({})
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
after { freddy.close }
|
23
|
+
|
24
|
+
it 'generates a trace' do
|
25
|
+
freddy.deliver_with_response(destination, {})
|
26
|
+
|
27
|
+
expect(logger.calls.map(&:first)).to eq([
|
28
|
+
# Initiator
|
29
|
+
"Span [freddy:request:#{destination}] started",
|
30
|
+
"Span [freddy:request:#{destination}] Publishing request",
|
31
|
+
|
32
|
+
# Service
|
33
|
+
"Span [freddy:respond:#{destination}] started",
|
34
|
+
"Span [freddy:respond:#{destination}] Received message through respond_to",
|
35
|
+
"Span [freddy:respond:#{destination}] Sending response",
|
36
|
+
"Span [freddy:respond:#{destination}] finished",
|
23
37
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
queue: destination,
|
29
|
-
payload: payload,
|
30
|
-
correlation_id: anything
|
31
|
-
)
|
38
|
+
# Initiator
|
39
|
+
"Span [freddy:request:#{destination}] finished"
|
40
|
+
])
|
41
|
+
end
|
32
42
|
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
context 'when receiving a traced request' do
|
45
|
+
let(:freddy) { Freddy.build(spy, config) }
|
46
|
+
let(:freddy2) { Freddy.build(spy, config) }
|
47
|
+
|
48
|
+
let(:destination) { random_destination }
|
49
|
+
let(:destination2) { random_destination }
|
50
|
+
|
51
|
+
before do
|
52
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
53
|
+
sleep 0.1 # emulate some processing
|
54
|
+
msg_handler.success({
|
55
|
+
trace_initiator: {},
|
56
|
+
current_receiver: freddy.deliver_with_response(destination2, {})
|
57
|
+
})
|
58
|
+
end
|
59
|
+
|
60
|
+
freddy2.respond_to(destination2) do |payload, msg_handler|
|
61
|
+
sleep 0.1 # emulate some processing
|
62
|
+
msg_handler.success({})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
after do
|
67
|
+
freddy.close
|
68
|
+
freddy2.close
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'generates a trace' do
|
72
|
+
freddy.deliver_with_response(destination, {})
|
73
|
+
|
74
|
+
expect(logger.calls.map(&:first)).to eq([
|
75
|
+
# Initiator
|
76
|
+
"Span [freddy:request:#{destination}] started",
|
77
|
+
"Span [freddy:request:#{destination}] Publishing request",
|
78
|
+
|
79
|
+
# Service 1
|
80
|
+
"Span [freddy:respond:#{destination}] started",
|
81
|
+
"Span [freddy:respond:#{destination}] Received message through respond_to",
|
82
|
+
"Span [freddy:request:#{destination2}] started",
|
83
|
+
"Span [freddy:request:#{destination2}] Publishing request",
|
84
|
+
|
85
|
+
# Service 2
|
86
|
+
"Span [freddy:respond:#{destination2}] started",
|
87
|
+
"Span [freddy:respond:#{destination2}] Received message through respond_to",
|
88
|
+
"Span [freddy:respond:#{destination2}] Sending response",
|
89
|
+
"Span [freddy:respond:#{destination2}] finished",
|
90
|
+
|
91
|
+
# Service 1
|
92
|
+
"Span [freddy:request:#{destination2}] finished",
|
93
|
+
"Span [freddy:respond:#{destination}] Sending response",
|
94
|
+
"Span [freddy:respond:#{destination}] finished",
|
95
|
+
|
96
|
+
# Initiator
|
97
|
+
"Span [freddy:request:#{destination}] finished"
|
98
|
+
])
|
99
|
+
end
|
43
100
|
end
|
44
101
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logasm/tracer'
|
3
|
+
|
4
|
+
describe 'Tracing' do
|
5
|
+
let(:tracer) { Logasm::Tracer.new(logger) }
|
6
|
+
let(:logger) { spy }
|
7
|
+
|
8
|
+
before { OpenTracing.global_tracer = tracer }
|
9
|
+
after { OpenTracing.global_tracer = nil }
|
10
|
+
|
11
|
+
context 'when receiving a traced request' do
|
12
|
+
let(:freddy) { Freddy.build(logger, config) }
|
13
|
+
let(:freddy2) { Freddy.build(logger, config) }
|
14
|
+
|
15
|
+
let(:destination) { random_destination }
|
16
|
+
let(:destination2) { random_destination }
|
17
|
+
|
18
|
+
before do
|
19
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
20
|
+
msg_handler.success({
|
21
|
+
trace_initiator: {
|
22
|
+
trace_id: Freddy.trace.context.trace_id,
|
23
|
+
parent_id: Freddy.trace.context.parent_id,
|
24
|
+
span_id: Freddy.trace.context.span_id
|
25
|
+
},
|
26
|
+
current_receiver: freddy.deliver_with_response(destination2, {})
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
freddy2.respond_to(destination2) do |payload, msg_handler|
|
31
|
+
msg_handler.success({
|
32
|
+
trace_id: Freddy.trace.context.trace_id,
|
33
|
+
parent_id: Freddy.trace.context.parent_id,
|
34
|
+
span_id: Freddy.trace.context.span_id
|
35
|
+
})
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
after do
|
40
|
+
freddy.close
|
41
|
+
freddy2.close
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'has trace_id from the trace initiator' do
|
45
|
+
response = freddy.deliver_with_response(destination, {})
|
46
|
+
trace_initiator = response.fetch(:trace_initiator)
|
47
|
+
current_receiver = response.fetch(:current_receiver)
|
48
|
+
expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'has parent_id' do
|
52
|
+
response = freddy.deliver_with_response(destination, {})
|
53
|
+
current_receiver = response.fetch(:current_receiver)
|
54
|
+
expect(current_receiver.fetch(:parent_id)).to_not be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'has generated span_id' do
|
58
|
+
response = freddy.deliver_with_response(destination, {})
|
59
|
+
trace_initiator = response.fetch(:trace_initiator)
|
60
|
+
current_receiver = response.fetch(:current_receiver)
|
61
|
+
expect(current_receiver.fetch(:span_id)).to_not be_nil
|
62
|
+
expect(current_receiver.fetch(:span_id)).to_not eq(trace_initiator.fetch(:span_id))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when receiving a nested traced request' do
|
67
|
+
let(:freddy) { Freddy.build(logger, config) }
|
68
|
+
let(:freddy2) { Freddy.build(logger, config) }
|
69
|
+
let(:freddy3) { Freddy.build(logger, config) }
|
70
|
+
|
71
|
+
let(:destination) { random_destination }
|
72
|
+
let(:destination2) { random_destination }
|
73
|
+
let(:destination3) { random_destination }
|
74
|
+
|
75
|
+
before do
|
76
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
77
|
+
msg_handler.success({
|
78
|
+
trace_initiator: {
|
79
|
+
trace_id: Freddy.trace.context.trace_id,
|
80
|
+
parent_id: Freddy.trace.context.parent_id,
|
81
|
+
span_id: Freddy.trace.context.span_id
|
82
|
+
}
|
83
|
+
}.merge(freddy.deliver_with_response(destination2, {})))
|
84
|
+
end
|
85
|
+
|
86
|
+
freddy2.respond_to(destination2) do |payload, msg_handler|
|
87
|
+
msg_handler.success({
|
88
|
+
previous_receiver: {
|
89
|
+
trace_id: Freddy.trace.context.trace_id,
|
90
|
+
parent_id: Freddy.trace.context.parent_id,
|
91
|
+
span_id: Freddy.trace.context.span_id
|
92
|
+
},
|
93
|
+
current_receiver: freddy2.deliver_with_response(destination3, {})
|
94
|
+
})
|
95
|
+
end
|
96
|
+
|
97
|
+
freddy3.respond_to(destination3) do |payload, msg_handler|
|
98
|
+
msg_handler.success({
|
99
|
+
trace_id: Freddy.trace.context.trace_id,
|
100
|
+
parent_id: Freddy.trace.context.parent_id,
|
101
|
+
span_id: Freddy.trace.context.span_id
|
102
|
+
})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
after do
|
107
|
+
freddy.close
|
108
|
+
freddy2.close
|
109
|
+
freddy3.close
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'has trace_id from the trace initiator' do
|
113
|
+
response = freddy.deliver_with_response(destination, {})
|
114
|
+
trace_initiator = response.fetch(:trace_initiator)
|
115
|
+
current_receiver = response.fetch(:current_receiver)
|
116
|
+
expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'has parent_id' do
|
120
|
+
response = freddy.deliver_with_response(destination, {})
|
121
|
+
current_receiver = response.fetch(:current_receiver)
|
122
|
+
expect(current_receiver.fetch(:parent_id)).to_not be_nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'has generated span_id' do
|
126
|
+
response = freddy.deliver_with_response(destination, {})
|
127
|
+
previous_receiver = response.fetch(:previous_receiver)
|
128
|
+
current_receiver = response.fetch(:current_receiver)
|
129
|
+
expect(current_receiver.fetch(:span_id)).to_not be_nil
|
130
|
+
expect(current_receiver.fetch(:span_id)).to_not eq(previous_receiver.fetch(:span_id))
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -22,6 +22,10 @@ RSpec.configure do |config|
|
|
22
22
|
config.run_all_when_everything_filtered = true
|
23
23
|
config.filter_run :focus
|
24
24
|
config.order = 'random'
|
25
|
+
|
26
|
+
config.before do
|
27
|
+
OpenTracing.global_tracer ||= OpenTracing::Tracer.new
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
def random_destination
|
@@ -61,3 +65,15 @@ def spawn_echo_responder(freddy, queue_name)
|
|
61
65
|
msg_handler.success(payload)
|
62
66
|
end
|
63
67
|
end
|
68
|
+
|
69
|
+
class ArrayLogger
|
70
|
+
attr_accessor :calls
|
71
|
+
|
72
|
+
def initialize
|
73
|
+
@calls = []
|
74
|
+
end
|
75
|
+
|
76
|
+
def info(*args)
|
77
|
+
@calls << args
|
78
|
+
end
|
79
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freddy-jruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Salemove TechMovers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.3'
|
89
|
+
name: opentracing
|
90
|
+
prerelease: false
|
91
|
+
type: :runtime
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.3'
|
83
97
|
description: Messaging API
|
84
98
|
email:
|
85
99
|
- techmovers@salemove.com
|
@@ -120,6 +134,7 @@ files:
|
|
120
134
|
- lib/freddy/responder_handler.rb
|
121
135
|
- lib/freddy/sync_response_container.rb
|
122
136
|
- lib/freddy/timeout_error.rb
|
137
|
+
- lib/freddy/trace_carrier.rb
|
123
138
|
- spec/freddy/consumers/respond_to_consumer_spec.rb
|
124
139
|
- spec/freddy/error_response_spec.rb
|
125
140
|
- spec/freddy/freddy_spec.rb
|
@@ -127,10 +142,12 @@ files:
|
|
127
142
|
- spec/freddy/payload_spec.rb
|
128
143
|
- spec/freddy/responder_handler_spec.rb
|
129
144
|
- spec/freddy/sync_response_container_spec.rb
|
145
|
+
- spec/freddy/trace_carrier_spec.rb
|
130
146
|
- spec/integration/concurrency_spec.rb
|
131
147
|
- spec/integration/logging_spec.rb
|
132
148
|
- spec/integration/reply_spec.rb
|
133
149
|
- spec/integration/tap_into_with_group_spec.rb
|
150
|
+
- spec/integration/tracing_spec.rb
|
134
151
|
- spec/spec_helper.rb
|
135
152
|
homepage:
|
136
153
|
licenses:
|
@@ -152,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
169
|
version: '0'
|
153
170
|
requirements: []
|
154
171
|
rubyforge_project:
|
155
|
-
rubygems_version: 2.
|
172
|
+
rubygems_version: 2.4.8
|
156
173
|
signing_key:
|
157
174
|
specification_version: 4
|
158
175
|
summary: API for inter-application messaging supporting acknowledgements and request-response
|
@@ -164,8 +181,10 @@ test_files:
|
|
164
181
|
- spec/freddy/payload_spec.rb
|
165
182
|
- spec/freddy/responder_handler_spec.rb
|
166
183
|
- spec/freddy/sync_response_container_spec.rb
|
184
|
+
- spec/freddy/trace_carrier_spec.rb
|
167
185
|
- spec/integration/concurrency_spec.rb
|
168
186
|
- spec/integration/logging_spec.rb
|
169
187
|
- spec/integration/reply_spec.rb
|
170
188
|
- spec/integration/tap_into_with_group_spec.rb
|
189
|
+
- spec/integration/tracing_spec.rb
|
171
190
|
- spec/spec_helper.rb
|