atatus 1.3.0 → 1.4.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.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile +72 -22
  4. data/LICENSE +1 -1
  5. data/atatus.gemspec +2 -2
  6. data/lib/atatus.rb +76 -16
  7. data/lib/atatus/agent.rb +78 -29
  8. data/lib/atatus/central_config.rb +72 -27
  9. data/lib/atatus/central_config/cache_control.rb +18 -1
  10. data/lib/atatus/child_durations.rb +64 -0
  11. data/lib/atatus/collector/base.rb +61 -29
  12. data/lib/atatus/collector/builder.rb +46 -2
  13. data/lib/atatus/collector/hist.rb +54 -0
  14. data/lib/atatus/collector/transport.rb +41 -11
  15. data/lib/atatus/config.rb +129 -28
  16. data/lib/atatus/config/bytes.rb +17 -0
  17. data/lib/atatus/config/duration.rb +17 -0
  18. data/lib/atatus/config/options.rb +29 -9
  19. data/lib/atatus/config/regexp_list.rb +17 -0
  20. data/lib/atatus/config/wildcard_pattern_list.rb +64 -0
  21. data/lib/atatus/context.rb +32 -1
  22. data/lib/atatus/context/request.rb +17 -0
  23. data/lib/atatus/context/request/socket.rb +18 -1
  24. data/lib/atatus/context/request/url.rb +17 -0
  25. data/lib/atatus/context/response.rb +27 -2
  26. data/lib/atatus/context/user.rb +17 -0
  27. data/lib/atatus/context_builder.rb +19 -4
  28. data/lib/atatus/deprecations.rb +17 -0
  29. data/lib/atatus/error.rb +27 -0
  30. data/lib/atatus/error/exception.rb +24 -0
  31. data/lib/atatus/error/log.rb +17 -0
  32. data/lib/atatus/error_builder.rb +17 -2
  33. data/lib/atatus/grape.rb +62 -0
  34. data/lib/atatus/graphql.rb +91 -0
  35. data/lib/atatus/grpc.rb +99 -0
  36. data/lib/atatus/instrumenter.rb +135 -30
  37. data/lib/atatus/internal_error.rb +17 -0
  38. data/lib/atatus/logging.rb +17 -2
  39. data/lib/atatus/metadata.rb +17 -0
  40. data/lib/atatus/metadata/process_info.rb +17 -0
  41. data/lib/atatus/metadata/service_info.rb +21 -6
  42. data/lib/atatus/metadata/system_info.rb +22 -3
  43. data/lib/atatus/metadata/system_info/container_info.rb +49 -10
  44. data/lib/atatus/metadata/system_info/hw_info.rb +1 -1
  45. data/lib/atatus/metrics.rb +69 -27
  46. data/lib/atatus/metrics/breakdown_set.rb +31 -0
  47. data/lib/atatus/metrics/{cpu_mem.rb → cpu_mem_set.rb} +110 -63
  48. data/lib/atatus/metrics/metric.rb +140 -0
  49. data/lib/atatus/metrics/set.rb +123 -0
  50. data/lib/atatus/metrics/span_scoped_set.rb +56 -0
  51. data/lib/atatus/metrics/transaction_set.rb +26 -0
  52. data/lib/atatus/metrics/vm_set.rb +58 -0
  53. data/lib/atatus/metricset.rb +48 -4
  54. data/lib/atatus/middleware.rb +28 -8
  55. data/lib/atatus/naively_hashable.rb +17 -0
  56. data/lib/atatus/normalizers.rb +23 -9
  57. data/lib/atatus/normalizers/grape.rb +22 -0
  58. data/lib/atatus/normalizers/grape/endpoint_run.rb +65 -0
  59. data/lib/atatus/normalizers/rails.rb +27 -0
  60. data/lib/atatus/normalizers/rails/action_controller.rb +44 -0
  61. data/lib/atatus/normalizers/rails/action_mailer.rb +43 -0
  62. data/lib/atatus/normalizers/{action_view.rb → rails/action_view.rb} +17 -0
  63. data/lib/atatus/normalizers/rails/active_record.rb +80 -0
  64. data/lib/atatus/opentracing.rb +75 -42
  65. data/lib/atatus/rails.rb +29 -13
  66. data/lib/atatus/railtie.rb +19 -6
  67. data/lib/atatus/resque.rb +29 -0
  68. data/lib/atatus/sinatra.rb +53 -0
  69. data/lib/atatus/span.rb +44 -15
  70. data/lib/atatus/span/context.rb +43 -28
  71. data/lib/atatus/span/context/db.rb +43 -0
  72. data/lib/atatus/span/context/destination.rb +77 -0
  73. data/lib/atatus/span/context/http.rb +43 -0
  74. data/lib/atatus/span_helpers.rb +18 -1
  75. data/lib/atatus/spies.rb +33 -15
  76. data/lib/atatus/spies/action_dispatch.rb +27 -6
  77. data/lib/atatus/spies/delayed_job.rb +26 -5
  78. data/lib/atatus/spies/dynamo_db.rb +62 -0
  79. data/lib/atatus/spies/elasticsearch.rb +53 -7
  80. data/lib/atatus/spies/faraday.rb +54 -20
  81. data/lib/atatus/spies/http.rb +36 -6
  82. data/lib/atatus/spies/json.rb +18 -0
  83. data/lib/atatus/spies/mongo.rb +41 -10
  84. data/lib/atatus/spies/net_http.rb +52 -11
  85. data/lib/atatus/spies/rake.rb +42 -23
  86. data/lib/atatus/spies/redis.rb +17 -0
  87. data/lib/atatus/spies/resque.rb +57 -0
  88. data/lib/atatus/spies/sequel.rb +54 -17
  89. data/lib/atatus/spies/shoryuken.rb +69 -0
  90. data/lib/atatus/spies/sidekiq.rb +46 -25
  91. data/lib/atatus/spies/sinatra.rb +20 -4
  92. data/lib/atatus/spies/sneakers.rb +74 -0
  93. data/lib/atatus/spies/sucker_punch.rb +58 -0
  94. data/lib/atatus/spies/tilt.rb +20 -1
  95. data/lib/atatus/sql.rb +36 -0
  96. data/lib/atatus/sql/signature.rb +169 -0
  97. data/lib/atatus/sql/tokenizer.rb +264 -0
  98. data/lib/atatus/sql/tokens.rb +63 -0
  99. data/lib/atatus/sql_summarizer.rb +24 -6
  100. data/lib/atatus/stacktrace.rb +17 -0
  101. data/lib/atatus/stacktrace/frame.rb +17 -3
  102. data/lib/atatus/stacktrace_builder.rb +23 -3
  103. data/lib/atatus/subscriber.rb +23 -4
  104. data/lib/atatus/trace_context.rb +84 -51
  105. data/lib/atatus/trace_context/traceparent.rb +111 -0
  106. data/lib/atatus/trace_context/tracestate.rb +148 -0
  107. data/lib/atatus/transaction.rb +74 -18
  108. data/lib/atatus/transport/base.rb +44 -27
  109. data/lib/atatus/transport/connection.rb +28 -72
  110. data/lib/atatus/transport/connection/http.rb +58 -35
  111. data/lib/atatus/transport/connection/proxy_pipe.rb +24 -5
  112. data/lib/atatus/transport/filters.rb +18 -1
  113. data/lib/atatus/transport/filters/hash_sanitizer.rb +77 -0
  114. data/lib/atatus/transport/filters/secrets_filter.rb +30 -55
  115. data/lib/atatus/transport/headers.rb +83 -0
  116. data/lib/atatus/transport/serializers.rb +17 -5
  117. data/lib/atatus/transport/serializers/context_serializer.rb +30 -3
  118. data/lib/atatus/transport/serializers/error_serializer.rb +17 -2
  119. data/lib/atatus/transport/serializers/metadata_serializer.rb +44 -22
  120. data/lib/atatus/transport/serializers/metricset_serializer.rb +34 -6
  121. data/lib/atatus/transport/serializers/span_serializer.rb +47 -12
  122. data/lib/atatus/transport/serializers/transaction_serializer.rb +18 -2
  123. data/lib/atatus/transport/user_agent.rb +48 -0
  124. data/lib/atatus/transport/worker.rb +31 -7
  125. data/lib/atatus/util.rb +18 -1
  126. data/lib/atatus/util/inflector.rb +17 -0
  127. data/lib/atatus/util/lru_cache.rb +17 -0
  128. data/lib/atatus/util/throttle.rb +17 -0
  129. data/lib/atatus/version.rb +19 -1
  130. metadata +46 -26
  131. data/Rakefile +0 -19
  132. data/bench/.gitignore +0 -2
  133. data/bench/app.rb +0 -53
  134. data/bench/benchmark.rb +0 -36
  135. data/bench/report.rb +0 -55
  136. data/bench/rubyprof.rb +0 -39
  137. data/bench/stackprof.rb +0 -23
  138. data/bin/build_docs +0 -5
  139. data/bin/console +0 -15
  140. data/bin/setup +0 -8
  141. data/bin/with_framework +0 -7
  142. data/lib/atatus/metrics/vm.rb +0 -60
  143. data/lib/atatus/normalizers/action_controller.rb +0 -27
  144. data/lib/atatus/normalizers/action_mailer.rb +0 -26
  145. data/lib/atatus/normalizers/active_record.rb +0 -45
  146. data/lib/atatus/util/prefixed_logger.rb +0 -18
  147. data/vendor/.gitkeep +0 -0
@@ -0,0 +1,148 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Atatus
21
+ class TraceContext
22
+ # @api private
23
+ class Tracestate
24
+ # @api private
25
+ class Entry
26
+ def initialize(key, value)
27
+ @key = key
28
+ @value = value
29
+ end
30
+
31
+ attr_reader :key, :value
32
+
33
+ def to_s
34
+ "#{key}=#{value}"
35
+ end
36
+ end
37
+
38
+ class EsEntry
39
+ ASSIGN = ':'
40
+ SPLIT = ';'
41
+
42
+ SHORT_TO_LONG = { 's' => 'sample_rate' }
43
+ LONG_TO_SHORT = { 'sample_rate' => 's' }
44
+
45
+ def initialize(values = nil)
46
+ parse(values)
47
+ end
48
+
49
+ attr_reader :sample_rate
50
+
51
+ def key
52
+ 'es'
53
+ end
54
+
55
+ def value
56
+ LONG_TO_SHORT.map do |l, s|
57
+ "#{s}#{ASSIGN}#{send(l)}"
58
+ end.join(SPLIT)
59
+ end
60
+
61
+ def empty?
62
+ !sample_rate
63
+ end
64
+
65
+ def sample_rate=(val)
66
+ float = Float(val).round(3)
67
+
68
+ return nil unless (0.0..1.0).include?(float)
69
+
70
+ @sample_rate = float
71
+ rescue ArgumentError => e
72
+ nil
73
+ end
74
+
75
+ def to_s
76
+ return nil if empty?
77
+
78
+ "es=#{value}"
79
+ end
80
+
81
+ private
82
+
83
+ def parse(values)
84
+ return unless values
85
+
86
+ values.split(SPLIT).map do |kv|
87
+ k, v = kv.split(ASSIGN)
88
+ next unless SHORT_TO_LONG.keys.include?(k)
89
+ send("#{SHORT_TO_LONG[k]}=", v)
90
+ end
91
+ end
92
+ end
93
+
94
+ extend Forwardable
95
+
96
+ def initialize(entries: {}, sample_rate: nil)
97
+ @entries = entries
98
+
99
+ self.sample_rate = sample_rate if sample_rate
100
+ end
101
+
102
+ attr_accessor :entries
103
+
104
+ def_delegators :es_entry, :sample_rate, :sample_rate=
105
+
106
+ def self.parse(header)
107
+ entries =
108
+ split_by_nl_and_comma(header)
109
+ .each_with_object({}) do |entry, hsh|
110
+ k, v = entry.split('=')
111
+
112
+ hsh[k] =
113
+ case k
114
+ when 'es' then EsEntry.new(v)
115
+ else Entry.new(k, v)
116
+ end
117
+ end
118
+
119
+ new(entries: entries)
120
+ end
121
+
122
+ def to_header
123
+ return "" unless entries.any?
124
+
125
+ entries.values.map(&:to_s).join(',')
126
+ end
127
+
128
+ private
129
+
130
+ def es_entry
131
+ # lazy generate this so we only add it if necessary
132
+ entries['es'] ||= EsEntry.new
133
+ end
134
+
135
+ class << self
136
+ private
137
+
138
+ def split_by_nl_and_comma(str)
139
+ # HTTP allows multiple headers with the same name, eg. multiple
140
+ # Set-Cookie headers per response.
141
+ # Rack handles this by joining the headers under the same key, separated
142
+ # by newlines, see https://www.rubydoc.info/github/rack/rack/file/SPEC
143
+ String(str).split("\n").map { |s| s.split(',') }.flatten
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -1,36 +1,71 @@
1
- # frozen_string_literal: true
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
2
17
 
3
- require 'securerandom'
4
- require 'forwardable'
18
+ # frozen_string_literal: true
5
19
 
6
20
  module Atatus
7
21
  # @api private
8
22
  class Transaction
9
23
  extend Forwardable
24
+ include ChildDurations::Methods
10
25
 
11
26
  def_delegators :@trace_context,
12
27
  :trace_id, :parent_id, :id, :ensure_parent_id
13
28
 
14
29
  DEFAULT_TYPE = 'custom'
30
+ MUTEX = Mutex.new
15
31
 
16
32
  # rubocop:disable Metrics/ParameterLists
17
33
  def initialize(
18
34
  name = nil,
19
35
  type = nil,
20
36
  sampled: true,
37
+ sample_rate: 1,
21
38
  context: nil,
22
- labels: nil,
39
+ config:,
23
40
  trace_context: nil
24
41
  )
25
42
  @name = name
26
43
  @type = type || DEFAULT_TYPE
44
+ @config = config
45
+
46
+ # Cache these values in case they are changed during the
47
+ # transaction's lifetime via the remote config
48
+ @span_frames_min_duration = config.span_frames_min_duration
49
+ @collect_metrics = config.collect_metrics?
50
+ @breakdown_metrics = config.breakdown_metrics?
51
+ @framework_name = config.framework_name
52
+ @transaction_max_spans = config.transaction_max_spans
53
+ @default_labels = config.default_labels
27
54
 
28
55
  @sampled = sampled
56
+ @sample_rate = sample_rate
29
57
 
30
58
  @context = context || Context.new # TODO: Lazy generate this?
31
- Util.reverse_merge!(@context.labels, labels) if labels
59
+ if @default_labels
60
+ Util.reverse_merge!(@context.labels, @default_labels)
61
+ end
32
62
 
33
- @trace_context = trace_context || TraceContext.new(recorded: sampled)
63
+ unless (@trace_context = trace_context)
64
+ @trace_context = TraceContext.new(
65
+ traceparent: TraceContext::Traceparent.new(recorded: sampled),
66
+ tracestate: TraceContext::Tracestate.new(sample_rate: sampled ? sample_rate : 0)
67
+ )
68
+ end
34
69
 
35
70
  @started_spans = 0
36
71
  @dropped_spans = 0
@@ -41,8 +76,24 @@ module Atatus
41
76
 
42
77
  attr_accessor :name, :type, :result, :spans, :ruby_time
43
78
 
44
- attr_reader :context, :duration, :started_spans, :dropped_spans,
45
- :timestamp, :trace_context, :notifications
79
+ attr_reader(
80
+ :breakdown_metrics,
81
+ :collect_metrics,
82
+ :context,
83
+ :dropped_spans,
84
+ :duration,
85
+ :framework_name,
86
+ :notifications,
87
+ :self_time,
88
+ :sample_rate,
89
+ :span_frames_min_duration,
90
+ :started_spans,
91
+ :timestamp,
92
+ :trace_context,
93
+ :transaction_max_spans
94
+ )
95
+
96
+ alias :collect_metrics? :collect_metrics
46
97
 
47
98
  def sampled?
48
99
  @sampled
@@ -63,6 +114,8 @@ module Atatus
63
114
  def stop(clock_end = Util.monotonic_micros)
64
115
  raise 'Transaction not yet start' unless timestamp
65
116
  @duration = clock_end - @clock_start
117
+ @self_time = @duration - child_durations.duration
118
+
66
119
  self
67
120
  end
68
121
 
@@ -75,21 +128,24 @@ module Atatus
75
128
  # spans
76
129
 
77
130
  def inc_started_spans!
78
- @started_spans += 1
131
+ MUTEX.synchronize do
132
+ @started_spans += 1
133
+ if @started_spans > transaction_max_spans
134
+ @dropped_spans += 1
135
+ return false
136
+ end
137
+ end
138
+ true
79
139
  end
80
140
 
81
- def inc_dropped_spans!
82
- @dropped_spans += 1
83
- end
141
+ # context
84
142
 
85
- def max_spans_reached?(config)
86
- started_spans > config.transaction_max_spans
143
+ def add_response(status = nil, **args)
144
+ context.response = Context::Response.new(status, **args)
87
145
  end
88
146
 
89
- # context
90
-
91
- def add_response(*args)
92
- context.response = Context::Response.new(*args)
147
+ def set_user(user)
148
+ context.user = Context::User.infer(@config, user)
93
149
  end
94
150
 
95
151
  def inspect
@@ -1,15 +1,35 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  require 'atatus/metadata'
21
+ require 'atatus/transport/user_agent'
22
+ require 'atatus/transport/headers'
4
23
  require 'atatus/transport/connection'
5
24
  require 'atatus/transport/worker'
6
25
  require 'atatus/transport/serializers'
7
26
  require 'atatus/transport/filters'
27
+ require 'atatus/transport/connection/http'
28
+
8
29
  require 'atatus/util/throttle'
9
30
 
10
31
  module Atatus
11
32
  module Transport
12
- # rubocop:disable Metrics/ClassLength
13
33
  # @api private
14
34
  class Base
15
35
  include Logging
@@ -28,7 +48,6 @@ module Atatus
28
48
  @stopped = Concurrent::AtomicBoolean.new
29
49
  @workers = Array.new(config.pool_size)
30
50
 
31
- @watcher_mutex = Mutex.new
32
51
  @worker_mutex = Mutex.new
33
52
  end
34
53
 
@@ -36,9 +55,13 @@ module Atatus
36
55
 
37
56
  def start
38
57
  debug '%s: Starting Transport', pid_str
58
+ # Set @stopped to false first, in case transport is restarted;
59
+ # ensure_worker_count requires @stopped to be false
60
+ # ~estolfo
61
+ @stopped.make_false unless @stopped.false?
39
62
 
40
- ensure_watcher_running
41
63
  ensure_worker_count
64
+ create_watcher
42
65
  end
43
66
 
44
67
  def stop
@@ -50,14 +73,13 @@ module Atatus
50
73
  stop_workers
51
74
  end
52
75
 
53
- # rubocop:disable Metrics/MethodLength
54
76
  def submit(resource)
55
77
  if @stopped.true?
56
78
  warn '%s: Transport stopping, no new events accepted', pid_str
79
+ debug 'Dropping: %s', resource.inspect
57
80
  return false
58
81
  end
59
82
 
60
- ensure_watcher_running
61
83
  queue.push(resource, true)
62
84
 
63
85
  true
@@ -68,31 +90,31 @@ module Atatus
68
90
  error '%s: Failed adding to the transport queue: %p', pid_str, e.inspect
69
91
  nil
70
92
  end
71
- # rubocop:enable Metrics/MethodLength
72
93
 
73
94
  def add_filter(key, callback)
74
95
  @filters.add(key, callback)
75
96
  end
76
97
 
98
+ def handle_forking!
99
+ # We can't just stop and start again because the StopMessage
100
+ # will then be the first message processed when the transport is
101
+ # restarted.
102
+ stop_watcher
103
+ ensure_worker_count
104
+ create_watcher
105
+ end
106
+
77
107
  private
78
108
 
79
109
  def pid_str
80
110
  format('[PID:%s]', Process.pid)
81
111
  end
82
112
 
83
- def ensure_watcher_running
84
- # pid has changed == we've forked
85
- return if @pid == Process.pid
86
-
87
- @watcher_mutex.synchronize do
88
- return if @pid == Process.pid
89
- @pid = Process.pid
90
-
91
- @watcher = Concurrent::TimerTask.execute(
92
- execution_interval: WATCHER_EXECUTION_INTERVAL,
93
- timeout_interval: WATCHER_TIMEOUT_INTERVAL
94
- ) { ensure_worker_count }
95
- end
113
+ def create_watcher
114
+ @watcher = Concurrent::TimerTask.execute(
115
+ execution_interval: WATCHER_EXECUTION_INTERVAL,
116
+ timeout_interval: WATCHER_TIMEOUT_INTERVAL
117
+ ) { ensure_worker_count }
96
118
  end
97
119
 
98
120
  def ensure_worker_count
@@ -124,7 +146,6 @@ module Atatus
124
146
  end
125
147
  end
126
148
 
127
- # rubocop:disable Metrics/MethodLength
128
149
  def stop_workers
129
150
  debug '%s: Stopping workers', pid_str
130
151
 
@@ -142,10 +163,10 @@ module Atatus
142
163
  thread.kill
143
164
  end
144
165
 
145
- @workers.clear
166
+ # Maintain the @worker array size for when transport is restarted
167
+ @workers.fill(nil)
146
168
  end
147
169
  end
148
- # rubocop:enable Metrics/MethodLength
149
170
 
150
171
  def send_stop_messages
151
172
  config.pool_size.times { queue.push(Worker::StopMessage.new, true) }
@@ -154,10 +175,7 @@ module Atatus
154
175
  end
155
176
 
156
177
  def stop_watcher
157
- @watcher_mutex.synchronize do
158
- return if watcher.nil? || @pid != Process.pid
159
- watcher.shutdown
160
- end
178
+ watcher&.shutdown
161
179
  end
162
180
 
163
181
  def throttled_queue_full_warning
@@ -169,6 +187,5 @@ module Atatus
169
187
  end).call
170
188
  end
171
189
  end
172
- # rubocop:enable Metrics/ClassLength
173
190
  end
174
191
  end