freddy 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Gemfile +1 -0
- data/README.md +18 -12
- data/freddy.gemspec +2 -1
- data/lib/freddy.rb +4 -4
- data/lib/freddy/consumers.rb +0 -14
- data/lib/freddy/consumers/respond_to_consumer.rb +10 -5
- data/lib/freddy/consumers/response_consumer.rb +1 -8
- data/lib/freddy/consumers/tap_into_consumer.rb +12 -4
- data/lib/freddy/delivery.rb +18 -11
- data/lib/freddy/producers.rb +0 -8
- data/lib/freddy/producers/reply_producer.rb +2 -6
- data/lib/freddy/producers/send_and_forget_producer.rb +13 -6
- data/lib/freddy/producers/send_and_wait_response_producer.rb +16 -12
- data/lib/freddy/trace_carrier.rb +30 -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 +86 -34
- data/spec/integration/tracing_spec.rb +29 -24
- data/spec/spec_helper.rb +16 -0
- metadata +19 -5
- data/lib/freddy/traces.rb +0 -40
- data/spec/freddy/traces/trace_id_spec.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 780979fe0cb5c2d61b74d2356643695dc3380e1d
|
4
|
+
data.tar.gz: 7778664a870a3b04ea9b0d697445ec2843a4a98a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd21e0ca6b7140f2096db0062f8793941928bab1f8f23b0c6c6f0e1a5641c2a244a7d624a543fae642d38d1af9f0b4cd6a736d77195d355699ef582135a68c39
|
7
|
+
data.tar.gz: ce44daf3488a6b7805d179f0176fb0b71a2080836526660a5482665a854c6e0bc214ac7a09e7c7fa4b5426a52dd65a2da253c4e3996c69420a275d91a2de56fe
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -137,33 +137,39 @@ responder_handler.shutdown
|
|
137
137
|
|
138
138
|
## Request Tracing
|
139
139
|
|
140
|
-
Freddy
|
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:
|
141
150
|
|
142
151
|
```ruby
|
143
152
|
freddy1 = Freddy.build
|
144
153
|
freddy1.respond_do('service1') do |payload, msg_handler|
|
145
|
-
puts "Trace id: #{Freddy.trace.
|
146
|
-
puts "Span id: #{Freddy.trace.span_id}"
|
154
|
+
puts "Trace id: #{Freddy.trace.context.trace_id}"
|
155
|
+
puts "Span id: #{Freddy.trace.context.span_id}"
|
147
156
|
|
148
157
|
freddy1.deliver('service2', {})
|
149
158
|
end
|
150
159
|
|
151
160
|
freddy2 = Freddy.build
|
152
161
|
freddy2.tap_into('service2') do |payload|
|
153
|
-
puts "Has same trace_id as the request in service1: #{Freddy.trace.
|
154
|
-
puts "Has service1 request span id as parent id: #{Freddy.trace.parent_id}"
|
155
|
-
puts "Has its own generated span id: #{Freddy.trace.span_id}"
|
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}"
|
156
165
|
end
|
157
166
|
```
|
158
167
|
|
159
|
-
In case you already have
|
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:
|
160
169
|
```ruby
|
161
|
-
Freddy.trace =
|
162
|
-
id: 'YourTraceId',
|
163
|
-
parent_id: 'YourParentId', # Use nil in case this is the root of the trace
|
164
|
-
span_id: 'YourSpanId'
|
165
|
-
})
|
170
|
+
Freddy.trace = trace_span
|
166
171
|
```
|
172
|
+
The `trace_span` must implement OpenTracing::Span interface.
|
167
173
|
|
168
174
|
## Notes about concurrency
|
169
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.2.0'
|
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,13 +26,14 @@ 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
|
|
33
35
|
def self.trace
|
34
|
-
Thread.current[:freddy_trace]
|
36
|
+
Thread.current[:freddy_trace]
|
35
37
|
end
|
36
38
|
|
37
39
|
def self.trace=(trace)
|
@@ -84,7 +86,6 @@ class Freddy
|
|
84
86
|
handler_adapter_factory = MessageHandlerAdapters::Factory.new(producer)
|
85
87
|
|
86
88
|
Consumers::RespondToConsumer.consume(
|
87
|
-
logger: @logger,
|
88
89
|
thread_pool: Thread.pool(@prefetch_buffer_size),
|
89
90
|
destination: destination,
|
90
91
|
channel: channel,
|
@@ -119,7 +120,6 @@ class Freddy
|
|
119
120
|
@logger.debug "Tapping into messages that match #{pattern}"
|
120
121
|
|
121
122
|
Consumers::TapIntoConsumer.consume(
|
122
|
-
logger: @logger,
|
123
123
|
thread_pool: Thread.pool(@prefetch_buffer_size),
|
124
124
|
pattern: pattern,
|
125
125
|
channel: @connection.create_channel(prefetch: @prefetch_buffer_size),
|
data/lib/freddy/consumers.rb
CHANGED
@@ -1,15 +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
|
-
trace: Freddy.trace.to_h
|
10
|
-
)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
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,10 +34,18 @@ class Freddy
|
|
37
34
|
def process_message(delivery, &block)
|
38
35
|
@consume_thread_pool.process do
|
39
36
|
begin
|
40
|
-
Freddy.trace = delivery.
|
37
|
+
Freddy.trace = delivery.build_trace("freddy:respond:#{@destination}")
|
38
|
+
Freddy.trace.log(
|
39
|
+
event: 'Received message through respond_to',
|
40
|
+
queue: @destination,
|
41
|
+
payload: delivery.payload,
|
42
|
+
correlation_id: delivery.correlation_id
|
43
|
+
)
|
44
|
+
|
41
45
|
block.call(delivery)
|
42
46
|
ensure
|
43
47
|
@channel.acknowledge(delivery.tag, false)
|
48
|
+
Freddy.trace.finish
|
44
49
|
Freddy.trace = nil
|
45
50
|
end
|
46
51
|
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,11 +42,20 @@ class Freddy
|
|
43
42
|
def process_message(queue, delivery, &block)
|
44
43
|
@consume_thread_pool.process do
|
45
44
|
begin
|
46
|
-
|
47
|
-
|
45
|
+
Freddy.trace = delivery.build_trace("freddy:observe:#{@pattern}",
|
46
|
+
tags: {queue: @pattern},
|
47
|
+
force_follows_from: true
|
48
|
+
)
|
49
|
+
Freddy.trace.log(
|
50
|
+
event: 'Received message through tap_into',
|
51
|
+
payload: delivery.payload,
|
52
|
+
correlation_id: delivery.correlation_id
|
53
|
+
)
|
54
|
+
|
48
55
|
block.call delivery.payload, delivery.routing_key
|
49
56
|
ensure
|
50
57
|
@channel.acknowledge(delivery.tag, false)
|
58
|
+
Freddy.trace.finish
|
51
59
|
Freddy.trace = nil
|
52
60
|
end
|
53
61
|
end
|
data/lib/freddy/delivery.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
class Freddy
|
2
2
|
class Delivery
|
3
|
-
attr_reader :routing_key, :payload, :tag
|
3
|
+
attr_reader :routing_key, :payload, :tag
|
4
4
|
|
5
5
|
def initialize(payload, metadata, routing_key, tag)
|
6
6
|
@payload = payload
|
7
7
|
@metadata = metadata
|
8
8
|
@routing_key = routing_key
|
9
9
|
@tag = tag
|
10
|
-
@trace = build_trace(metadata.headers || {})
|
11
10
|
end
|
12
11
|
|
13
12
|
def correlation_id
|
@@ -22,17 +21,25 @@ class Freddy
|
|
22
21
|
@metadata.reply_to
|
23
22
|
end
|
24
23
|
|
24
|
+
def build_trace(operation_name, tags: {}, force_follows_from: false)
|
25
|
+
carrier = TraceCarrier.new(@metadata)
|
26
|
+
parent =
|
27
|
+
if carrier.has_required_fields? && 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
|
+
|
25
39
|
private
|
26
40
|
|
27
|
-
def
|
28
|
-
|
29
|
-
Traces::Trace.build_from_existing_trace(
|
30
|
-
id: headers['x-trace-id'],
|
31
|
-
parent_id: headers['x-span-id']
|
32
|
-
)
|
33
|
-
else
|
34
|
-
Traces::Trace.build
|
35
|
-
end
|
41
|
+
def expecting_response?
|
42
|
+
type == 'request'
|
36
43
|
end
|
37
44
|
end
|
38
45
|
end
|
data/lib/freddy/producers.rb
CHANGED
@@ -1,9 +1 @@
|
|
1
|
-
class Freddy
|
2
|
-
module Producers
|
3
|
-
def self.log_send_event(logger, payload, destination)
|
4
|
-
logger.debug message: 'Sending message', queue: destination, payload: payload, trace: Freddy.trace.to_h
|
5
|
-
end
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
1
|
Dir[File.dirname(__FILE__) + '/producers/*.rb'].each(&method(:require))
|
@@ -9,15 +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
15
|
routing_key: destination,
|
16
|
-
content_type: CONTENT_TYPE
|
17
|
-
headers: {
|
18
|
-
'x-trace-id' => Freddy.trace.id,
|
19
|
-
'x-span-id' => Freddy.trace.span_id
|
20
|
-
}
|
16
|
+
content_type: CONTENT_TYPE
|
21
17
|
)
|
22
18
|
|
23
19
|
@exchange.publish Payload.dump(payload), properties
|
@@ -10,22 +10,29 @@ 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: {queue: destination}
|
16
|
+
)
|
17
|
+
span.log event: 'Sending message', payload: payload
|
14
18
|
|
15
19
|
properties = properties.merge(
|
16
20
|
routing_key: destination,
|
17
|
-
content_type: CONTENT_TYPE
|
18
|
-
headers: {
|
19
|
-
'x-trace-id' => Freddy.trace.id,
|
20
|
-
'x-span-id' => Freddy.trace.span_id
|
21
|
-
}
|
21
|
+
content_type: CONTENT_TYPE
|
22
22
|
)
|
23
|
+
OpenTracing.global_tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, TraceCarrier.new(properties))
|
23
24
|
json_payload = Payload.dump(payload)
|
24
25
|
|
25
26
|
# Connection adapters handle thread safety for #publish themselves. No
|
26
27
|
# need to lock these.
|
27
28
|
@topic_exchange.publish json_payload, properties.dup
|
28
29
|
@exchange.publish json_payload, properties.dup
|
30
|
+
ensure
|
31
|
+
# We don't know how many listeners there are and we do not know when
|
32
|
+
# this message gets processed. Instead we close the span immediately.
|
33
|
+
# Listeners should use FollowsFrom to add trace information.
|
34
|
+
# https://github.com/opentracing/specification/blob/master/specification.md
|
35
|
+
span.finish
|
29
36
|
end
|
30
37
|
end
|
31
38
|
end
|
@@ -23,14 +23,20 @@ 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: {queue: destination}
|
29
|
+
)
|
30
|
+
|
26
31
|
correlation_id = SecureRandom.uuid
|
27
32
|
|
28
33
|
container = SyncResponseContainer.new(
|
29
|
-
on_timeout(correlation_id, destination, timeout_in_seconds)
|
34
|
+
on_timeout(correlation_id, destination, timeout_in_seconds, span)
|
30
35
|
)
|
31
36
|
|
32
37
|
@request_manager.store(correlation_id,
|
33
38
|
callback: container,
|
39
|
+
trace: span,
|
34
40
|
destination: destination
|
35
41
|
)
|
36
42
|
|
@@ -41,21 +47,16 @@ class Freddy
|
|
41
47
|
properties = properties.merge(
|
42
48
|
routing_key: destination, content_type: CONTENT_TYPE,
|
43
49
|
correlation_id: correlation_id, reply_to: @response_queue.name,
|
44
|
-
mandatory: true, type: 'request'
|
45
|
-
headers: {
|
46
|
-
'x-trace-id' => Freddy.trace.id,
|
47
|
-
'x-span-id' => Freddy.trace.span_id
|
48
|
-
}
|
50
|
+
mandatory: true, type: 'request'
|
49
51
|
)
|
52
|
+
OpenTracing.global_tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, TraceCarrier.new(properties))
|
50
53
|
json_payload = Payload.dump(payload)
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
queue: destination,
|
55
|
+
span.log(
|
56
|
+
event: 'Publishing request',
|
55
57
|
payload: payload,
|
56
58
|
response_queue: @response_queue.name,
|
57
|
-
correlation_id: correlation_id
|
58
|
-
trace: Freddy.trace.to_h
|
59
|
+
correlation_id: correlation_id
|
59
60
|
)
|
60
61
|
|
61
62
|
# Connection adapters handle thread safety for #publish themselves. No
|
@@ -82,14 +83,17 @@ class Freddy
|
|
82
83
|
@logger.debug "Got response for request to #{request[:destination]} "\
|
83
84
|
"with correlation_id #{delivery.correlation_id}"
|
84
85
|
request[:callback].call(delivery.payload, delivery)
|
86
|
+
ensure
|
87
|
+
request[:trace].finish
|
85
88
|
end
|
86
89
|
|
87
|
-
def on_timeout(correlation_id, destination, timeout_in_seconds)
|
90
|
+
def on_timeout(correlation_id, destination, timeout_in_seconds, trace)
|
88
91
|
Proc.new do
|
89
92
|
@logger.warn "Request timed out waiting response from #{destination}"\
|
90
93
|
", correlation id #{correlation_id}"
|
91
94
|
|
92
95
|
@request_manager.delete(correlation_id)
|
96
|
+
trace.finish
|
93
97
|
end
|
94
98
|
end
|
95
99
|
end
|
@@ -0,0 +1,30 @@
|
|
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
|
+
|
26
|
+
def has_required_fields?
|
27
|
+
self['trace-id'] && self['parent-id'] && self['span-id']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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,46 +1,98 @@
|
|
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
|
-
msg_handler.success
|
15
|
+
before do
|
16
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
17
|
+
msg_handler.success({})
|
18
|
+
end
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
after { freddy.close }
|
22
|
+
|
23
|
+
it 'generates a trace' do
|
24
|
+
freddy.deliver_with_response(destination, {})
|
25
|
+
|
26
|
+
expect(logger.calls.map(&:first)).to eq([
|
27
|
+
# Initiator
|
28
|
+
"Span [freddy:request:#{destination}] started",
|
29
|
+
"Span [freddy:request:#{destination}] Publishing request",
|
30
|
+
|
31
|
+
# Service
|
32
|
+
"Span [freddy:respond:#{destination}] started",
|
33
|
+
"Span [freddy:respond:#{destination}] Received message through respond_to",
|
34
|
+
"Span [freddy:respond:#{destination}] Sending response",
|
35
|
+
"Span [freddy:respond:#{destination}] finished",
|
23
36
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
queue: destination,
|
29
|
-
payload: payload,
|
30
|
-
correlation_id: anything,
|
31
|
-
trace: anything
|
32
|
-
)
|
37
|
+
# Initiator
|
38
|
+
"Span [freddy:request:#{destination}] finished"
|
39
|
+
])
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
context 'when receiving a traced request' do
|
44
|
+
let(:freddy) { Freddy.build(spy, config) }
|
45
|
+
let(:freddy2) { Freddy.build(spy, config) }
|
46
|
+
|
47
|
+
let(:destination) { random_destination }
|
48
|
+
let(:destination2) { random_destination }
|
49
|
+
|
50
|
+
before do
|
51
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
52
|
+
msg_handler.success({
|
53
|
+
trace_initiator: {},
|
54
|
+
current_receiver: freddy.deliver_with_response(destination2, {})
|
55
|
+
})
|
56
|
+
end
|
57
|
+
|
58
|
+
freddy2.respond_to(destination2) do |payload, msg_handler|
|
59
|
+
msg_handler.success({})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
after do
|
64
|
+
freddy.close
|
65
|
+
freddy2.close
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'generates a trace' do
|
69
|
+
freddy.deliver_with_response(destination, {})
|
70
|
+
|
71
|
+
expect(logger.calls.map(&:first)).to eq([
|
72
|
+
# Initiator
|
73
|
+
"Span [freddy:request:#{destination}] started",
|
74
|
+
"Span [freddy:request:#{destination}] Publishing request",
|
75
|
+
|
76
|
+
# Service 1
|
77
|
+
"Span [freddy:respond:#{destination}] started",
|
78
|
+
"Span [freddy:respond:#{destination}] Received message through respond_to",
|
79
|
+
"Span [freddy:request:#{destination2}] started",
|
80
|
+
"Span [freddy:request:#{destination2}] Publishing request",
|
81
|
+
|
82
|
+
# Service 2
|
83
|
+
"Span [freddy:respond:#{destination2}] started",
|
84
|
+
"Span [freddy:respond:#{destination2}] Received message through respond_to",
|
85
|
+
"Span [freddy:respond:#{destination2}] Sending response",
|
86
|
+
"Span [freddy:respond:#{destination2}] finished",
|
87
|
+
|
88
|
+
# Service 1
|
89
|
+
"Span [freddy:request:#{destination2}] finished",
|
90
|
+
"Span [freddy:respond:#{destination}] Sending response",
|
91
|
+
"Span [freddy:respond:#{destination}] finished",
|
92
|
+
|
93
|
+
# Initiator
|
94
|
+
"Span [freddy:request:#{destination}] finished"
|
95
|
+
])
|
96
|
+
end
|
45
97
|
end
|
46
98
|
end
|
@@ -1,6 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'logasm/tracer'
|
2
3
|
|
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
|
+
|
4
11
|
context 'when receiving an untraced request' do
|
5
12
|
let(:freddy) { Freddy.build(logger, config) }
|
6
13
|
let(:destination) { random_destination }
|
@@ -8,9 +15,9 @@ describe 'Tracing' do
|
|
8
15
|
before do
|
9
16
|
freddy.respond_to(destination) do |payload, msg_handler|
|
10
17
|
msg_handler.success({
|
11
|
-
trace_id: Freddy.trace.
|
12
|
-
parent_id: Freddy.trace.parent_id,
|
13
|
-
span_id: Freddy.trace.span_id
|
18
|
+
trace_id: Freddy.trace.context.trace_id,
|
19
|
+
parent_id: Freddy.trace.context.parent_id,
|
20
|
+
span_id: Freddy.trace.context.span_id
|
14
21
|
})
|
15
22
|
end
|
16
23
|
end
|
@@ -44,9 +51,9 @@ describe 'Tracing' do
|
|
44
51
|
freddy.respond_to(destination) do |payload, msg_handler|
|
45
52
|
msg_handler.success({
|
46
53
|
trace_initiator: {
|
47
|
-
trace_id: Freddy.trace.
|
48
|
-
parent_id: Freddy.trace.parent_id,
|
49
|
-
span_id: Freddy.trace.span_id
|
54
|
+
trace_id: Freddy.trace.context.trace_id,
|
55
|
+
parent_id: Freddy.trace.context.parent_id,
|
56
|
+
span_id: Freddy.trace.context.span_id
|
50
57
|
},
|
51
58
|
current_receiver: freddy.deliver_with_response(destination2, {})
|
52
59
|
})
|
@@ -54,9 +61,9 @@ describe 'Tracing' do
|
|
54
61
|
|
55
62
|
freddy2.respond_to(destination2) do |payload, msg_handler|
|
56
63
|
msg_handler.success({
|
57
|
-
trace_id: Freddy.trace.
|
58
|
-
parent_id: Freddy.trace.parent_id,
|
59
|
-
span_id: Freddy.trace.span_id
|
64
|
+
trace_id: Freddy.trace.context.trace_id,
|
65
|
+
parent_id: Freddy.trace.context.parent_id,
|
66
|
+
span_id: Freddy.trace.context.span_id
|
60
67
|
})
|
61
68
|
end
|
62
69
|
end
|
@@ -73,11 +80,10 @@ describe 'Tracing' do
|
|
73
80
|
expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
|
74
81
|
end
|
75
82
|
|
76
|
-
it 'has
|
83
|
+
it 'has parent_id' do
|
77
84
|
response = freddy.deliver_with_response(destination, {})
|
78
|
-
trace_initiator = response.fetch(:trace_initiator)
|
79
85
|
current_receiver = response.fetch(:current_receiver)
|
80
|
-
expect(
|
86
|
+
expect(current_receiver.fetch(:parent_id)).to_not be_nil
|
81
87
|
end
|
82
88
|
|
83
89
|
it 'has generated span_id' do
|
@@ -102,9 +108,9 @@ describe 'Tracing' do
|
|
102
108
|
freddy.respond_to(destination) do |payload, msg_handler|
|
103
109
|
msg_handler.success({
|
104
110
|
trace_initiator: {
|
105
|
-
trace_id: Freddy.trace.
|
106
|
-
parent_id: Freddy.trace.parent_id,
|
107
|
-
span_id: Freddy.trace.span_id
|
111
|
+
trace_id: Freddy.trace.context.trace_id,
|
112
|
+
parent_id: Freddy.trace.context.parent_id,
|
113
|
+
span_id: Freddy.trace.context.span_id
|
108
114
|
}
|
109
115
|
}.merge(freddy.deliver_with_response(destination2, {})))
|
110
116
|
end
|
@@ -112,9 +118,9 @@ describe 'Tracing' do
|
|
112
118
|
freddy2.respond_to(destination2) do |payload, msg_handler|
|
113
119
|
msg_handler.success({
|
114
120
|
previous_receiver: {
|
115
|
-
trace_id: Freddy.trace.
|
116
|
-
parent_id: Freddy.trace.parent_id,
|
117
|
-
span_id: Freddy.trace.span_id
|
121
|
+
trace_id: Freddy.trace.context.trace_id,
|
122
|
+
parent_id: Freddy.trace.context.parent_id,
|
123
|
+
span_id: Freddy.trace.context.span_id
|
118
124
|
},
|
119
125
|
current_receiver: freddy2.deliver_with_response(destination3, {})
|
120
126
|
})
|
@@ -122,9 +128,9 @@ describe 'Tracing' do
|
|
122
128
|
|
123
129
|
freddy3.respond_to(destination3) do |payload, msg_handler|
|
124
130
|
msg_handler.success({
|
125
|
-
trace_id: Freddy.trace.
|
126
|
-
parent_id: Freddy.trace.parent_id,
|
127
|
-
span_id: Freddy.trace.span_id
|
131
|
+
trace_id: Freddy.trace.context.trace_id,
|
132
|
+
parent_id: Freddy.trace.context.parent_id,
|
133
|
+
span_id: Freddy.trace.context.span_id
|
128
134
|
})
|
129
135
|
end
|
130
136
|
end
|
@@ -142,11 +148,10 @@ describe 'Tracing' do
|
|
142
148
|
expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
|
143
149
|
end
|
144
150
|
|
145
|
-
it 'has
|
151
|
+
it 'has parent_id' do
|
146
152
|
response = freddy.deliver_with_response(destination, {})
|
147
|
-
previous_receiver = response.fetch(:previous_receiver)
|
148
153
|
current_receiver = response.fetch(:current_receiver)
|
149
|
-
expect(
|
154
|
+
expect(current_receiver.fetch(:parent_id)).to_not be_nil
|
150
155
|
end
|
151
156
|
|
152
157
|
it 'has generated span_id' do
|
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
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Salemove TechMovers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: opentracing
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.3'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
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,7 +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
|
123
|
-
- lib/freddy/
|
137
|
+
- lib/freddy/trace_carrier.rb
|
124
138
|
- spec/freddy/consumers/respond_to_consumer_spec.rb
|
125
139
|
- spec/freddy/error_response_spec.rb
|
126
140
|
- spec/freddy/freddy_spec.rb
|
@@ -128,7 +142,7 @@ files:
|
|
128
142
|
- spec/freddy/payload_spec.rb
|
129
143
|
- spec/freddy/responder_handler_spec.rb
|
130
144
|
- spec/freddy/sync_response_container_spec.rb
|
131
|
-
- spec/freddy/
|
145
|
+
- spec/freddy/trace_carrier_spec.rb
|
132
146
|
- spec/integration/concurrency_spec.rb
|
133
147
|
- spec/integration/logging_spec.rb
|
134
148
|
- spec/integration/reply_spec.rb
|
@@ -167,7 +181,7 @@ test_files:
|
|
167
181
|
- spec/freddy/payload_spec.rb
|
168
182
|
- spec/freddy/responder_handler_spec.rb
|
169
183
|
- spec/freddy/sync_response_container_spec.rb
|
170
|
-
- spec/freddy/
|
184
|
+
- spec/freddy/trace_carrier_spec.rb
|
171
185
|
- spec/integration/concurrency_spec.rb
|
172
186
|
- spec/integration/logging_spec.rb
|
173
187
|
- spec/integration/reply_spec.rb
|
data/lib/freddy/traces.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
class Freddy
|
2
|
-
module Traces
|
3
|
-
class Trace
|
4
|
-
def self.build
|
5
|
-
id = TraceId.generate
|
6
|
-
span_id = TraceId.generate
|
7
|
-
new(id: id, parent_id: nil, span_id: span_id)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.build_from_existing_trace(id:, parent_id:)
|
11
|
-
span_id = TraceId.generate
|
12
|
-
new(id: id, parent_id: parent_id, span_id: span_id)
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :id, :parent_id, :span_id
|
16
|
-
|
17
|
-
def initialize(id:, parent_id:, span_id:)
|
18
|
-
@id = id
|
19
|
-
@parent_id = parent_id
|
20
|
-
@span_id = span_id
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_h
|
24
|
-
{id: @id, parent_id: @parent_id, span_id: @span_id}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
module TraceId
|
29
|
-
TRACE_ID_UPPER_BOUND = 2 ** 64
|
30
|
-
|
31
|
-
# Generates 64-bit lower-hex encoded ID. This was chosen to be compatible
|
32
|
-
# with tracing frameworks like zipkin.
|
33
|
-
def self.generate
|
34
|
-
rand(TRACE_ID_UPPER_BOUND).to_s(16)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
NO_TRACE = Trace.new(id: nil, parent_id: nil, span_id: nil)
|
39
|
-
end
|
40
|
-
end
|