lhc 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/docs/interceptors.md +1 -1
- data/docs/interceptors/zipkin.md +23 -0
- data/lib/lhc.rb +3 -0
- data/lib/lhc/interceptors/zipkin.rb +98 -0
- data/lib/lhc/version.rb +1 -1
- data/spec/basic_methods/request_without_rails_spec.rb +21 -2
- data/spec/interceptors/zipkin/distributed_tracing_spec.rb +52 -0
- data/spec/support/zipkin_mock.rb +113 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '059dd1e79e371a3df375a6a565b4797c11bf71da'
|
4
|
+
data.tar.gz: b2a5925b099b34812d6cb1aef256290d0cace0e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aff5120baa514889103791b6681e4776bbf8e5055db5c8b126fae5580487f72da833d3a472fd15b8c930807a1500d58e4a1c3c7c7c332ed47b8f9d460d2ac36d
|
7
|
+
data.tar.gz: a9959d2b3c84fd4125fe682eb172d6e06b81c225758931a0b493a7dadfa6a6b0faf4460e6a5ae7424482351a4fc8596312b22d6cabffe74e3de5952d16472303
|
data/.rubocop.yml
CHANGED
data/docs/interceptors.md
CHANGED
@@ -23,7 +23,7 @@ Interceptors
|
|
23
23
|
## Core Interceptors
|
24
24
|
|
25
25
|
There are some interceptors that are part of LHC already, that cover some basic usecases:
|
26
|
-
like [Caching](/docs/interceptors/caching.md), [Monitoring](/docs/interceptors/monitoring.md), [Authentication](/docs/interceptors/authentication.md), [Rollbar](/docs/interceptors/rollbar.md).
|
26
|
+
like [Caching](/docs/interceptors/caching.md), [Monitoring](/docs/interceptors/monitoring.md), [Authentication](/docs/interceptors/authentication.md), [Rollbar](/docs/interceptors/rollbar.md), [Zipkin](/docs/interceptors/zipkin.md).
|
27
27
|
|
28
28
|
## Callbacks
|
29
29
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Zipkin
|
2
|
+
|
3
|
+
Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in microservice architectures [Zipkin Distributed Tracing](https://zipkin.io/).
|
4
|
+
|
5
|
+
Add the zipkin interceptor to your basic set of LHC interceptors.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
LHC.config.interceptors = [LHC::Zipkin]
|
9
|
+
```
|
10
|
+
|
11
|
+
The following configuration needs to happen in the application that wants to run this interceptor:
|
12
|
+
|
13
|
+
1. Add `gem 'zipkin-tracer'` to your Gemfile.
|
14
|
+
2. Add the necessary Rack middleware and configuration
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
config.middleware.use ZipkinTracer::RackHandler, {
|
18
|
+
service_name: 'service-name', # name your service will be known as in zipkin
|
19
|
+
service_port: 80, # the port information that is sent along the trace
|
20
|
+
json_api_host: 'http://zipkin-collector', # the zipkin endpoint
|
21
|
+
sample_rate: 1 # sample rate, where 1 = 100% of all requests, and 0.1 is 10% of all requests
|
22
|
+
}
|
23
|
+
```
|
data/lib/lhc.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'typhoeus'
|
2
2
|
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
3
4
|
|
4
5
|
module LHC
|
5
6
|
autoload :BasicMethodsConcern,
|
@@ -117,6 +118,8 @@ module LHC
|
|
117
118
|
'lhc/response'
|
118
119
|
autoload :Rollbar,
|
119
120
|
'lhc/interceptors/rollbar'
|
121
|
+
autoload :Zipkin,
|
122
|
+
'lhc/interceptors/zipkin'
|
120
123
|
|
121
124
|
require 'lhc/railtie' if defined?(Rails)
|
122
125
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
class LHC::Zipkin < LHC::Interceptor
|
2
|
+
B3_HEADERS = {
|
3
|
+
trace_id: 'X-B3-TraceId'.freeze,
|
4
|
+
parent_id: 'X-B3-ParentSpanId'.freeze,
|
5
|
+
span_id: 'X-B3-SpanId'.freeze,
|
6
|
+
sampled: 'X-B3-Sampled'.freeze,
|
7
|
+
flags: 'X-B3-Flags'.freeze
|
8
|
+
}.freeze
|
9
|
+
TRUE = '1'.freeze # true in binary annotation
|
10
|
+
|
11
|
+
def before_request
|
12
|
+
return unless dependencies?
|
13
|
+
ZipkinTracer::TraceContainer.with_trace_id(trace_id) do
|
14
|
+
B3_HEADERS.each { |method, header| request.headers[header] = trace_id.send(method).to_s }
|
15
|
+
start_trace! if ::Trace.tracer && trace_id.sampled?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_response
|
20
|
+
return unless dependencies?
|
21
|
+
end_trace!
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def start_trace!
|
27
|
+
record_path
|
28
|
+
record_remote_endpoint
|
29
|
+
record_local_endpoint
|
30
|
+
end
|
31
|
+
|
32
|
+
def end_trace!
|
33
|
+
record_status
|
34
|
+
record_error if !response.success?
|
35
|
+
record_end
|
36
|
+
end
|
37
|
+
|
38
|
+
def trace_id
|
39
|
+
@trace_id ||= ZipkinTracer::TraceGenerator.new.next_trace_id
|
40
|
+
end
|
41
|
+
|
42
|
+
def span
|
43
|
+
@span ||= ::Trace.tracer.start_span(trace_id, url.path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def url
|
47
|
+
@url ||= URI(request.raw.url)
|
48
|
+
end
|
49
|
+
|
50
|
+
def status
|
51
|
+
@status ||= response.code.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def service_name
|
55
|
+
@service_name ||= url.host
|
56
|
+
end
|
57
|
+
|
58
|
+
def record_local_endpoint
|
59
|
+
span.record(::Trace::Annotation::CLIENT_SEND, local_endpoint)
|
60
|
+
end
|
61
|
+
|
62
|
+
def record_remote_endpoint
|
63
|
+
span.record_tag(::Trace::BinaryAnnotation::SERVER_ADDRESS, TRUE, ::Trace::BinaryAnnotation::Type::BOOL, remote_endpoint)
|
64
|
+
end
|
65
|
+
|
66
|
+
def record_path
|
67
|
+
span.record_tag(::Trace::BinaryAnnotation::PATH, url.path, ::Trace::BinaryAnnotation::Type::STRING, local_endpoint)
|
68
|
+
end
|
69
|
+
|
70
|
+
def record_end
|
71
|
+
span.record(::Trace::Annotation::CLIENT_RECV, local_endpoint)
|
72
|
+
::Trace.tracer.end_span(span)
|
73
|
+
end
|
74
|
+
|
75
|
+
def record_error
|
76
|
+
span.record_tag(::Trace::BinaryAnnotation::ERROR, status, ::Trace::BinaryAnnotation::Type::STRING, local_endpoint)
|
77
|
+
end
|
78
|
+
|
79
|
+
def record_status
|
80
|
+
span.record_tag(::Trace::BinaryAnnotation::STATUS, status, ::Trace::BinaryAnnotation::Type::STRING, local_endpoint)
|
81
|
+
end
|
82
|
+
|
83
|
+
def local_endpoint
|
84
|
+
@local_endpoint ||= ::Trace.default_endpoint
|
85
|
+
end
|
86
|
+
|
87
|
+
def remote_endpoint
|
88
|
+
@remote_endpoint ||= ::Trace::Endpoint.remote_endpoint(url, service_name, local_endpoint.ip_format)
|
89
|
+
end
|
90
|
+
|
91
|
+
def dependencies?
|
92
|
+
(
|
93
|
+
defined?(ZipkinTracer::TraceContainer) &&
|
94
|
+
ZipkinTracer::TraceContainer.current &&
|
95
|
+
defined?(Trace)
|
96
|
+
) || warn('[WARNING] Zipkin interceptor is enabled but dependencies are not found. See: https://github.com/local-ch/lhc/blob/master/docs/interceptors/zipkin.md')
|
97
|
+
end
|
98
|
+
end
|
data/lib/lhc/version.rb
CHANGED
@@ -1,9 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe LHC do
|
4
|
-
context '
|
4
|
+
context 'GET' do
|
5
|
+
before do
|
6
|
+
stub_request(:get, "http://datastore/v2/feedbacks").to_return(status: 200, body: "{}")
|
7
|
+
end
|
5
8
|
it "is able to call .request without LHC raising NoMethodError: undefined method `blank?' for nil:NilClass when calling it outside of the rails context" do
|
6
|
-
expect { LHC.request(url: "http://datastore/v2/feedbacks", method: :get) }.not_to raise_error
|
9
|
+
expect { LHC.request(url: "http://datastore/v2/feedbacks", method: :get) }.not_to raise_error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'POST' do
|
14
|
+
before do
|
15
|
+
stub_request(:post, "http://datastore/v2/feedbacks").to_return(status: 200, body: "{}")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is able to call .request without LHC raising NoMethodError: undefined method `deep_symbolize_keys' for {}:Hash" do
|
19
|
+
options = {
|
20
|
+
url: "http://datastore/v2/feedbacks",
|
21
|
+
method: :post,
|
22
|
+
body: "{}",
|
23
|
+
headers: { 'Content-Type' => 'application/json' }
|
24
|
+
}
|
25
|
+
expect { LHC.request(options) }.not_to raise_error
|
7
26
|
end
|
8
27
|
end
|
9
28
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe LHC::Zipkin do
|
4
|
+
before(:each) do
|
5
|
+
LHC.config.interceptors = [described_class]
|
6
|
+
LHC.config.endpoint(:local, 'http://local.ch')
|
7
|
+
stub_request(:get, 'http://local.ch').to_return(status: 200, body: 'The Website')
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with zipkin tracer integration' do
|
11
|
+
before(:all) do
|
12
|
+
::ZipkinTracer::TraceContainer.setup_mock(
|
13
|
+
trace_id: 'trace_id',
|
14
|
+
parent_id: 'parent_id',
|
15
|
+
span_id: 'span_id',
|
16
|
+
sampled: 'sampled',
|
17
|
+
flags: 'flags'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'adds the proper X-B3 headers' do
|
22
|
+
headers = LHC.get(:local).request.headers
|
23
|
+
expect(headers['X-B3-TraceId']).to eq('trace_id')
|
24
|
+
expect(headers['X-B3-ParentSpanId']).to eq('parent_id')
|
25
|
+
expect(headers['X-B3-SpanId']).to eq('span_id')
|
26
|
+
expect(headers['X-B3-Sampled']).to eq('sampled')
|
27
|
+
expect(headers['X-B3-Flags']).to eq('flags')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'wihtout zipkin integration' do
|
32
|
+
before(:all) do
|
33
|
+
TemporaryZipkinTracer = ::ZipkinTracer
|
34
|
+
Object.send(:remove_const, :ZipkinTracer)
|
35
|
+
end
|
36
|
+
|
37
|
+
after(:all) do
|
38
|
+
::ZipkinTracer = TemporaryZipkinTracer
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'adds the proper X-B3 headers' do
|
42
|
+
headers = nil
|
43
|
+
expect { headers = LHC.get(:local).request.headers }.not_to raise_error
|
44
|
+
|
45
|
+
expect(headers['X-B3-TraceId']).to be_nil
|
46
|
+
expect(headers['X-B3-ParentSpanId']).to be_nil
|
47
|
+
expect(headers['X-B3-SpanId']).to be_nil
|
48
|
+
expect(headers['X-B3-Sampled']).to be_nil
|
49
|
+
expect(headers['X-B3-Flags']).to be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module ZipkinTracer
|
2
|
+
class TraceContainer
|
3
|
+
attr_reader :trace_id, :parent_id, :span_id, :sampled, :flags
|
4
|
+
class << self
|
5
|
+
attr_accessor :current
|
6
|
+
|
7
|
+
def setup_mock(trace_id:, parent_id:, span_id:, sampled:, flags:)
|
8
|
+
@current = new(trace_id: trace_id, parent_id: parent_id, span_id: span_id, sampled: sampled, flags: flags)
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_trace_id(trace_id)
|
12
|
+
yield trace_id
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(trace_id:, parent_id:, span_id:, sampled:, flags:)
|
17
|
+
@trace_id = trace_id
|
18
|
+
@parent_id = parent_id
|
19
|
+
@span_id = span_id
|
20
|
+
@sampled = sampled
|
21
|
+
@flags = flags
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class TraceGenerator
|
26
|
+
def next_trace_id
|
27
|
+
TraceId.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class TraceId
|
32
|
+
def trace_id
|
33
|
+
'trace_id'
|
34
|
+
end
|
35
|
+
|
36
|
+
def parent_id
|
37
|
+
'parent_id'
|
38
|
+
end
|
39
|
+
|
40
|
+
def span_id
|
41
|
+
'span_id'
|
42
|
+
end
|
43
|
+
|
44
|
+
def sampled
|
45
|
+
'sampled'
|
46
|
+
end
|
47
|
+
|
48
|
+
def flags
|
49
|
+
'flags'
|
50
|
+
end
|
51
|
+
|
52
|
+
def sampled?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Span
|
58
|
+
def record_tag(*)
|
59
|
+
end
|
60
|
+
|
61
|
+
def record(*)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module Trace
|
67
|
+
def self.default_endpoint
|
68
|
+
Endpoint.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.tracer
|
72
|
+
Tracer.new
|
73
|
+
end
|
74
|
+
|
75
|
+
class Tracer
|
76
|
+
def start_span(*)
|
77
|
+
return ZipkinTracer::Span.new
|
78
|
+
end
|
79
|
+
|
80
|
+
def end_span(*)
|
81
|
+
return ZipkinTracer::Span.new
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Annotation
|
86
|
+
CLIENT_SEND = 'client_send'.freeze
|
87
|
+
CLIENT_RECV = 'client_recv'.freeze
|
88
|
+
end
|
89
|
+
|
90
|
+
class BinaryAnnotation
|
91
|
+
PATH = 'path'.freeze
|
92
|
+
SERVER_ADDRESS = 'server_address'.freeze
|
93
|
+
STATUS = 'status'.freeze
|
94
|
+
ERROR = 'error'.freeze
|
95
|
+
|
96
|
+
class Type
|
97
|
+
STRING = 'string'.freeze
|
98
|
+
BOOL = 'bool'.freeze
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Endpoint
|
103
|
+
class << self
|
104
|
+
def remote_endpoint(*)
|
105
|
+
new
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def ip_format
|
110
|
+
'ipv4'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lhc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.
|
4
|
+
version: 8.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- https://github.com/local-ch/lhc/contributors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|
@@ -199,6 +199,7 @@ files:
|
|
199
199
|
- docs/interceptors/prometheus.md
|
200
200
|
- docs/interceptors/retry.md
|
201
201
|
- docs/interceptors/rollbar.md
|
202
|
+
- docs/interceptors/zipkin.md
|
202
203
|
- docs/request.md
|
203
204
|
- docs/response.md
|
204
205
|
- lhc.gemspec
|
@@ -225,6 +226,7 @@ files:
|
|
225
226
|
- lib/lhc/interceptors/prometheus.rb
|
226
227
|
- lib/lhc/interceptors/retry.rb
|
227
228
|
- lib/lhc/interceptors/rollbar.rb
|
229
|
+
- lib/lhc/interceptors/zipkin.rb
|
228
230
|
- lib/lhc/railtie.rb
|
229
231
|
- lib/lhc/request.rb
|
230
232
|
- lib/lhc/response.rb
|
@@ -316,6 +318,7 @@ files:
|
|
316
318
|
- spec/interceptors/retry/main_spec.rb
|
317
319
|
- spec/interceptors/return_response_spec.rb
|
318
320
|
- spec/interceptors/rollbar/main_spec.rb
|
321
|
+
- spec/interceptors/zipkin/distributed_tracing_spec.rb
|
319
322
|
- spec/rails_helper.rb
|
320
323
|
- spec/request/encoding_spec.rb
|
321
324
|
- spec/request/error_handling_spec.rb
|
@@ -341,6 +344,7 @@ files:
|
|
341
344
|
- spec/support/fixtures/json/localina_content_ad.json
|
342
345
|
- spec/support/load_json.rb
|
343
346
|
- spec/support/reset_config.rb
|
347
|
+
- spec/support/zipkin_mock.rb
|
344
348
|
- spec/timeouts/no_signal_spec.rb
|
345
349
|
- spec/timeouts/timings_spec.rb
|
346
350
|
homepage: https://github.com/local-ch/lhc
|
@@ -364,7 +368,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
364
368
|
requirements:
|
365
369
|
- Ruby >= 2.0.0
|
366
370
|
rubyforge_project:
|
367
|
-
rubygems_version: 2.6.
|
371
|
+
rubygems_version: 2.6.14
|
368
372
|
signing_key:
|
369
373
|
specification_version: 4
|
370
374
|
summary: LocalHttpClient
|
@@ -450,6 +454,7 @@ test_files:
|
|
450
454
|
- spec/interceptors/retry/main_spec.rb
|
451
455
|
- spec/interceptors/return_response_spec.rb
|
452
456
|
- spec/interceptors/rollbar/main_spec.rb
|
457
|
+
- spec/interceptors/zipkin/distributed_tracing_spec.rb
|
453
458
|
- spec/rails_helper.rb
|
454
459
|
- spec/request/encoding_spec.rb
|
455
460
|
- spec/request/error_handling_spec.rb
|
@@ -475,5 +480,6 @@ test_files:
|
|
475
480
|
- spec/support/fixtures/json/localina_content_ad.json
|
476
481
|
- spec/support/load_json.rb
|
477
482
|
- spec/support/reset_config.rb
|
483
|
+
- spec/support/zipkin_mock.rb
|
478
484
|
- spec/timeouts/no_signal_spec.rb
|
479
485
|
- spec/timeouts/timings_spec.rb
|