atatus 1.6.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile +49 -13
  4. data/LICENSE +1 -1
  5. data/atatus.gemspec +3 -3
  6. data/lib/atatus/agent.rb +18 -7
  7. data/lib/atatus/central_config.rb +19 -8
  8. data/lib/atatus/collector/base.rb +113 -1
  9. data/lib/atatus/collector/builder.rb +8 -0
  10. data/lib/atatus/collector/layer.rb +1 -1
  11. data/lib/atatus/collector/transport.rb +23 -2
  12. data/lib/atatus/{sql_summarizer.rb → config/log_level_map.rb} +22 -28
  13. data/lib/atatus/config/options.rb +2 -1
  14. data/lib/atatus/config/regexp_list.rb +1 -1
  15. data/lib/atatus/config/round_float.rb +31 -0
  16. data/lib/atatus/config/server_info.rb +50 -0
  17. data/lib/atatus/config/wildcard_pattern_list.rb +3 -1
  18. data/lib/atatus/config.rb +94 -70
  19. data/lib/atatus/context/company.rb +38 -0
  20. data/lib/atatus/context/request/socket.rb +1 -2
  21. data/lib/atatus/context/response.rb +1 -3
  22. data/lib/atatus/context.rb +4 -8
  23. data/lib/atatus/context_builder.rb +3 -3
  24. data/lib/atatus/error.rb +2 -1
  25. data/lib/atatus/error_builder.rb +1 -1
  26. data/lib/atatus/fields.rb +98 -0
  27. data/lib/atatus/graphql.rb +2 -0
  28. data/lib/atatus/grpc.rb +5 -7
  29. data/lib/atatus/instrumenter.rb +38 -24
  30. data/lib/atatus/metadata/cloud_info.rb +156 -0
  31. data/lib/atatus/metadata/service_info.rb +3 -3
  32. data/lib/atatus/metadata/system_info/container_info.rb +20 -8
  33. data/lib/atatus/metadata/system_info.rb +20 -5
  34. data/lib/atatus/metadata.rb +3 -1
  35. data/lib/atatus/metrics/cpu_mem_set.rb +10 -38
  36. data/lib/atatus/metrics/jvm_set.rb +88 -0
  37. data/lib/atatus/metrics/metric.rb +2 -0
  38. data/lib/atatus/metrics.rb +32 -16
  39. data/lib/atatus/middleware.rb +8 -3
  40. data/lib/atatus/naively_hashable.rb +1 -0
  41. data/lib/atatus/normalizers/rails/active_record.rb +25 -7
  42. data/lib/atatus/normalizers.rb +2 -2
  43. data/lib/atatus/opentracing.rb +5 -3
  44. data/lib/atatus/rails.rb +1 -1
  45. data/lib/atatus/span/context/db.rb +1 -1
  46. data/lib/atatus/span/context/destination.rb +58 -32
  47. data/lib/atatus/span/context/http.rb +2 -0
  48. data/lib/atatus/span/context/links.rb +32 -0
  49. data/lib/atatus/{sql.rb → span/context/message.rb} +16 -12
  50. data/lib/atatus/span/context/service.rb +55 -0
  51. data/lib/atatus/span/context.rb +28 -3
  52. data/lib/atatus/span.rb +35 -5
  53. data/lib/atatus/span_helpers.rb +12 -23
  54. data/lib/atatus/spies/action_dispatch.rb +10 -13
  55. data/lib/atatus/spies/azure_storage_table.rb +148 -0
  56. data/lib/atatus/spies/delayed_job.rb +19 -13
  57. data/lib/atatus/spies/dynamo_db.rb +56 -15
  58. data/lib/atatus/spies/elasticsearch.rb +54 -39
  59. data/lib/atatus/spies/faraday.rb +92 -58
  60. data/lib/atatus/spies/http.rb +33 -37
  61. data/lib/atatus/spies/json.rb +5 -9
  62. data/lib/atatus/spies/mongo.rb +26 -19
  63. data/lib/atatus/spies/net_http.rb +53 -51
  64. data/lib/atatus/spies/racecar.rb +77 -0
  65. data/lib/atatus/spies/rake.rb +27 -27
  66. data/lib/atatus/spies/redis.rb +11 -12
  67. data/lib/atatus/spies/resque.rb +18 -23
  68. data/lib/atatus/spies/s3.rb +132 -0
  69. data/lib/atatus/spies/sequel.rb +50 -40
  70. data/lib/atatus/spies/shoryuken.rb +4 -6
  71. data/lib/atatus/spies/sidekiq.rb +23 -31
  72. data/lib/atatus/spies/sinatra.rb +20 -28
  73. data/lib/atatus/spies/sneakers.rb +2 -0
  74. data/lib/atatus/spies/sns.rb +126 -0
  75. data/lib/atatus/spies/sqs.rb +231 -0
  76. data/lib/atatus/spies/sucker_punch.rb +20 -22
  77. data/lib/atatus/spies/tilt.rb +10 -13
  78. data/lib/atatus/spies.rb +20 -0
  79. data/lib/atatus/sql/signature.rb +4 -2
  80. data/lib/atatus/sql/tokenizer.rb +23 -7
  81. data/lib/atatus/stacktrace/frame.rb +1 -0
  82. data/lib/atatus/stacktrace_builder.rb +12 -16
  83. data/lib/atatus/subscriber.rb +1 -0
  84. data/lib/atatus/trace_context/traceparent.rb +5 -8
  85. data/lib/atatus/trace_context/tracestate.rb +16 -14
  86. data/lib/atatus/trace_context.rb +6 -16
  87. data/lib/atatus/transaction.rb +25 -4
  88. data/lib/atatus/transport/base.rb +1 -3
  89. data/lib/atatus/transport/connection/http.rb +11 -3
  90. data/lib/atatus/transport/connection/proxy_pipe.rb +1 -2
  91. data/lib/atatus/transport/connection.rb +3 -2
  92. data/lib/atatus/transport/filters/hash_sanitizer.rb +16 -34
  93. data/lib/atatus/transport/filters/secrets_filter.rb +35 -12
  94. data/lib/atatus/transport/serializers/context_serializer.rb +1 -2
  95. data/lib/atatus/transport/serializers/metadata_serializer.rb +54 -8
  96. data/lib/atatus/transport/serializers/metricset_serializer.rb +2 -2
  97. data/lib/atatus/transport/serializers/span_serializer.rb +55 -9
  98. data/lib/atatus/transport/serializers/transaction_serializer.rb +1 -0
  99. data/lib/atatus/transport/serializers.rb +9 -6
  100. data/lib/atatus/transport/user_agent.rb +16 -9
  101. data/lib/atatus/transport/worker.rb +2 -1
  102. data/lib/atatus/util/deep_dup.rb +65 -0
  103. data/lib/atatus/util/precision_validator.rb +46 -0
  104. data/lib/atatus/util.rb +2 -0
  105. data/lib/atatus/version.rb +1 -1
  106. data/lib/atatus.rb +48 -5
  107. metadata +41 -11
@@ -0,0 +1,231 @@
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
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class SQSSpy
25
+ TYPE = 'messaging'
26
+ SUBTYPE = 'sqs'
27
+
28
+ REGION_REGEXP = %r{https://sqs\.([a-z0-9-]+)\.amazonaws}
29
+
30
+ def self.without_net_http
31
+ return yield unless defined?(NetHTTPSpy)
32
+
33
+ # rubocop:disable Style/ExplicitBlockArgument
34
+ Atatus::Spies::NetHTTPSpy.disable_in do
35
+ yield
36
+ end
37
+ # rubocop:enable Style/ExplicitBlockArgument
38
+ end
39
+
40
+ def self.queue_name(params)
41
+ if params[:queue_url]
42
+ params[:queue_url].split('/')[-1]
43
+ end
44
+ end
45
+
46
+ def self.region_from_url(url)
47
+ if match = REGION_REGEXP.match(url)
48
+ match[1]
49
+ end
50
+ end
51
+
52
+ def self.span_context(queue_name, region)
53
+ Atatus::Span::Context.new(
54
+ message: { queue_name: queue_name },
55
+ destination: {
56
+ service: { resource: "#{SUBTYPE}/#{queue_name}" },
57
+ cloud: { region: region }
58
+ }
59
+ # span links added here?
60
+ )
61
+ end
62
+
63
+ # @api private
64
+ module Ext
65
+ def self.prepended(mod)
66
+ def send_message(params = {}, options = {})
67
+ unless (transaction = Atatus.current_transaction)
68
+ return super(params, options)
69
+ end
70
+
71
+ queue_name = Atatus::Spies::SQSSpy.queue_name(params)
72
+ span_name = queue_name ? "SQS SEND to #{queue_name}" : 'SQS SEND'
73
+ region = Atatus::Spies::SQSSpy.region_from_url(params[:queue_url])
74
+ context = Atatus::Spies::SQSSpy.span_context(
75
+ queue_name,
76
+ region || config.region
77
+ )
78
+
79
+ Atatus.with_span(
80
+ span_name,
81
+ TYPE,
82
+ subtype: SUBTYPE,
83
+ action: 'send',
84
+ context: context
85
+ ) do |span|
86
+ trace_context = span&.trace_context || transaction.trace_context
87
+ trace_context.apply_headers do |key, value|
88
+ params[:message_attributes] ||= {}
89
+ params[:message_attributes][key] ||= {}
90
+ params[:message_attributes][key][:string_value] = value
91
+ params[:message_attributes][key][:data_type] = 'String'
92
+ end
93
+
94
+ Atatus::Spies::SQSSpy.without_net_http do
95
+ super(params, options)
96
+ end
97
+ end
98
+ end
99
+
100
+ def send_message_batch(params = {}, options = {})
101
+ unless (transaction = Atatus.current_transaction)
102
+ return super(params, options)
103
+ end
104
+
105
+ queue_name = Atatus::Spies::SQSSpy.queue_name(params)
106
+ span_name =
107
+ queue_name ? "SQS SEND_BATCH to #{queue_name}" : 'SQS SEND_BATCH'
108
+ region = Atatus::Spies::SQSSpy.region_from_url(params[:queue_url])
109
+ context = Atatus::Spies::SQSSpy.span_context(
110
+ queue_name,
111
+ region || config.region
112
+ )
113
+
114
+ Atatus.with_span(
115
+ span_name,
116
+ TYPE,
117
+ subtype: SUBTYPE,
118
+ action: 'send_batch',
119
+ context: context
120
+ ) do |span|
121
+ trace_context = span&.trace_context || transaction.trace_context
122
+
123
+ trace_context.apply_headers do |key, value|
124
+ params[:entries].each do |message|
125
+ message[:message_attributes] ||= {}
126
+ message[:message_attributes][key] ||= {}
127
+ message[:message_attributes][key][:string_value] = value
128
+ message[:message_attributes][key][:data_type] = 'String'
129
+ end
130
+ end
131
+
132
+ Atatus::Spies::SQSSpy.without_net_http do
133
+ super(params, options)
134
+ end
135
+ end
136
+ end
137
+
138
+ def receive_message(params = {}, options = {})
139
+ unless Atatus.current_transaction
140
+ return super(params, options)
141
+ end
142
+
143
+ queue_name = Atatus::Spies::SQSSpy.queue_name(params)
144
+ span_name =
145
+ queue_name ? "SQS RECEIVE from #{queue_name}" : 'SQS RECEIVE'
146
+ region = Atatus::Spies::SQSSpy.region_from_url(params[:queue_url])
147
+ context = Atatus::Spies::SQSSpy.span_context(
148
+ queue_name,
149
+ region || config.region
150
+ )
151
+
152
+ Atatus.with_span(
153
+ span_name,
154
+ TYPE,
155
+ subtype: SUBTYPE,
156
+ action: 'receive',
157
+ context: context
158
+ ) do
159
+ Atatus::Spies::SQSSpy.without_net_http do
160
+ super(params, options)
161
+ end
162
+ end
163
+ end
164
+
165
+ def delete_message(params = {}, options = {})
166
+ unless Atatus.current_transaction
167
+ return super(params, options)
168
+ end
169
+
170
+ queue_name = Atatus::Spies::SQSSpy.queue_name(params)
171
+ span_name = queue_name ? "SQS DELETE from #{queue_name}" : 'SQS DELETE'
172
+ region = Atatus::Spies::SQSSpy.region_from_url(params[:queue_url])
173
+ context = Atatus::Spies::SQSSpy.span_context(
174
+ queue_name,
175
+ region || config.region
176
+ )
177
+
178
+ Atatus.with_span(
179
+ span_name,
180
+ TYPE,
181
+ subtype: SUBTYPE,
182
+ action: 'delete',
183
+ context: context
184
+ ) do
185
+ Atatus::Spies::SQSSpy.without_net_http do
186
+ super(params, options)
187
+ end
188
+ end
189
+ end
190
+
191
+ def delete_message_batch(params = {}, options = {})
192
+ unless Atatus.current_transaction
193
+ return super(params, options)
194
+ end
195
+
196
+ queue_name = Atatus::Spies::SQSSpy.queue_name(params)
197
+ span_name =
198
+ queue_name ? "SQS DELETE_BATCH from #{queue_name}" : 'SQS DELETE_BATCH'
199
+ region = Atatus::Spies::SQSSpy.region_from_url(params[:queue_url])
200
+ context = Atatus::Spies::SQSSpy.span_context(
201
+ queue_name,
202
+ region || config.region
203
+ )
204
+
205
+ Atatus.with_span(
206
+ span_name,
207
+ TYPE,
208
+ subtype: SUBTYPE,
209
+ action: 'delete_batch',
210
+ context: context
211
+ ) do
212
+ Atatus::Spies::SQSSpy.without_net_http do
213
+ super(params, options)
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def install
221
+ ::Aws::SQS::Client.prepend(Ext)
222
+ end
223
+ end
224
+
225
+ register(
226
+ 'Aws::SQS::Client',
227
+ 'aws-sdk-sqs',
228
+ SQSSpy.new
229
+ )
230
+ end
231
+ end
@@ -25,30 +25,28 @@ module Atatus
25
25
  TYPE = 'sucker_punch'
26
26
 
27
27
  def install
28
- if defined?(::SuckerPunch) && defined?(::SuckerPunch::Job) && defined?(::SuckerPunch::Job::ClassMethods)
28
+ ::SuckerPunch::Job::ClassMethods.class_eval do
29
+ alias :__run_perform_without_atatus :__run_perform
29
30
 
30
- ::SuckerPunch::Job::ClassMethods.class_eval do
31
- alias :__run_perform_without_atatus :__run_perform
32
-
33
- def __run_perform(*args)
34
- # This method is reached via JobClass#async_perform
35
- # or JobClass#perform_in.
36
- name = to_s
37
- transaction = Atatus.start_transaction(name, TYPE)
38
- __run_perform_without_atatus(*args)
39
- transaction.done 'success'
40
- rescue ::Exception => e
41
- # Note that SuckerPunch by default doesn't raise the errors from
42
- # the user-defined JobClass#perform method as it uses an error
43
- # handler, accessed via `SuckerPunch.exception_handler`.
44
- Atatus.report(e, handled: false)
45
- transaction.done 'error'
46
- raise
47
- ensure
48
- Atatus.end_transaction
49
- end
31
+ def __run_perform(*args)
32
+ # This method is reached via JobClass#async_perform
33
+ # or JobClass#perform_in.
34
+ name = to_s
35
+ transaction = Atatus.start_transaction(name, TYPE)
36
+ __run_perform_without_atatus(*args)
37
+ transaction.done 'success'
38
+ transaction&.outcome = Transaction::Outcome::SUCCESS
39
+ rescue ::Exception => e
40
+ # Note that SuckerPunch by default doesn't raise the errors from
41
+ # the user-defined JobClass#perform method as it uses an error
42
+ # handler, accessed via `SuckerPunch.exception_handler`.
43
+ Atatus.report(e, handled: false)
44
+ transaction.done 'error'
45
+ transaction&.outcome = Transaction::Outcome::FAILURE
46
+ raise
47
+ ensure
48
+ Atatus.end_transaction
50
49
  end
51
-
52
50
  end
53
51
  end
54
52
  end
@@ -24,23 +24,20 @@ module Atatus
24
24
  class TiltSpy
25
25
  TYPE = 'template.tilt'
26
26
 
27
- def install
28
- if defined?(::Tilt) && defined?(::Tilt::Template)
29
-
30
- ::Tilt::Template.class_eval do
31
- alias render_without_apm render
27
+ # @api private
28
+ module Ext
29
+ def render(*args, &block)
30
+ name = options[:__atatus_template_name] || 'Unknown template'
32
31
 
33
- def render(*args, &block)
34
- name = options[:__atatus_template_name] || 'Unknown template'
35
-
36
- Atatus.with_span name, TYPE do
37
- render_without_apm(*args, &block)
38
- end
39
- end
32
+ Atatus.with_span name, TYPE do
33
+ super(*args, &block)
40
34
  end
41
-
42
35
  end
43
36
  end
37
+
38
+ def install
39
+ ::Tilt::Template.prepend(Ext)
40
+ end
44
41
  end
45
42
 
46
43
  register 'Tilt::Template', 'tilt/template', TiltSpy.new
data/lib/atatus/spies.rb CHANGED
@@ -56,6 +56,26 @@ module Atatus
56
56
  end
57
57
  end
58
58
 
59
+ def self.without_faraday
60
+ return yield unless defined?(FaradaySpy)
61
+
62
+ # rubocop:disable Style/ExplicitBlockArgument
63
+ Atatus::Spies::FaradaySpy.disable_in do
64
+ yield
65
+ end
66
+ # rubocop:enable Style/ExplicitBlockArgument
67
+ end
68
+
69
+ def self.without_net_http
70
+ return yield unless defined?(NetHTTPSpy)
71
+
72
+ # rubocop:disable Style/ExplicitBlockArgument
73
+ Atatus::Spies::NetHTTPSpy.disable_in do
74
+ yield
75
+ end
76
+ # rubocop:enable Style/ExplicitBlockArgument
77
+ end
78
+
59
79
  def self.register_require_hook(registration)
60
80
  registration.require_paths.each do |path|
61
81
  require_hooks[path] = registration
@@ -36,8 +36,8 @@ module Atatus
36
36
  end
37
37
 
38
38
  def initialize(sql)
39
- @sql = sql
40
- @tokenizer = Tokenizer.new(sql)
39
+ @sql = sql.encode('utf-8', invalid: :replace, undef: :replace)
40
+ @tokenizer = Tokenizer.new(@sql)
41
41
  end
42
42
 
43
43
  def parse
@@ -158,9 +158,11 @@ module Atatus
158
158
  def scan_dotted_identifier
159
159
  table = @tokenizer.text
160
160
 
161
+ # rubocop:disable Style/WhileUntilModifier
161
162
  while scan_token(PERIOD) && scan_token(IDENT)
162
163
  table += ".#{@tokenizer.text}"
163
164
  end
165
+ # rubocop:enable Style/WhileUntilModifier
164
166
 
165
167
  table
166
168
  end
@@ -69,7 +69,7 @@ module Atatus
69
69
  when '[' then scan_quoted_indentifier(']')
70
70
  when '(' then LPAREN
71
71
  when ')' then RPAREN
72
- when '/' then scan_bracketed_comment
72
+ when '/' then scan_bracketed_or_cql_comment
73
73
  when '-' then scan_simple_comment
74
74
  when "'" then scan_string_literal
75
75
  when ALPHA then scan_keyword_or_identifier(possible_keyword: true)
@@ -110,7 +110,7 @@ module Atatus
110
110
  next next_char
111
111
  end
112
112
 
113
- next next_char if peek =~ ALPHA
113
+ next next_char if ALPHA.match?(peek)
114
114
 
115
115
  break
116
116
  end
@@ -130,7 +130,7 @@ module Atatus
130
130
  end
131
131
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
132
132
 
133
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
133
+ # rubocop:disable Metrics/CyclomaticComplexity
134
134
  def scan_dollar_sign
135
135
  while (peek = peek_char)
136
136
  case peek
@@ -165,7 +165,7 @@ module Atatus
165
165
 
166
166
  OTHER
167
167
  end
168
- # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
168
+ # rubocop:enable Metrics/CyclomaticComplexity
169
169
 
170
170
  def scan_quoted_indentifier(delimiter)
171
171
  while (char = next_char)
@@ -185,10 +185,16 @@ module Atatus
185
185
  IDENT
186
186
  end
187
187
 
188
+ def scan_bracketed_or_cql_comment
189
+ case peek_char
190
+ when '*' then scan_bracketed_comment
191
+ when '/' then scan_cql_comment
192
+ else OTHER
193
+ end
194
+ end
195
+
188
196
  # rubocop:disable Metrics/CyclomaticComplexity
189
197
  def scan_bracketed_comment
190
- return OTHER unless peek_char == '*'
191
-
192
198
  nesting = 1
193
199
 
194
200
  while (char = next_char)
@@ -207,6 +213,16 @@ module Atatus
207
213
  end
208
214
  # rubocop:enable Metrics/CyclomaticComplexity
209
215
 
216
+ def scan_cql_comment
217
+ return OTHER unless peek_char == '/'
218
+
219
+ while (char = next_char)
220
+ break if char == "\n"
221
+ end
222
+
223
+ COMMENT
224
+ end
225
+
210
226
  def scan_simple_comment
211
227
  return OTHER unless peek_char == '-'
212
228
 
@@ -251,7 +267,7 @@ module Atatus
251
267
  when 'e', 'E'
252
268
  return NUMBER if exponent
253
269
  next_char
254
- next_char if peek_char =~ /[+-]/
270
+ next_char if /[+-]/.match?(peek_char)
255
271
  else break
256
272
  end
257
273
  end
@@ -38,6 +38,7 @@ module Atatus
38
38
  :module,
39
39
  :colno
40
40
  )
41
+
41
42
  def build_context(context_line_count)
42
43
  return unless abs_path && context_line_count > 0
43
44
 
@@ -23,7 +23,7 @@ require 'atatus/util/lru_cache'
23
23
  module Atatus
24
24
  # @api private
25
25
  class StacktraceBuilder
26
- JAVA_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
26
+ JAVA_FORMAT = /^(.+)\.([^.]+)\(([^:]+):(\d+)\)$/.freeze
27
27
  RUBY_FORMAT = /^(.+?):(\d+)(?::in `(.+?)')?$/.freeze
28
28
 
29
29
  RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d}.freeze
@@ -38,7 +38,9 @@ module Atatus
38
38
 
39
39
  def initialize(config)
40
40
  @config = config
41
- @cache = Util::LruCache.new(2048, &method(:build_frame))
41
+ @cache = Util::LruCache.new(2048) do |cache, frame|
42
+ build_frame(cache, frame)
43
+ end
42
44
  end
43
45
 
44
46
  attr_reader :config
@@ -59,7 +61,7 @@ module Atatus
59
61
 
60
62
  frame = Stacktrace::Frame.new
61
63
  frame.abs_path = abs_path
62
- frame.filename = strip_load_path(config, abs_path)
64
+ frame.filename = strip_load_path(abs_path)
63
65
  frame.function = function
64
66
  frame.lineno = lineno.to_i
65
67
  frame.library_frame = library_frame?(config, abs_path)
@@ -86,14 +88,13 @@ module Atatus
86
88
  [file, number, method, module_name]
87
89
  end
88
90
 
89
- # rubocop:disable Metrics/CyclomaticComplexity
90
91
  def library_frame?(config, abs_path)
91
92
  return false unless abs_path
92
93
 
93
94
  return true if abs_path.start_with?(GEMS_PATH)
94
95
 
95
96
  if abs_path.start_with?(config.__root_path)
96
- return true if abs_path.start_with?(config.__root_path + '/vendor')
97
+ return true if abs_path.start_with?("#{config.__root_path}/vendor")
97
98
  return false
98
99
  end
99
100
 
@@ -102,20 +103,15 @@ module Atatus
102
103
 
103
104
  false
104
105
  end
105
- # rubocop:enable Metrics/CyclomaticComplexity
106
106
 
107
- def strip_load_path(config, path)
107
+ def strip_load_path(path)
108
108
  return nil if path.nil?
109
109
 
110
- if path.start_with?(config.__root_path)
111
- prefix = config.__root_path
112
- else
113
- prefix =
114
- $LOAD_PATH
115
- .map(&:to_s)
116
- .select { |s| path.start_with?(s) }
117
- .max_by(&:length)
118
- end
110
+ prefix =
111
+ $LOAD_PATH
112
+ .map(&:to_s)
113
+ .select { |s| path.start_with?(s) }
114
+ .max_by(&:length)
119
115
 
120
116
  prefix ? path[prefix.chomp(File::SEPARATOR).length + 1..-1] : path
121
117
  end
@@ -45,6 +45,7 @@ module Atatus
45
45
  # AS::Notifications API
46
46
 
47
47
  Notification = Struct.new(:id, :span)
48
+
48
49
  def start(name, id, payload)
49
50
  return unless (transaction = @agent.current_transaction)
50
51
 
@@ -22,27 +22,24 @@ module Atatus
22
22
  # @api private
23
23
  class Traceparent
24
24
  VERSION = '00'
25
- HEX_REGEX = /[^[:xdigit:]]/.freeze
25
+ NON_HEX_REGEX = /[^[:xdigit:]]/.freeze
26
26
 
27
27
  TRACE_ID_LENGTH = 16
28
28
  ID_LENGTH = 8
29
29
 
30
- # rubocop:disable Metrics/ParameterLists
31
30
  def initialize(
32
31
  version: VERSION,
33
32
  trace_id: nil,
34
- span_id: nil,
33
+ parent_id: nil,
35
34
  id: nil,
36
35
  recorded: true
37
36
  )
38
37
  @version = version
39
38
  @trace_id = trace_id || hex(TRACE_ID_LENGTH)
40
- # TODO: rename span_id kw arg to parent_id with next major version bump
41
- @parent_id = span_id
39
+ @parent_id = parent_id
42
40
  @id = id || hex(ID_LENGTH)
43
41
  @recorded = recorded
44
42
  end
45
- # rubocop:enable Metrics/ParameterLists
46
43
 
47
44
  attr_accessor :version, :id, :trace_id, :parent_id, :recorded
48
45
 
@@ -58,8 +55,8 @@ module Atatus
58
55
  values[-1] = Util.hex_to_bits(values[-1])
59
56
  end
60
57
 
61
- raise_invalid(header) if HEX_REGEX =~ t.trace_id
62
- raise_invalid(header) if HEX_REGEX =~ t.parent_id
58
+ raise_invalid(header) if NON_HEX_REGEX.match?(t.trace_id)
59
+ raise_invalid(header) if NON_HEX_REGEX.match?(t.parent_id)
63
60
  end
64
61
  end
65
62
 
@@ -17,6 +17,8 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
+ require 'atatus/util/precision_validator'
21
+
20
22
  module Atatus
21
23
  class TraceContext
22
24
  # @api private
@@ -35,12 +37,13 @@ module Atatus
35
37
  end
36
38
  end
37
39
 
40
+ # @api private
38
41
  class EsEntry
39
42
  ASSIGN = ':'
40
43
  SPLIT = ';'
41
44
 
42
- SHORT_TO_LONG = { 's' => 'sample_rate' }
43
- LONG_TO_SHORT = { 'sample_rate' => 's' }
45
+ SHORT_TO_LONG = { 's' => 'sample_rate' }.freeze
46
+ LONG_TO_SHORT = { 'sample_rate' => 's' }.freeze
44
47
 
45
48
  def initialize(values = nil)
46
49
  parse(values)
@@ -63,13 +66,9 @@ module Atatus
63
66
  end
64
67
 
65
68
  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
69
+ @sample_rate = Util::PrecisionValidator.validate(
70
+ val, precision: 4, minimum: 0.0001
71
+ )
73
72
  end
74
73
 
75
74
  def to_s
@@ -85,7 +84,7 @@ module Atatus
85
84
 
86
85
  values.split(SPLIT).map do |kv|
87
86
  k, v = kv.split(ASSIGN)
88
- next unless SHORT_TO_LONG.keys.include?(k)
87
+ next unless SHORT_TO_LONG.key?(k)
89
88
  send("#{SHORT_TO_LONG[k]}=", v)
90
89
  end
91
90
  end
@@ -93,6 +92,8 @@ module Atatus
93
92
 
94
93
  extend Forwardable
95
94
 
95
+ ENTRY_SPLIT_REGEX = /\s*[\n,]+\s*/
96
+
96
97
  def initialize(entries: {}, sample_rate: nil)
97
98
  @entries = entries
98
99
 
@@ -108,7 +109,7 @@ module Atatus
108
109
  split_by_nl_and_comma(header)
109
110
  .each_with_object({}) do |entry, hsh|
110
111
  k, v = entry.split('=')
111
-
112
+ next unless k && v && !k.empty? && !v.empty?
112
113
  hsh[k] =
113
114
  case k
114
115
  when 'es' then EsEntry.new(v)
@@ -138,9 +139,10 @@ module Atatus
138
139
  def split_by_nl_and_comma(str)
139
140
  # HTTP allows multiple headers with the same name, eg. multiple
140
141
  # 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
142
+ # Rack handles this by joining the headers under the same key,
143
+ # separated by newlines.
144
+ # See https://www.rubydoc.info/github/rack/rack/file/SPEC
145
+ String(str).split(ENTRY_SPLIT_REGEX).flatten
144
146
  end
145
147
  end
146
148
  end