freddy 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -0
- data/freddy.gemspec +1 -1
- data/lib/freddy/consumers/respond_to_consumer.rb +2 -0
- data/lib/freddy/consumers/tap_into_consumer.rb +2 -0
- data/lib/freddy/consumers.rb +2 -1
- data/lib/freddy/delivery.rb +15 -1
- data/lib/freddy/producers/reply_producer.rb +6 -1
- data/lib/freddy/producers/send_and_forget_producer.rb +8 -1
- data/lib/freddy/producers/send_and_wait_response_producer.rb +7 -2
- data/lib/freddy/producers.rb +1 -1
- data/lib/freddy/traces.rb +40 -0
- data/lib/freddy.rb +8 -0
- data/spec/freddy/traces/trace_id_spec.rb +9 -0
- data/spec/integration/logging_spec.rb +4 -2
- data/spec/integration/tracing_spec.rb +160 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55a9bea9337f7d20d2e713bd012ed8f216fef328
|
4
|
+
data.tar.gz: 2c49767c9226fdf1b882cf221cec8ff7726203f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d3c30c1d5d54c72539821bc465f0887371ee30fdcbed3fa4711ee3b9f65137922025a4aa6956ca862b08b851d5bc8bff2d4aac7279a80250475c45a90e475ab
|
7
|
+
data.tar.gz: 13f606726903475c3619ee4f7dd1d18ebe257cbb48844cb0831cf81809b468eeb7143c4d4519b8cd1e6fa00e9d4703dcf9ce1282ebf70d4867419c85a89f42ae
|
data/README.md
CHANGED
@@ -135,6 +135,35 @@ The following operations are supported:
|
|
135
135
|
responder_handler.shutdown
|
136
136
|
```
|
137
137
|
|
138
|
+
## Request Tracing
|
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.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
freddy1 = Freddy.build
|
144
|
+
freddy1.respond_do('service1') do |payload, msg_handler|
|
145
|
+
puts "Trace id: #{Freddy.trace.id}"
|
146
|
+
puts "Span id: #{Freddy.trace.span_id}"
|
147
|
+
|
148
|
+
freddy1.deliver('service2', {})
|
149
|
+
end
|
150
|
+
|
151
|
+
freddy2 = Freddy.build
|
152
|
+
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}"
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
In case you already have trace IDs (e.g. provided by REST API) then you can set them yourself:
|
160
|
+
```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
|
+
})
|
166
|
+
```
|
138
167
|
|
139
168
|
## Notes about concurrency
|
140
169
|
|
data/freddy.gemspec
CHANGED
@@ -37,9 +37,11 @@ class Freddy
|
|
37
37
|
def process_message(delivery, &block)
|
38
38
|
@consume_thread_pool.process do
|
39
39
|
begin
|
40
|
+
Freddy.trace = delivery.trace
|
40
41
|
block.call(delivery)
|
41
42
|
ensure
|
42
43
|
@channel.acknowledge(delivery.tag, false)
|
44
|
+
Freddy.trace = nil
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
@@ -44,9 +44,11 @@ class Freddy
|
|
44
44
|
@consume_thread_pool.process do
|
45
45
|
begin
|
46
46
|
Consumers.log_receive_event(@logger, queue.name, delivery)
|
47
|
+
Freddy.trace = delivery.trace
|
47
48
|
block.call delivery.payload, delivery.routing_key
|
48
49
|
ensure
|
49
50
|
@channel.acknowledge(delivery.tag, false)
|
51
|
+
Freddy.trace = nil
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|
data/lib/freddy/consumers.rb
CHANGED
data/lib/freddy/delivery.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
class Freddy
|
2
2
|
class Delivery
|
3
|
-
attr_reader :routing_key, :payload, :tag
|
3
|
+
attr_reader :routing_key, :payload, :tag, :trace
|
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 || {})
|
10
11
|
end
|
11
12
|
|
12
13
|
def correlation_id
|
@@ -20,5 +21,18 @@ class Freddy
|
|
20
21
|
def reply_to
|
21
22
|
@metadata.reply_to
|
22
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
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
|
36
|
+
end
|
23
37
|
end
|
24
38
|
end
|
@@ -12,7 +12,12 @@ class Freddy
|
|
12
12
|
Producers.log_send_event(@logger, payload, destination)
|
13
13
|
|
14
14
|
properties = properties.merge(
|
15
|
-
routing_key: destination,
|
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
21
|
)
|
17
22
|
|
18
23
|
@exchange.publish Payload.dump(payload), properties
|
@@ -12,7 +12,14 @@ class Freddy
|
|
12
12
|
def produce(destination, payload, properties)
|
13
13
|
Producers.log_send_event(@logger, payload, destination)
|
14
14
|
|
15
|
-
properties = properties.merge(
|
15
|
+
properties = properties.merge(
|
16
|
+
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
|
+
}
|
22
|
+
)
|
16
23
|
json_payload = Payload.dump(payload)
|
17
24
|
|
18
25
|
# Connection adapters handle thread safety for #publish themselves. No
|
@@ -41,7 +41,11 @@ class Freddy
|
|
41
41
|
properties = properties.merge(
|
42
42
|
routing_key: destination, content_type: CONTENT_TYPE,
|
43
43
|
correlation_id: correlation_id, reply_to: @response_queue.name,
|
44
|
-
mandatory: true, type: 'request'
|
44
|
+
mandatory: true, type: 'request',
|
45
|
+
headers: {
|
46
|
+
'x-trace-id' => Freddy.trace.id,
|
47
|
+
'x-span-id' => Freddy.trace.span_id
|
48
|
+
}
|
45
49
|
)
|
46
50
|
json_payload = Payload.dump(payload)
|
47
51
|
|
@@ -50,7 +54,8 @@ class Freddy
|
|
50
54
|
queue: destination,
|
51
55
|
payload: payload,
|
52
56
|
response_queue: @response_queue.name,
|
53
|
-
correlation_id: correlation_id
|
57
|
+
correlation_id: correlation_id,
|
58
|
+
trace: Freddy.trace.to_h
|
54
59
|
)
|
55
60
|
|
56
61
|
# Connection adapters handle thread safety for #publish themselves. No
|
data/lib/freddy/producers.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Freddy
|
2
2
|
module Producers
|
3
3
|
def self.log_send_event(logger, payload, destination)
|
4
|
-
logger.debug message: 'Sending message', queue: destination, payload: payload
|
4
|
+
logger.debug message: 'Sending message', queue: destination, payload: payload, trace: Freddy.trace.to_h
|
5
5
|
end
|
6
6
|
end
|
7
7
|
end
|
@@ -0,0 +1,40 @@
|
|
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
|
data/lib/freddy.rb
CHANGED
@@ -30,6 +30,14 @@ class Freddy
|
|
30
30
|
new(connection, logger, max_concurrency)
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.trace
|
34
|
+
Thread.current[:freddy_trace] || Traces::NO_TRACE
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.trace=(trace)
|
38
|
+
Thread.current[:freddy_trace] = trace
|
39
|
+
end
|
40
|
+
|
33
41
|
def initialize(connection, logger, max_concurrency)
|
34
42
|
@connection = connection
|
35
43
|
@logger = logger
|
@@ -27,7 +27,8 @@ describe 'Logging' do
|
|
27
27
|
message: 'Received message',
|
28
28
|
queue: destination,
|
29
29
|
payload: payload,
|
30
|
-
correlation_id: anything
|
30
|
+
correlation_id: anything,
|
31
|
+
trace: anything
|
31
32
|
)
|
32
33
|
end
|
33
34
|
|
@@ -38,7 +39,8 @@ describe 'Logging' do
|
|
38
39
|
queue: destination,
|
39
40
|
payload: payload,
|
40
41
|
response_queue: anything,
|
41
|
-
correlation_id: anything
|
42
|
+
correlation_id: anything,
|
43
|
+
trace: anything
|
42
44
|
)
|
43
45
|
end
|
44
46
|
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Tracing' do
|
4
|
+
context 'when receiving an untraced request' do
|
5
|
+
let(:freddy) { Freddy.build(logger, config) }
|
6
|
+
let(:destination) { random_destination }
|
7
|
+
|
8
|
+
before do
|
9
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
10
|
+
msg_handler.success({
|
11
|
+
trace_id: Freddy.trace.id,
|
12
|
+
parent_id: Freddy.trace.parent_id,
|
13
|
+
span_id: Freddy.trace.span_id
|
14
|
+
})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
after { freddy.close }
|
19
|
+
|
20
|
+
it 'has generated trace_id' do
|
21
|
+
response = freddy.deliver_with_response(destination, {})
|
22
|
+
expect(response.fetch(:trace_id)).to_not be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has no parent_id' do
|
26
|
+
response = freddy.deliver_with_response(destination, {})
|
27
|
+
expect(response.fetch(:parent_id)).to be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has generated span_id' do
|
31
|
+
response = freddy.deliver_with_response(destination, {})
|
32
|
+
expect(response.fetch(:span_id)).to_not be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when receiving a traced request' do
|
37
|
+
let(:freddy) { Freddy.build(logger, config) }
|
38
|
+
let(:freddy2) { Freddy.build(logger, config) }
|
39
|
+
|
40
|
+
let(:destination) { random_destination }
|
41
|
+
let(:destination2) { random_destination }
|
42
|
+
|
43
|
+
before do
|
44
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
45
|
+
msg_handler.success({
|
46
|
+
trace_initiator: {
|
47
|
+
trace_id: Freddy.trace.id,
|
48
|
+
parent_id: Freddy.trace.parent_id,
|
49
|
+
span_id: Freddy.trace.span_id
|
50
|
+
},
|
51
|
+
current_receiver: freddy.deliver_with_response(destination2, {})
|
52
|
+
})
|
53
|
+
end
|
54
|
+
|
55
|
+
freddy2.respond_to(destination2) do |payload, msg_handler|
|
56
|
+
msg_handler.success({
|
57
|
+
trace_id: Freddy.trace.id,
|
58
|
+
parent_id: Freddy.trace.parent_id,
|
59
|
+
span_id: Freddy.trace.span_id
|
60
|
+
})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
after do
|
65
|
+
freddy.close
|
66
|
+
freddy2.close
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'has trace_id from the trace initiator' do
|
70
|
+
response = freddy.deliver_with_response(destination, {})
|
71
|
+
trace_initiator = response.fetch(:trace_initiator)
|
72
|
+
current_receiver = response.fetch(:current_receiver)
|
73
|
+
expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'has request initiator span_id as parent_id' do
|
77
|
+
response = freddy.deliver_with_response(destination, {})
|
78
|
+
trace_initiator = response.fetch(:trace_initiator)
|
79
|
+
current_receiver = response.fetch(:current_receiver)
|
80
|
+
expect(trace_initiator.fetch(:span_id)).to eq(current_receiver.fetch(:parent_id))
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'has generated span_id' do
|
84
|
+
response = freddy.deliver_with_response(destination, {})
|
85
|
+
trace_initiator = response.fetch(:trace_initiator)
|
86
|
+
current_receiver = response.fetch(:current_receiver)
|
87
|
+
expect(current_receiver.fetch(:span_id)).to_not be_nil
|
88
|
+
expect(current_receiver.fetch(:span_id)).to_not eq(trace_initiator.fetch(:span_id))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when receiving a nested traced request' do
|
93
|
+
let(:freddy) { Freddy.build(logger, config) }
|
94
|
+
let(:freddy2) { Freddy.build(logger, config) }
|
95
|
+
let(:freddy3) { Freddy.build(logger, config) }
|
96
|
+
|
97
|
+
let(:destination) { random_destination }
|
98
|
+
let(:destination2) { random_destination }
|
99
|
+
let(:destination3) { random_destination }
|
100
|
+
|
101
|
+
before do
|
102
|
+
freddy.respond_to(destination) do |payload, msg_handler|
|
103
|
+
msg_handler.success({
|
104
|
+
trace_initiator: {
|
105
|
+
trace_id: Freddy.trace.id,
|
106
|
+
parent_id: Freddy.trace.parent_id,
|
107
|
+
span_id: Freddy.trace.span_id
|
108
|
+
}
|
109
|
+
}.merge(freddy.deliver_with_response(destination2, {})))
|
110
|
+
end
|
111
|
+
|
112
|
+
freddy2.respond_to(destination2) do |payload, msg_handler|
|
113
|
+
msg_handler.success({
|
114
|
+
previous_receiver: {
|
115
|
+
trace_id: Freddy.trace.id,
|
116
|
+
parent_id: Freddy.trace.parent_id,
|
117
|
+
span_id: Freddy.trace.span_id
|
118
|
+
},
|
119
|
+
current_receiver: freddy2.deliver_with_response(destination3, {})
|
120
|
+
})
|
121
|
+
end
|
122
|
+
|
123
|
+
freddy3.respond_to(destination3) do |payload, msg_handler|
|
124
|
+
msg_handler.success({
|
125
|
+
trace_id: Freddy.trace.id,
|
126
|
+
parent_id: Freddy.trace.parent_id,
|
127
|
+
span_id: Freddy.trace.span_id
|
128
|
+
})
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
after do
|
133
|
+
freddy.close
|
134
|
+
freddy2.close
|
135
|
+
freddy3.close
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'has trace_id from the trace initiator' do
|
139
|
+
response = freddy.deliver_with_response(destination, {})
|
140
|
+
trace_initiator = response.fetch(:trace_initiator)
|
141
|
+
current_receiver = response.fetch(:current_receiver)
|
142
|
+
expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'has request initiator span_id as parent_id' do
|
146
|
+
response = freddy.deliver_with_response(destination, {})
|
147
|
+
previous_receiver = response.fetch(:previous_receiver)
|
148
|
+
current_receiver = response.fetch(:current_receiver)
|
149
|
+
expect(previous_receiver.fetch(:span_id)).to eq(current_receiver.fetch(:parent_id))
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'has generated span_id' do
|
153
|
+
response = freddy.deliver_with_response(destination, {})
|
154
|
+
previous_receiver = response.fetch(:previous_receiver)
|
155
|
+
current_receiver = response.fetch(:current_receiver)
|
156
|
+
expect(current_receiver.fetch(:span_id)).to_not be_nil
|
157
|
+
expect(current_receiver.fetch(:span_id)).to_not eq(previous_receiver.fetch(:span_id))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
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.0
|
4
|
+
version: 1.1.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-
|
11
|
+
date: 2017-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- lib/freddy/responder_handler.rb
|
121
121
|
- lib/freddy/sync_response_container.rb
|
122
122
|
- lib/freddy/timeout_error.rb
|
123
|
+
- lib/freddy/traces.rb
|
123
124
|
- spec/freddy/consumers/respond_to_consumer_spec.rb
|
124
125
|
- spec/freddy/error_response_spec.rb
|
125
126
|
- spec/freddy/freddy_spec.rb
|
@@ -127,10 +128,12 @@ files:
|
|
127
128
|
- spec/freddy/payload_spec.rb
|
128
129
|
- spec/freddy/responder_handler_spec.rb
|
129
130
|
- spec/freddy/sync_response_container_spec.rb
|
131
|
+
- spec/freddy/traces/trace_id_spec.rb
|
130
132
|
- spec/integration/concurrency_spec.rb
|
131
133
|
- spec/integration/logging_spec.rb
|
132
134
|
- spec/integration/reply_spec.rb
|
133
135
|
- spec/integration/tap_into_with_group_spec.rb
|
136
|
+
- spec/integration/tracing_spec.rb
|
134
137
|
- spec/spec_helper.rb
|
135
138
|
homepage:
|
136
139
|
licenses:
|
@@ -164,8 +167,10 @@ test_files:
|
|
164
167
|
- spec/freddy/payload_spec.rb
|
165
168
|
- spec/freddy/responder_handler_spec.rb
|
166
169
|
- spec/freddy/sync_response_container_spec.rb
|
170
|
+
- spec/freddy/traces/trace_id_spec.rb
|
167
171
|
- spec/integration/concurrency_spec.rb
|
168
172
|
- spec/integration/logging_spec.rb
|
169
173
|
- spec/integration/reply_spec.rb
|
170
174
|
- spec/integration/tap_into_with_group_spec.rb
|
175
|
+
- spec/integration/tracing_spec.rb
|
171
176
|
- spec/spec_helper.rb
|