freddy-jruby 1.0.0 → 1.3.2

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