freddy-jruby 1.0.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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