unleash 6.4.1 → 6.5.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/CHANGELOG.md +5 -0
- data/README.md +51 -1
- data/lib/unleash/client.rb +5 -0
- data/lib/unleash/configuration.rb +6 -6
- data/lib/unleash/environment_resolver.rb +27 -0
- data/lib/unleash/impact_metrics.rb +43 -0
- data/lib/unleash/metrics_reporter.rb +26 -4
- data/lib/unleash/version.rb +1 -1
- data/unleash-client.gemspec +1 -2
- metadata +7 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f2aacf28333e06f4ca46369b9ece6fd1472a8e363cdc4a0fe1aeb0810d879a2
|
|
4
|
+
data.tar.gz: bc8e366ec18bff858f835260af79650ab6179daa6af93bf84a675eb3d74ad904
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c397ef806b4e77fb8a1d8afbacd3ded420f5979fcf1999b03e524d77dca3f04205fa28e4a1e83037c7670837654c5f0e193f74b53654dedb034c1e9016fb22e
|
|
7
|
+
data.tar.gz: 07bfa665ecb17ffbe86b9dbb66c3247d824a27e27d846000f181ad5783d8af3a6e9adb9a47d2e2f37732bec80c137683609b63726cd58616164efe464a4fcd4c
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -28,7 +28,7 @@ You can use this client with [Unleash Enterprise](https://www.getunleash.io/pric
|
|
|
28
28
|
Add this line to your application's Gemfile:
|
|
29
29
|
|
|
30
30
|
```ruby
|
|
31
|
-
gem 'unleash', '~> 6.
|
|
31
|
+
gem 'unleash', '~> 6.5.0'
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
And then execute:
|
|
@@ -534,6 +534,56 @@ Unleash.configure do |config|
|
|
|
534
534
|
end
|
|
535
535
|
```
|
|
536
536
|
|
|
537
|
+
## Impact metrics
|
|
538
|
+
|
|
539
|
+
Impact metrics are lightweight, application-level time-series metrics stored and visualized directly inside Unleash. They allow you to connect specific application data, such as request counts, error rates, or latency, to your feature flags and release plans.
|
|
540
|
+
|
|
541
|
+
These metrics help validate feature impact and automate release processes. For instance, you can monitor usage patterns or performance to determine if a feature meets its goals.
|
|
542
|
+
|
|
543
|
+
The SDK automatically attaches context labels to metrics: `appName` and `environment`.
|
|
544
|
+
|
|
545
|
+
### Counters
|
|
546
|
+
|
|
547
|
+
Use counters for cumulative values that only increase (total requests, errors):
|
|
548
|
+
|
|
549
|
+
```ruby
|
|
550
|
+
client = Unleash::Client.new
|
|
551
|
+
|
|
552
|
+
client.impact_metrics.define_counter(
|
|
553
|
+
'request_count',
|
|
554
|
+
'Total number of HTTP requests processed'
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
client.impact_metrics.increment_counter('request_count')
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Gauges
|
|
561
|
+
|
|
562
|
+
Use gauges for point-in-time values that can go up or down:
|
|
563
|
+
|
|
564
|
+
```ruby
|
|
565
|
+
client.impact_metrics.define_gauge(
|
|
566
|
+
'total_users',
|
|
567
|
+
'Total number of registered users'
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
client.impact_metrics.update_gauge('total_users', user_count)
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Histograms
|
|
574
|
+
|
|
575
|
+
Histograms measure value distribution (request duration, response size):
|
|
576
|
+
|
|
577
|
+
```ruby
|
|
578
|
+
client.impact_metrics.define_histogram(
|
|
579
|
+
'request_time_ms',
|
|
580
|
+
'Time taken to process a request in milliseconds',
|
|
581
|
+
[50, 100, 200, 500, 1000]
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
client.impact_metrics.observe_histogram('request_time_ms', 125)
|
|
585
|
+
```
|
|
586
|
+
|
|
537
587
|
## Development
|
|
538
588
|
|
|
539
589
|
After checking out the repo, run `bin/setup` to install dependencies.
|
data/lib/unleash/client.rb
CHANGED
|
@@ -6,12 +6,15 @@ require 'unleash/streaming_client_executor'
|
|
|
6
6
|
require 'unleash/variant'
|
|
7
7
|
require 'unleash/util/http'
|
|
8
8
|
require 'unleash/util/event_source_wrapper'
|
|
9
|
+
require 'unleash/environment_resolver'
|
|
10
|
+
require 'unleash/impact_metrics'
|
|
9
11
|
require 'logger'
|
|
10
12
|
require 'time'
|
|
11
13
|
|
|
12
14
|
module Unleash
|
|
13
15
|
class Client
|
|
14
16
|
attr_accessor :fetcher_scheduled_executor, :metrics_scheduled_executor
|
|
17
|
+
attr_reader :impact_metrics
|
|
15
18
|
|
|
16
19
|
# rubocop:disable Metrics/AbcSize
|
|
17
20
|
def initialize(*opts)
|
|
@@ -23,6 +26,8 @@ module Unleash
|
|
|
23
26
|
Unleash.engine = YggdrasilEngine.new
|
|
24
27
|
Unleash.engine.register_custom_strategies(Unleash.configuration.strategies.custom_strategies)
|
|
25
28
|
|
|
29
|
+
@impact_metrics = ImpactMetrics.new(Unleash.engine, Unleash.configuration.app_name)
|
|
30
|
+
|
|
26
31
|
Unleash.toggle_fetcher = Unleash::ToggleFetcher.new Unleash.engine unless Unleash.configuration.streaming_mode?
|
|
27
32
|
|
|
28
33
|
if Unleash.configuration.disable_client
|
|
@@ -107,6 +107,12 @@ module Unleash
|
|
|
107
107
|
self.experimental_mode[:format] == 'delta'
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
def generate_custom_http_headers
|
|
111
|
+
return self.custom_http_headers.call if self.custom_http_headers.respond_to?(:call)
|
|
112
|
+
|
|
113
|
+
self.custom_http_headers
|
|
114
|
+
end
|
|
115
|
+
|
|
110
116
|
private
|
|
111
117
|
|
|
112
118
|
def set_defaults
|
|
@@ -153,12 +159,6 @@ module Unleash
|
|
|
153
159
|
raise ArgumentError, "custom_http_headers must be a Hash or a Proc."
|
|
154
160
|
end
|
|
155
161
|
|
|
156
|
-
def generate_custom_http_headers
|
|
157
|
-
return self.custom_http_headers.call if self.custom_http_headers.respond_to?(:call)
|
|
158
|
-
|
|
159
|
-
self.custom_http_headers
|
|
160
|
-
end
|
|
161
|
-
|
|
162
162
|
def set_option(opt, val)
|
|
163
163
|
__send__("#{opt}=", val)
|
|
164
164
|
rescue NoMethodError
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Unleash
|
|
2
|
+
class EnvironmentResolver
|
|
3
|
+
def self.extract_environment_from_custom_headers(custom_headers)
|
|
4
|
+
authorization_header = extract_authorization_header(custom_headers)
|
|
5
|
+
extract_environment_from_header(authorization_header)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.extract_authorization_header(custom_headers)
|
|
9
|
+
return nil if custom_headers.nil? || !custom_headers.is_a?(Hash)
|
|
10
|
+
|
|
11
|
+
key = custom_headers.keys.find{ |k| k.to_s.downcase == 'authorization' }
|
|
12
|
+
custom_headers[key] if key
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.extract_environment_from_header(authorization_header)
|
|
16
|
+
return nil if authorization_header.nil? || authorization_header.empty?
|
|
17
|
+
|
|
18
|
+
after_colon = authorization_header.split(':', 2)[1]
|
|
19
|
+
return nil unless after_colon&.include?('.')
|
|
20
|
+
|
|
21
|
+
environment = after_colon.split('.')[0]
|
|
22
|
+
environment unless environment.empty?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private_class_method :extract_authorization_header, :extract_environment_from_header
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Unleash
|
|
2
|
+
class ImpactMetrics
|
|
3
|
+
def initialize(engine, app_name)
|
|
4
|
+
@engine = engine
|
|
5
|
+
@app_name = app_name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def define_counter(name, help_text)
|
|
9
|
+
@engine.define_counter(name, help_text)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def increment_counter(name, value = 1)
|
|
13
|
+
@engine.inc_counter(name, value, base_labels)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def define_gauge(name, help_text)
|
|
17
|
+
@engine.define_gauge(name, help_text)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def update_gauge(name, value)
|
|
21
|
+
@engine.set_gauge(name, value, base_labels)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def define_histogram(name, help_text, buckets = nil)
|
|
25
|
+
@engine.define_histogram(name, help_text, buckets)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def observe_histogram(name, value)
|
|
29
|
+
@engine.observe_histogram(name, value, base_labels)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def base_labels
|
|
35
|
+
{
|
|
36
|
+
'appName' => @app_name,
|
|
37
|
+
'environment' => EnvironmentResolver.extract_environment_from_custom_headers(
|
|
38
|
+
Unleash.configuration.generate_custom_http_headers
|
|
39
|
+
) || Unleash.configuration.environment
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -23,7 +23,8 @@ module Unleash
|
|
|
23
23
|
def post
|
|
24
24
|
Unleash.logger.debug "post() Report"
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
impact_metrics = collect_impact_metrics_safely
|
|
27
|
+
report = build_report(impact_metrics)
|
|
27
28
|
return unless report
|
|
28
29
|
|
|
29
30
|
send_report(report)
|
|
@@ -44,15 +45,20 @@ module Unleash
|
|
|
44
45
|
}
|
|
45
46
|
end
|
|
46
47
|
|
|
47
|
-
def build_report
|
|
48
|
+
def build_report(impact_metrics)
|
|
48
49
|
report = generate_report
|
|
49
|
-
|
|
50
|
+
has_data = !report.nil? || !impact_metrics.empty?
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
return nil if !has_data && Time.now - self.last_time < LONGEST_WITHOUT_A_REPORT
|
|
53
|
+
|
|
54
|
+
report ||= generate_report_from_bucket({
|
|
52
55
|
'start': self.last_time.utc.iso8601,
|
|
53
56
|
'stop': Time.now.utc.iso8601,
|
|
54
57
|
'toggles': {}
|
|
55
58
|
})
|
|
59
|
+
|
|
60
|
+
report[:impactMetrics] = impact_metrics unless impact_metrics.empty?
|
|
61
|
+
report
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
def send_report(report)
|
|
@@ -66,8 +72,24 @@ module Unleash
|
|
|
66
72
|
else
|
|
67
73
|
# :nocov:
|
|
68
74
|
Unleash.logger.error "Error when sending report to unleash server. Server responded with http code #{response.code}."
|
|
75
|
+
restore_impact_metrics(report[:impactMetrics])
|
|
69
76
|
# :nocov:
|
|
70
77
|
end
|
|
71
78
|
end
|
|
79
|
+
|
|
80
|
+
def restore_impact_metrics(impact_metrics)
|
|
81
|
+
return if impact_metrics.nil? || impact_metrics.empty?
|
|
82
|
+
|
|
83
|
+
Unleash.engine&.restore_impact_metrics(impact_metrics)
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
Unleash.logger.warn "Failed to restore impact metrics: #{e.message}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def collect_impact_metrics_safely
|
|
89
|
+
Unleash.engine&.collect_impact_metrics || []
|
|
90
|
+
rescue StandardError => e
|
|
91
|
+
Unleash.logger.warn "Failed to collect impact metrics: #{e.message}"
|
|
92
|
+
[]
|
|
93
|
+
end
|
|
72
94
|
end
|
|
73
95
|
end
|
data/lib/unleash/version.rb
CHANGED
data/unleash-client.gemspec
CHANGED
|
@@ -24,12 +24,11 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
spec.required_ruby_version = ">= 2.7"
|
|
25
25
|
|
|
26
26
|
spec.add_dependency "ld-eventsource", "2.2.4" unless RUBY_ENGINE == 'jruby'
|
|
27
|
-
spec.add_dependency "yggdrasil-engine", "~> 1.
|
|
27
|
+
spec.add_dependency "yggdrasil-engine", "~> 1.2.1"
|
|
28
28
|
|
|
29
29
|
spec.add_dependency "base64", "~> 0.3.0"
|
|
30
30
|
spec.add_dependency "logger", "~> 1.6"
|
|
31
31
|
|
|
32
|
-
spec.add_development_dependency "bundler", "~> 2.1"
|
|
33
32
|
spec.add_development_dependency "rake", "~> 12.3"
|
|
34
33
|
spec.add_development_dependency "rspec", "~> 3.12"
|
|
35
34
|
spec.add_development_dependency "rspec-json_expectations", "~> 2.2"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: unleash
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Renato Arruda
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ld-eventsource
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.
|
|
33
|
+
version: 1.2.1
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.
|
|
40
|
+
version: 1.2.1
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: base64
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,20 +66,6 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '1.6'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: bundler
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '2.1'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '2.1'
|
|
83
69
|
- !ruby/object:Gem::Dependency
|
|
84
70
|
name: rake
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -219,6 +205,8 @@ files:
|
|
|
219
205
|
- lib/unleash/client.rb
|
|
220
206
|
- lib/unleash/configuration.rb
|
|
221
207
|
- lib/unleash/context.rb
|
|
208
|
+
- lib/unleash/environment_resolver.rb
|
|
209
|
+
- lib/unleash/impact_metrics.rb
|
|
222
210
|
- lib/unleash/metrics_reporter.rb
|
|
223
211
|
- lib/unleash/scheduled_executor.rb
|
|
224
212
|
- lib/unleash/spec_version.rb
|
|
@@ -251,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
251
239
|
- !ruby/object:Gem::Version
|
|
252
240
|
version: '0'
|
|
253
241
|
requirements: []
|
|
254
|
-
rubygems_version: 3.
|
|
242
|
+
rubygems_version: 3.5.22
|
|
255
243
|
signing_key:
|
|
256
244
|
specification_version: 4
|
|
257
245
|
summary: Unleash feature toggle client.
|