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,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