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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 55a9bea9337f7d20d2e713bd012ed8f216fef328
4
- data.tar.gz: 2c49767c9226fdf1b882cf221cec8ff7726203f9
3
+ metadata.gz: 780979fe0cb5c2d61b74d2356643695dc3380e1d
4
+ data.tar.gz: 7778664a870a3b04ea9b0d697445ec2843a4a98a
5
5
  SHA512:
6
- metadata.gz: 7d3c30c1d5d54c72539821bc465f0887371ee30fdcbed3fa4711ee3b9f65137922025a4aa6956ca862b08b851d5bc8bff2d4aac7279a80250475c45a90e475ab
7
- data.tar.gz: 13f606726903475c3619ee4f7dd1d18ebe257cbb48844cb0831cf81809b468eeb7143c4d4519b8cd1e6fa00e9d4703dcf9ce1282ebf70d4867419c85a89f42ae
6
+ metadata.gz: dd21e0ca6b7140f2096db0062f8793941928bab1f8f23b0c6c6f0e1a5641c2a244a7d624a543fae642d38d1af9f0b4cd6a736d77195d355699ef582135a68c39
7
+ data.tar.gz: ce44daf3488a6b7805d179f0176fb0b71a2080836526660a5482665a854c6e0bc214ac7a09e7c7fa4b5426a52dd65a2da253c4e3996c69420a275d91a2de56fe
data/.travis.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.0
4
- - jruby-9.0.4.0
3
+ - 2.4.1
4
+ - jruby-9.1.5.0
5
5
  addons:
6
6
  code_climate:
7
7
  repo_token: 1f3842b985fdeff6a36168165d491ca5f444667e9381a85c899a61706a9dd285
data/Gemfile CHANGED
@@ -4,5 +4,6 @@ gem 'rspec'
4
4
  gem 'pry'
5
5
  gem 'codeclimate-test-reporter'
6
6
  gem 'hamster', '~> 3.0'
7
+ gem 'logasm-tracer', '0.2.0'
7
8
 
8
9
  gemspec
data/README.md CHANGED
@@ -137,33 +137,39 @@ responder_handler.shutdown
137
137
 
138
138
  ## Request Tracing
139
139
 
140
- Freddy helps application developers to implement distributed tracing. Freddy provides trace ID, parent ID and span ID when receiving a request. These can be accessed through a thread-local variable `Freddy.trace`. Calling `deliver` or `deliver_with_response` will pass these IDs to the next service.
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.id}"
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.id}"
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 trace IDs (e.g. provided by REST API) then you can set them yourself:
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 = Freddy::Traces::Trace.new({
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.1.0'
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
- connection = Adapters.determine.connect(config)
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] || Traces::NO_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),
@@ -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(logger:, thread_pool:, destination:, channel:, handler_adapter_factory:)
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.trace
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
- process_message(channel, queue, delivery, &block)
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(logger:, thread_pool:, pattern:, channel:, options:)
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
- Consumers.log_receive_event(@logger, queue.name, delivery)
47
- Freddy.trace = delivery.trace
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
@@ -1,13 +1,12 @@
1
1
  class Freddy
2
2
  class Delivery
3
- attr_reader :routing_key, :payload, :tag, :trace
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 build_trace(headers)
28
- if headers['x-trace-id'] && headers['x-span-id']
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
@@ -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
- Producers.log_send_event(@logger, payload, destination)
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
- Producers.log_send_event(@logger, payload, destination)
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
- @logger.debug(
53
- message: 'Publishing request',
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
@@ -3,7 +3,6 @@ require 'spec_helper'
3
3
  describe Freddy::Consumers::RespondToConsumer do
4
4
  let(:consumer) do
5
5
  described_class.new(
6
- logger: logger,
7
6
  thread_pool: thread_pool,
8
7
  destination: destination,
9
8
  channel: channel,
@@ -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(:freddy1) { Freddy.build(logger1, config) }
5
- let(:freddy2) { Freddy.build(logger2, config) }
4
+ describe 'Logging with Logasm::Tracer' do
5
+ let(:logger) { ArrayLogger.new }
6
+ let(:tracer) { Logasm::Tracer.new(logger) }
6
7
 
7
- let(:logger1) { spy('logger1') }
8
- let(:logger2) { spy('logger2') }
8
+ before { OpenTracing.global_tracer = tracer }
9
+ after { OpenTracing.global_tracer = nil }
9
10
 
10
- let(:destination) { random_destination }
11
- let(:payload) { {pay: 'load'} }
11
+ context 'when receiving an untraced request' do
12
+ let(:freddy) { Freddy.build(spy, config) }
13
+ let(:destination) { random_destination }
12
14
 
13
- after { [freddy1, freddy2].each(&:close) }
14
-
15
- before do
16
- freddy1.respond_to destination do |payload, msg_handler|
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
- freddy2.deliver_with_response(destination, payload) { }
21
- default_sleep
22
- end
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
- it 'logs all consumed messages' do
25
- expect(logger1).to have_received(:info).with(/Listening for requests on \S+/)
26
- expect(logger1).to have_received(:debug).with(
27
- message: 'Received message',
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
- it 'logs all produced messages' do
36
- expect(logger2).to have_received(:debug).with(/Consuming messages on \S+/)
37
- expect(logger2).to have_received(:debug).with(
38
- message: 'Publishing request',
39
- queue: destination,
40
- payload: payload,
41
- response_queue: anything,
42
- correlation_id: anything,
43
- trace: anything
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.id,
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.id,
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.id,
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 request initiator span_id as parent_id' do
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(trace_initiator.fetch(:span_id)).to eq(current_receiver.fetch(:parent_id))
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.id,
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.id,
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.id,
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 request initiator span_id as parent_id' do
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(previous_receiver.fetch(:span_id)).to eq(current_receiver.fetch(:parent_id))
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.1.0
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-29 00:00:00.000000000 Z
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/traces.rb
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/traces/trace_id_spec.rb
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/traces/trace_id_spec.rb
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
@@ -1,9 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Freddy::Traces::TraceId do
4
- describe '.generate' do
5
- it 'generates a trace id' do
6
- expect(described_class.generate).to_not be_nil
7
- end
8
- end
9
- end