lhc 13.0.0 → 13.1.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
  SHA256:
3
- metadata.gz: 4795a0c91e246b2139465a030a8f98e0ba2deb07a48664632524b00ad2f02403
4
- data.tar.gz: 821adbc1157dbd699ad9a40f556a3ea7e94088aa8a320de6685b475a11596351
3
+ metadata.gz: 98300a9c64d910094f32a96b4428c71aa4d1d8927e1dc9e4b939c1119e28333c
4
+ data.tar.gz: 7cb93bf8911d06c5fbd015b36b2ccc62c3ff211a1e70a2aa10dfd8fe447be878
5
5
  SHA512:
6
- metadata.gz: 9f8a0035d222617beeafce69dfadb17138d5eb31ea6fed900b6aca4582bbafb332b564e3557f42a1dbbcb287f2e6c55fe100c5e74f92e916f0686172b06f9f07
7
- data.tar.gz: '058260bffcbacc6edac359e9852a519c3b49fe902bf48a3c7635226c43c563f73c1308ca11fbf6096213ade013ffc40e22c22e4901e455628b19e0d87cc74c7d'
6
+ metadata.gz: 24d10469629a7bb4942bd2a77fabc74910a98179e5998fef6e502fa88e25d583274becd33897f4f509ed97e3e9a3ed2d246145ac9a0908ae3b367d4d0f00ca9d
7
+ data.tar.gz: c22f9044dbc3a2052c3487eb014f04e86791017da1b3d438d6c9e48e63d30bf4bf5e19663854f3b1a2ba675ad240da84e3c528a0ca25037759a3576109275657
data/README.md CHANGED
@@ -73,6 +73,10 @@ use it like:
73
73
  * [Installation](#installation-1)
74
74
  * [Environment](#environment)
75
75
  * [What it tracks](#what-it-tracks)
76
+ * [Before and after request tracking](#before-and-after-request-tracking)
77
+ * [Response tracking](#response-tracking)
78
+ * [Timeout tracking](#timeout-tracking)
79
+ * [Caching tracking](#caching-tracking)
76
80
  * [Configure](#configure-1)
77
81
  * [Prometheus Interceptor](#prometheus-interceptor)
78
82
  * [Retry Interceptor](#retry-interceptor)
@@ -95,6 +99,7 @@ use it like:
95
99
 
96
100
 
97
101
 
102
+
98
103
  ## Basic methods
99
104
 
100
105
  Available are `get`, `post`, `put` & `delete`.
@@ -744,11 +749,15 @@ It tracks request attempts with `before_request` and `after_request` (counts).
744
749
  In case your workers/processes are getting killed due limited time constraints,
745
750
  you are able to detect deltas with relying on "before_request", and "after_request" counts:
746
751
 
752
+ ###### Before and after request tracking
753
+
747
754
  ```ruby
748
755
  "lhc.<app_name>.<env>.<host>.<http_method>.before_request", 1
749
756
  "lhc.<app_name>.<env>.<host>.<http_method>.after_request", 1
750
757
  ```
751
758
 
759
+ ###### Response tracking
760
+
752
761
  In case of a successful response it reports the response code with a count and the response time with a gauge value.
753
762
 
754
763
  ```ruby
@@ -759,6 +768,17 @@ In case of a successful response it reports the response code with a count and t
759
768
  "lhc.<app_name>.<env>.<host>.<http_method>.time", 43
760
769
  ```
761
770
 
771
+ In case of a unsuccessful response it reports the response code with a count but no time:
772
+
773
+ ```ruby
774
+ LHC.get('http://local.ch')
775
+
776
+ "lhc.<app_name>.<env>.<host>.<http_method>.count", 1
777
+ "lhc.<app_name>.<env>.<host>.<http_method>.500", 1
778
+ ```
779
+
780
+ ###### Timeout tracking
781
+
762
782
  Timeouts are also reported:
763
783
 
764
784
  ```ruby
@@ -767,6 +787,30 @@ Timeouts are also reported:
767
787
 
768
788
  All the dots in the host are getting replaced with underscore, because dot is the default separator in graphite.
769
789
 
790
+ ###### Caching tracking
791
+
792
+ When you want to track caching stats please make sure you have enabled the `LHC::Caching` and the `LHC::Monitoring` interceptor.
793
+
794
+ Make sure that the `LHC::Caching` is listed before `LHC::Monitoring` interceptor when configuring interceptors:
795
+
796
+ ```ruby
797
+ LHC.configure do |c|
798
+ c.interceptors = [LHC::Caching, LHC::Monitoring]
799
+ end
800
+ ```
801
+
802
+ If a response was served from cache it tracks:
803
+
804
+ ```ruby
805
+ "lhc.<app_name>.<env>.<host>.<http_method>.cache.hit", 1
806
+ ```
807
+
808
+ If a response was not served from cache it tracks:
809
+
810
+ ```ruby
811
+ "lhc.<app_name>.<env>.<host>.<http_method>.cache.miss", 1
812
+ ```
813
+
770
814
  ##### Configure
771
815
 
772
816
  It is possible to set the key for Monitoring Interceptor on per request basis:
@@ -64,8 +64,10 @@ class LHC::Error < StandardError
64
64
  end
65
65
 
66
66
  def to_s
67
- return response if response.is_a?(String)
67
+ return response.to_s unless response.is_a?(LHC::Response)
68
68
  request = response.request
69
+ return unless request.is_a?(LHC::Request)
70
+
69
71
  debug = []
70
72
  debug << [request.method, request.url].map { |str| self.class.fix_invalid_encoding(str) }.join(' ')
71
73
  debug << "Options: #{request.options}"
@@ -29,4 +29,8 @@ class LHC::Interceptor
29
29
  def self.dup
30
30
  self
31
31
  end
32
+
33
+ def all_interceptor_classes
34
+ @all_interceptors ||= LHC::Interceptors.new(request).all.map(&:class)
35
+ end
32
36
  end
@@ -75,10 +75,6 @@ class LHC::Auth < LHC::Interceptor
75
75
  @refresh_client_token_option ||= auth_options[:refresh_client_token] || refresh_client_token
76
76
  end
77
77
 
78
- def all_interceptor_classes
79
- @all_interceptors ||= LHC::Interceptors.new(request).all.map(&:class)
80
- end
81
-
82
78
  def auth_options
83
79
  request.options[:auth] || {}
84
80
  end
@@ -41,15 +41,14 @@ class LHC::Caching < LHC::Interceptor
41
41
 
42
42
  def before_request
43
43
  return unless cache?(request)
44
- key = key(request, options[:key])
45
- response_data = multilevel_cache.fetch(key)
46
- return unless response_data
44
+ return if response_data.blank?
47
45
  from_cache(request, response_data)
48
46
  end
49
47
 
50
48
  def after_response
51
49
  return unless response.success?
52
50
  return unless cache?(request)
51
+ return if response_data.present?
53
52
  multilevel_cache.write(
54
53
  key(request, options[:key]),
55
54
  to_cache(response),
@@ -59,6 +58,13 @@ class LHC::Caching < LHC::Interceptor
59
58
 
60
59
  private
61
60
 
61
+ # from cache
62
+ def response_data
63
+ # stop calling multi-level cache if it already returned nil for this interceptor instance
64
+ return @response_data if defined? @response_data
65
+ @response_data ||= multilevel_cache.fetch(key(request, options[:key]))
66
+ end
67
+
62
68
  # performs read/write (fetch/write) on all configured cache levels (e.g. local & central)
63
69
  def multilevel_cache
64
70
  MultilevelCache.new(
@@ -13,34 +13,59 @@ class LHC::Monitoring < LHC::Interceptor
13
13
 
14
14
  def before_request
15
15
  return unless statsd
16
- LHC::Monitoring.statsd.count("#{key(request)}.before_request", 1)
16
+ LHC::Monitoring.statsd.count("#{key}.before_request", 1)
17
17
  end
18
18
 
19
19
  def after_request
20
20
  return unless statsd
21
- LHC::Monitoring.statsd.count("#{key(request)}.count", 1)
22
- LHC::Monitoring.statsd.count("#{key(request)}.after_request", 1)
21
+ LHC::Monitoring.statsd.count("#{key}.count", 1)
22
+ LHC::Monitoring.statsd.count("#{key}.after_request", 1)
23
23
  end
24
24
 
25
25
  def after_response
26
26
  return unless statsd
27
- key = key(response)
28
- LHC::Monitoring.statsd.timing("#{key}.time", response.time) if response.success?
29
- key += response.timeout? ? '.timeout' : ".#{response.code}"
30
- LHC::Monitoring.statsd.count(key, 1)
27
+ monitor_time!
28
+ monitor_cache!
29
+ monitor_response!
31
30
  end
32
31
 
33
32
  private
34
33
 
35
- def key(target)
36
- request = target.is_a?(LHC::Request) ? target : target.request
34
+ def monitor_time!
35
+ LHC::Monitoring.statsd.timing("#{key}.time", response.time) if response.success?
36
+ end
37
+
38
+ def monitor_cache!
39
+ return if request.options[:cache].blank?
40
+ return unless monitor_caching_configuration_check
41
+ if response.from_cache?
42
+ LHC::Monitoring.statsd.count("#{key}.cache.hit", 1)
43
+ else
44
+ LHC::Monitoring.statsd.count("#{key}.cache.miss", 1)
45
+ end
46
+ end
47
+
48
+ def monitor_caching_configuration_check
49
+ return true if all_interceptor_classes.include?(LHC::Caching) && all_interceptor_classes.index(self.class) > all_interceptor_classes.index(LHC::Caching)
50
+ warn("[WARNING] Your interceptors must include LHC::Caching and LHC::Monitoring and also in that order.")
51
+ end
52
+
53
+ def monitor_response!
54
+ if response.timeout?
55
+ LHC::Monitoring.statsd.count("#{key}.timeout", 1)
56
+ else
57
+ LHC::Monitoring.statsd.count("#{key}.#{response.code}", 1)
58
+ end
59
+ end
60
+
61
+ def key
37
62
  key = options(request.options)[:key]
38
63
  return key if key.present?
39
64
 
40
65
  url = sanitize_url(request.url)
41
66
  key = [
42
67
  'lhc',
43
- Rails.application.class.parent_name.underscore,
68
+ module_parent_name.underscore,
44
69
  LHC::Monitoring.env || Rails.env,
45
70
  URI.parse(url).host.gsub(/\./, '_'),
46
71
  request.method
@@ -48,6 +73,10 @@ class LHC::Monitoring < LHC::Interceptor
48
73
  key.join('.')
49
74
  end
50
75
 
76
+ def module_parent_name
77
+ (ActiveSupport.gem_version >= Gem::Version.new('6.0.0')) ? Rails.application.class.module_parent_name : Rails.application.class.parent_name
78
+ end
79
+
51
80
  def sanitize_url(url)
52
81
  return url if url.match(%r{https?://})
53
82
  "http://#{url}"
@@ -25,7 +25,11 @@ class LHC::Request
25
25
  interceptors.intercept(:before_raw_request)
26
26
  self.raw = create_request
27
27
  interceptors.intercept(:before_request)
28
- run! if self_executing && !response
28
+ if self_executing && !response
29
+ run!
30
+ elsif response
31
+ on_complete(response)
32
+ end
29
33
  end
30
34
 
31
35
  def url
@@ -128,7 +132,7 @@ class LHC::Request
128
132
  end
129
133
 
130
134
  def on_complete(response)
131
- self.response = LHC::Response.new(response, self)
135
+ self.response = response.is_a?(LHC::Response) ? response : LHC::Response.new(response, self)
132
136
  interceptors.intercept(:after_response)
133
137
  handle_error(self.response) unless self.response.success?
134
138
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LHC
4
- VERSION ||= '13.0.0'
4
+ VERSION ||= '13.1.0'
5
5
  end
@@ -45,7 +45,7 @@ describe LHC::Error do
45
45
 
46
46
  context 'some mocked response' do
47
47
  let(:request) do
48
- double('request',
48
+ double('LHC::Request',
49
49
  method: 'GET',
50
50
  url: 'http://example.com/sessions',
51
51
  headers: { 'Bearer Token' => "aaaaaaaa-bbbb-cccc-dddd-eeee" },
@@ -55,7 +55,7 @@ describe LHC::Error do
55
55
  end
56
56
 
57
57
  let(:response) do
58
- double('response',
58
+ double('LHC::Response',
59
59
  request: request,
60
60
  code: 500,
61
61
  options: { return_code: :internal_error, response_headers: "" },
@@ -64,6 +64,11 @@ describe LHC::Error do
64
64
 
65
65
  subject { LHC::Error.new('The error message', response) }
66
66
 
67
+ before do
68
+ allow(request).to receive(:is_a?).with(LHC::Request).and_return(true)
69
+ allow(response).to receive(:is_a?).with(LHC::Response).and_return(true)
70
+ end
71
+
67
72
  it 'produces correct debug output' do
68
73
  expect(subject.to_s.split("\n")).to eq(<<-MSG.strip_heredoc.split("\n"))
69
74
  GET http://example.com/sessions
@@ -14,7 +14,7 @@ describe LHC do
14
14
  uri = URI.parse(response.request.url)
15
15
  path = [
16
16
  'web',
17
- Rails.application.class.parent_name,
17
+ ((ActiveSupport.gem_version >= Gem::Version.new('6.0.0')) ? Rails.application.class.module_parent_name : Rails.application.class.parent_name).underscore,
18
18
  Rails.env,
19
19
  response.request.method,
20
20
  uri.scheme,
@@ -47,7 +47,7 @@ describe LHC::Caching do
47
47
 
48
48
  it 'lets you configure the cache key that will be used' do
49
49
  LHC.config.endpoint(:local, 'http://local.ch', cache: { key: 'STATICKEY' })
50
- expect(Rails.cache).to receive(:fetch).with("LHC_CACHE(v#{LHC::Caching::CACHE_VERSION}): STATICKEY").and_call_original
50
+ expect(Rails.cache).to receive(:fetch).at_least(:once).with("LHC_CACHE(v#{LHC::Caching::CACHE_VERSION}): STATICKEY").and_call_original
51
51
  expect(Rails.cache).to receive(:write).with("LHC_CACHE(v#{LHC::Caching::CACHE_VERSION}): STATICKEY", anything, anything).and_call_original
52
52
  stub
53
53
  LHC.get(:local)
@@ -66,8 +66,8 @@ describe LHC::Caching do
66
66
  stub
67
67
  LHC.config.endpoint(:local, 'http://local.ch', cache: true)
68
68
  original_response = LHC.get(:local)
69
- cached_response = LHC.get(:local)
70
69
  expect(original_response.from_cache?).to eq false
70
+ cached_response = LHC.get(:local)
71
71
  expect(cached_response.from_cache?).to eq true
72
72
  end
73
73
  end
@@ -62,7 +62,8 @@ describe LHC::Caching do
62
62
 
63
63
  context 'found in central cache' do
64
64
  it 'serves it from central cache if found there' do
65
- expect(redis_cache).to receive(:fetch).and_return(nil, body: '<h1>Hi there</h1>', code: 200, headers: nil, return_code: nil, mock: :webmock)
65
+ expect(redis_cache).to receive(:fetch).and_return(nil,
66
+ body: '<h1>Hi there</h1>', code: 200, headers: nil, return_code: nil, mock: :webmock)
66
67
  expect(redis_cache).to receive(:write).and_return(true)
67
68
  expect(Rails.cache).to receive(:fetch).and_call_original
68
69
  expect(Rails.cache).to receive(:write).and_call_original
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Monitoring do
6
+ let(:stub) do
7
+ stub_request(:get, 'http://local.ch').to_return(status: 200, body: 'The Website')
8
+ end
9
+
10
+ module Statsd
11
+ def self.count(_path, _value); end
12
+
13
+ def self.timing(_path, _value); end
14
+ end
15
+
16
+ before(:each) do
17
+ LHC::Monitoring.statsd = Statsd
18
+ Rails.cache.clear
19
+ allow(Statsd).to receive(:count).with('lhc.dummy.test.local_ch.get.before_request', 1)
20
+ allow(Statsd).to receive(:count).with('lhc.dummy.test.local_ch.get.count', 1)
21
+ allow(Statsd).to receive(:count).with('lhc.dummy.test.local_ch.get.after_request', 1)
22
+ allow(Statsd).to receive(:count).with('lhc.dummy.test.local_ch.get.200', 1)
23
+ end
24
+
25
+ context 'interceptors configured correctly' do
26
+ before do
27
+ LHC.config.interceptors = [LHC::Caching, LHC::Monitoring]
28
+ end
29
+
30
+ context 'requesting with cache option' do
31
+ it 'monitors miss/hit for caching' do
32
+ stub
33
+ expect(Statsd).to receive(:count).with('lhc.dummy.test.local_ch.get.cache.miss', 1)
34
+ expect(Statsd).to receive(:count).with('lhc.dummy.test.local_ch.get.cache.hit', 1)
35
+ LHC.get('http://local.ch', cache: true)
36
+ LHC.get('http://local.ch', cache: true)
37
+ end
38
+ end
39
+
40
+ context 'request uncached' do
41
+ it 'requesting without cache option' do
42
+ stub
43
+ expect(Statsd).not_to receive(:count).with('lhc.dummy.test.local_ch.get.cache.miss', 1)
44
+ expect(Statsd).not_to receive(:count).with('lhc.dummy.test.local_ch.get.cache.hit', 1)
45
+ LHC.get('http://local.ch')
46
+ LHC.get('http://local.ch')
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'wrong interceptor order' do
52
+ before(:each) do
53
+ LHC.config.interceptors = [LHC::Monitoring, LHC::Caching] # monitoring needs to be after Caching
54
+ end
55
+
56
+ it 'does monitors miss/hit for caching and warns about wrong order of interceptors' do
57
+ stub
58
+ expect(Statsd).not_to receive(:count).with('lhc.dummy.test.local_ch.get.cache.miss', 1)
59
+ expect(Statsd).not_to receive(:count).with('lhc.dummy.test.local_ch.get.cache.hit', 1)
60
+ expect(-> {
61
+ LHC.get('http://local.ch', cache: true)
62
+ LHC.get('http://local.ch', cache: true)
63
+ }).to output("[WARNING] Your interceptors must include LHC::Caching and LHC::Monitoring and also in that order.\n[WARNING] Your interceptors must include LHC::Caching and LHC::Monitoring and also in that order.\n").to_stderr
64
+ end
65
+ end
66
+ end
@@ -12,7 +12,7 @@ describe LHC do
12
12
 
13
13
  def before_request
14
14
  if @@cached
15
- return LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from local cache'), nil)
15
+ return LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok, response_body: 'Im served from local cache'), nil)
16
16
  end
17
17
  end
18
18
  end
@@ -22,7 +22,7 @@ describe LHC do
22
22
 
23
23
  def before_request
24
24
  if request.response.nil?
25
- return LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from remote cache'), nil)
25
+ return LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok, response_body: 'Im served from remote cache'), nil)
26
26
  end
27
27
  end
28
28
  end
@@ -8,7 +8,7 @@ describe LHC do
8
8
  class CacheInterceptor < LHC::Interceptor
9
9
 
10
10
  def before_request
11
- LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from cache'), nil)
11
+ LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok, response_body: 'Im served from cache'), nil)
12
12
  end
13
13
  end
14
14
  LHC.configure { |c| c.interceptors = [CacheInterceptor] }
@@ -23,7 +23,7 @@ describe LHC do
23
23
  before(:each) do
24
24
  class AnotherInterceptor < LHC::Interceptor
25
25
  def before_request
26
- LHC::Response.new(Typhoeus::Response.new({}), nil)
26
+ LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok), nil)
27
27
  end
28
28
  end
29
29
  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: 13.0.0
4
+ version: 13.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: 2020-09-28 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -357,6 +357,7 @@ files:
357
357
  - spec/interceptors/define_spec.rb
358
358
  - spec/interceptors/dup_spec.rb
359
359
  - spec/interceptors/logging/main_spec.rb
360
+ - spec/interceptors/monitoring/caching_spec.rb
360
361
  - spec/interceptors/monitoring/main_spec.rb
361
362
  - spec/interceptors/prometheus_spec.rb
362
363
  - spec/interceptors/response_competition_spec.rb
@@ -511,6 +512,7 @@ test_files:
511
512
  - spec/interceptors/define_spec.rb
512
513
  - spec/interceptors/dup_spec.rb
513
514
  - spec/interceptors/logging/main_spec.rb
515
+ - spec/interceptors/monitoring/caching_spec.rb
514
516
  - spec/interceptors/monitoring/main_spec.rb
515
517
  - spec/interceptors/prometheus_spec.rb
516
518
  - spec/interceptors/response_competition_spec.rb