appsignal 2.5.0.alpha.1-java

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