freddy 1.1.0 → 1.2.0
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 +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
|