appsignal 3.10.0-java → 3.11.0-java

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +88 -0
  4. data/Gemfile +1 -0
  5. data/benchmark.rake +99 -42
  6. data/lib/appsignal/cli/demo.rb +0 -1
  7. data/lib/appsignal/config.rb +54 -98
  8. data/lib/appsignal/demo.rb +15 -20
  9. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  10. data/lib/appsignal/event_formatter.rb +3 -2
  11. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  12. data/lib/appsignal/hooks/action_cable.rb +21 -16
  13. data/lib/appsignal/hooks/active_job.rb +14 -8
  14. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  15. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  16. data/lib/appsignal/integrations/action_cable.rb +5 -7
  17. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  18. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  19. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  20. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  21. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  22. data/lib/appsignal/integrations/excon.rb +1 -0
  23. data/lib/appsignal/integrations/http.rb +1 -0
  24. data/lib/appsignal/integrations/net_http.rb +1 -0
  25. data/lib/appsignal/integrations/object.rb +6 -0
  26. data/lib/appsignal/integrations/que.rb +13 -20
  27. data/lib/appsignal/integrations/railtie.rb +1 -1
  28. data/lib/appsignal/integrations/rake.rb +1 -5
  29. data/lib/appsignal/integrations/redis.rb +1 -0
  30. data/lib/appsignal/integrations/redis_client.rb +1 -0
  31. data/lib/appsignal/integrations/resque.rb +2 -5
  32. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  33. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  34. data/lib/appsignal/integrations/unicorn.rb +1 -0
  35. data/lib/appsignal/integrations/webmachine.rb +2 -5
  36. data/lib/appsignal/logger.rb +7 -3
  37. data/lib/appsignal/probes/helpers.rb +1 -0
  38. data/lib/appsignal/probes/mri.rb +1 -0
  39. data/lib/appsignal/probes/sidekiq.rb +1 -0
  40. data/lib/appsignal/probes.rb +3 -0
  41. data/lib/appsignal/rack/abstract_middleware.rb +18 -12
  42. data/lib/appsignal/rack/event_handler.rb +39 -8
  43. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  44. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  45. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  46. data/lib/appsignal/rack.rb +29 -0
  47. data/lib/appsignal/span.rb +1 -0
  48. data/lib/appsignal/transaction.rb +308 -101
  49. data/lib/appsignal/utils/data.rb +0 -1
  50. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  51. data/lib/appsignal/utils/integration_logger.rb +0 -13
  52. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  53. data/lib/appsignal/utils/json.rb +0 -1
  54. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  55. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  56. data/lib/appsignal/utils.rb +6 -0
  57. data/lib/appsignal/version.rb +1 -1
  58. data/lib/appsignal.rb +6 -5
  59. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  60. data/spec/lib/appsignal/config_spec.rb +138 -43
  61. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  62. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  63. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  64. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  65. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  66. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  67. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  68. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  69. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  70. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +48 -3
  71. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  72. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  73. data/spec/lib/appsignal/rack_spec.rb +63 -0
  74. data/spec/lib/appsignal/transaction_spec.rb +1634 -1071
  75. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  76. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  77. data/spec/lib/appsignal_spec.rb +323 -10
  78. data/spec/support/helpers/transaction_helpers.rb +44 -20
  79. data/spec/support/matchers/transaction.rb +15 -1
  80. data/spec/support/testing.rb +1 -1
  81. metadata +6 -2
@@ -6,28 +6,80 @@ module Appsignal
6
6
  class Transaction
7
7
  HTTP_REQUEST = "http_request"
8
8
  BACKGROUND_JOB = "background_job"
9
+ # @api private
9
10
  ACTION_CABLE = "action_cable"
11
+ # @api private
10
12
  FRONTEND = "frontend"
13
+ # @api private
11
14
  BLANK = ""
15
+ # @api private
12
16
  ALLOWED_TAG_KEY_TYPES = [Symbol, String].freeze
13
- ALLOWED_TAG_VALUE_TYPES = [Symbol, String, Integer].freeze
17
+ # @api private
18
+ ALLOWED_TAG_VALUE_TYPES = [Symbol, String, Integer, TrueClass, FalseClass].freeze
19
+ # @api private
14
20
  BREADCRUMB_LIMIT = 20
21
+ # @api private
15
22
  ERROR_CAUSES_LIMIT = 10
16
23
 
17
24
  class << self
18
- def create(id, namespace, request, options = {})
25
+ # Create a new transaction and set it as the currently active
26
+ # transaction.
27
+ #
28
+ # @param id_or_namespace [String] Namespace of the to be created transaction.
29
+ # @return [Transaction]
30
+ def create(id_or_namespace, arg_namespace = nil, request = nil, options = {})
31
+ if id_or_namespace && arg_namespace
32
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
33
+ "Appsignal::Transaction.create: " \
34
+ "A new Transaction is created using the transaction ID argument. " \
35
+ "This argument is deprecated without replacement."
36
+ )
37
+ end
38
+ if arg_namespace
39
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
40
+ "Appsignal::Transaction.create: " \
41
+ "A Transaction is created using the namespace argument. " \
42
+ "Specify the namespace as the first argument to the 'create' " \
43
+ "method without the ID argument."
44
+ )
45
+ end
46
+ if request
47
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
48
+ "Appsignal::Transaction.create: " \
49
+ "A Transaction is created using the request argument. " \
50
+ "This argument is deprecated. Please use the `Appsignal.set_*` helpers instead."
51
+ )
52
+ end
19
53
  # Allow middleware to force a new transaction
20
- Thread.current[:appsignal_transaction] = nil if options.include?(:force) && options[:force]
54
+ if options[:force]
55
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
56
+ "Appsignal::Transaction.create: " \
57
+ "A Transaction is created using the `:force => true` option argument. " \
58
+ "The options argument is deprecated without replacement."
59
+ )
60
+ Thread.current[:appsignal_transaction] = nil
61
+ end
62
+ if arg_namespace
63
+ id = id_or_namespace
64
+ namespace = arg_namespace
65
+ else
66
+ id = SecureRandom.uuid
67
+ namespace = id_or_namespace
68
+ end
21
69
 
22
70
  # Check if we already have a running transaction
23
71
  if Thread.current[:appsignal_transaction].nil?
24
72
  # If not, start a new transaction
25
73
  Thread.current[:appsignal_transaction] =
26
- Appsignal::Transaction.new(id, namespace, request, options)
74
+ Appsignal::Transaction.new(
75
+ id,
76
+ namespace,
77
+ request,
78
+ options
79
+ )
27
80
  else
28
81
  # Otherwise, log the issue about trying to start another transaction
29
- Appsignal.internal_logger.warn_once_then_debug(
30
- :transaction_id,
82
+ Appsignal.internal_logger.warn(
31
83
  "Trying to start new transaction with id " \
32
84
  "'#{id}', but a transaction with id '#{current.transaction_id}' " \
33
85
  "is already running. Using transaction '#{current.transaction_id}'."
@@ -56,6 +108,8 @@ module Appsignal
56
108
  current && !current.nil_transaction?
57
109
  end
58
110
 
111
+ # Complete the currently active transaction and unset it as the active
112
+ # transaction.
59
113
  def complete_current!
60
114
  current.complete
61
115
  rescue => e
@@ -73,14 +127,21 @@ module Appsignal
73
127
  end
74
128
  end
75
129
 
130
+ # @api private
76
131
  attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options,
77
- :discarded, :breadcrumbs, :custom_data
132
+ :breadcrumbs, :custom_data
78
133
 
79
- def initialize(transaction_id, namespace, request, options = {})
134
+ # Use {.create} to create new transactions.
135
+ #
136
+ # @param transaction_id [String] ID of the to be created transaction.
137
+ # @param namespace [String] Namespace of the to be created transaction.
138
+ # @see create
139
+ # @api private
140
+ def initialize(transaction_id, namespace, request = nil, options = {})
80
141
  @transaction_id = transaction_id
81
142
  @action = nil
82
143
  @namespace = namespace
83
- @request = request
144
+ @request = request || InternalGenericRequest.new({})
84
145
  @paused = false
85
146
  @discarded = false
86
147
  @tags = {}
@@ -90,6 +151,8 @@ module Appsignal
90
151
  @options = options
91
152
  @options[:params_method] ||= :params
92
153
  @params = nil
154
+ @session_data = nil
155
+ @headers = nil
93
156
 
94
157
  @ext = Appsignal::Extension.start_transaction(
95
158
  @transaction_id,
@@ -108,38 +171,46 @@ module Appsignal
108
171
  "because it was manually discarded."
109
172
  return
110
173
  end
111
- sample_data if @ext.finish(0)
174
+ _sample_data if @ext.finish(0)
112
175
  @ext.complete
113
176
  end
114
177
 
178
+ # @api private
115
179
  def pause!
116
180
  @paused = true
117
181
  end
118
182
 
183
+ # @api private
119
184
  def resume!
120
185
  @paused = false
121
186
  end
122
187
 
188
+ # @api private
123
189
  def paused?
124
190
  @paused == true
125
191
  end
126
192
 
193
+ # @api private
127
194
  def discard!
128
195
  @discarded = true
129
196
  end
130
197
 
198
+ # @api private
131
199
  def restore!
132
200
  @discarded = false
133
201
  end
134
202
 
203
+ # @api private
135
204
  def discarded?
136
205
  @discarded == true
137
206
  end
138
207
 
208
+ # @api private
139
209
  def store(key)
140
210
  @store[key]
141
211
  end
142
212
 
213
+ # @api private
143
214
  def params
144
215
  parameters = @params || request_params
145
216
 
@@ -148,6 +219,9 @@ module Appsignal
148
219
  else
149
220
  parameters
150
221
  end
222
+ rescue => e
223
+ Appsignal.internal_logger.error("Exception while fetching params: #{e.class}: #{e}")
224
+ nil
151
225
  end
152
226
 
153
227
  # Set parameters on the transaction.
@@ -166,7 +240,7 @@ module Appsignal
166
240
  # @yield This block is called when the transaction is sampled. The block's
167
241
  # return value will become the new parameters.
168
242
  # @return [void]
169
- # @see {Helpers::Instrumentation#set_params}
243
+ # @see Helpers::Instrumentation#set_params
170
244
  def set_params(given_params = nil, &block)
171
245
  @params = block if block
172
246
  @params = given_params if given_params
@@ -191,7 +265,9 @@ module Appsignal
191
265
  # @yield This block is called when the transaction is sampled. The block's
192
266
  # return value will become the new parameters.
193
267
  # @return [void]
194
- # @see {Helpers::Instrumentation#set_params_if_nil}
268
+ #
269
+ # @see #set_params
270
+ # @see Helpers::Instrumentation#set_params_if_nil
195
271
  def set_params_if_nil(given_params = nil, &block)
196
272
  set_params(given_params, &block) unless @params
197
273
  end
@@ -214,6 +290,84 @@ module Appsignal
214
290
  @tags.merge!(given_tags)
215
291
  end
216
292
 
293
+ # Set session data on the transaction.
294
+ #
295
+ # When both the `given_session_data` and a block is given to this method,
296
+ # the `given_session_data` argument is leading and the block will _not_ be
297
+ # called.
298
+ #
299
+ # @param given_session_data [Hash] A hash containing session data.
300
+ # @yield This block is called when the transaction is sampled. The block's
301
+ # return value will become the new session data.
302
+ # @return [void]
303
+ #
304
+ # @since 3.10.1
305
+ # @see Helpers::Instrumentation#set_session_data
306
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
307
+ # Sample data guide
308
+ def set_session_data(given_session_data = nil, &block)
309
+ @session_data = block if block
310
+ @session_data = given_session_data if given_session_data
311
+ end
312
+
313
+ # Set session data on the transaction if not already set.
314
+ #
315
+ # When both the `given_session_data` and a block is given to this method,
316
+ # the `given_session_data` argument is leading and the block will _not_ be
317
+ # called.
318
+ #
319
+ # @param given_session_data [Hash] A hash containing session data.
320
+ # @yield This block is called when the transaction is sampled. The block's
321
+ # return value will become the new session data.
322
+ # @return [void]
323
+ #
324
+ # @since 3.10.1
325
+ # @see #set_session_data
326
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
327
+ # Sample data guide
328
+ def set_session_data_if_nil(given_session_data = nil, &block)
329
+ set_session_data(given_session_data, &block) unless @session_data
330
+ end
331
+
332
+ # Set headers on the transaction.
333
+ #
334
+ # When both the `given_headers` and a block is given to this method,
335
+ # the `given_headers` argument is leading and the block will _not_ be
336
+ # called.
337
+ #
338
+ # @param given_headers [Hash] A hash containing headers.
339
+ # @yield This block is called when the transaction is sampled. The block's
340
+ # return value will become the new headers.
341
+ # @return [void]
342
+ #
343
+ # @since 3.10.1
344
+ # @see Helpers::Instrumentation#set_headers
345
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
346
+ # Sample data guide
347
+ def set_headers(given_headers = nil, &block)
348
+ @headers = block if block
349
+ @headers = given_headers if given_headers
350
+ end
351
+
352
+ # Set headers on the transaction if not already set.
353
+ #
354
+ # When both the `given_headers` and a block is given to this method,
355
+ # the `given_headers` argument is leading and the block will _not_ be
356
+ # called.
357
+ #
358
+ # @param given_headers [Hash] A hash containing headers.
359
+ # @yield This block is called when the transaction is sampled. The block's
360
+ # return value will become the new headers.
361
+ # @return [void]
362
+ #
363
+ # @since 3.10.1
364
+ # @see #set_headers
365
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
366
+ # Sample data guide
367
+ def set_headers_if_nil(given_headers = nil, &block)
368
+ set_headers(given_headers, &block) unless @headers
369
+ end
370
+
217
371
  # Set custom data on the transaction.
218
372
  #
219
373
  # When this method is called multiple times, it will overwrite the
@@ -325,6 +479,8 @@ module Appsignal
325
479
  @ext.set_namespace(namespace)
326
480
  end
327
481
 
482
+ # @deprecated Use the {#set_action} helper.
483
+ # @api private
328
484
  def set_http_or_background_action(from = request.params)
329
485
  return unless from
330
486
 
@@ -337,8 +493,6 @@ module Appsignal
337
493
 
338
494
  # Set queue start time for transaction.
339
495
  #
340
- # Most commononly called by {set_http_or_background_queue_start}.
341
- #
342
496
  # @param start [Integer] Queue start time in milliseconds.
343
497
  # @raise [RangeError] When the queue start time value is too big, this
344
498
  # method raises a RangeError.
@@ -368,14 +522,21 @@ module Appsignal
368
522
  # AppSignal as seconds.
369
523
  #
370
524
  # @see https://docs.appsignal.com/ruby/instrumentation/request-queue-time.html
525
+ # @deprecated Use {#set_queue_start} instead.
371
526
  # @return [void]
372
527
  def set_http_or_background_queue_start
528
+ Appsignal::Utils::StdoutAndLoggerMessage.warning \
529
+ "The Appsignal::Transaction#set_http_or_background_queue_start " \
530
+ "method has been deprecated. " \
531
+ "Please use the Appsignal::Transaction#set_queue_start method instead."
532
+
373
533
  start = http_queue_start || background_queue_start
374
534
  return unless start
375
535
 
376
536
  set_queue_start(start)
377
537
  end
378
538
 
539
+ # @api private
379
540
  def set_metadata(key, value)
380
541
  return unless key && value
381
542
  return if Appsignal.config[:filter_metadata].include?(key.to_s)
@@ -383,47 +544,29 @@ module Appsignal
383
544
  @ext.set_metadata(key, value)
384
545
  end
385
546
 
547
+ # @deprecated Use one of the set_tags, set_params, set_session_data,
548
+ # set_params or set_custom_data helpers instead.
549
+ # @api private
386
550
  def set_sample_data(key, data)
387
- return unless key && data
388
-
389
- if !data.is_a?(Array) && !data.is_a?(Hash)
390
- Appsignal.internal_logger.error(
391
- "Invalid sample data for '#{key}'. Value is not an Array or Hash: '#{data.inspect}'"
392
- )
393
- return
394
- end
395
-
396
- @ext.set_sample_data(
397
- key.to_s,
398
- Appsignal::Utils::Data.generate(data)
551
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
552
+ "Appsignal::Transaction#set_sample_data is deprecated. " \
553
+ "Please use one of the instrumentation helpers: set_tags, " \
554
+ "set_params, set_session_data, set_params or set_custom_data."
399
555
  )
400
- rescue RuntimeError => e
401
- begin
402
- inspected_data = data.inspect
403
- Appsignal.internal_logger.error(
404
- "Error generating data (#{e.class}: #{e.message}) for '#{inspected_data}'"
405
- )
406
- rescue => e
407
- Appsignal.internal_logger.error(
408
- "Error generating data (#{e.class}: #{e.message}). Can't inspect data."
409
- )
410
- end
556
+ _set_sample_data(key, data)
411
557
  end
412
558
 
559
+ # @deprecated No replacement.
560
+ # @api private
413
561
  def sample_data
414
- {
415
- :params => sanitized_params,
416
- :environment => sanitized_environment,
417
- :session_data => sanitized_session_data,
418
- :metadata => sanitized_metadata,
419
- :tags => sanitized_tags,
420
- :breadcrumbs => breadcrumbs,
421
- :custom_data => custom_data
422
- }.each do |key, data|
423
- set_sample_data(key, data)
424
- end
562
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
563
+ "Appsignal::Transaction#sample_data is deprecated. " \
564
+ "Please remove any calls to this method."
565
+ )
566
+ _sample_data
425
567
  end
426
568
 
569
+ # @see Appsignal::Helpers::Instrumentation#set_error
427
570
  def set_error(error)
428
571
  unless error.is_a?(Exception)
429
572
  Appsignal.internal_logger.error "Appsignal::Transaction#set_error: Cannot set error. " \
@@ -470,19 +613,23 @@ module Appsignal
470
613
 
471
614
  causes_sample_data.last[:is_root_cause] = false if root_cause_missing
472
615
 
473
- set_sample_data(
616
+ _set_sample_data(
474
617
  "error_causes",
475
618
  causes_sample_data
476
619
  )
477
620
  end
478
621
  alias_method :add_exception, :set_error
479
622
 
623
+ # @see Helpers::Instrumentation#instrument
624
+ # @api private
480
625
  def start_event
481
626
  return if paused?
482
627
 
483
628
  @ext.start_event(0)
484
629
  end
485
630
 
631
+ # @see Helpers::Instrumentation#instrument
632
+ # @api private
486
633
  def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
487
634
  return if paused?
488
635
 
@@ -495,6 +642,8 @@ module Appsignal
495
642
  )
496
643
  end
497
644
 
645
+ # @see Helpers::Instrumentation#instrument
646
+ # @api private
498
647
  def record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT)
499
648
  return if paused?
500
649
 
@@ -508,6 +657,7 @@ module Appsignal
508
657
  )
509
658
  end
510
659
 
660
+ # @see Helpers::Instrumentation#instrument
511
661
  def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
512
662
  start_event
513
663
  yield if block_given?
@@ -521,7 +671,8 @@ module Appsignal
521
671
  end
522
672
  alias_method :to_hash, :to_h
523
673
 
524
- class GenericRequest
674
+ # @api private
675
+ class InternalGenericRequest
525
676
  attr_reader :env
526
677
 
527
678
  def initialize(env)
@@ -533,8 +684,64 @@ module Appsignal
533
684
  end
534
685
  end
535
686
 
687
+ # @deprecated Use the instrumentation helpers to set metadata on the
688
+ # transaction, rather than rely on the GenericRequest automation. See the
689
+ # {Helpers::Instrumentation} module for a list of helpers.
690
+ # @api private
691
+ class GenericRequest < InternalGenericRequest
692
+ def initialize(_env)
693
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
694
+ "The use of Appsignal::Transaction::GenericRequest is deprecated. " \
695
+ "Use the `Appsignal.set_*` helpers instead. " \
696
+ "https://docs.appsignal.com/guides/custom-data/sample-data.html"
697
+ )
698
+ super
699
+ end
700
+ end
701
+
536
702
  private
537
703
 
704
+ def _set_sample_data(key, data)
705
+ return unless key && data
706
+
707
+ if !data.is_a?(Array) && !data.is_a?(Hash)
708
+ Appsignal.internal_logger.error(
709
+ "Invalid sample data for '#{key}'. Value is not an Array or Hash: '#{data.inspect}'"
710
+ )
711
+ return
712
+ end
713
+
714
+ @ext.set_sample_data(
715
+ key.to_s,
716
+ Appsignal::Utils::Data.generate(data)
717
+ )
718
+ rescue RuntimeError => e
719
+ begin
720
+ inspected_data = data.inspect
721
+ Appsignal.internal_logger.error(
722
+ "Error generating data (#{e.class}: #{e.message}) for '#{inspected_data}'"
723
+ )
724
+ rescue => e
725
+ Appsignal.internal_logger.error(
726
+ "Error generating data (#{e.class}: #{e.message}). Can't inspect data."
727
+ )
728
+ end
729
+ end
730
+
731
+ def _sample_data
732
+ {
733
+ :params => sanitized_params,
734
+ :environment => sanitized_environment,
735
+ :session_data => sanitized_session_data,
736
+ :metadata => sanitized_metadata,
737
+ :tags => sanitized_tags,
738
+ :breadcrumbs => breadcrumbs,
739
+ :custom_data => custom_data
740
+ }.each do |key, data|
741
+ _set_sample_data(key, data)
742
+ end
743
+ end
744
+
538
745
  # Returns calculated background queue start time in milliseconds, based on
539
746
  # environment values.
540
747
  #
@@ -558,25 +765,7 @@ module Appsignal
558
765
  # @return [Integer] queue start in milliseconds.
559
766
  def http_queue_start
560
767
  env = environment
561
- return unless env
562
-
563
- env_var = env["HTTP_X_QUEUE_START"] || env["HTTP_X_REQUEST_START"]
564
- return unless env_var
565
-
566
- cleaned_value = env_var.tr("^0-9", "")
567
- return if cleaned_value.empty?
568
-
569
- value = cleaned_value.to_i
570
- if value > 4_102_441_200_000
571
- # Value is in microseconds. Transform to milliseconds.
572
- value / 1_000
573
- elsif value < 946_681_200_000
574
- # Value is too low to be plausible
575
- nil
576
- else
577
- # Value is in milliseconds
578
- value
579
- end
768
+ Appsignal::Rack::Utils.queue_start_from(env)
580
769
  end
581
770
 
582
771
  def sanitized_params
@@ -592,28 +781,25 @@ module Appsignal
592
781
  begin
593
782
  request.send options[:params_method]
594
783
  rescue => e
595
- # Getting params from the request has been know to fail.
596
- Appsignal.internal_logger.debug "Exception while getting params: #{e}"
784
+ Appsignal.internal_logger.warn "Exception while getting params: #{e}"
597
785
  nil
598
786
  end
599
787
  end
600
788
 
601
- # Returns sanitized environment for a transaction.
602
- #
603
- # The environment of a transaction can contain a lot of information, not
604
- # all of it useful for debugging.
605
- #
606
- # @return [nil] if no environment is present.
607
- # @return [Hash<String, Object>]
608
- def sanitized_environment
609
- env = environment
610
- return if env.empty?
611
-
612
- {}.tap do |out|
613
- Appsignal.config[:request_headers].each do |key|
614
- out[key] = env[key] if env[key]
789
+ def session_data
790
+ if @session_data
791
+ if @session_data.respond_to? :call
792
+ @session_data.call
793
+ else
794
+ @session_data
615
795
  end
796
+ elsif request.respond_to?(:session)
797
+ request.session
616
798
  end
799
+ rescue => e
800
+ Appsignal.internal_logger.error \
801
+ "Exception while fetching session data: #{e.class}: #{e}"
802
+ nil
617
803
  end
618
804
 
619
805
  # Returns sanitized session data.
@@ -625,23 +811,21 @@ module Appsignal
625
811
  # @return [nil] if the {#request} session data is `nil`.
626
812
  # @return [Hash<String, Object>]
627
813
  def sanitized_session_data
628
- return if !Appsignal.config[:send_session_data] ||
629
- !request.respond_to?(:session)
630
-
631
- session = request.session
632
- return unless session
814
+ return unless Appsignal.config[:send_session_data]
633
815
 
634
816
  Appsignal::Utils::HashSanitizer.sanitize(
635
- session.to_hash, Appsignal.config[:filter_session_data]
817
+ session_data&.to_hash, Appsignal.config[:filter_session_data]
636
818
  )
637
819
  end
638
820
 
639
- # Returns sanitized metadata set by {#set_metadata} and from the
640
- # {#environment}.
821
+ # Returns sanitized metadata set on the request environment.
641
822
  #
642
823
  # @return [Hash<String, Object>]
643
824
  def sanitized_metadata
644
- metadata = environment[:metadata]
825
+ env = environment
826
+ return unless env
827
+
828
+ metadata = env[:metadata]
645
829
  return unless metadata
646
830
 
647
831
  metadata
@@ -649,17 +833,40 @@ module Appsignal
649
833
  .reject { |key, _value| Appsignal.config[:filter_metadata].include?(key) }
650
834
  end
651
835
 
652
- # Returns the environment for a transaction.
836
+ def environment
837
+ if @headers
838
+ if @headers.respond_to? :call
839
+ @headers.call
840
+ else
841
+ @headers
842
+ end
843
+ elsif request.respond_to?(:env)
844
+ request.env
845
+ end
846
+ rescue => e
847
+ Appsignal.internal_logger.error \
848
+ "Exception while fetching headers: #{e.class}: #{e}"
849
+ nil
850
+ end
851
+
852
+ # Returns sanitized environment for a transaction.
653
853
  #
654
- # Returns an empty Hash when the {#request} object doesn't listen to the
655
- # `#env` method or the `#env` is nil.
854
+ # The environment of a transaction can contain a lot of information, not
855
+ # all of it useful for debugging.
656
856
  #
857
+ # @return [nil] if no environment is present.
657
858
  # @return [Hash<String, Object>]
658
- def environment
659
- return {} unless request.respond_to?(:env)
660
- return {} unless request.env
859
+ def sanitized_environment
860
+ env = environment
861
+ return unless env
862
+ return unless env.respond_to?(:empty?)
863
+ return if env.empty?
661
864
 
662
- request.env
865
+ {}.tap do |out|
866
+ Appsignal.config[:request_headers].each do |key|
867
+ out[key] = env[key] if env[key]
868
+ end
869
+ end
663
870
  end
664
871
 
665
872
  # Only keep tags if they meet the following criteria:
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Appsignal
4
4
  module Utils
5
- # @api private
6
5
  class Data
7
6
  class << self
8
7
  def generate(body)
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Appsignal
4
4
  module Utils
5
- # @api private
6
5
  class HashSanitizer
7
6
  FILTERED = "[FILTERED]"
8
7
  RECURSIVE = "[RECURSIVE VALUE]"
@@ -2,20 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Utils
5
- # Subclass of logger with method to only log a warning once
6
- # prevents the local log from filling up with repeated messages.
7
5
  class IntegrationLogger < ::Logger
8
- def seen_keys
9
- @seen_keys ||= Set.new
10
- end
11
-
12
- def warn_once_then_debug(key, message)
13
- if seen_keys.add?(key).nil?
14
- debug message
15
- else
16
- warn message
17
- end
18
- end
19
6
  end
20
7
  end
21
8
  end