appsignal 2.5.0.alpha.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rspec +4 -0
  4. data/.rubocop.yml +66 -0
  5. data/.rubocop_todo.yml +124 -0
  6. data/.travis.yml +72 -0
  7. data/.yardopts +8 -0
  8. data/CHANGELOG.md +639 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +20 -0
  11. data/README.md +264 -0
  12. data/Rakefile +214 -0
  13. data/appsignal.gemspec +42 -0
  14. data/benchmark.rake +77 -0
  15. data/bin/appsignal +13 -0
  16. data/ext/Rakefile +27 -0
  17. data/ext/agent.yml +64 -0
  18. data/ext/appsignal_extension.c +692 -0
  19. data/ext/base.rb +79 -0
  20. data/ext/extconf.rb +35 -0
  21. data/gemfiles/capistrano2.gemfile +7 -0
  22. data/gemfiles/capistrano3.gemfile +7 -0
  23. data/gemfiles/grape.gemfile +7 -0
  24. data/gemfiles/no_dependencies.gemfile +5 -0
  25. data/gemfiles/padrino.gemfile +7 -0
  26. data/gemfiles/que.gemfile +5 -0
  27. data/gemfiles/rails-3.2.gemfile +6 -0
  28. data/gemfiles/rails-4.0.gemfile +6 -0
  29. data/gemfiles/rails-4.1.gemfile +6 -0
  30. data/gemfiles/rails-4.2.gemfile +10 -0
  31. data/gemfiles/rails-5.0.gemfile +5 -0
  32. data/gemfiles/rails-5.1.gemfile +5 -0
  33. data/gemfiles/resque.gemfile +12 -0
  34. data/gemfiles/sequel-435.gemfile +11 -0
  35. data/gemfiles/sequel.gemfile +11 -0
  36. data/gemfiles/sinatra.gemfile +6 -0
  37. data/gemfiles/webmachine.gemfile +5 -0
  38. data/lib/appsignal.rb +804 -0
  39. data/lib/appsignal/auth_check.rb +65 -0
  40. data/lib/appsignal/capistrano.rb +10 -0
  41. data/lib/appsignal/cli.rb +108 -0
  42. data/lib/appsignal/cli/demo.rb +63 -0
  43. data/lib/appsignal/cli/diagnose.rb +500 -0
  44. data/lib/appsignal/cli/helpers.rb +72 -0
  45. data/lib/appsignal/cli/install.rb +277 -0
  46. data/lib/appsignal/cli/notify_of_deploy.rb +113 -0
  47. data/lib/appsignal/config.rb +287 -0
  48. data/lib/appsignal/demo.rb +107 -0
  49. data/lib/appsignal/event_formatter.rb +74 -0
  50. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +24 -0
  51. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +14 -0
  52. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +14 -0
  53. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +32 -0
  54. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +19 -0
  55. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +89 -0
  56. data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
  57. data/lib/appsignal/extension.rb +63 -0
  58. data/lib/appsignal/extension/jruby.rb +460 -0
  59. data/lib/appsignal/garbage_collection_profiler.rb +48 -0
  60. data/lib/appsignal/hooks.rb +105 -0
  61. data/lib/appsignal/hooks/action_cable.rb +113 -0
  62. data/lib/appsignal/hooks/active_support_notifications.rb +52 -0
  63. data/lib/appsignal/hooks/celluloid.rb +30 -0
  64. data/lib/appsignal/hooks/data_mapper.rb +18 -0
  65. data/lib/appsignal/hooks/delayed_job.rb +19 -0
  66. data/lib/appsignal/hooks/mongo_ruby_driver.rb +21 -0
  67. data/lib/appsignal/hooks/net_http.rb +29 -0
  68. data/lib/appsignal/hooks/passenger.rb +22 -0
  69. data/lib/appsignal/hooks/puma.rb +35 -0
  70. data/lib/appsignal/hooks/que.rb +21 -0
  71. data/lib/appsignal/hooks/rake.rb +39 -0
  72. data/lib/appsignal/hooks/redis.rb +30 -0
  73. data/lib/appsignal/hooks/sequel.rb +60 -0
  74. data/lib/appsignal/hooks/shoryuken.rb +43 -0
  75. data/lib/appsignal/hooks/sidekiq.rb +144 -0
  76. data/lib/appsignal/hooks/unicorn.rb +40 -0
  77. data/lib/appsignal/hooks/webmachine.rb +23 -0
  78. data/lib/appsignal/integrations/capistrano/appsignal.cap +39 -0
  79. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +52 -0
  80. data/lib/appsignal/integrations/data_mapper.rb +33 -0
  81. data/lib/appsignal/integrations/delayed_job_plugin.rb +54 -0
  82. data/lib/appsignal/integrations/grape.rb +53 -0
  83. data/lib/appsignal/integrations/mongo_ruby_driver.rb +55 -0
  84. data/lib/appsignal/integrations/object.rb +35 -0
  85. data/lib/appsignal/integrations/padrino.rb +84 -0
  86. data/lib/appsignal/integrations/que.rb +43 -0
  87. data/lib/appsignal/integrations/railtie.rb +41 -0
  88. data/lib/appsignal/integrations/rake.rb +2 -0
  89. data/lib/appsignal/integrations/resque.rb +20 -0
  90. data/lib/appsignal/integrations/resque_active_job.rb +30 -0
  91. data/lib/appsignal/integrations/sinatra.rb +17 -0
  92. data/lib/appsignal/integrations/webmachine.rb +38 -0
  93. data/lib/appsignal/js_exception_transaction.rb +54 -0
  94. data/lib/appsignal/marker.rb +63 -0
  95. data/lib/appsignal/minutely.rb +42 -0
  96. data/lib/appsignal/rack/generic_instrumentation.rb +49 -0
  97. data/lib/appsignal/rack/js_exception_catcher.rb +70 -0
  98. data/lib/appsignal/rack/rails_instrumentation.rb +51 -0
  99. data/lib/appsignal/rack/sinatra_instrumentation.rb +99 -0
  100. data/lib/appsignal/rack/streaming_listener.rb +73 -0
  101. data/lib/appsignal/system.rb +81 -0
  102. data/lib/appsignal/transaction.rb +498 -0
  103. data/lib/appsignal/transmitter.rb +107 -0
  104. data/lib/appsignal/utils.rb +127 -0
  105. data/lib/appsignal/utils/params_sanitizer.rb +59 -0
  106. data/lib/appsignal/utils/query_params_sanitizer.rb +55 -0
  107. data/lib/appsignal/version.rb +3 -0
  108. data/lib/sequel/extensions/appsignal_integration.rb +3 -0
  109. data/resources/appsignal.yml.erb +39 -0
  110. data/resources/cacert.pem +3866 -0
  111. data/spec/.rubocop.yml +7 -0
  112. data/spec/lib/appsignal/auth_check_spec.rb +80 -0
  113. data/spec/lib/appsignal/capistrano2_spec.rb +224 -0
  114. data/spec/lib/appsignal/capistrano3_spec.rb +237 -0
  115. data/spec/lib/appsignal/cli/demo_spec.rb +67 -0
  116. data/spec/lib/appsignal/cli/diagnose_spec.rb +988 -0
  117. data/spec/lib/appsignal/cli/helpers_spec.rb +171 -0
  118. data/spec/lib/appsignal/cli/install_spec.rb +632 -0
  119. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +168 -0
  120. data/spec/lib/appsignal/cli_spec.rb +56 -0
  121. data/spec/lib/appsignal/config_spec.rb +637 -0
  122. data/spec/lib/appsignal/demo_spec.rb +87 -0
  123. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +44 -0
  124. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +21 -0
  125. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +21 -0
  126. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +52 -0
  127. data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +21 -0
  128. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +113 -0
  129. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +112 -0
  130. data/spec/lib/appsignal/event_formatter_spec.rb +100 -0
  131. data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
  132. data/spec/lib/appsignal/extension_spec.rb +137 -0
  133. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +66 -0
  134. data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
  135. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +92 -0
  136. data/spec/lib/appsignal/hooks/celluloid_spec.rb +35 -0
  137. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +39 -0
  138. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +358 -0
  139. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +44 -0
  140. data/spec/lib/appsignal/hooks/net_http_spec.rb +53 -0
  141. data/spec/lib/appsignal/hooks/passenger_spec.rb +30 -0
  142. data/spec/lib/appsignal/hooks/puma_spec.rb +80 -0
  143. data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
  144. data/spec/lib/appsignal/hooks/rake_spec.rb +73 -0
  145. data/spec/lib/appsignal/hooks/redis_spec.rb +55 -0
  146. data/spec/lib/appsignal/hooks/sequel_spec.rb +46 -0
  147. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +192 -0
  148. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +419 -0
  149. data/spec/lib/appsignal/hooks/unicorn_spec.rb +52 -0
  150. data/spec/lib/appsignal/hooks/webmachine_spec.rb +35 -0
  151. data/spec/lib/appsignal/hooks_spec.rb +195 -0
  152. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +65 -0
  153. data/spec/lib/appsignal/integrations/grape_spec.rb +225 -0
  154. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +127 -0
  155. data/spec/lib/appsignal/integrations/object_spec.rb +249 -0
  156. data/spec/lib/appsignal/integrations/padrino_spec.rb +323 -0
  157. data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
  158. data/spec/lib/appsignal/integrations/railtie_spec.rb +129 -0
  159. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +83 -0
  160. data/spec/lib/appsignal/integrations/resque_spec.rb +92 -0
  161. data/spec/lib/appsignal/integrations/sinatra_spec.rb +73 -0
  162. data/spec/lib/appsignal/integrations/webmachine_spec.rb +69 -0
  163. data/spec/lib/appsignal/js_exception_transaction_spec.rb +128 -0
  164. data/spec/lib/appsignal/marker_spec.rb +51 -0
  165. data/spec/lib/appsignal/minutely_spec.rb +50 -0
  166. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +90 -0
  167. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +147 -0
  168. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +117 -0
  169. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +213 -0
  170. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +161 -0
  171. data/spec/lib/appsignal/system_spec.rb +131 -0
  172. data/spec/lib/appsignal/transaction_spec.rb +1146 -0
  173. data/spec/lib/appsignal/transmitter_spec.rb +152 -0
  174. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +136 -0
  175. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +192 -0
  176. data/spec/lib/appsignal/utils_spec.rb +150 -0
  177. data/spec/lib/appsignal_spec.rb +1049 -0
  178. data/spec/spec_helper.rb +116 -0
  179. data/spec/support/fixtures/containers/cgroups/docker +14 -0
  180. data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
  181. data/spec/support/fixtures/containers/cgroups/lxc +10 -0
  182. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  183. data/spec/support/fixtures/containers/cgroups/none +1 -0
  184. data/spec/support/fixtures/generated_config.yml +24 -0
  185. data/spec/support/fixtures/uploaded_file.txt +0 -0
  186. data/spec/support/helpers/api_request_helper.rb +19 -0
  187. data/spec/support/helpers/cli_helpers.rb +26 -0
  188. data/spec/support/helpers/config_helpers.rb +21 -0
  189. data/spec/support/helpers/dependency_helper.rb +73 -0
  190. data/spec/support/helpers/directory_helper.rb +27 -0
  191. data/spec/support/helpers/env_helpers.rb +33 -0
  192. data/spec/support/helpers/example_exception.rb +13 -0
  193. data/spec/support/helpers/example_standard_error.rb +13 -0
  194. data/spec/support/helpers/log_helpers.rb +22 -0
  195. data/spec/support/helpers/std_streams_helper.rb +66 -0
  196. data/spec/support/helpers/system_helpers.rb +8 -0
  197. data/spec/support/helpers/time_helpers.rb +11 -0
  198. data/spec/support/helpers/transaction_helpers.rb +37 -0
  199. data/spec/support/matchers/contains_log.rb +7 -0
  200. data/spec/support/mocks/fake_gc_profiler.rb +19 -0
  201. data/spec/support/mocks/mock_extension.rb +6 -0
  202. data/spec/support/project_fixture/config/application.rb +0 -0
  203. data/spec/support/project_fixture/config/appsignal.yml +32 -0
  204. data/spec/support/project_fixture/config/environments/development.rb +0 -0
  205. data/spec/support/project_fixture/config/environments/production.rb +0 -0
  206. data/spec/support/project_fixture/config/environments/test.rb +0 -0
  207. data/spec/support/project_fixture/log/.gitkeep +0 -0
  208. data/spec/support/rails/my_app.rb +6 -0
  209. data/spec/support/shared_examples/instrument.rb +43 -0
  210. data/spec/support/stubs/delayed_job.rb +0 -0
  211. metadata +483 -0
@@ -0,0 +1,79 @@
1
+ require "digest"
2
+ require "logger"
3
+ require "fileutils"
4
+ require "open-uri"
5
+ require "zlib"
6
+ require "yaml"
7
+ require "rubygems/package"
8
+ require File.expand_path("../../lib/appsignal/system.rb", __FILE__)
9
+
10
+ EXT_PATH = File.expand_path("..", __FILE__).freeze
11
+ AGENT_CONFIG = YAML.load(File.read(File.join(EXT_PATH, "agent.yml"))).freeze
12
+
13
+ PLATFORM = Appsignal::System.agent_platform
14
+ ARCH = "#{RbConfig::CONFIG["host_cpu"]}-#{PLATFORM}".freeze
15
+ CA_CERT_PATH = File.join(EXT_PATH, "../resources/cacert.pem").freeze
16
+
17
+ def ext_path(path)
18
+ File.join(EXT_PATH, path)
19
+ end
20
+
21
+ def logger
22
+ @logger ||= Logger.new(File.join(EXT_PATH, "install.log"))
23
+ end
24
+
25
+ def installation_failed(reason)
26
+ logger.error "Installation failed: #{reason}"
27
+ File.open(File.join(EXT_PATH, "Makefile"), "w") do |file|
28
+ file.write "default:\nclean:\ninstall:"
29
+ end
30
+ end
31
+
32
+ def write_agent_architecture
33
+ File.open(File.join(EXT_PATH, "appsignal.architecture"), "w") do |file|
34
+ file.write ARCH
35
+ end
36
+ end
37
+
38
+ def check_architecture
39
+ if AGENT_CONFIG["triples"].keys.include?(ARCH)
40
+ true
41
+ else
42
+ installation_failed(
43
+ "AppSignal currently does not support your system architecture (#{ARCH})." \
44
+ "Please let us know at support@appsignal.com, we aim to support everything our customers run."
45
+ )
46
+ false
47
+ end
48
+ end
49
+
50
+ def download_archive(arch_config, type)
51
+ logger.info "Downloading agent release from #{arch_config[type]["download_url"]}"
52
+ open(arch_config[type]["download_url"], :ssl_ca_cert => CA_CERT_PATH)
53
+ end
54
+
55
+ def verify_archive(archive, arch_config, type)
56
+ if Digest::SHA256.hexdigest(archive.read) == arch_config[type]["checksum"]
57
+ logger.info "Checksum of downloaded archive verified, extracting archive"
58
+ true
59
+ else
60
+ installation_failed(
61
+ "Aborting installation, checksum of downloaded archive could not be verified: " \
62
+ "Expected '#{arch_config[type]["checksum"]}', got '#{checksum}'."
63
+ )
64
+ false
65
+ end
66
+ end
67
+
68
+ def unarchive(archive)
69
+ Gem::Package::TarReader.new(Zlib::GzipReader.open(archive)) do |tar|
70
+ tar.each do |entry|
71
+ next unless entry.file?
72
+
73
+ File.open(ext_path(entry.full_name), "wb") do |f|
74
+ f.write(entry.read)
75
+ end
76
+ end
77
+ end
78
+ FileUtils.chmod(0o755, ext_path("appsignal-agent"))
79
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path("../../lib/appsignal/version.rb", __FILE__)
2
+ require File.expand_path("../base.rb", __FILE__)
3
+
4
+ def install
5
+ logger.info "Installing appsignal agent #{Appsignal::VERSION} for Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
6
+ write_agent_architecture
7
+ return unless check_architecture
8
+ arch_config = AGENT_CONFIG["triples"][ARCH]
9
+
10
+ unless File.exist?(ext_path("appsignal-agent")) &&
11
+ File.exist?(ext_path("libappsignal.a")) &&
12
+ File.exist?(ext_path("appsignal.h"))
13
+ archive = download_archive(arch_config, "static")
14
+ return unless verify_archive(archive, arch_config, "static")
15
+ unarchive(archive)
16
+ end
17
+
18
+ logger.info "Creating makefile"
19
+ require "mkmf"
20
+ if !have_library("appsignal", "appsignal_start", "appsignal.h")
21
+ installation_failed "Aborting installation, libappsignal.a or appsignal.h not found"
22
+ elsif !find_executable("appsignal-agent", EXT_PATH)
23
+ installation_failed "Aborting installation, appsignal-agent not found"
24
+ else
25
+ create_makefile "appsignal_extension"
26
+ logger.info "Successfully created Makefile for appsignal extension"
27
+ end
28
+ rescue => ex
29
+ installation_failed "Exception while installing: #{ex}"
30
+ ex.backtrace.each do |line|
31
+ logger.error line
32
+ end
33
+ end
34
+
35
+ install
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'capistrano', '< 3.0'
4
+ gem 'net-ssh', '2.9.2'
5
+ gem 'rack', '~> 1.6'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'capistrano', '~> 3.0'
4
+ gem 'net-ssh', '2.9.2'
5
+ gem 'rack', '~> 1.6'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'grape', '0.14.0'
4
+ gem 'rack', '~> 1.6'
5
+ gem 'activesupport', '~> 4.2'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rack', '~> 1.6'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'padrino', '~> 0.13.0'
4
+ gem 'rack', '~> 1.6'
5
+ gem 'activesupport', '~> 4.2'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'que'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 3.2.14'
4
+ gem 'test-unit'
5
+
6
+ gemspec :path => '../'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 4.0.0'
4
+ gem 'mime-types', '~> 2.6'
5
+
6
+ gemspec :path => '../'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 4.1.0'
4
+ gem 'mime-types', '~> 2.6'
5
+
6
+ gemspec :path => '../'
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 4.2.0'
4
+ gem 'mime-types', '~> 2.6'
5
+
6
+ gemspec :path => '../'
7
+
8
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.1.0")
9
+ gem 'nokogiri', '~> 1.6.0'
10
+ end
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 5.0.0'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 5.1.0'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'resque'
4
+ gem 'rails', '~> 4.2.0'
5
+ gem 'sinatra'
6
+ gem 'mime-types', '~> 2.6'
7
+
8
+ gemspec :path => '../'
9
+
10
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.1.0")
11
+ gem 'nokogiri', '~> 1.6.0'
12
+ end
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sequel', '~> 4.35'
4
+ if RUBY_PLATFORM == "java"
5
+ gem 'jdbc-sqlite3'
6
+ else
7
+ gem 'sqlite3'
8
+ end
9
+ gem 'rack', '~> 1.6'
10
+
11
+ gemspec :path => '../'
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sequel', '< 4.35'
4
+ if RUBY_PLATFORM == "java"
5
+ gem 'jdbc-sqlite3'
6
+ else
7
+ gem 'sqlite3'
8
+ end
9
+ gem 'rack', '~> 1.6'
10
+
11
+ gemspec :path => '../'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sinatra'
4
+ gem 'rack', '~> 1.6'
5
+
6
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'webmachine'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,804 @@
1
+ require "json"
2
+ require "logger"
3
+ require "securerandom"
4
+
5
+ # AppSignal for Ruby gem's main module.
6
+ #
7
+ # Provides method to control the AppSignal instrumentation and the system agent.
8
+ # Also provides instrumentation helpers for ease of use.
9
+ module Appsignal
10
+ class << self
11
+ extend Gem::Deprecate
12
+
13
+ # Accessor for the AppSignal configuration.
14
+ # Return the current AppSignal configuration.
15
+ #
16
+ # Can return `nil` if no configuration has been set or automatically loaded
17
+ # by an automatic integration or by calling {.start}.
18
+ #
19
+ # @example
20
+ # Appsignal.config
21
+ #
22
+ # @example Setting the configuration
23
+ # Appsignal.config = Appsignal::Config.new(Dir.pwd, "production")
24
+ #
25
+ # @return [Config, nil]
26
+ # @see Config
27
+ attr_accessor :config
28
+ # Accessor for toggle if the AppSignal C-extension is loaded.
29
+ #
30
+ # Can be `nil` if extension has not been loaded yet. See
31
+ # {.extension_loaded?} for a boolean return value.
32
+ #
33
+ # @api private
34
+ # @return [Boolean, nil]
35
+ # @see Extension
36
+ # @see extension_loaded?
37
+ attr_accessor :extension_loaded
38
+ # @!attribute [rw] logger
39
+ # Accessor for the AppSignal logger.
40
+ #
41
+ # If no logger has been set, it will return a "in memory logger", using
42
+ # `in_memory_log`. Once AppSignal is started (using {.start}) the
43
+ # contents of the "in memory logger" is written to the new logger.
44
+ #
45
+ # @note some classes may have options to set custom loggers. Their
46
+ # defaults are pointed to this attribute.
47
+ # @api private
48
+ # @return [Logger]
49
+ # @see start_logger
50
+ attr_writer :logger
51
+
52
+ # @api private
53
+ def extensions
54
+ @extensions ||= []
55
+ end
56
+
57
+ # @api private
58
+ def initialize_extensions
59
+ Appsignal.logger.debug("Initializing extensions")
60
+ extensions.each do |extension|
61
+ Appsignal.logger.debug("Initializing #{extension}")
62
+ extension.initializer
63
+ end
64
+ end
65
+
66
+ # @api private
67
+ def testing?
68
+ false
69
+ end
70
+
71
+ # Start the AppSignal integration.
72
+ #
73
+ # Starts AppSignal with the given configuration. If no configuration is set
74
+ # yet it will try to automatically load the configuration using the
75
+ # environment loaded from environment variables and the currently working
76
+ # directory.
77
+ #
78
+ # This is not required for the automatic integrations AppSignal offers, but
79
+ # this is required for all non-automatic integrations and pure Ruby
80
+ # applications. For more information, see our [integrations
81
+ # list](http://docs.appsignal.com/ruby/integrations/) and our [Integrating
82
+ # AppSignal](http://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html)
83
+ # guide.
84
+ #
85
+ # To start the logger see {.start_logger}.
86
+ #
87
+ # @example
88
+ # Appsignal.start
89
+ #
90
+ # @example with custom loaded configuration
91
+ # Appsignal.config = Appsignal::Config.new(Dir.pwd, "production")
92
+ # Appsignal.start
93
+ #
94
+ # @return [void]
95
+ # @since 0.7.0
96
+ def start # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
+ unless extension_loaded?
98
+ logger.info("Not starting appsignal, extension is not loaded")
99
+ return
100
+ end
101
+
102
+ logger.debug("Starting appsignal")
103
+
104
+ unless @config
105
+ @config = Config.new(
106
+ Dir.pwd,
107
+ ENV["APPSIGNAL_APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"]
108
+ )
109
+ end
110
+
111
+ if config.valid?
112
+ logger.level =
113
+ if config[:debug]
114
+ Logger::DEBUG
115
+ else
116
+ Logger::INFO
117
+ end
118
+ if config.active?
119
+ logger.info "Starting AppSignal #{Appsignal::VERSION} "\
120
+ "(#{$PROGRAM_NAME}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})"
121
+ config.write_to_environment
122
+ Appsignal::Extension.start
123
+ Appsignal::Hooks.load_hooks
124
+ Appsignal::EventFormatter.initialize_formatters
125
+ initialize_extensions
126
+
127
+ if config[:enable_allocation_tracking] && !Appsignal::System.jruby?
128
+ Appsignal::Extension.install_allocation_event_hook
129
+ end
130
+
131
+ if config[:enable_gc_instrumentation]
132
+ GC::Profiler.enable
133
+ Appsignal::Minutely.add_gc_probe
134
+ end
135
+
136
+ Appsignal::Minutely.start if config[:enable_minutely_probes]
137
+ else
138
+ logger.info("Not starting, not active for #{config.env}")
139
+ end
140
+ else
141
+ logger.error("Not starting, no valid config for this environment")
142
+ end
143
+ end
144
+
145
+ # Stop AppSignal's agent.
146
+ #
147
+ # Stops the AppSignal agent. Call this before the end of your program to
148
+ # make sure the agent is stopped as well.
149
+ #
150
+ # @example
151
+ # Appsignal.start
152
+ # # Run your application
153
+ # Appsignal.stop
154
+ #
155
+ # @param called_by [String] Name of the thing that requested the agent to
156
+ # be stopped. Will be used in the AppSignal log file.
157
+ # @return [void]
158
+ # @since 1.0.0
159
+ def stop(called_by = nil)
160
+ if called_by
161
+ logger.debug("Stopping appsignal (#{called_by})")
162
+ else
163
+ logger.debug("Stopping appsignal")
164
+ end
165
+ Appsignal::Extension.stop
166
+ end
167
+
168
+ def forked
169
+ return unless active?
170
+ Appsignal.start_logger
171
+ logger.debug("Forked process, resubscribing and restarting extension")
172
+ Appsignal::Extension.start
173
+ end
174
+
175
+ def get_server_state(key)
176
+ Appsignal::Extension.get_server_state(key)
177
+ end
178
+
179
+ # Creates an AppSignal transaction for the given block.
180
+ #
181
+ # If AppSignal is not {.active?} it will still execute the block, but not
182
+ # create a transaction for it.
183
+ #
184
+ # A event is created for this transaction with the name given in the `name`
185
+ # argument. The event name must start with either `perform_job` or
186
+ # `process_action` to differentiate between the "web" and "background"
187
+ # namespace. Custom namespaces are not supported by this helper method.
188
+ #
189
+ # This helper method also captures any exception that occurs in the given
190
+ # block.
191
+ #
192
+ # The other (request) `env` argument hash keys, not listed here, can be
193
+ # found on the {Appsignal::Transaction::ENV_METHODS} array.
194
+ # Each of these keys are available as keys in the `env` hash argument.
195
+ #
196
+ # @example
197
+ # Appsignal.monitor_transaction("perform_job.nightly_update") do
198
+ # # your code
199
+ # end
200
+ #
201
+ # @example with an environment
202
+ # Appsignal.monitor_transaction(
203
+ # "perform_job.nightly_update",
204
+ # :metadata => { "user_id" => 1 }
205
+ # ) do
206
+ # # your code
207
+ # end
208
+ #
209
+ # @param name [String] main event name.
210
+ # @param env [Hash<Symbol, Object>]
211
+ # @option env [Hash<Symbol/String, Object>] :params Params for the
212
+ # monitored request/job, see {Appsignal::Transaction#params=} for more
213
+ # information.
214
+ # @option env [String] :controller name of the controller in which the
215
+ # transaction was recorded.
216
+ # @option env [String] :class name of the Ruby class in which the
217
+ # transaction was recorded. If `:controller` is also given, `:controller`
218
+ # is used instead.
219
+ # @option env [String] :action name of the controller action in which the
220
+ # transaction was recorded.
221
+ # @option env [String] :method name of the Ruby method in which the
222
+ # transaction was recorded. If `:action` is also given, `:action`
223
+ # is used instead.
224
+ # @option env [Integer] :queue_start the moment the request/job was queued.
225
+ # Used to track how long requests/jobs were queued before being executed.
226
+ # @option env [Hash<Symbol/String, String/Fixnum>] :metadata Additional
227
+ # metadata for the transaction, see {Appsignal::Transaction#set_metadata}
228
+ # for more information.
229
+ # @yield the block to monitor.
230
+ # @raise [Exception] any exception that occurs within the given block is re-raised by
231
+ # this method.
232
+ # @return [Object] the value of the given block is returned.
233
+ # @since 0.10.0
234
+ def monitor_transaction(name, env = {})
235
+ unless active?
236
+ return yield
237
+ end
238
+
239
+ if name.start_with?("perform_job".freeze)
240
+ namespace = Appsignal::Transaction::BACKGROUND_JOB
241
+ request = Appsignal::Transaction::GenericRequest.new(env)
242
+ elsif name.start_with?("process_action".freeze)
243
+ namespace = Appsignal::Transaction::HTTP_REQUEST
244
+ request = ::Rack::Request.new(env)
245
+ else
246
+ logger.error("Unrecognized name '#{name}'")
247
+ return
248
+ end
249
+ transaction = Appsignal::Transaction.create(
250
+ SecureRandom.uuid,
251
+ namespace,
252
+ request
253
+ )
254
+ begin
255
+ Appsignal.instrument(name) do
256
+ yield
257
+ end
258
+ rescue Exception => error # rubocop:disable Lint/RescueException
259
+ transaction.set_error(error)
260
+ raise error
261
+ ensure
262
+ transaction.set_http_or_background_action(request.env)
263
+ transaction.set_http_or_background_queue_start
264
+ Appsignal::Transaction.complete_current!
265
+ end
266
+ end
267
+
268
+ # Monitor a transaction, stop AppSignal and wait for this single
269
+ # transaction to be flushed.
270
+ #
271
+ # Useful for cases such as Rake tasks and Resque-like systems where a
272
+ # process is forked and immediately exits after the transaction finishes.
273
+ #
274
+ # @see monitor_transaction
275
+ def monitor_single_transaction(name, env = {}, &block)
276
+ monitor_transaction(name, env, &block)
277
+ ensure
278
+ stop("monitor_single_transaction")
279
+ end
280
+
281
+ # Listen for an error to occur and send it to AppSignal.
282
+ #
283
+ # Uses {.send_error} to directly send the error in a separate transaction.
284
+ # Does not add the error to the current transaction.
285
+ #
286
+ # Make sure that AppSignal is integrated in your application beforehand.
287
+ # AppSignal won't record errors unless {Config#active?} is `true`.
288
+ #
289
+ # @example
290
+ # # my_app.rb
291
+ # # setup AppSignal beforehand
292
+ #
293
+ # Appsignal.listen_for_error do
294
+ # # my code
295
+ # raise "foo"
296
+ # end
297
+ #
298
+ # @see Transaction.set_tags
299
+ # @see Transaction.set_namespace
300
+ # @see .send_error
301
+ # @see https://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html
302
+ # AppSignal integration guide
303
+ #
304
+ # @param tags [Hash, nil]
305
+ # @param namespace [String] the namespace for this error.
306
+ # @yield yields the given block.
307
+ # @return [Object] returns the return value of the given block.
308
+ def listen_for_error(tags = nil, namespace = Appsignal::Transaction::HTTP_REQUEST)
309
+ yield
310
+ rescue Exception => error # rubocop:disable Lint/RescueException
311
+ send_error(error, tags, namespace)
312
+ raise error
313
+ end
314
+ alias :listen_for_exception :listen_for_error
315
+
316
+ # Send an error to AppSignal regardless of the context.
317
+ #
318
+ # Records and send the exception to AppSignal.
319
+ #
320
+ # This instrumentation helper does not require a transaction to be active,
321
+ # it starts a new transaction by itself.
322
+ #
323
+ # Use {.set_error} if your want to add an exception to the current
324
+ # transaction.
325
+ #
326
+ # **Note**: Does not do anything if AppSignal is not active or when the
327
+ # "error" is not a class extended from Ruby's Exception class.
328
+ #
329
+ # @example Send an exception
330
+ # begin
331
+ # raise "oh no!"
332
+ # rescue => e
333
+ # Appsignal.send_error(e)
334
+ # end
335
+ #
336
+ # @example Send an exception with tags
337
+ # begin
338
+ # raise "oh no!"
339
+ # rescue => e
340
+ # Appsignal.send_error(e, :key => "value")
341
+ # end
342
+ #
343
+ # @param error [Exception] The error to send to AppSignal.
344
+ # @param tags [Hash{String, Symbol => String, Symbol, Integer}] Additional
345
+ # tags to add to the error. See also {.tag_request}.
346
+ # @param namespace [String] The namespace in which the error occurred.
347
+ # See also {.set_namespace}.
348
+ # @return [void]
349
+ #
350
+ # @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
351
+ # Exception handling guide
352
+ # @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
353
+ # Tagging guide
354
+ # @since 0.6.0
355
+ def send_error(error, tags = nil, namespace = Appsignal::Transaction::HTTP_REQUEST)
356
+ return unless active?
357
+ unless error.is_a?(Exception)
358
+ logger.error("Can't send error, given value is not an exception")
359
+ return
360
+ end
361
+ transaction = Appsignal::Transaction.new(
362
+ SecureRandom.uuid,
363
+ namespace,
364
+ Appsignal::Transaction::GenericRequest.new({})
365
+ )
366
+ transaction.set_tags(tags) if tags
367
+ transaction.set_error(error)
368
+ transaction.complete
369
+ end
370
+ alias :send_exception :send_error
371
+
372
+ # Set an error on the current transaction.
373
+ #
374
+ # **Note**: Does not do anything if AppSignal is not active, no transaction
375
+ # is currently active or when the "error" is not a class extended from
376
+ # Ruby's Exception class.
377
+ #
378
+ # @example Manual instrumentation of set_error.
379
+ # # Manually starting AppSignal here
380
+ # # Manually starting a transaction here.
381
+ # begin
382
+ # raise "oh no!"
383
+ # rescue => e
384
+ # Appsignal.set_error(error)
385
+ # end
386
+ # # Manually completing the transaction here.
387
+ # # Manually stopping AppSignal here
388
+ #
389
+ # @example In a Rails application
390
+ # class SomeController < ApplicationController
391
+ # # The AppSignal transaction is created by our integration for you.
392
+ # def create
393
+ # # Do something that breaks
394
+ # rescue => e
395
+ # Appsignal.set_error(e)
396
+ # end
397
+ # end
398
+ #
399
+ # @param exception [Exception] The error to add to the current transaction.
400
+ # @param tags [Hash{String, Symbol => String, Symbol, Integer}] Additional
401
+ # tags to add to the error. See also {.tag_request}.
402
+ # @param namespace [String] The namespace in which the error occurred.
403
+ # See also {.set_namespace}.
404
+ # @return [void]
405
+ #
406
+ # @see Transaction#set_error
407
+ # @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
408
+ # Exception handling guide
409
+ # @since 0.6.6
410
+ def set_error(exception, tags = nil, namespace = nil)
411
+ return if !active? ||
412
+ Appsignal::Transaction.current.nil? ||
413
+ exception.nil?
414
+ transaction = Appsignal::Transaction.current
415
+ transaction.set_error(exception)
416
+ transaction.set_tags(tags) if tags
417
+ transaction.set_namespace(namespace) if namespace
418
+ end
419
+ alias :set_exception :set_error
420
+ alias :add_exception :set_error
421
+
422
+ # Set a custom action name for the current transaction.
423
+ #
424
+ # When using an integration such as the Rails or Sinatra AppSignal will try
425
+ # to find the action name from the controller or endpoint for you.
426
+ #
427
+ # If you want to customize the action name as it appears on AppSignal.com
428
+ # you can use this method. This overrides the action name AppSignal
429
+ # generates in an integration.
430
+ #
431
+ # @example in a Rails controller
432
+ # class SomeController < ApplicationController
433
+ # before_action :set_appsignal_action
434
+ #
435
+ # def set_appsignal_action
436
+ # Appsignal.set_action("DynamicController#dynamic_method")
437
+ # end
438
+ # end
439
+ #
440
+ # @param action [String]
441
+ # @return [void]
442
+ # @see Transaction#set_action
443
+ # @since 2.2.0
444
+ def set_action(action)
445
+ return if !active? ||
446
+ Appsignal::Transaction.current.nil? ||
447
+ action.nil?
448
+ Appsignal::Transaction.current.set_action(action)
449
+ end
450
+
451
+ # Set a custom namespace for the current transaction.
452
+ #
453
+ # When using an integration such as Rails or Sidekiq AppSignal will try to
454
+ # find a appropriate namespace for the transaction.
455
+ #
456
+ # A Rails controller will be automatically put in the "http_request"
457
+ # namespace, while a Sidekiq background job is put in the "background_job"
458
+ # namespace.
459
+ #
460
+ # Note: The "http_request" namespace gets transformed on AppSignal.com to
461
+ # "Web" and "background_job" gets transformed to "Background".
462
+ #
463
+ # If you want to customize the namespace in which transactions appear you
464
+ # can use this method. This overrides the namespace AppSignal uses by
465
+ # default.
466
+ #
467
+ # A common request we've seen is to split the administration panel from the
468
+ # main application.
469
+ #
470
+ # @example create a custom admin namespace
471
+ # class AdminController < ApplicationController
472
+ # before_action :set_appsignal_namespace
473
+ #
474
+ # def set_appsignal_namespace
475
+ # Appsignal.set_namespace("admin")
476
+ # end
477
+ # end
478
+ #
479
+ # @param namespace [String]
480
+ # @return [void]
481
+ # @see Transaction#set_namespace
482
+ # @since 2.2.0
483
+ def set_namespace(namespace)
484
+ return if !active? ||
485
+ Appsignal::Transaction.current.nil? ||
486
+ namespace.nil?
487
+ Appsignal::Transaction.current.set_namespace(namespace)
488
+ end
489
+
490
+ # Set tags on the current transaction.
491
+ #
492
+ # Tags are extra bits of information that are added to transaction and
493
+ # appear on sample details pages on AppSignal.com.
494
+ #
495
+ # @example
496
+ # Appsignal.tag_request(:locale => "en")
497
+ # Appsignal.tag_request("locale" => "en")
498
+ # Appsignal.tag_request("user_id" => 1)
499
+ #
500
+ # @example Nested hashes are not supported
501
+ # # Bad
502
+ # Appsignal.tag_request(:user => { :locale => "en" })
503
+ #
504
+ # @example in a Rails controller
505
+ # class SomeController < ApplicationController
506
+ # before_action :set_appsignal_tags
507
+ #
508
+ # def set_appsignal_tags
509
+ # Appsignal.tag_request(:locale => I18n.locale)
510
+ # end
511
+ # end
512
+ #
513
+ # @param tags [Hash] Collection of tags.
514
+ # @option tags [String, Symbol, Integer] :any
515
+ # The name of the tag as a Symbol.
516
+ # @option tags [String, Symbol, Integer] "any"
517
+ # The name of the tag as a String.
518
+ # @return [void]
519
+ #
520
+ # @see Transaction.set_tags
521
+ # @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
522
+ # Tagging guide
523
+ def tag_request(tags = {})
524
+ return unless active?
525
+ transaction = Appsignal::Transaction.current
526
+ return false unless transaction
527
+ transaction.set_tags(tags)
528
+ end
529
+ alias :tag_job :tag_request
530
+
531
+ # Instrument helper for AppSignal.
532
+ #
533
+ # For more help, read our custom instrumentation guide, listed under "See
534
+ # also".
535
+ #
536
+ # @example Simple instrumentation
537
+ # Appsignal.instrument("fetch.issue_fetcher") do
538
+ # # To be instrumented code
539
+ # end
540
+ #
541
+ # @example Instrumentation with title and body
542
+ # Appsignal.instrument(
543
+ # "fetch.issue_fetcher",
544
+ # "Fetching issue",
545
+ # "GitHub API"
546
+ # ) do
547
+ # # To be instrumented code
548
+ # end
549
+ #
550
+ # @param name [String] Name of the instrumented event. Read our event
551
+ # naming guide listed under "See also".
552
+ # @param title [String, nil] Human readable name of the event.
553
+ # @param body [String, nil] Value of importance for the event, such as the
554
+ # server against an API call is made.
555
+ # @param body_format [Integer] Enum for the type of event that is
556
+ # instrumented. Accepted values are {EventFormatter::DEFAULT} and
557
+ # {EventFormatter::SQL_BODY_FORMAT}, but we recommend you use
558
+ # {.instrument_sql} instead of {EventFormatter::SQL_BODY_FORMAT}.
559
+ # @yield yields the given block of code instrumented in an AppSignal
560
+ # event.
561
+ # @return [Object] Returns the block's return value.
562
+ #
563
+ # @see Appsignal::Transaction#instrument
564
+ # @see .instrument_sql
565
+ # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
566
+ # AppSignal custom instrumentation guide
567
+ # @see http://docs.appsignal.com/api/event-names.html
568
+ # AppSignal event naming guide
569
+ # @since 1.3.0
570
+ def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
571
+ Appsignal::Transaction.current.start_event
572
+ yield if block_given?
573
+ ensure
574
+ Appsignal::Transaction.current.finish_event(name, title, body, body_format)
575
+ end
576
+
577
+ # Instrumentation helper for SQL queries.
578
+ #
579
+ # This helper filters out values from SQL queries so you don't have to.
580
+ #
581
+ # @example SQL query instrumentation
582
+ # Appsignal.instrument_sql("perform.query", nil, "SELECT * FROM ...") do
583
+ # # To be instrumented code
584
+ # end
585
+ #
586
+ # @example SQL query instrumentation
587
+ # Appsignal.instrument_sql("perform.query", nil, "WHERE email = 'foo@..'") do
588
+ # # query value will replace 'foo..' with a question mark `?`.
589
+ # end
590
+ #
591
+ # @param name [String] Name of the instrumented event. Read our event
592
+ # naming guide listed under "See also".
593
+ # @param title [String, nil] Human readable name of the event.
594
+ # @param body [String, nil] SQL query that's being executed.
595
+ # @yield yields the given block of code instrumented in an AppSignal event.
596
+ # @return [Object] Returns the block's return value.
597
+ #
598
+ # @see .instrument
599
+ # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
600
+ # AppSignal custom instrumentation guide
601
+ # @see http://docs.appsignal.com/api/event-names.html
602
+ # AppSignal event naming guide
603
+ # @since 2.0.0
604
+ def instrument_sql(name, title = nil, body = nil, &block)
605
+ instrument(name, title, body, Appsignal::EventFormatter::SQL_BODY_FORMAT, &block)
606
+ end
607
+
608
+ def set_gauge(key, value)
609
+ Appsignal::Extension.set_gauge(key.to_s, value.to_f)
610
+ rescue RangeError
611
+ Appsignal.logger.warn("Gauge value #{value} for key '#{key}' is too big")
612
+ end
613
+
614
+ def set_host_gauge(key, value)
615
+ Appsignal::Extension.set_host_gauge(key.to_s, value.to_f)
616
+ rescue RangeError
617
+ Appsignal.logger.warn("Host gauge value #{value} for key '#{key}' is too big")
618
+ end
619
+
620
+ def set_process_gauge(key, value)
621
+ Appsignal::Extension.set_process_gauge(key.to_s, value.to_f)
622
+ rescue RangeError
623
+ Appsignal.logger.warn("Process gauge value #{value} for key '#{key}' is too big")
624
+ end
625
+
626
+ def increment_counter(key, value = 1)
627
+ Appsignal::Extension.increment_counter(key.to_s, value)
628
+ rescue RangeError
629
+ Appsignal.logger.warn("Counter value #{value} for key '#{key}' is too big")
630
+ end
631
+
632
+ def add_distribution_value(key, value)
633
+ Appsignal::Extension.add_distribution_value(key.to_s, value.to_f)
634
+ rescue RangeError
635
+ Appsignal.logger.warn("Distribution value #{value} for key '#{key}' is too big")
636
+ end
637
+
638
+ # In memory logger used before any logger is started with {.start_logger}.
639
+ #
640
+ # The contents of this logger are flushed to the logger in {.start_logger}.
641
+ #
642
+ # @api private
643
+ # @return [StringIO]
644
+ def in_memory_log
645
+ if defined?(@in_memory_log) && @in_memory_log
646
+ @in_memory_log
647
+ else
648
+ @in_memory_log = StringIO.new
649
+ end
650
+ end
651
+
652
+ def logger
653
+ @logger ||= Logger.new(in_memory_log).tap do |l|
654
+ l.level = Logger::INFO
655
+ l.formatter = log_formatter("appsignal")
656
+ end
657
+ end
658
+
659
+ # @api private
660
+ def log_formatter(prefix = nil)
661
+ pre = "#{prefix}: " if prefix
662
+ proc do |severity, datetime, _progname, msg|
663
+ "[#{datetime.strftime("%Y-%m-%dT%H:%M:%S")} (process) "\
664
+ "##{Process.pid}][#{severity}] #{pre}#{msg}\n"
665
+ end
666
+ end
667
+
668
+ # Start the AppSignal logger.
669
+ #
670
+ # Sets the log level and sets the logger. Uses a file-based logger or the
671
+ # STDOUT-based logger. See the `:log` configuration option.
672
+ #
673
+ # @param path_arg [nil] Deprecated param. Use the `:log_path`
674
+ # configuration option instead.
675
+ # @return [void]
676
+ # @since 0.7.0
677
+ def start_logger(path_arg = nil)
678
+ if config && config[:log] == "file" && config.log_file_path
679
+ start_file_logger(config.log_file_path)
680
+ else
681
+ start_stdout_logger
682
+ end
683
+
684
+ logger.level =
685
+ if config && config[:debug]
686
+ Logger::DEBUG
687
+ else
688
+ Logger::INFO
689
+ end
690
+
691
+ if in_memory_log
692
+ logger << in_memory_log.string
693
+ end
694
+
695
+ if path_arg
696
+ logger.info("Setting the path in start_logger has no effect anymore, set it in the config instead")
697
+ end
698
+ end
699
+
700
+ # Returns if the C-extension was loaded properly.
701
+ #
702
+ # @return [Boolean]
703
+ # @see Extension
704
+ # @since 1.0.0
705
+ def extension_loaded?
706
+ !!extension_loaded
707
+ end
708
+
709
+ # Returns the active state of the AppSignal integration.
710
+ #
711
+ # Conditions apply for AppSignal to be marked as active:
712
+ #
713
+ # - There is a config set on the {.config} attribute.
714
+ # - The set config is active {Config.active?}.
715
+ # - The AppSignal Extension is loaded {.extension_loaded?}.
716
+ #
717
+ # This logic is used within instrument helper such as {.instrument} so it's
718
+ # not necessary to wrap {.instrument} calls with this method.
719
+ #
720
+ # @example Do this
721
+ # Appsignal.instrument(..) do
722
+ # # Do this
723
+ # end
724
+ #
725
+ # @example Don't do this
726
+ # if Appsignal.active?
727
+ # Appsignal.instrument(..) do
728
+ # # Don't do this
729
+ # end
730
+ # end
731
+ #
732
+ # @return [Boolean]
733
+ # @since 0.2.7
734
+ def active?
735
+ config && config.active? && extension_loaded?
736
+ end
737
+
738
+ # @deprecated No replacement
739
+ def is_ignored_error?(error) # rubocop:disable Style/PredicateName
740
+ Appsignal.config[:ignore_errors].include?(error.class.name)
741
+ end
742
+ alias :is_ignored_exception? :is_ignored_error?
743
+ deprecate :is_ignored_error?, :none, 2017, 3
744
+
745
+ # @deprecated No replacement
746
+ def is_ignored_action?(action) # rubocop:disable Style/PredicateName
747
+ Appsignal.config[:ignore_actions].include?(action)
748
+ end
749
+ deprecate :is_ignored_action?, :none, 2017, 3
750
+
751
+ # Convenience method for skipping instrumentations around a block of code.
752
+ #
753
+ # @example
754
+ # Appsignal.without_instrumentation do
755
+ # # Complex code here
756
+ # end
757
+ #
758
+ # @yield block of code that shouldn't be instrumented.
759
+ # @return [Object] Returns the return value of the block.
760
+ # @since 0.8.7
761
+ def without_instrumentation
762
+ Appsignal::Transaction.current.pause! if Appsignal::Transaction.current
763
+ yield
764
+ ensure
765
+ Appsignal::Transaction.current.resume! if Appsignal::Transaction.current
766
+ end
767
+
768
+ private
769
+
770
+ def start_stdout_logger
771
+ @logger = Logger.new($stdout)
772
+ logger.formatter = log_formatter("appsignal")
773
+ end
774
+
775
+ def start_file_logger(path)
776
+ @logger = Logger.new(path)
777
+ logger.formatter = log_formatter
778
+ rescue SystemCallError => error
779
+ start_stdout_logger
780
+ logger.warn "Unable to start logger with log path '#{path}'."
781
+ logger.warn error
782
+ end
783
+ end
784
+ end
785
+
786
+ require "appsignal/system"
787
+ require "appsignal/utils"
788
+ require "appsignal/extension"
789
+ require "appsignal/auth_check"
790
+ require "appsignal/config"
791
+ require "appsignal/event_formatter"
792
+ require "appsignal/hooks"
793
+ require "appsignal/marker"
794
+ require "appsignal/minutely"
795
+ require "appsignal/garbage_collection_profiler"
796
+ require "appsignal/integrations/railtie" if defined?(::Rails)
797
+ require "appsignal/integrations/resque"
798
+ require "appsignal/integrations/resque_active_job"
799
+ require "appsignal/transaction"
800
+ require "appsignal/version"
801
+ require "appsignal/rack/generic_instrumentation"
802
+ require "appsignal/rack/js_exception_catcher"
803
+ require "appsignal/js_exception_transaction"
804
+ require "appsignal/transmitter"