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,65 @@
1
+ module Appsignal
2
+ # Class used to perform a Push API validation / authentication check against
3
+ # the AppSignal Push API.
4
+ #
5
+ # @example
6
+ # config = Appsignal::Config.new(Dir.pwd, "production")
7
+ # auth_check = Appsignal::AuthCheck.new(config)
8
+ # # Valid push_api_key
9
+ # auth_check.perform # => "200"
10
+ # # Invalid push_api_key
11
+ # auth_check.perform # => "401"
12
+ #
13
+ # @!attribute [r] config
14
+ # @return [Appsignal::Config] config to use in the authentication request.
15
+ # @api private
16
+ class AuthCheck
17
+ # Path used on the AppSignal Push API
18
+ # https://push.appsignal.com/1/auth
19
+ ACTION = "auth".freeze
20
+
21
+ attr_reader :config, :logger
22
+
23
+ def initialize(config, logger = nil)
24
+ @config = config
25
+ if logger # rubocop:disable Style/GuardClause
26
+ warn "Deprecated: `logger` argument will be removed in the next " \
27
+ "major version."
28
+ end
29
+ end
30
+
31
+ # Perform push api validation request and return response status code.
32
+ #
33
+ # @return [String] response status code.
34
+ # @raise [StandardError] see {Appsignal::Transmitter#transmit}.
35
+ def perform
36
+ Appsignal::Transmitter.new(ACTION, config).transmit({}).code
37
+ end
38
+
39
+ # Perform push api validation request and return a descriptive response
40
+ # tuple.
41
+ #
42
+ # @return [Array<String/nil, String>] response tuple.
43
+ # - First value is the response status code.
44
+ # - Second value is a description of the response and the exception error
45
+ # message if an exception occured.
46
+ def perform_with_result
47
+ status = perform
48
+ result =
49
+ case status
50
+ when "200"
51
+ "AppSignal has confirmed authorization!"
52
+ when "401"
53
+ "API key not valid with AppSignal..."
54
+ else
55
+ "Could not confirm authorization: " \
56
+ "#{status.nil? ? "nil" : status}"
57
+ end
58
+ [status, result]
59
+ rescue => e
60
+ result = "Something went wrong while trying to "\
61
+ "authenticate with AppSignal: #{e}"
62
+ [nil, result]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ require "appsignal"
2
+ require "capistrano/version"
3
+
4
+ if defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION) >= Gem::Version.new(3)
5
+ # Capistrano 3+
6
+ load File.expand_path("../integrations/capistrano/appsignal.cap", __FILE__)
7
+ else
8
+ # Capistrano 2
9
+ require "appsignal/integrations/capistrano/capistrano_2_tasks"
10
+ end
@@ -0,0 +1,108 @@
1
+ require "optparse"
2
+ require "logger"
3
+ require "appsignal"
4
+ require "appsignal/cli/helpers"
5
+ require "appsignal/cli/demo"
6
+ require "appsignal/cli/diagnose"
7
+ require "appsignal/cli/install"
8
+ require "appsignal/cli/notify_of_deploy"
9
+
10
+ module Appsignal
11
+ # @api private
12
+ class CLI
13
+ AVAILABLE_COMMANDS = %w[demo diagnose install notify_of_deploy].freeze
14
+
15
+ class << self
16
+ attr_accessor :options
17
+
18
+ def run(argv = ARGV)
19
+ @options = {}
20
+ global = global_option_parser
21
+ commands = command_option_parser
22
+ global.order!(argv)
23
+ command = argv.shift
24
+ if command
25
+ if AVAILABLE_COMMANDS.include?(command)
26
+ commands[command].parse!(argv)
27
+ case command.to_sym
28
+ when :demo
29
+ Appsignal::CLI::Demo.run(options)
30
+ when :diagnose
31
+ Appsignal::CLI::Diagnose.run(options)
32
+ when :install
33
+ Appsignal::CLI::Install.run(argv.shift)
34
+ when :notify_of_deploy
35
+ Appsignal::CLI::NotifyOfDeploy.run(options)
36
+ end
37
+ else
38
+ puts "Command '#{command}' does not exist, run appsignal -h to "\
39
+ "see the help"
40
+ exit(1)
41
+ end
42
+ else
43
+ # Print help
44
+ puts global
45
+ exit(0)
46
+ end
47
+ end
48
+
49
+ def global_option_parser
50
+ OptionParser.new do |o|
51
+ o.banner = "Usage: appsignal <command> [options]"
52
+
53
+ o.on "-v", "--version", "Print version and exit" do |_arg|
54
+ puts "AppSignal #{Appsignal::VERSION}"
55
+ exit(0)
56
+ end
57
+
58
+ o.on "-h", "--help", "Show help and exit" do
59
+ puts o
60
+ exit(0)
61
+ end
62
+
63
+ o.separator ""
64
+ o.separator "Available commands: #{AVAILABLE_COMMANDS.join(", ")}"
65
+ end
66
+ end
67
+
68
+ def command_option_parser
69
+ {
70
+ "demo" => OptionParser.new do |o|
71
+ o.banner = "Usage: appsignal demo [options]"
72
+
73
+ o.on "--environment=<app_env>", "The environment to demo" do |arg|
74
+ options[:environment] = arg
75
+ end
76
+ end,
77
+ "diagnose" => OptionParser.new do |o|
78
+ o.banner = "Usage: appsignal diagnose [options]"
79
+
80
+ o.on "--environment=<app_env>", "The environment to diagnose" do |arg|
81
+ options[:environment] = arg
82
+ end
83
+ end,
84
+ "install" => OptionParser.new,
85
+ "notify_of_deploy" => OptionParser.new do |o|
86
+ o.banner = "Usage: appsignal notify_of_deploy [options]"
87
+
88
+ o.on "--revision=<revision>", "The revision you're deploying" do |arg|
89
+ options[:revision] = arg
90
+ end
91
+
92
+ o.on "--user=<user>", "The name of the user that's deploying" do |arg|
93
+ options[:user] = arg
94
+ end
95
+
96
+ o.on "--environment=<app_env>", "The environment you're deploying to" do |arg|
97
+ options[:environment] = arg
98
+ end
99
+
100
+ o.on "--name=<name>", "The name of the app (optional)" do |arg|
101
+ options[:name] = arg
102
+ end
103
+ end
104
+ }
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,63 @@
1
+ require "appsignal/demo"
2
+
3
+ module Appsignal
4
+ class CLI
5
+ # Command line tool for sending demonstration samples to AppSignal.com
6
+ #
7
+ # This command line tool is useful when testing AppSignal on a system and
8
+ # validating the local configuration. It tests if the installation of
9
+ # AppSignal has succeeded and if the AppSignal agent is able to run on the
10
+ # machine's architecture and communicate with the AppSignal servers.
11
+ #
12
+ # The same test is also run during installation with
13
+ # {Appsignal::CLI::Install}.
14
+ #
15
+ # ## Exit codes
16
+ #
17
+ # - Exits with status code `0` if the demo command has finished.
18
+ # - Exits with status code `1` if the demo command failed to finished.
19
+ #
20
+ # @example On the command line in your project
21
+ # appsignal demo
22
+ #
23
+ # @example With a specific environment
24
+ # appsignal demo --environment=production
25
+ #
26
+ # @example Standalone run
27
+ # gem install appsignal
28
+ # export APPSIGNAL_APP_NAME="My test app"
29
+ # export APPSIGNAL_APP_ENV="test"
30
+ # export APPSIGNAL_PUSH_API_KEY="xxxx-xxxx-xxxx-xxxx"
31
+ # appsignal demo
32
+ #
33
+ # @since 2.0.0
34
+ # @see Appsignal::Demo
35
+ # @see Appsignal::CLI::Install
36
+ # @see http://docs.appsignal.com/ruby/command-line/demo.html
37
+ # AppSignal demo documentation
38
+ # @see http://docs.appsignal.com/support/debugging.html
39
+ # Debugging AppSignal guide
40
+ # @api private
41
+ class Demo
42
+ class << self
43
+ # @param options [Hash]
44
+ # @option options :environment [String] environment to load
45
+ # configuration for.
46
+ # @return [void]
47
+ def run(options = {})
48
+ ENV["APPSIGNAL_APP_ENV"] = options[:environment] if options[:environment]
49
+
50
+ puts "Sending demonstration sample data..."
51
+ if Appsignal::Demo.transmit
52
+ puts "Demonstration sample data sent!"
53
+ puts "It may take about a minute for the data to appear on https://appsignal.com/accounts"
54
+ else
55
+ puts "Error: Unable to start the AppSignal agent and send data to AppSignal.com"
56
+ puts "Please use `appsignal diagnose` to debug your configuration."
57
+ exit 1
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,500 @@
1
+ require "rbconfig"
2
+ require "bundler/cli"
3
+ require "bundler/cli/common"
4
+ require "etc"
5
+
6
+ module Appsignal
7
+ class CLI
8
+ # Command line tool to run diagnostics on your project.
9
+ #
10
+ # This command line tool is useful when testing AppSignal on a system and
11
+ # validating the local configuration. It outputs useful information to
12
+ # debug issues and it checks if AppSignal agent is able to run on the
13
+ # machine's architecture and communicate with the AppSignal servers.
14
+ #
15
+ # This diagnostic tool outputs the following:
16
+ # - if AppSignal can run on the host system.
17
+ # - if the configuration is valid and active.
18
+ # - if the Push API key is present and valid (internet connection required).
19
+ # - if the required system paths exist and are writable.
20
+ # - outputs AppSignal version information.
21
+ # - outputs information about the host system and Ruby.
22
+ # - outputs last lines from the available log files.
23
+ #
24
+ # ## Exit codes
25
+ #
26
+ # - Exits with status code `0` if the diagnose command has finished.
27
+ # - Exits with status code `1` if the diagnose command failed to finished.
28
+ #
29
+ # @example On the command line in your project
30
+ # appsignal diagnose
31
+ #
32
+ # @example With a specific environment
33
+ # appsignal diagnose --environment=production
34
+ #
35
+ # @see http://docs.appsignal.com/support/debugging.html Debugging AppSignal
36
+ # @see http://docs.appsignal.com/ruby/command-line/diagnose.html
37
+ # AppSignal diagnose documentation
38
+ # @since 1.1.0
39
+ class Diagnose
40
+ extend CLI::Helpers
41
+
42
+ DIAGNOSE_ENDPOINT = "https://appsignal.com/diag".freeze
43
+
44
+ module Data
45
+ def data
46
+ @data ||= Hash.new { |hash, key| hash[key] = {} }
47
+ end
48
+
49
+ def data_section(key)
50
+ @section = key
51
+ yield
52
+ @section = nil
53
+ end
54
+
55
+ def current_section
56
+ @section
57
+ end
58
+
59
+ def save(key, value)
60
+ data[current_section][key] = value
61
+ end
62
+ end
63
+ extend Data
64
+
65
+ class << self
66
+ # @param options [Hash]
67
+ # @option options :environment [String] environment to load
68
+ # configuration for.
69
+ # @return [void]
70
+ # @api private
71
+ def run(options = {})
72
+ $stdout.sync = true
73
+ header
74
+ empty_line
75
+
76
+ library_information
77
+ empty_line
78
+
79
+ host_information
80
+ empty_line
81
+
82
+ configure_appsignal(options)
83
+ run_agent_diagnose_mode
84
+ empty_line
85
+
86
+ config
87
+ empty_line
88
+
89
+ check_api_key
90
+ empty_line
91
+
92
+ paths_section
93
+ empty_line
94
+
95
+ log_files
96
+
97
+ transmit_report_to_appsignal if send_report_to_appsignal?
98
+ end
99
+
100
+ private
101
+
102
+ def send_report_to_appsignal?
103
+ puts "\nDiagnostics report"
104
+ puts " Do you want to send this diagnostics report to AppSignal?"
105
+ puts " If you share this diagnostics report you will be given\n" \
106
+ " a support token you can use to refer to your diagnotics \n" \
107
+ " report when you contact us at support@appsignal.com\n\n"
108
+ send_diagnostics = yes_or_no(
109
+ " Send diagnostics report to AppSignal? (Y/n): ",
110
+ :default => "y"
111
+ )
112
+ unless send_diagnostics
113
+ puts " Not sending diagnostics information to AppSignal."
114
+ return false
115
+ end
116
+ true
117
+ end
118
+
119
+ def transmit_report_to_appsignal
120
+ puts "\n Transmitting diagnostics report"
121
+ transmitter = Transmitter.new(
122
+ DIAGNOSE_ENDPOINT,
123
+ Appsignal.config
124
+ )
125
+ response = transmitter.transmit(:diagnose => data)
126
+
127
+ unless response.code == "200"
128
+ puts " Error: Something went wrong while submitting the report "\
129
+ "to AppSignal."
130
+ puts " Response code: #{response.code}"
131
+ puts " Response body:\n#{response.body}"
132
+ return
133
+ end
134
+
135
+ puts " Please email us at support@appsignal.com with the following"
136
+ puts " support token."
137
+ begin
138
+ response_data = JSON.parse(response.body)
139
+ puts " Your support token: #{response_data["token"]}"
140
+ rescue JSON::ParserError
141
+ puts " Error: Couldn't decode server response."
142
+ puts " #{response.body}"
143
+ end
144
+ end
145
+
146
+ def puts_and_save(key, label, value)
147
+ save key, value
148
+ puts_value label, value
149
+ end
150
+
151
+ def puts_value(label, value, options = {})
152
+ options[:level] ||= 1
153
+ puts "#{" " * options[:level]}#{label}: #{value}"
154
+ end
155
+
156
+ def configure_appsignal(options)
157
+ current_path = Dir.pwd
158
+ initial_config = {}
159
+ if rails_app?
160
+ data[:app][:rails] = true
161
+ current_path = Rails.root
162
+ initial_config[:name] = Rails.application.class.parent_name
163
+ initial_config[:log_path] = current_path.join("log")
164
+ end
165
+
166
+ Appsignal.config = Appsignal::Config.new(
167
+ current_path,
168
+ options[:environment],
169
+ initial_config
170
+ )
171
+ Appsignal.config.write_to_environment
172
+ end
173
+
174
+ def run_agent_diagnose_mode
175
+ puts "Agent diagnostics"
176
+ unless Appsignal.extension_loaded?
177
+ puts " Extension is not loaded. No agent report created."
178
+ return
179
+ end
180
+
181
+ ENV["_APPSIGNAL_DIAGNOSE"] = "true"
182
+ diagnostics_report_string = Appsignal::Extension.diagnose
183
+ ENV.delete("_APPSIGNAL_DIAGNOSE")
184
+
185
+ begin
186
+ report = JSON.parse(diagnostics_report_string)
187
+ data[:agent] = report
188
+ print_agent_report(report)
189
+ rescue JSON::ParserError => error
190
+ puts " Error while parsing agent diagnostics report:"
191
+ puts " Error: #{error}"
192
+ puts " Output: #{diagnostics_report_string}"
193
+ data[:agent] = {
194
+ :error => error,
195
+ :output => diagnostics_report_string.split("\n")
196
+ }
197
+ end
198
+ end
199
+
200
+ def print_agent_report(report)
201
+ if report["error"]
202
+ puts " Error: #{report["error"]}"
203
+ return
204
+ end
205
+
206
+ agent_diagnostic_test_definition.each do |part, categories|
207
+ categories.each do |category, tests|
208
+ tests.each do |test_name, test_definition|
209
+ test_report = report
210
+ .fetch(part, {})
211
+ .fetch(category, {})
212
+ .fetch(test_name, {})
213
+
214
+ print_agent_test(test_definition, test_report)
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def print_agent_test(definition, test)
221
+ value = test["result"]
222
+ error = test["error"]
223
+ output = test["output"]
224
+
225
+ print " #{definition[:label]}: "
226
+ display_value = definition[:values][value]
227
+ print display_value.nil? ? "-" : display_value
228
+ print "\n Error: #{error}" if error
229
+ print "\n Output: #{output}" if output
230
+ print "\n"
231
+ end
232
+
233
+ def agent_diagnostic_test_definition
234
+ {
235
+ "extension" => {
236
+ "config" => {
237
+ "valid" => {
238
+ :label => "Extension config",
239
+ :values => { true => "valid", false => "invalid" }
240
+ }
241
+ }
242
+ },
243
+ "agent" => {
244
+ "boot" => {
245
+ "started" => {
246
+ :label => "Agent started",
247
+ :values => { true => "started", false => "not started" }
248
+ }
249
+ },
250
+ "config" => {
251
+ "valid" => {
252
+ :label => "Agent config",
253
+ :values => { true => "valid", false => "invalid" }
254
+ }
255
+ },
256
+ "logger" => {
257
+ "started" => {
258
+ :label => "Agent logger",
259
+ :values => { true => "started", false => "not started" }
260
+ }
261
+ },
262
+ "lock_path" => {
263
+ "created" => {
264
+ :label => "Agent lock path",
265
+ :values => { true => "writable", false => "not writable" }
266
+ }
267
+ }
268
+ }
269
+ }
270
+ end
271
+
272
+ def header
273
+ puts "AppSignal diagnose"
274
+ puts "=" * 80
275
+ puts "Use this information to debug your configuration."
276
+ puts "More information is available on the documentation site."
277
+ puts "http://docs.appsignal.com/"
278
+ puts "Send this output to support@appsignal.com if you need help."
279
+ puts "=" * 80
280
+ end
281
+
282
+ def library_information
283
+ puts "AppSignal library"
284
+ data_section :library do
285
+ save :language, "ruby"
286
+ puts_and_save :package_version, "Gem version", Appsignal::VERSION
287
+ puts_and_save :agent_version, "Agent version", Appsignal::Extension.agent_version
288
+ puts_and_save :agent_architecture, "Agent architecture",
289
+ Appsignal::System.installed_agent_architecture
290
+ puts_and_save :package_install_path, "Gem install path", gem_path
291
+ puts_and_save :extension_loaded, "Extension loaded", Appsignal.extension_loaded
292
+ end
293
+ end
294
+
295
+ def host_information
296
+ rbconfig = RbConfig::CONFIG
297
+ puts "Host information"
298
+ data_section :host do
299
+ puts_and_save :architecture, "Architecture", rbconfig["host_cpu"]
300
+
301
+ os_label = os = rbconfig["host_os"]
302
+ os_label = "#{os} (Microsoft Windows is not supported.)" if Gem.win_platform?
303
+ save :os, os
304
+ puts_value "Operating System", os_label
305
+
306
+ puts_and_save :language_version, "Ruby version",
307
+ "#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}"
308
+
309
+ puts_value "Heroku", "true" if Appsignal::System.heroku?
310
+ save :heroku, Appsignal::System.heroku?
311
+
312
+ save :root, Process.uid.zero?
313
+ puts_value "root user",
314
+ Process.uid.zero? ? "true (not recommended)" : "false"
315
+ puts_and_save :running_in_container, "Running in container",
316
+ Appsignal::Extension.running_in_container?
317
+ end
318
+ end
319
+
320
+ def config
321
+ puts "Configuration"
322
+ data_section :config do
323
+ puts_environment
324
+
325
+ Appsignal.config.config_hash.each do |key, value|
326
+ puts_and_save key, key, value
327
+ end
328
+ end
329
+ end
330
+
331
+ def puts_environment
332
+ env = Appsignal.config.env
333
+ puts_and_save :env, "Environment", env
334
+
335
+ return unless env == ""
336
+ puts " Warning: No environment set, no config loaded!"
337
+ puts " Please make sure appsignal diagnose is run within your "
338
+ puts " project directory with an environment."
339
+ puts " appsignal diagnose --environment=production"
340
+ end
341
+
342
+ def paths_section
343
+ puts "Paths"
344
+ data[:process] = process_user
345
+ data_section :paths do
346
+ appsignal_paths.each do |name, path|
347
+ path_info = {
348
+ :path => path,
349
+ :configured => !path.nil?,
350
+ :exists => false,
351
+ :writable => false
352
+ }
353
+ save name, path_info
354
+
355
+ puts_value name, path.to_s.inspect
356
+
357
+ unless path_info[:configured]
358
+ puts_value "Configured?", "false", :level => 2
359
+ next
360
+ end
361
+ unless File.exist?(path)
362
+ puts_value "Exists?", "false", :level => 2
363
+ next
364
+ end
365
+
366
+ path_info[:exists] = true
367
+ path_info[:writable] = File.writable?(path)
368
+ puts_value "Writable?", path_info[:writable], :level => 2
369
+
370
+ file_owner = path_ownership(path)
371
+ path_info[:ownership] = file_owner
372
+ save name, path_info
373
+
374
+ owned = process_user[:uid] == file_owner[:uid]
375
+ owner = "#{owned} " \
376
+ "(file: #{file_owner[:user]}:#{file_owner[:uid]}, " \
377
+ "process: #{process_user[:user]}:#{process_user[:uid]})"
378
+ puts_value "Ownership?", owner, :level => 2
379
+ end
380
+ end
381
+ end
382
+
383
+ def path_ownership(path)
384
+ file_uid = File.stat(path).uid
385
+ {
386
+ :uid => file_uid,
387
+ :user => username_for_uid(file_uid)
388
+ }
389
+ end
390
+
391
+ def process_user
392
+ return @process_user if defined?(@process_user)
393
+
394
+ process_uid = Process.uid
395
+ @process_user = {
396
+ :uid => process_uid,
397
+ :user => username_for_uid(process_uid)
398
+ }
399
+ end
400
+
401
+ def appsignal_paths
402
+ config = Appsignal.config
403
+ log_file_path = config.log_file_path
404
+ {
405
+ :working_dir => Dir.pwd,
406
+ :root_path => config.root_path,
407
+ :log_dir_path => log_file_path ? File.dirname(log_file_path) : "",
408
+ :log_file_path => log_file_path
409
+ }
410
+ end
411
+
412
+ def check_api_key
413
+ puts "Validation"
414
+ data_section :validation do
415
+ auth_check = ::Appsignal::AuthCheck.new(Appsignal.config)
416
+ status, error = auth_check.perform_with_result
417
+ result =
418
+ case status
419
+ when "200"
420
+ "valid"
421
+ when "401"
422
+ "invalid"
423
+ else
424
+ "Failed with status #{status}\n#{error.inspect}"
425
+ end
426
+ puts_and_save :push_api_key, "Validating Push API key", result
427
+ end
428
+ end
429
+
430
+ def log_files
431
+ puts "Log files"
432
+ data_section :logs do
433
+ install_log
434
+ empty_line
435
+ mkmf_log
436
+ end
437
+ end
438
+
439
+ def install_log
440
+ puts " Extension install log"
441
+ filename = File.join("ext", "install.log")
442
+ log_info = log_file_info(File.join(gem_path, filename))
443
+ save filename, log_info
444
+ puts_log_file log_info
445
+ end
446
+
447
+ def mkmf_log
448
+ puts " Makefile install log"
449
+ filename = File.join("ext", "mkmf.log")
450
+ log_info = log_file_info(File.join(gem_path, filename))
451
+ save filename, log_info
452
+ puts_log_file log_info
453
+ end
454
+
455
+ def log_file_info(log_file)
456
+ {
457
+ :path => log_file,
458
+ :exists => File.exist?(log_file)
459
+ }.tap do |info|
460
+ next unless info[:exists]
461
+ info[:content] = File.read(log_file).split("\n")
462
+ end
463
+ end
464
+
465
+ def puts_log_file(log_info)
466
+ puts_value "Path", log_info[:path].to_s.inspect, :level => 2
467
+ if log_info[:exists]
468
+ puts " Contents:"
469
+ puts log_info[:content].join("\n")
470
+ else
471
+ puts " File not found."
472
+ end
473
+ end
474
+
475
+ def username_for_uid(uid)
476
+ passwd_struct = Etc.getpwuid(uid)
477
+ return unless passwd_struct
478
+ passwd_struct.name
479
+ end
480
+
481
+ def empty_line
482
+ puts "\n"
483
+ end
484
+
485
+ def rails_app?
486
+ require "rails"
487
+ require File.expand_path(File.join(Dir.pwd, "config", "application.rb"))
488
+ true
489
+ rescue LoadError
490
+ false
491
+ end
492
+
493
+ def gem_path
494
+ @gem_path ||= \
495
+ Bundler::CLI::Common.select_spec("appsignal").full_gem_path.strip
496
+ end
497
+ end
498
+ end
499
+ end
500
+ end