appsignal 2.4.3 → 2.5.0.alpha.1

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