appsignal 2.4.3 → 2.5.0.alpha.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +14 -5
  3. data/.rubocop_todo.yml +3 -0
  4. data/.travis.yml +3 -4
  5. data/CHANGELOG.md +3 -0
  6. data/Rakefile +21 -6
  7. data/appsignal.gemspec +12 -5
  8. data/ext/Rakefile +27 -0
  9. data/ext/agent.yml +52 -21
  10. data/ext/base.rb +79 -0
  11. data/ext/extconf.rb +5 -76
  12. data/gemfiles/sequel-435.gemfile +5 -1
  13. data/gemfiles/sequel.gemfile +5 -1
  14. data/lib/appsignal.rb +11 -6
  15. data/lib/appsignal/cli.rb +1 -2
  16. data/lib/appsignal/cli/install.rb +3 -3
  17. data/lib/appsignal/config.rb +56 -37
  18. data/lib/appsignal/extension.rb +28 -4
  19. data/lib/appsignal/extension/jruby.rb +460 -0
  20. data/lib/appsignal/hooks/sidekiq.rb +4 -4
  21. data/lib/appsignal/integrations/capistrano/appsignal.cap +7 -2
  22. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +8 -3
  23. data/lib/appsignal/system.rb +16 -1
  24. data/lib/appsignal/transaction.rb +2 -2
  25. data/lib/appsignal/utils.rb +3 -1
  26. data/lib/appsignal/version.rb +1 -3
  27. data/spec/.rubocop.yml +3 -0
  28. data/spec/lib/appsignal/capistrano2_spec.rb +55 -41
  29. data/spec/lib/appsignal/capistrano3_spec.rb +87 -61
  30. data/spec/lib/appsignal/cli/diagnose_spec.rb +3 -3
  31. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +4 -4
  32. data/spec/lib/appsignal/config_spec.rb +54 -21
  33. data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
  34. data/spec/lib/appsignal/extension_spec.rb +28 -12
  35. data/spec/lib/appsignal/hooks/sequel_spec.rb +7 -1
  36. data/spec/lib/appsignal/integrations/que_spec.rb +5 -5
  37. data/spec/lib/appsignal/system_spec.rb +5 -4
  38. data/spec/lib/appsignal/transaction_spec.rb +8 -8
  39. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +4 -4
  40. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +3 -3
  41. data/spec/lib/appsignal_spec.rb +5 -3
  42. data/spec/spec_helper.rb +29 -8
  43. data/spec/support/helpers/config_helpers.rb +3 -2
  44. metadata +12 -7
@@ -1,7 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'sequel', '~> 4.35'
4
- gem 'sqlite3'
4
+ if RUBY_PLATFORM == "java"
5
+ gem 'jdbc-sqlite3'
6
+ else
7
+ gem 'sqlite3'
8
+ end
5
9
  gem 'rack', '~> 1.6'
6
10
 
7
11
  gemspec :path => '../'
@@ -1,7 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'sequel', '< 4.35'
4
- gem 'sqlite3'
4
+ if RUBY_PLATFORM == "java"
5
+ gem 'jdbc-sqlite3'
6
+ else
7
+ gem 'sqlite3'
8
+ end
5
9
  gem 'rack', '~> 1.6'
6
10
 
7
11
  gemspec :path => '../'
@@ -63,6 +63,11 @@ module Appsignal
63
63
  end
64
64
  end
65
65
 
66
+ # @api private
67
+ def testing?
68
+ false
69
+ end
70
+
66
71
  # Start the AppSignal integration.
67
72
  #
68
73
  # Starts AppSignal with the given configuration. If no configuration is set
@@ -88,7 +93,7 @@ module Appsignal
88
93
  #
89
94
  # @return [void]
90
95
  # @since 0.7.0
91
- def start
96
+ def start # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
97
  unless extension_loaded?
93
98
  logger.info("Not starting appsignal, extension is not loaded")
94
99
  return
@@ -119,7 +124,7 @@ module Appsignal
119
124
  Appsignal::EventFormatter.initialize_formatters
120
125
  initialize_extensions
121
126
 
122
- if config[:enable_allocation_tracking]
127
+ if config[:enable_allocation_tracking] && !Appsignal::System.jruby?
123
128
  Appsignal::Extension.install_allocation_event_hook
124
129
  end
125
130
 
@@ -553,7 +558,7 @@ module Appsignal
553
558
  # {.instrument_sql} instead of {EventFormatter::SQL_BODY_FORMAT}.
554
559
  # @yield yields the given block of code instrumented in an AppSignal
555
560
  # event.
556
- # @return [Object] Returns the blocks return value.
561
+ # @return [Object] Returns the block's return value.
557
562
  #
558
563
  # @see Appsignal::Transaction#instrument
559
564
  # @see .instrument_sql
@@ -588,7 +593,7 @@ module Appsignal
588
593
  # @param title [String, nil] Human readable name of the event.
589
594
  # @param body [String, nil] SQL query that's being executed.
590
595
  # @yield yields the given block of code instrumented in an AppSignal event.
591
- # @return [Object] Returns the blocks return value.
596
+ # @return [Object] Returns the block's return value.
592
597
  #
593
598
  # @see .instrument
594
599
  # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
@@ -698,7 +703,7 @@ module Appsignal
698
703
  # @see Extension
699
704
  # @since 1.0.0
700
705
  def extension_loaded?
701
- !!@extension_loaded
706
+ !!extension_loaded
702
707
  end
703
708
 
704
709
  # Returns the active state of the AppSignal integration.
@@ -778,6 +783,7 @@ module Appsignal
778
783
  end
779
784
  end
780
785
 
786
+ require "appsignal/system"
781
787
  require "appsignal/utils"
782
788
  require "appsignal/extension"
783
789
  require "appsignal/auth_check"
@@ -796,4 +802,3 @@ require "appsignal/rack/generic_instrumentation"
796
802
  require "appsignal/rack/js_exception_catcher"
797
803
  require "appsignal/js_exception_transaction"
798
804
  require "appsignal/transmitter"
799
- require "appsignal/system"
@@ -1,6 +1,5 @@
1
1
  require "optparse"
2
2
  require "logger"
3
- require "yaml"
4
3
  require "appsignal"
5
4
  require "appsignal/cli/helpers"
6
5
  require "appsignal/cli/demo"
@@ -11,7 +10,7 @@ require "appsignal/cli/notify_of_deploy"
11
10
  module Appsignal
12
11
  # @api private
13
12
  class CLI
14
- AVAILABLE_COMMANDS = %w(demo diagnose install notify_of_deploy).freeze
13
+ AVAILABLE_COMMANDS = %w[demo diagnose install notify_of_deploy].freeze
15
14
 
16
15
  class << self
17
16
  attr_accessor :options
@@ -92,7 +92,7 @@ module Appsignal
92
92
  puts "Installing for Sinatra"
93
93
  config[:name] = required_input(" Enter application name: ")
94
94
  puts
95
- configure(config, %w(development production staging), true)
95
+ configure(config, %w[development production staging], true)
96
96
 
97
97
  puts "Finish Sinatra configuration"
98
98
  puts " Sinatra requires some manual configuration."
@@ -110,7 +110,7 @@ module Appsignal
110
110
  puts "Installing for Padrino"
111
111
  config[:name] = required_input(" Enter application name: ")
112
112
  puts
113
- configure(config, %w(development production staging), true)
113
+ configure(config, %w[development production staging], true)
114
114
 
115
115
  puts "Finish Padrino installation"
116
116
  puts " Padrino requires some manual configuration."
@@ -130,7 +130,7 @@ module Appsignal
130
130
  config[:name] = required_input(" Enter application name: ")
131
131
  puts
132
132
 
133
- configure(config, %w(development production staging), true)
133
+ configure(config, %w[development production staging], true)
134
134
 
135
135
  puts "Manual Grape configuration needed"
136
136
  puts " See the installation instructions at:"
@@ -63,6 +63,12 @@ module Appsignal
63
63
  "APPSIGNAL_FILES_WORLD_ACCESSIBLE" => :files_world_accessible
64
64
  }.freeze
65
65
 
66
+ # Mapping of old and deprecated AppSignal configuration keys
67
+ DEPRECATED_CONFIG_KEY_MAPPING = {
68
+ :api_key => :push_api_key,
69
+ :ignore_exceptions => :ignore_errors
70
+ }.freeze
71
+
66
72
  attr_reader :root_path, :env, :initial_config, :config_hash
67
73
  attr_accessor :logger
68
74
 
@@ -161,6 +167,26 @@ module Appsignal
161
167
  ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = config_hash[:files_world_accessible].to_s
162
168
  end
163
169
 
170
+ def validate
171
+ # Strip path from endpoint so we're backwards compatible with
172
+ # earlier versions of the gem.
173
+ # TODO: Move to its own method, maybe in `#[]=`?
174
+ endpoint_uri = URI(config_hash[:endpoint])
175
+ config_hash[:endpoint] =
176
+ if endpoint_uri.port == 443
177
+ "#{endpoint_uri.scheme}://#{endpoint_uri.host}"
178
+ else
179
+ "#{endpoint_uri.scheme}://#{endpoint_uri.host}:#{endpoint_uri.port}"
180
+ end
181
+
182
+ if config_hash[:push_api_key]
183
+ @valid = true
184
+ else
185
+ @valid = false
186
+ @logger.error "Push api key not set after loading config"
187
+ end
188
+ end
189
+
164
190
  private
165
191
 
166
192
  def config_file
@@ -181,18 +207,12 @@ module Appsignal
181
207
  configurations = YAML.load(ERB.new(IO.read(config_file)).result)
182
208
  config_for_this_env = configurations[env]
183
209
  if config_for_this_env
184
- config_for_this_env = Hash[config_for_this_env.map do |key, value|
185
- [key.to_sym, value]
186
- end] # convert keys to symbols
187
-
188
- # Backwards compatibility with config files generated by earlier
189
- # versions of the gem
190
- if !config_for_this_env[:push_api_key] && config_for_this_env[:api_key]
191
- config_for_this_env[:push_api_key] = config_for_this_env[:api_key]
192
- end
193
- if !config_for_this_env[:ignore_errors] && config_for_this_env[:ignore_exceptions]
194
- config_for_this_env[:ignore_errors] = config_for_this_env[:ignore_exceptions]
195
- end
210
+ config_for_this_env =
211
+ config_for_this_env.each_with_object({}) do |(key, value), hash|
212
+ hash[key.to_sym] = value # convert keys to symbols
213
+ end
214
+
215
+ config_for_this_env = maintain_backwards_compatibility(config_for_this_env)
196
216
 
197
217
  merge(@config_hash, config_for_this_env)
198
218
  else
@@ -200,35 +220,53 @@ module Appsignal
200
220
  end
201
221
  end
202
222
 
223
+ # Maintain backwards compatibility with config files generated by earlier
224
+ # versions of the gem
225
+ #
226
+ # Used by {#load_from_disk}. No compatibility for env variables or initial config currently.
227
+ def maintain_backwards_compatibility(configuration)
228
+ configuration.tap do |config|
229
+ DEPRECATED_CONFIG_KEY_MAPPING.each do |old_key, new_key|
230
+ old_config_value = config.delete(old_key)
231
+ next unless old_config_value
232
+ logger.warn "Old configuration key found. Please update the "\
233
+ "'#{old_key}' to '#{new_key}'."
234
+
235
+ next if config[new_key] # Skip if new key is already in use
236
+ config[new_key] = old_config_value
237
+ end
238
+ end
239
+ end
240
+
203
241
  def load_from_environment
204
242
  config = {}
205
243
 
206
244
  # Configuration with string type
207
- %w(APPSIGNAL_PUSH_API_KEY APPSIGNAL_APP_NAME APPSIGNAL_PUSH_API_ENDPOINT
245
+ %w[APPSIGNAL_PUSH_API_KEY APPSIGNAL_APP_NAME APPSIGNAL_PUSH_API_ENDPOINT
208
246
  APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH APPSIGNAL_HTTP_PROXY
209
247
  APPSIGNAL_LOG APPSIGNAL_LOG_PATH APPSIGNAL_WORKING_DIR_PATH
210
- APPSIGNAL_HOSTNAME APPSIGNAL_CA_FILE_PATH).each do |var|
248
+ APPSIGNAL_HOSTNAME APPSIGNAL_CA_FILE_PATH].each do |var|
211
249
  env_var = ENV[var]
212
250
  next unless env_var
213
251
  config[ENV_TO_KEY_MAPPING[var]] = env_var
214
252
  end
215
253
 
216
254
  # Configuration with boolean type
217
- %w(APPSIGNAL_ACTIVE APPSIGNAL_DEBUG APPSIGNAL_INSTRUMENT_NET_HTTP
255
+ %w[APPSIGNAL_ACTIVE APPSIGNAL_DEBUG APPSIGNAL_INSTRUMENT_NET_HTTP
218
256
  APPSIGNAL_INSTRUMENT_REDIS APPSIGNAL_INSTRUMENT_SEQUEL
219
257
  APPSIGNAL_SKIP_SESSION_DATA APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING
220
258
  APPSIGNAL_ENABLE_ALLOCATION_TRACKING APPSIGNAL_ENABLE_GC_INSTRUMENTATION
221
259
  APPSIGNAL_RUNNING_IN_CONTAINER APPSIGNAL_ENABLE_HOST_METRICS
222
260
  APPSIGNAL_SEND_PARAMS APPSIGNAL_ENABLE_MINUTELY_PROBES
223
- APPSIGNAL_FILES_WORLD_ACCESSIBLE).each do |var|
261
+ APPSIGNAL_FILES_WORLD_ACCESSIBLE].each do |var|
224
262
  env_var = ENV[var]
225
263
  next unless env_var
226
264
  config[ENV_TO_KEY_MAPPING[var]] = env_var.casecmp("true").zero?
227
265
  end
228
266
 
229
267
  # Configuration with array of strings type
230
- %w(APPSIGNAL_IGNORE_ACTIONS APPSIGNAL_IGNORE_ERRORS
231
- APPSIGNAL_IGNORE_NAMESPACES APPSIGNAL_FILTER_PARAMETERS).each do |var|
268
+ %w[APPSIGNAL_IGNORE_ACTIONS APPSIGNAL_IGNORE_ERRORS
269
+ APPSIGNAL_IGNORE_NAMESPACES APPSIGNAL_FILTER_PARAMETERS].each do |var|
232
270
  env_var = ENV[var]
233
271
  next unless env_var
234
272
  config[ENV_TO_KEY_MAPPING[var]] = env_var.split(",")
@@ -245,24 +283,5 @@ module Appsignal
245
283
  original_config[key] = value
246
284
  end
247
285
  end
248
-
249
- def validate
250
- # Strip path from endpoint so we're backwards compatible with
251
- # earlier versions of the gem.
252
- endpoint_uri = URI(config_hash[:endpoint])
253
- config_hash[:endpoint] =
254
- if endpoint_uri.port == 443
255
- "#{endpoint_uri.scheme}://#{endpoint_uri.host}"
256
- else
257
- "#{endpoint_uri.scheme}://#{endpoint_uri.host}:#{endpoint_uri.port}"
258
- end
259
-
260
- if config_hash[:push_api_key]
261
- @valid = true
262
- else
263
- @valid = false
264
- @logger.error "Push api key not set after loading config"
265
- end
266
- end
267
286
  end
268
287
  end
@@ -1,12 +1,17 @@
1
1
  require "yaml"
2
2
 
3
3
  begin
4
- require "appsignal_extension"
5
- Appsignal.extension_loaded = true
4
+ if Appsignal::System.jruby?
5
+ require "appsignal/extension/jruby"
6
+ # {Appsignal.extension_loaded} is set in the jRuby extension file
7
+ else
8
+ require "appsignal_extension"
9
+ Appsignal.extension_loaded = true
10
+ end
6
11
  rescue LoadError => err
7
12
  Appsignal.logger.error(
8
13
  "Failed to load extension (#{err}), please check the install.log file in " \
9
- "the ext directory of the gem and e-mail us at support@appsignal.com"
14
+ "the ext directory of the gem and email us at support@appsignal.com"
10
15
  )
11
16
  Appsignal.extension_loaded = false
12
17
  end
@@ -25,11 +30,30 @@ module Appsignal
25
30
  agent_config["version"]
26
31
  end
27
32
 
33
+ # Do nothing if the extension methods are not loaded
34
+ #
35
+ # Disabled in testing so we can make sure that we don't miss a extension
36
+ # function implementation.
28
37
  def method_missing(m, *args, &block)
29
- # Do nothing if the extension methods are not loaded
38
+ super if Appsignal.testing?
30
39
  end
31
40
  end
32
41
 
42
+ if Appsignal::System.jruby?
43
+ extend Appsignal::Extension::Jruby
44
+
45
+ # Reassign Transaction class for jRuby extension usage.
46
+ #
47
+ # Makes sure the generated docs aren't always overwritten with the jRuby
48
+ # version.
49
+ Transaction = Jruby::Transaction
50
+ # Reassign Data class for jRuby extension usage.
51
+ #
52
+ # Makes sure the generated docs aren't always overwritten with the jRuby
53
+ # version.
54
+ Data = Jruby::Data
55
+ end
56
+
33
57
  class Data
34
58
  def inspect
35
59
  "#<#{self.class.name}:#{object_id} #{self}>"
@@ -0,0 +1,460 @@
1
+ require "ffi"
2
+
3
+ module Appsignal
4
+ class Extension
5
+ # jRuby extension wrapper
6
+ #
7
+ # Only loaded if the system is detected as jRuby.
8
+ #
9
+ # @api private
10
+ module Jruby # rubocop:disable Metrics/ModuleLength
11
+ extend FFI::Library
12
+
13
+ # jRuby extension String helpers.
14
+ #
15
+ # Based on the make_appsignal_string and make_ruby_string helpers from the
16
+ # AppSignal C-extension in `ext/appsignal_extension.c`.
17
+ module StringHelpers
18
+ class AppsignalString < FFI::Struct
19
+ layout :len, :size_t,
20
+ :buf, :pointer
21
+ end
22
+
23
+ def make_appsignal_string(ruby_string)
24
+ unless ruby_string.is_a?(String)
25
+ raise ArgumentError, "argument is not a string"
26
+ end
27
+
28
+ AppsignalString.new.tap do |appsignal_string|
29
+ appsignal_string[:len] = ruby_string.bytesize
30
+ appsignal_string[:buf] = FFI::MemoryPointer.from_string(ruby_string)
31
+ end
32
+ end
33
+
34
+ def make_ruby_string(appsignal_string)
35
+ appsignal_string[:buf].read_string(appsignal_string[:len]).tap do |ruby_string|
36
+ ruby_string.force_encoding(Encoding::UTF_8)
37
+ end
38
+ end
39
+ end
40
+ include StringHelpers
41
+
42
+ def self.lib_extension
43
+ if Appsignal::System.agent_platform.include?("darwin")
44
+ "dylib"
45
+ else
46
+ "so"
47
+ end
48
+ end
49
+
50
+ begin
51
+ ffi_lib File.join(File.dirname(__FILE__), "../../../ext/libappsignal.#{lib_extension}")
52
+ typedef AppsignalString.by_value, :appsignal_string
53
+
54
+ attach_function :appsignal_start, [], :void
55
+ attach_function :appsignal_stop, [], :void
56
+ attach_function :appsignal_diagnose, [], :appsignal_string
57
+ attach_function :appsignal_get_server_state,
58
+ [:appsignal_string],
59
+ :appsignal_string
60
+ attach_function :appsignal_running_in_container, [], :bool
61
+
62
+ # Metrics methods
63
+ attach_function :appsignal_set_gauge,
64
+ [:appsignal_string, :double],
65
+ :void
66
+ attach_function :appsignal_set_host_gauge,
67
+ [:appsignal_string, :double],
68
+ :void
69
+ attach_function :appsignal_set_process_gauge,
70
+ [:appsignal_string, :double],
71
+ :void
72
+ attach_function :appsignal_increment_counter,
73
+ [:appsignal_string, :int64],
74
+ :void
75
+ attach_function :appsignal_add_distribution_value,
76
+ [:appsignal_string, :double],
77
+ :void
78
+
79
+ # Transaction methods
80
+ attach_function :appsignal_free_transaction,
81
+ [],
82
+ :void
83
+ attach_function :appsignal_start_transaction,
84
+ [:appsignal_string, :appsignal_string, :long],
85
+ :pointer
86
+ attach_function :appsignal_start_event,
87
+ [:pointer, :long],
88
+ :void
89
+ attach_function :appsignal_finish_event,
90
+ [:pointer, :appsignal_string, :appsignal_string, :appsignal_string, :int64, :long],
91
+ :void
92
+ attach_function :appsignal_finish_event_data,
93
+ [:pointer, :appsignal_string, :appsignal_string, :pointer, :int64, :long],
94
+ :void
95
+ attach_function :appsignal_record_event,
96
+ [:pointer, :appsignal_string, :appsignal_string, :appsignal_string, :int64, :long, :long],
97
+ :void
98
+ attach_function :appsignal_record_event_data,
99
+ [:pointer, :appsignal_string, :appsignal_string, :pointer, :int64, :long, :long],
100
+ :void
101
+ attach_function :appsignal_set_transaction_error,
102
+ [:pointer, :appsignal_string, :appsignal_string, :pointer],
103
+ :void
104
+ attach_function :appsignal_set_transaction_action,
105
+ [:pointer, :appsignal_string],
106
+ :void
107
+ attach_function :appsignal_set_transaction_namespace,
108
+ [:pointer, :appsignal_string],
109
+ :void
110
+ attach_function :appsignal_set_transaction_sample_data,
111
+ [:pointer, :appsignal_string, :pointer],
112
+ :void
113
+ attach_function :appsignal_set_transaction_queue_start,
114
+ [:pointer, :long],
115
+ :void
116
+ attach_function :appsignal_set_transaction_metadata,
117
+ [:pointer, :appsignal_string, :appsignal_string],
118
+ :void
119
+ attach_function :appsignal_finish_transaction,
120
+ [:pointer, :long],
121
+ :void
122
+ attach_function :appsignal_complete_transaction,
123
+ [:pointer],
124
+ :void
125
+ attach_function :appsignal_transaction_to_json,
126
+ [:pointer],
127
+ :appsignal_string
128
+
129
+ # Data struct methods
130
+ attach_function :appsignal_free_data, [], :void
131
+ attach_function :appsignal_data_map_new, [], :pointer
132
+ attach_function :appsignal_data_array_new, [], :pointer
133
+ attach_function :appsignal_data_map_set_string,
134
+ [:pointer, :appsignal_string, :appsignal_string],
135
+ :void
136
+ attach_function :appsignal_data_map_set_integer,
137
+ [:pointer, :appsignal_string, :int64],
138
+ :void
139
+ attach_function :appsignal_data_map_set_float,
140
+ [:pointer, :appsignal_string, :double],
141
+ :void
142
+ attach_function :appsignal_data_map_set_boolean,
143
+ [:pointer, :appsignal_string, :bool],
144
+ :void
145
+ attach_function :appsignal_data_map_set_null,
146
+ [:pointer, :appsignal_string],
147
+ :void
148
+ attach_function :appsignal_data_map_set_data,
149
+ [:pointer, :appsignal_string, :pointer],
150
+ :void
151
+ attach_function :appsignal_data_array_append_string,
152
+ [:pointer, :appsignal_string],
153
+ :void
154
+ attach_function :appsignal_data_array_append_integer,
155
+ [:pointer, :int64],
156
+ :void
157
+ attach_function :appsignal_data_array_append_float,
158
+ [:pointer, :double],
159
+ :void
160
+ attach_function :appsignal_data_array_append_boolean,
161
+ [:pointer, :bool],
162
+ :void
163
+ attach_function :appsignal_data_array_append_null,
164
+ [:pointer],
165
+ :void
166
+ attach_function :appsignal_data_array_append_data,
167
+ [:pointer, :pointer],
168
+ :void
169
+ attach_function :appsignal_data_equal,
170
+ [:pointer, :pointer],
171
+ :bool
172
+ attach_function :appsignal_data_to_json,
173
+ [:pointer],
174
+ :appsignal_string
175
+
176
+ Appsignal.extension_loaded = true
177
+ rescue LoadError => err
178
+ Appsignal.logger.error(
179
+ "Failed to load extension (#{err}), please email us at " \
180
+ "support@appsignal.com"
181
+ )
182
+ Appsignal.extension_loaded = false
183
+ end
184
+
185
+ def start
186
+ appsignal_start
187
+ end
188
+
189
+ def stop
190
+ appsignal_stop
191
+ end
192
+
193
+ def diagnose
194
+ make_ruby_string(appsignal_diagnose)
195
+ end
196
+
197
+ def get_server_state(key)
198
+ state = appsignal_get_server_state(make_appsignal_string(key))
199
+ make_ruby_string state if state[:len] > 0
200
+ end
201
+
202
+ def start_transaction(transaction_id, namespace, gc_duration_ms)
203
+ transaction = appsignal_start_transaction(
204
+ make_appsignal_string(transaction_id),
205
+ make_appsignal_string(namespace),
206
+ gc_duration_ms
207
+ )
208
+
209
+ return if !transaction || transaction.null?
210
+ Transaction.new(transaction)
211
+ end
212
+
213
+ def data_map_new
214
+ Data.new(appsignal_data_map_new)
215
+ end
216
+
217
+ def data_array_new
218
+ Data.new(appsignal_data_array_new)
219
+ end
220
+
221
+ def running_in_container?
222
+ appsignal_running_in_container
223
+ end
224
+
225
+ def set_gauge(key, value)
226
+ appsignal_set_gauge(make_appsignal_string(key), value)
227
+ end
228
+
229
+ def set_host_gauge(key, value)
230
+ appsignal_set_host_gauge(make_appsignal_string(key), value)
231
+ end
232
+
233
+ def set_process_gauge(key, value)
234
+ appsignal_set_process_gauge(make_appsignal_string(key), value)
235
+ end
236
+
237
+ def increment_counter(key, value)
238
+ appsignal_increment_counter(make_appsignal_string(key), value)
239
+ end
240
+
241
+ def add_distribution_value(key, value)
242
+ appsignal_add_distribution_value(make_appsignal_string(key), value)
243
+ end
244
+
245
+ class Transaction # rubocop:disable Metrics/ClassLength
246
+ include StringHelpers
247
+
248
+ attr_reader :pointer
249
+
250
+ def initialize(pointer)
251
+ @pointer = FFI::AutoPointer.new(
252
+ pointer,
253
+ Extension.method(:appsignal_free_transaction)
254
+ )
255
+ end
256
+
257
+ def start_event(gc_duration_ms)
258
+ Extension.appsignal_start_event(pointer, gc_duration_ms)
259
+ end
260
+
261
+ def finish_event(name, title, body, body_format, gc_duration_ms)
262
+ case body
263
+ when String
264
+ method = :appsignal_finish_event
265
+ body_arg = make_appsignal_string(body)
266
+ when Data
267
+ method = :appsignal_finish_event_data
268
+ body_arg = body.pointer
269
+ else
270
+ raise ArgumentError,
271
+ "body argument should be a String or Appsignal::Extension::Data"
272
+ end
273
+ Extension.public_send(
274
+ method,
275
+ pointer,
276
+ make_appsignal_string(name),
277
+ make_appsignal_string(title),
278
+ body_arg,
279
+ body_format,
280
+ gc_duration_ms
281
+ )
282
+ end
283
+
284
+ def record_event(name, title, body, body_format, duration, gc_duration_ms) # rubocop:disable Metrics/ParameterLists
285
+ case body
286
+ when String
287
+ method = :appsignal_record_event
288
+ body_arg = make_appsignal_string(body)
289
+ when Data
290
+ method = :appsignal_record_event_data
291
+ body_arg = body.pointer
292
+ else
293
+ raise ArgumentError,
294
+ "body argument should be a String or Appsignal::Extension::Data"
295
+ end
296
+ Extension.public_send(
297
+ method,
298
+ pointer,
299
+ make_appsignal_string(name),
300
+ make_appsignal_string(title),
301
+ body_arg,
302
+ body_format,
303
+ duration,
304
+ gc_duration_ms
305
+ )
306
+ end
307
+
308
+ def set_error(name, message, backtrace)
309
+ Extension.appsignal_set_transaction_error(
310
+ pointer,
311
+ make_appsignal_string(name),
312
+ make_appsignal_string(message),
313
+ backtrace.pointer
314
+ )
315
+ end
316
+
317
+ def set_action(action_name) # rubocop:disable Style/AccessorMethodName
318
+ Extension.appsignal_set_transaction_action(
319
+ pointer,
320
+ make_appsignal_string(action_name)
321
+ )
322
+ end
323
+
324
+ def set_namespace(namespace) # rubocop:disable Style/AccessorMethodName
325
+ Extension.appsignal_set_transaction_namespace(
326
+ pointer,
327
+ make_appsignal_string(namespace)
328
+ )
329
+ end
330
+
331
+ def set_sample_data(key, payload)
332
+ Extension.appsignal_set_transaction_sample_data(
333
+ pointer,
334
+ make_appsignal_string(key),
335
+ payload.pointer
336
+ )
337
+ end
338
+
339
+ def set_queue_start(time) # rubocop:disable Style/AccessorMethodName
340
+ Extension.appsignal_set_transaction_queue_start(pointer, time)
341
+ end
342
+
343
+ def set_metadata(key, value)
344
+ Extension.appsignal_set_transaction_metadata(
345
+ pointer,
346
+ make_appsignal_string(key),
347
+ make_appsignal_string(value)
348
+ )
349
+ end
350
+
351
+ def finish(gc_duration_ms)
352
+ Extension.appsignal_finish_transaction(pointer, gc_duration_ms)
353
+ end
354
+
355
+ def complete
356
+ Extension.appsignal_complete_transaction(pointer)
357
+ end
358
+
359
+ def to_json
360
+ json = Extension.appsignal_transaction_to_json(pointer)
361
+ make_ruby_string(json) if json[:len] > 0
362
+ end
363
+ end
364
+
365
+ class Data
366
+ include StringHelpers
367
+ attr_reader :pointer
368
+
369
+ def initialize(pointer)
370
+ @pointer = FFI::AutoPointer.new(
371
+ pointer,
372
+ Extension.method(:appsignal_free_data)
373
+ )
374
+ end
375
+
376
+ def set_string(key, value)
377
+ Extension.appsignal_data_map_set_string(
378
+ pointer,
379
+ make_appsignal_string(key),
380
+ make_appsignal_string(value)
381
+ )
382
+ end
383
+
384
+ def set_integer(key, value)
385
+ Extension.appsignal_data_map_set_integer(
386
+ pointer,
387
+ make_appsignal_string(key),
388
+ value
389
+ )
390
+ end
391
+
392
+ def set_float(key, value)
393
+ Extension.appsignal_data_map_set_float(
394
+ pointer,
395
+ make_appsignal_string(key),
396
+ value
397
+ )
398
+ end
399
+
400
+ def set_boolean(key, value)
401
+ Extension.appsignal_data_map_set_boolean(
402
+ pointer,
403
+ make_appsignal_string(key),
404
+ value
405
+ )
406
+ end
407
+
408
+ def set_nil(key) # rubocop:disable Style/AccessorMethodName
409
+ Extension.appsignal_data_map_set_null(
410
+ pointer,
411
+ make_appsignal_string(key)
412
+ )
413
+ end
414
+
415
+ def set_data(key, value)
416
+ Extension.appsignal_data_map_set_data(
417
+ pointer,
418
+ make_appsignal_string(key),
419
+ value.pointer
420
+ )
421
+ end
422
+
423
+ def append_string(value)
424
+ Extension.appsignal_data_array_append_string(
425
+ pointer,
426
+ make_appsignal_string(value)
427
+ )
428
+ end
429
+
430
+ def append_integer(value)
431
+ Extension.appsignal_data_array_append_integer(pointer, value)
432
+ end
433
+
434
+ def append_float(value)
435
+ Extension.appsignal_data_array_append_float(pointer, value)
436
+ end
437
+
438
+ def append_boolean(value)
439
+ Extension.appsignal_data_array_append_boolean(pointer, value)
440
+ end
441
+
442
+ def append_nil
443
+ Extension.appsignal_data_array_append_null(pointer)
444
+ end
445
+
446
+ def append_data(value)
447
+ Extension.appsignal_data_array_append_data(pointer, value.pointer)
448
+ end
449
+
450
+ def ==(other)
451
+ Extension.appsignal_data_equal(pointer, other.pointer)
452
+ end
453
+
454
+ def to_s
455
+ make_ruby_string Extension.appsignal_data_to_json(pointer)
456
+ end
457
+ end
458
+ end
459
+ end
460
+ end