appsignal 4.5.17 → 4.6.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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/CLAUDE.md +177 -0
  4. data/README.md +17 -0
  5. data/appsignal.gemspec +1 -0
  6. data/build_matrix.yml +29 -0
  7. data/lib/appsignal/auth_check.rb +3 -3
  8. data/lib/appsignal/check_in/cron.rb +1 -1
  9. data/lib/appsignal/check_in/event.rb +1 -1
  10. data/lib/appsignal/check_in/scheduler.rb +3 -1
  11. data/lib/appsignal/check_in.rb +9 -6
  12. data/lib/appsignal/cli/diagnose.rb +1 -1
  13. data/lib/appsignal/cli.rb +1 -1
  14. data/lib/appsignal/config.rb +231 -30
  15. data/lib/appsignal/custom_marker.rb +3 -3
  16. data/lib/appsignal/environment.rb +7 -1
  17. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +1 -1
  18. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +1 -1
  19. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +1 -1
  20. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +1 -1
  21. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +1 -1
  22. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +1 -1
  23. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -1
  24. data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +1 -1
  25. data/lib/appsignal/event_formatter/view_component/render_formatter.rb +1 -1
  26. data/lib/appsignal/event_formatter.rb +41 -3
  27. data/lib/appsignal/extension.rb +1 -1
  28. data/lib/appsignal/garbage_collection.rb +1 -1
  29. data/lib/appsignal/helpers/instrumentation.rb +82 -35
  30. data/lib/appsignal/helpers/metrics.rb +12 -9
  31. data/lib/appsignal/hooks/action_cable.rb +1 -1
  32. data/lib/appsignal/hooks/action_mailer.rb +1 -0
  33. data/lib/appsignal/hooks/active_job.rb +1 -1
  34. data/lib/appsignal/hooks/active_support_notifications.rb +1 -1
  35. data/lib/appsignal/hooks/at_exit.rb +1 -1
  36. data/lib/appsignal/hooks/celluloid.rb +1 -1
  37. data/lib/appsignal/hooks/data_mapper.rb +1 -1
  38. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  39. data/lib/appsignal/hooks/dry_monitor.rb +1 -1
  40. data/lib/appsignal/hooks/excon.rb +1 -1
  41. data/lib/appsignal/hooks/gvl.rb +1 -1
  42. data/lib/appsignal/hooks/http.rb +1 -1
  43. data/lib/appsignal/hooks/mongo_ruby_driver.rb +1 -1
  44. data/lib/appsignal/hooks/mri.rb +1 -1
  45. data/lib/appsignal/hooks/net_http.rb +1 -1
  46. data/lib/appsignal/hooks/ownership.rb +1 -1
  47. data/lib/appsignal/hooks/passenger.rb +1 -1
  48. data/lib/appsignal/hooks/puma.rb +1 -1
  49. data/lib/appsignal/hooks/que.rb +1 -1
  50. data/lib/appsignal/hooks/rake.rb +1 -1
  51. data/lib/appsignal/hooks/redis.rb +1 -1
  52. data/lib/appsignal/hooks/redis_client.rb +1 -1
  53. data/lib/appsignal/hooks/resque.rb +1 -1
  54. data/lib/appsignal/hooks/sequel.rb +2 -1
  55. data/lib/appsignal/hooks/shoryuken.rb +1 -1
  56. data/lib/appsignal/hooks/sidekiq.rb +1 -0
  57. data/lib/appsignal/hooks/unicorn.rb +1 -1
  58. data/lib/appsignal/hooks/webmachine.rb +1 -1
  59. data/lib/appsignal/hooks.rb +5 -3
  60. data/lib/appsignal/integrations/action_cable.rb +1 -1
  61. data/lib/appsignal/integrations/active_support_notifications.rb +1 -1
  62. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -1
  63. data/lib/appsignal/integrations/data_mapper.rb +1 -1
  64. data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
  65. data/lib/appsignal/integrations/dry_monitor.rb +1 -1
  66. data/lib/appsignal/integrations/excon.rb +1 -1
  67. data/lib/appsignal/integrations/http.rb +1 -1
  68. data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -1
  69. data/lib/appsignal/integrations/net_http.rb +1 -1
  70. data/lib/appsignal/integrations/object.rb +18 -2
  71. data/lib/appsignal/integrations/ownership.rb +1 -1
  72. data/lib/appsignal/integrations/puma.rb +1 -1
  73. data/lib/appsignal/integrations/que.rb +1 -1
  74. data/lib/appsignal/integrations/railtie.rb +2 -2
  75. data/lib/appsignal/integrations/rake.rb +2 -2
  76. data/lib/appsignal/integrations/redis.rb +1 -1
  77. data/lib/appsignal/integrations/redis_client.rb +1 -1
  78. data/lib/appsignal/integrations/resque.rb +2 -2
  79. data/lib/appsignal/integrations/shoryuken.rb +1 -1
  80. data/lib/appsignal/integrations/sidekiq.rb +3 -3
  81. data/lib/appsignal/integrations/unicorn.rb +1 -1
  82. data/lib/appsignal/integrations/webmachine.rb +1 -1
  83. data/lib/appsignal/internal_errors.rb +2 -2
  84. data/lib/appsignal/loaders.rb +1 -1
  85. data/lib/appsignal/logger.rb +36 -19
  86. data/lib/appsignal/marker.rb +1 -1
  87. data/lib/appsignal/probes/gvl.rb +4 -3
  88. data/lib/appsignal/probes/helpers.rb +1 -1
  89. data/lib/appsignal/probes/mri.rb +3 -3
  90. data/lib/appsignal/probes/sidekiq.rb +4 -3
  91. data/lib/appsignal/probes.rb +20 -15
  92. data/lib/appsignal/rack.rb +1 -1
  93. data/lib/appsignal/sample_data.rb +31 -12
  94. data/lib/appsignal/span.rb +1 -1
  95. data/lib/appsignal/system.rb +9 -8
  96. data/lib/appsignal/transaction.rb +72 -52
  97. data/lib/appsignal/transmitter.rb +1 -1
  98. data/lib/appsignal/utils.rb +1 -1
  99. data/lib/appsignal/version.rb +2 -1
  100. data/lib/appsignal.rb +22 -14
  101. data/lib/puma/plugin/appsignal.rb +1 -1
  102. data/sig/appsignal.rbi +2599 -0
  103. data/sig/appsignal.rbs +2420 -0
  104. metadata +20 -6
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
- # @api private
5
+ # @!visibility private
6
6
  class MriProbe
7
7
  include Helpers
8
8
 
9
- # @api private
9
+ # @!visibility private
10
10
  def self.dependencies_present?
11
11
  defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
12
12
  end
@@ -17,7 +17,7 @@ module Appsignal
17
17
  @gc_profiler = gc_profiler
18
18
  end
19
19
 
20
- # @api private
20
+ # @!visibility private
21
21
  def call
22
22
  stat = RubyVM.stat
23
23
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
- # @api private
5
+ # @!visibility private
6
6
  class SidekiqProbe
7
7
  include Helpers
8
8
 
@@ -38,14 +38,15 @@ module Appsignal
38
38
  end
39
39
  end
40
40
 
41
- # @api private
41
+ # @!visibility private
42
42
  attr_reader :config
43
43
 
44
+ # @!visibility private
44
45
  def self.sidekiq7_and_greater?
45
46
  Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("7.0.0")
46
47
  end
47
48
 
48
- # @api private
49
+ # @!visibility private
49
50
  def self.dependencies_present?
50
51
  return true if sidekiq7_and_greater?
51
52
  return false unless defined?(::Redis::VERSION) # Sidekiq <= 6
@@ -2,10 +2,11 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
- # @api private
5
+ # @return [Integer]
6
+ # @!visibility private
6
7
  ITERATION_IN_SECONDS = 60
7
8
 
8
- # @api private
9
+ # @!visibility private
9
10
  class ProbeCollection
10
11
  def initialize
11
12
  @probes = {}
@@ -23,13 +24,12 @@ module Appsignal
23
24
  end
24
25
 
25
26
  # Fetch a probe using its name.
26
- # @param key [Symbol/String] The name of the probe to fetch.
27
+ # @param key [Symbol, String] The name of the probe to fetch.
27
28
  # @return [Object] Returns the registered probe.
28
29
  def [](key)
29
30
  probes[key]
30
31
  end
31
32
 
32
- # @api private
33
33
  def internal_register(name, probe)
34
34
  if probes.key?(name)
35
35
  logger.debug "A probe with the name `#{name}` is already " \
@@ -38,12 +38,10 @@ module Appsignal
38
38
  probes[name] = probe
39
39
  end
40
40
 
41
- # @api private
42
41
  def unregister(name)
43
42
  probes.delete(name)
44
43
  end
45
44
 
46
- # @api private
47
45
  def each(&block)
48
46
  probes.each(&block)
49
47
  end
@@ -58,14 +56,14 @@ module Appsignal
58
56
  end
59
57
 
60
58
  class << self
61
- # @api private
59
+ # @!visibility private
62
60
  def mutex
63
61
  @mutex ||= Thread::Mutex.new
64
62
  end
65
63
 
66
64
  # @see ProbeCollection
67
65
  # @return [ProbeCollection] Returns list of probes.
68
- # @api private
66
+ # @!visibility private
69
67
  def probes
70
68
  @probes ||= ProbeCollection.new
71
69
  end
@@ -129,7 +127,7 @@ module Appsignal
129
127
  # # "started" # Printed on Appsignal::Probes.start
130
128
  # # "called" # Repeated every minute
131
129
  #
132
- # @param name [Symbol/String] Name of the probe. Can be used with
130
+ # @param name [Symbol, String] Name of the probe. Can be used with
133
131
  # {ProbeCollection#[]}. This name will be used in errors in the log and
134
132
  # allows overwriting of probes by registering new ones with the same
135
133
  # name.
@@ -153,7 +151,7 @@ module Appsignal
153
151
  # # Then unregister a probe if needed
154
152
  # Appsignal::Probes.unregister :my_probe
155
153
  #
156
- # @param name [Symbol/String] Name of the probe used to {register} the
154
+ # @param name [Symbol, String] Name of the probe used to {register} the
157
155
  # probe.
158
156
  # @return [void]
159
157
  def unregister(name)
@@ -162,6 +160,7 @@ module Appsignal
162
160
  uninitialize_probe(name)
163
161
  end
164
162
 
163
+ # @return [void]
165
164
  # @api private
166
165
  def start
167
166
  stop
@@ -184,8 +183,10 @@ module Appsignal
184
183
  logger.debug("Gathering minutely metrics with '#{name}' probe")
185
184
  probe.call
186
185
  rescue => ex
187
- logger.error "Error in minutely probe '#{name}': #{ex}"
188
- logger.debug ex.backtrace.join("\n")
186
+ logger.error(
187
+ "Error in minutely probe '#{name}': #{ex.class}: #{ex.message}\n" \
188
+ "#{ex.backtrace.join("\n")}"
189
+ )
189
190
  end
190
191
  end
191
192
  end_time = Time.now
@@ -212,13 +213,15 @@ module Appsignal
212
213
 
213
214
  # Stop the minutely probes mechanism. Stop the thread and clear all probe
214
215
  # instances.
216
+ #
217
+ # @return [void]
215
218
  def stop
216
219
  defined?(@thread) && @thread.kill
217
220
  @started = false
218
221
  probe_instances.clear
219
222
  end
220
223
 
221
- # @api private
224
+ # @!visibility private
222
225
  def wait_time
223
226
  ITERATION_IN_SECONDS - Time.now.sec
224
227
  end
@@ -256,8 +259,10 @@ module Appsignal
256
259
  end
257
260
  rescue => error
258
261
  logger = Appsignal.internal_logger
259
- logger.error "Error while initializing minutely probe '#{name}': #{error}"
260
- logger.debug error.backtrace.join("\n")
262
+ logger.error(
263
+ "Error while initializing minutely probe '#{name}': #{error.class}: #{error.message}\n" \
264
+ "#{error.backtrace.join("\n")}"
265
+ )
261
266
  end
262
267
 
263
268
  def uninitialize_probe(name)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- # @api private
4
+ # @!visibility private
5
5
  module Rack
6
6
  APPSIGNAL_TRANSACTION = "appsignal.transaction"
7
7
  APPSIGNAL_EVENT_HANDLER_ID = "appsignal.event_handler_id"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- # @api private
4
+ # @!visibility private
5
5
  class SampleData
6
6
  def initialize(key, accepted_type = nil)
7
7
  @key = key
@@ -21,7 +21,6 @@ module Appsignal
21
21
  end
22
22
  end
23
23
 
24
- # @api private
25
24
  def set_empty_value!
26
25
  @empty = true
27
26
  @blocks.clear
@@ -36,12 +35,18 @@ module Appsignal
36
35
  else
37
36
  block_or_value
38
37
  end
38
+
39
39
  unless accepted_type?(new_value)
40
40
  log_unsupported_data_type(new_value)
41
41
  next
42
42
  end
43
43
 
44
- value = merge_values(value, new_value)
44
+ # Before trying to merge values, convert them to Ruby classes
45
+ # This way we don't need to check if something is of a type often
46
+ value_new = convert_to_ruby_class(new_value)
47
+ value_original = convert_to_ruby_class(value)
48
+
49
+ value = merge_values(value_original, value_new)
45
50
  new_value
46
51
  end
47
52
 
@@ -52,7 +57,6 @@ module Appsignal
52
57
  @blocks.any?
53
58
  end
54
59
 
55
- # @api private
56
60
  def empty?
57
61
  @empty
58
62
  end
@@ -81,15 +85,19 @@ module Appsignal
81
85
  end
82
86
  end
83
87
 
88
+ def mergable?(value_original, value_new)
89
+ value_new.instance_of?(value_original.class)
90
+ end
91
+
84
92
  def merge_values(value_original, value_new)
85
- unless value_new.instance_of?(value_original.class)
86
- unless value_original == UNSET_VALUE
87
- Appsignal.internal_logger.warn(
88
- "The sample data '#{@key}' changed type from " \
89
- "'#{value_original.class}' to '#{value_new.class}'. " \
90
- "These types can not be merged. Using new '#{value_new.class}' type."
91
- )
92
- end
93
+ return value_new if value_original == UNSET_VALUE
94
+
95
+ unless mergable?(value_original, value_new)
96
+ Appsignal.internal_logger.warn(
97
+ "The sample data '#{@key}' changed type from " \
98
+ "'#{value_original.class}' to '#{value_new.class}'. " \
99
+ "These types can not be merged. Using new '#{value_new.class}' type."
100
+ )
93
101
  return value_new
94
102
  end
95
103
 
@@ -103,6 +111,17 @@ module Appsignal
103
111
  end
104
112
  end
105
113
 
114
+ # Convert any subclasses of Hash to a Ruby Hash class, so we don't have to
115
+ # account for the original value being a value that's not a pure Hash or
116
+ # Array object.
117
+ def convert_to_ruby_class(value)
118
+ if value.is_a? Hash
119
+ value.to_h
120
+ else
121
+ value
122
+ end
123
+ end
124
+
106
125
  def log_unsupported_data_type(value)
107
126
  Appsignal.internal_logger.error(
108
127
  "Sample data '#{@key}': Unsupported data type '#{value.class}' received: #{value.inspect}"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- # @api private
4
+ # @!visibility private
5
5
  class Span
6
6
  def initialize(namespace = nil, ext = nil)
7
7
  @ext = ext || Appsignal::Extension::Span.root(namespace || "")
@@ -5,7 +5,7 @@ module Appsignal
5
5
  #
6
6
  # Provides useful methods to find out more about the host system.
7
7
  #
8
- # @api private
8
+ # @!visibility private
9
9
  module System
10
10
  LINUX_TARGET = "linux"
11
11
  LINUX_ARM_ARCHITECTURE = "aarch64"
@@ -13,6 +13,9 @@ module Appsignal
13
13
  FREEBSD_TARGET = "freebsd"
14
14
  GEM_EXT_PATH = File.expand_path("../../ext", __dir__).freeze
15
15
 
16
+ # Returns if system is recognized as a Heroku dyno.
17
+ #
18
+ # @return [Boolean]
16
19
  def self.heroku?
17
20
  ENV.key? "DYNO"
18
21
  end
@@ -27,7 +30,6 @@ module Appsignal
27
30
  # - Use `export APPSIGNAL_BUILD_FOR_LINUX_ARM=1` to enable the experimental
28
31
  # Linux ARM build.
29
32
  #
30
- # @api private
31
33
  # @return [String]
32
34
  def self.agent_platform
33
35
  return LINUX_TARGET if force_linux_arm_build?
@@ -63,7 +65,6 @@ module Appsignal
63
65
  # - Use `export APPSIGNAL_BUILD_FOR_LINUX_ARM=1` to enable the experimental
64
66
  # Linux ARM build.
65
67
  #
66
- # @api private
67
68
  # @return [String]
68
69
  def self.agent_architecture
69
70
  return LINUX_ARM_ARCHITECTURE if force_linux_arm_build?
@@ -74,34 +75,34 @@ module Appsignal
74
75
 
75
76
  # Returns whether or not the musl build was forced by the user.
76
77
  #
77
- # @api private
78
+ # @return [Boolean]
78
79
  def self.force_musl_build?
79
80
  %w[true 1].include?(ENV.fetch("APPSIGNAL_BUILD_FOR_MUSL", nil))
80
81
  end
81
82
 
82
83
  # Returns whether or not the linux ARM build was selected by the user.
83
84
  #
84
- # @api private
85
+ # @return [Boolean]
85
86
  def self.force_linux_arm_build?
86
87
  %w[true 1].include?(ENV.fetch("APPSIGNAL_BUILD_FOR_LINUX_ARM", nil))
87
88
  end
88
89
 
89
- # @api private
90
90
  def self.versionify(version)
91
91
  Gem::Version.new(version)
92
92
  end
93
93
 
94
- # @api private
95
94
  def self.ldd_version_output
96
95
  `ldd --version 2>&1`
97
96
  end
98
97
 
99
- # @api private
100
98
  def self.extract_ldd_version(string)
101
99
  ldd_version = string.match(/\d+\.\d+/)
102
100
  ldd_version && ldd_version[0]
103
101
  end
104
102
 
103
+ # Returns if the app is recognized as a JRuby app.
104
+ #
105
+ # @return [Boolean]
105
106
  def self.jruby?
106
107
  RUBY_PLATFORM == "java"
107
108
  end