appsignal 3.10.0-java → 3.11.0-java

Sign up to get free protection for your applications and to get access to all the features.
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