appsignal 2.1.2 → 2.2.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +14 -0
  3. data/.rubocop_todo.yml +16 -171
  4. data/.travis.yml +14 -1
  5. data/.yardopts +8 -0
  6. data/CHANGELOG.md +21 -3
  7. data/README.md +20 -2
  8. data/Rakefile +60 -62
  9. data/appsignal.gemspec +24 -23
  10. data/ext/agent.yml +11 -11
  11. data/ext/appsignal_extension.c +43 -12
  12. data/ext/extconf.rb +9 -9
  13. data/gemfiles/padrino.gemfile +1 -1
  14. data/lib/appsignal.rb +403 -26
  15. data/lib/appsignal/auth_check.rb +28 -1
  16. data/lib/appsignal/cli.rb +1 -0
  17. data/lib/appsignal/cli/demo.rb +40 -0
  18. data/lib/appsignal/cli/diagnose.rb +345 -89
  19. data/lib/appsignal/cli/helpers.rb +9 -4
  20. data/lib/appsignal/cli/install.rb +6 -6
  21. data/lib/appsignal/cli/notify_of_deploy.rb +58 -0
  22. data/lib/appsignal/config.rb +40 -38
  23. data/lib/appsignal/demo.rb +20 -0
  24. data/lib/appsignal/event_formatter.rb +4 -0
  25. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +1 -0
  26. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +1 -0
  27. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +1 -0
  28. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +1 -0
  29. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +1 -0
  30. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +2 -1
  31. data/lib/appsignal/event_formatter/moped/query_formatter.rb +1 -0
  32. data/lib/appsignal/extension.rb +1 -0
  33. data/lib/appsignal/garbage_collection_profiler.rb +20 -19
  34. data/lib/appsignal/hooks.rb +1 -0
  35. data/lib/appsignal/hooks/active_support_notifications.rb +1 -0
  36. data/lib/appsignal/hooks/celluloid.rb +1 -0
  37. data/lib/appsignal/hooks/data_mapper.rb +1 -0
  38. data/lib/appsignal/hooks/delayed_job.rb +1 -0
  39. data/lib/appsignal/hooks/mongo_ruby_driver.rb +1 -0
  40. data/lib/appsignal/hooks/net_http.rb +1 -0
  41. data/lib/appsignal/hooks/passenger.rb +1 -0
  42. data/lib/appsignal/hooks/puma.rb +1 -0
  43. data/lib/appsignal/hooks/rake.rb +1 -0
  44. data/lib/appsignal/hooks/redis.rb +1 -0
  45. data/lib/appsignal/hooks/sequel.rb +1 -0
  46. data/lib/appsignal/hooks/shoryuken.rb +1 -0
  47. data/lib/appsignal/hooks/sidekiq.rb +1 -0
  48. data/lib/appsignal/hooks/unicorn.rb +1 -0
  49. data/lib/appsignal/hooks/webmachine.rb +1 -0
  50. data/lib/appsignal/integrations/capistrano/appsignal.cap +4 -4
  51. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +2 -0
  52. data/lib/appsignal/integrations/data_mapper.rb +1 -1
  53. data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -0
  54. data/lib/appsignal/integrations/grape.rb +3 -1
  55. data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -0
  56. data/lib/appsignal/integrations/padrino.rb +36 -21
  57. data/lib/appsignal/integrations/railtie.rb +2 -2
  58. data/lib/appsignal/integrations/resque.rb +1 -0
  59. data/lib/appsignal/integrations/resque_active_job.rb +1 -0
  60. data/lib/appsignal/integrations/webmachine.rb +27 -24
  61. data/lib/appsignal/js_exception_transaction.rb +8 -8
  62. data/lib/appsignal/marker.rb +41 -4
  63. data/lib/appsignal/minutely.rb +1 -0
  64. data/lib/appsignal/rack/generic_instrumentation.rb +3 -2
  65. data/lib/appsignal/rack/js_exception_catcher.rb +55 -15
  66. data/lib/appsignal/rack/rails_instrumentation.rb +2 -1
  67. data/lib/appsignal/rack/sinatra_instrumentation.rb +4 -2
  68. data/lib/appsignal/rack/streaming_listener.rb +4 -2
  69. data/lib/appsignal/system.rb +5 -31
  70. data/lib/appsignal/transaction.rb +71 -6
  71. data/lib/appsignal/transmitter.rb +24 -13
  72. data/lib/appsignal/utils.rb +18 -11
  73. data/lib/appsignal/utils/params_sanitizer.rb +2 -1
  74. data/lib/appsignal/utils/query_params_sanitizer.rb +1 -0
  75. data/lib/appsignal/version.rb +1 -1
  76. data/resources/appsignal.yml.erb +1 -1
  77. data/spec/lib/appsignal/auth_check_spec.rb +64 -22
  78. data/spec/lib/appsignal/cli/diagnose_spec.rb +539 -81
  79. data/spec/lib/appsignal/cli/helpers_spec.rb +74 -2
  80. data/spec/lib/appsignal/cli/install_spec.rb +3 -3
  81. data/spec/lib/appsignal/config_spec.rb +38 -72
  82. data/spec/lib/appsignal/demo_spec.rb +2 -2
  83. data/spec/lib/appsignal/event_formatter_spec.rb +2 -2
  84. data/spec/lib/appsignal/extension_spec.rb +4 -0
  85. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +18 -21
  86. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +1 -1
  87. data/spec/lib/appsignal/hooks/redis_spec.rb +34 -44
  88. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -2
  89. data/spec/lib/appsignal/integrations/grape_spec.rb +6 -6
  90. data/spec/lib/appsignal/integrations/padrino_spec.rb +241 -122
  91. data/spec/lib/appsignal/integrations/railtie_spec.rb +34 -16
  92. data/spec/lib/appsignal/integrations/resque_spec.rb +1 -1
  93. data/spec/lib/appsignal/integrations/sinatra_spec.rb +38 -10
  94. data/spec/lib/appsignal/integrations/webmachine_spec.rb +2 -2
  95. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +7 -6
  96. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +95 -58
  97. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +9 -6
  98. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +11 -10
  99. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +20 -13
  100. data/spec/lib/appsignal/system_spec.rb +2 -32
  101. data/spec/lib/appsignal/transaction_spec.rb +48 -7
  102. data/spec/lib/appsignal/transmitter_spec.rb +52 -33
  103. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +3 -1
  104. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +3 -3
  105. data/spec/lib/appsignal/utils_spec.rb +49 -6
  106. data/spec/lib/appsignal_spec.rb +60 -5
  107. data/spec/spec_helper.rb +4 -3
  108. data/spec/support/helpers/api_request_helper.rb +1 -4
  109. data/spec/support/helpers/dependency_helper.rb +4 -0
  110. data/spec/support/helpers/system_helpers.rb +1 -17
  111. data/spec/support/helpers/time_helpers.rb +1 -1
  112. metadata +19 -8
  113. data/spec/lib/appsignal/system/container_spec.rb +0 -67
  114. data/spec/lib/appsignal/utils/gzip_spec.rb +0 -10
@@ -1,35 +1,36 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/appsignal/version', __FILE__)
2
+ require File.expand_path("../lib/appsignal/version", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
- gem.authors = [
6
- 'Robert Beekman',
7
- 'Thijs Cadier'
5
+ gem.authors = [
6
+ "Robert Beekman",
7
+ "Thijs Cadier"
8
8
  ]
9
- gem.email = ['support@appsignal.com']
10
- gem.description = 'The official appsignal.com gem'
11
- gem.summary = 'Logs performance and exception data from your app to '\
12
- 'appsignal.com'
13
- gem.homepage = 'https://github.com/appsignal/appsignal'
14
- gem.license = 'MIT'
9
+ gem.email = ["support@appsignal.com"]
10
+ gem.description = "The official appsignal.com gem"
11
+ gem.summary = "Logs performance and exception data from your app to "\
12
+ "appsignal.com"
13
+ gem.homepage = "https://github.com/appsignal/appsignal"
14
+ gem.license = "MIT"
15
15
 
16
- gem.files = `git ls-files`.split($\)
17
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.files = `git ls-files`.split($\) # rubocop:disable Style/SpecialGlobalVars
17
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
- gem.name = 'appsignal'
20
- gem.require_paths = ['lib', 'ext']
19
+ gem.name = "appsignal"
20
+ gem.require_paths = %w(lib ext)
21
21
  gem.version = Appsignal::VERSION
22
- gem.required_ruby_version = '>= 1.9'
22
+ gem.required_ruby_version = ">= 1.9"
23
23
 
24
24
  gem.extensions = %w(ext/extconf.rb)
25
25
 
26
- gem.add_dependency 'rack'
27
- gem.add_dependency 'thread_safe'
26
+ gem.add_dependency "rack"
27
+ gem.add_dependency "thread_safe"
28
28
 
29
- gem.add_development_dependency 'rake', '~> 11'
30
- gem.add_development_dependency 'rspec', '~> 3.5'
31
- gem.add_development_dependency 'pry'
32
- gem.add_development_dependency 'timecop'
33
- gem.add_development_dependency 'webmock'
34
- gem.add_development_dependency 'rubocop', '0.46.0'
29
+ gem.add_development_dependency "rake", "~> 11"
30
+ gem.add_development_dependency "rspec", "~> 3.5"
31
+ gem.add_development_dependency "pry"
32
+ gem.add_development_dependency "timecop"
33
+ gem.add_development_dependency "webmock"
34
+ gem.add_development_dependency "rubocop", "0.46.0"
35
+ gem.add_development_dependency "yard"
35
36
  end
@@ -1,18 +1,18 @@
1
1
  ---
2
- version: '5464697'
2
+ version: f16607c
3
3
  triples:
4
4
  x86_64-linux:
5
- checksum: a70f22af18f50ea1a02adf4ef88f68047b353a185fd100e8a61bc7ade87095bc
6
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/5464697/appsignal-x86_64-linux-all-static.tar.gz
5
+ checksum: '0479e8b0aeba95360e9fd8cfd7e7f7f4c1a8dfc555f6149c0c35d27593a05193'
6
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/f16607c/appsignal-x86_64-linux-all-static.tar.gz
7
7
  i686-linux:
8
- checksum: 2101667a3c56dfe78436c4f25b763a1db2028dd6ebe247b64d3c616066269879
9
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/5464697/appsignal-i686-linux-all-static.tar.gz
8
+ checksum: 3c3c63f26bec0304649cad4fb69410dd6b13313a178f3db7cdeba72b24834715
9
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/f16607c/appsignal-i686-linux-all-static.tar.gz
10
10
  x86-linux:
11
- checksum: 2101667a3c56dfe78436c4f25b763a1db2028dd6ebe247b64d3c616066269879
12
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/5464697/appsignal-i686-linux-all-static.tar.gz
11
+ checksum: 3c3c63f26bec0304649cad4fb69410dd6b13313a178f3db7cdeba72b24834715
12
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/f16607c/appsignal-i686-linux-all-static.tar.gz
13
13
  x86_64-darwin:
14
- checksum: 496f3348ae0e957b2610bb82ae23ab3c9878483a01e447a9f27537c99bd9f69d
15
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/5464697/appsignal-x86_64-darwin-all-static.tar.gz
14
+ checksum: 853398d93cda5f16edd2a931346f97fedea4c16f729bb987564cfbc29e73ef33
15
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/f16607c/appsignal-x86_64-darwin-all-static.tar.gz
16
16
  universal-darwin:
17
- checksum: 496f3348ae0e957b2610bb82ae23ab3c9878483a01e447a9f27537c99bd9f69d
18
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/5464697/appsignal-x86_64-darwin-all-static.tar.gz
17
+ checksum: 853398d93cda5f16edd2a931346f97fedea4c16f729bb987564cfbc29e73ef33
18
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/f16607c/appsignal-x86_64-darwin-all-static.tar.gz
@@ -32,6 +32,10 @@ static VALUE stop(VALUE self) {
32
32
  return Qnil;
33
33
  }
34
34
 
35
+ static VALUE diagnose(VALUE self) {
36
+ return make_ruby_string(appsignal_diagnose());
37
+ }
38
+
35
39
  static VALUE get_server_state(VALUE self, VALUE key) {
36
40
  appsignal_string_t string;
37
41
 
@@ -125,7 +129,7 @@ static VALUE record_event(VALUE self, VALUE name, VALUE title, VALUE body, VALUE
125
129
  Check_Type(title, T_STRING);
126
130
  duration_type = TYPE(duration);
127
131
  if (duration_type != T_FIXNUM && duration_type != T_BIGNUM) {
128
- rb_raise(rb_eTypeError, "duration should be a Fixnum or Bignum");
132
+ rb_raise(rb_eTypeError, "duration should be an Integer");
129
133
  }
130
134
  Check_Type(body_format, T_FIXNUM);
131
135
 
@@ -211,13 +215,26 @@ static VALUE set_transaction_action(VALUE self, VALUE action) {
211
215
  return Qnil;
212
216
  }
213
217
 
218
+ static VALUE set_transaction_namespace(VALUE self, VALUE namespace) {
219
+ appsignal_transaction_t* transaction;
220
+
221
+ Check_Type(namespace, T_STRING);
222
+ Data_Get_Struct(self, appsignal_transaction_t, transaction);
223
+
224
+ appsignal_set_transaction_namespace(
225
+ transaction,
226
+ make_appsignal_string(namespace)
227
+ );
228
+ return Qnil;
229
+ }
230
+
214
231
  static VALUE set_transaction_queue_start(VALUE self, VALUE queue_start) {
215
232
  appsignal_transaction_t* transaction;
216
233
  int queue_start_type;
217
234
 
218
235
  queue_start_type = TYPE(queue_start);
219
236
  if (queue_start_type != T_FIXNUM && queue_start_type != T_BIGNUM) {
220
- rb_raise(rb_eTypeError, "queue_start should be a Fixnum or Bignum");
237
+ rb_raise(rb_eTypeError, "queue_start should be an Integer");
221
238
  }
222
239
 
223
240
  Data_Get_Struct(self, appsignal_transaction_t, transaction);
@@ -305,18 +322,21 @@ static VALUE data_set_string(VALUE self, VALUE key, VALUE value) {
305
322
  return Qnil;
306
323
  }
307
324
 
308
- static VALUE data_set_fixnum(VALUE self, VALUE key, VALUE value) {
325
+ static VALUE data_set_integer(VALUE self, VALUE key, VALUE value) {
309
326
  appsignal_data_t* data;
327
+ VALUE value_type = TYPE(value);
310
328
 
311
329
  Check_Type(key, T_STRING);
312
- Check_Type(value, T_FIXNUM);
330
+ if (value_type != T_FIXNUM && value_type != T_BIGNUM) {
331
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Integer)", rb_obj_classname(value));
332
+ }
313
333
 
314
334
  Data_Get_Struct(self, appsignal_data_t, data);
315
335
 
316
336
  appsignal_data_map_set_integer(
317
337
  data,
318
338
  make_appsignal_string(key),
319
- FIX2LONG(value)
339
+ NUM2LONG(value)
320
340
  );
321
341
 
322
342
  return Qnil;
@@ -404,16 +424,19 @@ static VALUE data_append_string(VALUE self, VALUE value) {
404
424
  return Qnil;
405
425
  }
406
426
 
407
- static VALUE data_append_fixnum(VALUE self, VALUE value) {
427
+ static VALUE data_append_integer(VALUE self, VALUE value) {
408
428
  appsignal_data_t* data;
429
+ VALUE value_type = TYPE(value);
409
430
 
410
- Check_Type(value, T_FIXNUM);
431
+ if (value_type != T_FIXNUM && value_type != T_BIGNUM) {
432
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Integer)", rb_obj_classname(value));
433
+ }
411
434
 
412
435
  Data_Get_Struct(self, appsignal_data_t, data);
413
436
 
414
437
  appsignal_data_array_append_integer(
415
438
  data,
416
- FIX2LONG(value)
439
+ NUM2LONG(value)
417
440
  );
418
441
 
419
442
  return Qnil;
@@ -579,6 +602,10 @@ static VALUE install_allocation_event_hook() {
579
602
  return Qnil;
580
603
  }
581
604
 
605
+ static VALUE running_in_container() {
606
+ return appsignal_running_in_container() == 1 ? Qtrue : Qfalse;
607
+ }
608
+
582
609
  void Init_appsignal_extension(void) {
583
610
  Appsignal = rb_define_module("Appsignal");
584
611
  Extension = rb_define_class_under(Appsignal, "Extension", rb_cObject);
@@ -586,8 +613,10 @@ void Init_appsignal_extension(void) {
586
613
  Data = rb_define_class_under(Extension, "Data", rb_cObject);
587
614
 
588
615
  // Starting and stopping
589
- rb_define_singleton_method(Extension, "start", start, 0);
590
- rb_define_singleton_method(Extension, "stop", stop, 0);
616
+ rb_define_singleton_method(Extension, "start", start, 0);
617
+ rb_define_singleton_method(Extension, "stop", stop, 0);
618
+ // Diagnostics
619
+ rb_define_singleton_method(Extension, "diagnose", diagnose, 0);
591
620
 
592
621
  // Server state
593
622
  rb_define_singleton_method(Extension, "get_server_state", get_server_state, 1);
@@ -602,6 +631,7 @@ void Init_appsignal_extension(void) {
602
631
  rb_define_method(Transaction, "set_error", set_transaction_error, 3);
603
632
  rb_define_method(Transaction, "set_sample_data", set_transaction_sample_data, 2);
604
633
  rb_define_method(Transaction, "set_action", set_transaction_action, 1);
634
+ rb_define_method(Transaction, "set_namespace", set_transaction_namespace, 1);
605
635
  rb_define_method(Transaction, "set_queue_start", set_transaction_queue_start, 1);
606
636
  rb_define_method(Transaction, "set_metadata", set_transaction_metadata, 2);
607
637
  rb_define_method(Transaction, "finish", finish_transaction, 1);
@@ -613,7 +643,7 @@ void Init_appsignal_extension(void) {
613
643
 
614
644
  // Add content to a data map
615
645
  rb_define_method(Data, "set_string", data_set_string, 2);
616
- rb_define_method(Data, "set_fixnum", data_set_fixnum, 2);
646
+ rb_define_method(Data, "set_integer", data_set_integer, 2);
617
647
  rb_define_method(Data, "set_float", data_set_float, 2);
618
648
  rb_define_method(Data, "set_boolean", data_set_boolean, 2);
619
649
  rb_define_method(Data, "set_nil", data_set_nil, 1);
@@ -621,7 +651,7 @@ void Init_appsignal_extension(void) {
621
651
 
622
652
  // Add content to a data array
623
653
  rb_define_method(Data, "append_string", data_append_string, 1);
624
- rb_define_method(Data, "append_fixnum", data_append_fixnum, 1);
654
+ rb_define_method(Data, "append_integer", data_append_integer, 1);
625
655
  rb_define_method(Data, "append_float", data_append_float, 1);
626
656
  rb_define_method(Data, "append_boolean", data_append_boolean, 1);
627
657
  rb_define_method(Data, "append_nil", data_append_nil, 0);
@@ -635,6 +665,7 @@ void Init_appsignal_extension(void) {
635
665
 
636
666
  // Event hook installation
637
667
  rb_define_singleton_method(Extension, "install_allocation_event_hook", install_allocation_event_hook, 0);
668
+ rb_define_singleton_method(Extension, "running_in_container?", running_in_container, 0);
638
669
 
639
670
  // Metrics
640
671
  rb_define_singleton_method(Extension, "set_gauge", set_gauge, 2);
@@ -7,10 +7,10 @@ require "rubygems/package"
7
7
  require "yaml"
8
8
  require File.expand_path("../../lib/appsignal/version.rb", __FILE__)
9
9
 
10
- EXT_PATH = File.expand_path("..", __FILE__)
11
- AGENT_CONFIG = YAML.load(File.read(File.join(EXT_PATH, "agent.yml")))
12
- ARCH = "#{Gem::Platform.local.cpu}-#{Gem::Platform.local.os}"
13
- CA_CERT_PATH = File.join(EXT_PATH, "../resources/cacert.pem")
10
+ EXT_PATH = File.expand_path("..", __FILE__).freeze
11
+ AGENT_CONFIG = YAML.load(File.read(File.join(EXT_PATH, "agent.yml"))).freeze
12
+ ARCH = "#{Gem::Platform.local.cpu}-#{Gem::Platform.local.os}".freeze
13
+ CA_CERT_PATH = File.join(EXT_PATH, "../resources/cacert.pem").freeze
14
14
 
15
15
  def ext_path(path)
16
16
  File.join(EXT_PATH, path)
@@ -66,14 +66,14 @@ def install
66
66
 
67
67
  Gem::Package::TarReader.new(Zlib::GzipReader.open(archive)) do |tar|
68
68
  tar.each do |entry|
69
- if entry.file?
70
- File.open(ext_path(entry.full_name), "wb") do |f|
71
- f.write(entry.read)
72
- end
69
+ next unless entry.file?
70
+
71
+ File.open(ext_path(entry.full_name), "wb") do |f|
72
+ f.write(entry.read)
73
73
  end
74
74
  end
75
75
  end
76
- FileUtils.chmod(0755, ext_path("appsignal-agent"))
76
+ FileUtils.chmod(0o755, ext_path("appsignal-agent"))
77
77
  end
78
78
 
79
79
  logger.info "Creating makefile"
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'padrino', '~> 0.12.0'
3
+ gem 'padrino', '~> 0.13.0'
4
4
  gem 'rack', '~> 1.6'
5
5
  gem 'activesupport', '~> 4.2'
6
6
 
@@ -2,17 +2,59 @@ require "json"
2
2
  require "logger"
3
3
  require "securerandom"
4
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.
5
9
  module Appsignal
6
10
  class << self
7
11
  extend Gem::Deprecate
8
12
 
9
- attr_accessor :config, :extension_loaded
10
- attr_writer :logger, :in_memory_log
11
-
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
12
53
  def extensions
13
54
  @extensions ||= []
14
55
  end
15
56
 
57
+ # @api private
16
58
  def initialize_extensions
17
59
  Appsignal.logger.debug("Initializing extensions")
18
60
  extensions.each do |extension|
@@ -21,14 +63,39 @@ module Appsignal
21
63
  end
22
64
  end
23
65
 
66
+ # Start the AppSignal integration.
67
+ #
68
+ # Starts AppSignal with the given configuration. If no configuration is set
69
+ # yet it will try to automatically load the configuration using the
70
+ # environment loaded from environment variables and the currently working
71
+ # directory.
72
+ #
73
+ # This is not required for the automatic integrations AppSignal offers, but
74
+ # this is required for all non-automatic integrations and pure Ruby
75
+ # applications. For more information, see our [integrations
76
+ # list](http://docs.appsignal.com/ruby/integrations/) and our [Integrating
77
+ # AppSignal](http://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html)
78
+ # guide.
79
+ #
80
+ # To start the logger see {.start_logger}.
81
+ #
82
+ # @example
83
+ # Appsignal.start
84
+ #
85
+ # @example with custom loaded configuration
86
+ # Appsignal.config = Appsignal::Config.new(Dir.pwd, "production")
87
+ # Appsignal.start
88
+ #
89
+ # @return [void]
90
+ # @since 0.7.0
24
91
  def start
25
92
  unless extension_loaded?
26
93
  logger.info("Not starting appsignal, extension is not loaded")
27
94
  return
28
- else
29
- logger.debug("Starting appsignal")
30
95
  end
31
96
 
97
+ logger.debug("Starting appsignal")
98
+
32
99
  unless @config
33
100
  @config = Config.new(
34
101
  Dir.pwd,
@@ -44,7 +111,8 @@ module Appsignal
44
111
  Logger::INFO
45
112
  end
46
113
  if config.active?
47
- logger.info("Starting AppSignal #{Appsignal::VERSION} (#{$0}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})")
114
+ logger.info "Starting AppSignal #{Appsignal::VERSION} "\
115
+ "(#{$PROGRAM_NAME}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})"
48
116
  config.write_to_environment
49
117
  Appsignal::Extension.start
50
118
  Appsignal::Hooks.load_hooks
@@ -69,14 +137,20 @@ module Appsignal
69
137
  end
70
138
  end
71
139
 
72
- def in_memory_log
73
- if defined?(@in_memory_log) && @in_memory_log
74
- @in_memory_log
75
- else
76
- @in_memory_log = StringIO.new
77
- end
78
- end
79
-
140
+ # Stop AppSignal's agent.
141
+ #
142
+ # Stops the AppSignal agent. Call this before the end of your program to
143
+ # make sure the agent is stopped as well.
144
+ #
145
+ # @example
146
+ # Appsignal.start
147
+ # # Run your application
148
+ # Appsignal.stop
149
+ #
150
+ # @param called_by [String] Name of the thing that requested the agent to
151
+ # be stopped. Will be used in the AppSignal log file.
152
+ # @return [void]
153
+ # @since 1.0.0
80
154
  def stop(called_by = nil)
81
155
  if called_by
82
156
  logger.debug("Stopping appsignal (#{called_by})")
@@ -97,7 +171,7 @@ module Appsignal
97
171
  Appsignal::Extension.get_server_state(key)
98
172
  end
99
173
 
100
- # Wrap a transaction with appsignal monitoring.
174
+ # Wrap a transaction with AppSignal monitoring.
101
175
  def monitor_transaction(name, env = {})
102
176
  unless active?
103
177
  return yield
@@ -110,7 +184,8 @@ module Appsignal
110
184
  namespace = Appsignal::Transaction::HTTP_REQUEST
111
185
  request = ::Rack::Request.new(env)
112
186
  else
113
- logger.error("Unrecognized name '#{name}'") and return
187
+ logger.error("Unrecognized name '#{name}'")
188
+ return
114
189
  end
115
190
  transaction = Appsignal::Transaction.create(
116
191
  SecureRandom.uuid,
@@ -131,11 +206,11 @@ module Appsignal
131
206
  end
132
207
  end
133
208
 
134
- # Monitor a transaction, stop Appsignal and wait for this single transaction to be
135
- # flushed.
209
+ # Monitor a transaction, stop AppSignal and wait for this single
210
+ # transaction to be flushed.
136
211
  #
137
- # Useful for cases such as Rake tasks and Resque-like systems where a process is
138
- # forked and immediately exits after the transaction finishes.
212
+ # Useful for cases such as Rake tasks and Resque-like systems where a
213
+ # process is forked and immediately exits after the transaction finishes.
139
214
  def monitor_single_transaction(name, env = {}, &block)
140
215
  monitor_transaction(name, env, &block)
141
216
  ensure
@@ -150,10 +225,49 @@ module Appsignal
150
225
  end
151
226
  alias :listen_for_exception :listen_for_error
152
227
 
228
+ # Send an error to AppSignal regardless of the context.
229
+ #
230
+ # Records and send the exception to AppSignal.
231
+ #
232
+ # This instrumentation helper does not require a transaction to be active,
233
+ # it starts a new transaction by itself.
234
+ #
235
+ # Use {.set_error} if your want to add an exception to the current
236
+ # transaction.
237
+ #
238
+ # **Note**: Does not do anything if AppSignal is not active or when the
239
+ # "error" is not a class extended from Ruby's Exception class.
240
+ #
241
+ # @example Send an exception
242
+ # begin
243
+ # raise "oh no!"
244
+ # rescue => e
245
+ # Appsignal.send_error(e)
246
+ # end
247
+ #
248
+ # @example Send an exception with tags
249
+ # begin
250
+ # raise "oh no!"
251
+ # rescue => e
252
+ # Appsignal.send_error(e, :key => "value")
253
+ # end
254
+ #
255
+ # @param error [Exception] The error to send to AppSignal.
256
+ # @param tags [Hash{String, Symbol => String, Symbol, Integer}] Additional
257
+ # tags to add to the error. See also {.tag_request}.
258
+ # @param namespace [Exception] The namespace in which the error occurred.
259
+ # See also {.set_namespace}.
260
+ # @return [void]
261
+ #
262
+ # @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
263
+ # Exception handling guide
264
+ # @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
265
+ # Tagging guide
266
+ # @since 0.6.0
153
267
  def send_error(error, tags = nil, namespace = Appsignal::Transaction::HTTP_REQUEST)
154
268
  return unless active?
155
269
  unless error.is_a?(Exception)
156
- logger.error('Can\'t send error, given value is not an exception')
270
+ logger.error("Can't send error, given value is not an exception")
157
271
  return
158
272
  end
159
273
  transaction = Appsignal::Transaction.new(
@@ -167,6 +281,40 @@ module Appsignal
167
281
  end
168
282
  alias :send_exception :send_error
169
283
 
284
+ # Set an error on the current transaction.
285
+ #
286
+ # **Note**: Does not do anything if AppSignal is not active, no transaction
287
+ # is currently active or when the "error" is not a class extended from
288
+ # Ruby's Exception class.
289
+ #
290
+ # @example Manual instrumentation of set_error.
291
+ # # Manually starting AppSignal here
292
+ # # Manually starting a transaction here.
293
+ # begin
294
+ # raise "oh no!"
295
+ # rescue => e
296
+ # Appsignal.set_error(error)
297
+ # end
298
+ # # Manually completing the transaction here.
299
+ # # Manually stopping AppSignal here
300
+ #
301
+ # @example In a Rails application
302
+ # class SomeController < ApplicationController
303
+ # # The AppSignal transaction is created by our integration for you.
304
+ # def create
305
+ # # Do something that breaks
306
+ # rescue => e
307
+ # Appsignal.set_error(e)
308
+ # end
309
+ # end
310
+ #
311
+ # @param exception [Exception] The error to add to the current transaction.
312
+ # @return [void]
313
+ #
314
+ # @see Transaction#set_error
315
+ # @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
316
+ # Exception handling guide
317
+ # @since 0.6.6
170
318
  def set_error(exception)
171
319
  return if !active? ||
172
320
  Appsignal::Transaction.current.nil? ||
@@ -176,14 +324,152 @@ module Appsignal
176
324
  alias :set_exception :set_error
177
325
  alias :add_exception :set_error
178
326
 
179
- def tag_request(params = {})
327
+ # Set a custom action name for the current transaction.
328
+ #
329
+ # When using an integration such as the Rails or Sinatra AppSignal will try
330
+ # to find the action name from the controller or endpoint for you.
331
+ #
332
+ # If you want to customize the action name as it appears on AppSignal.com
333
+ # you can use this method. This overrides the action name AppSignal
334
+ # generates in an integration.
335
+ #
336
+ # @example in a Rails controller
337
+ # class SomeController < ApplicationController
338
+ # before_action :set_appsignal_action
339
+ #
340
+ # def set_appsignal_action
341
+ # Appsignal.set_action("DynamicController#dynamic_method")
342
+ # end
343
+ # end
344
+ #
345
+ # @param action [String]
346
+ # @return [void]
347
+ # @see Transaction#set_action
348
+ # @since 2.2.0
349
+ def set_action(action)
350
+ return if !active? ||
351
+ Appsignal::Transaction.current.nil? ||
352
+ action.nil?
353
+ Appsignal::Transaction.current.set_action(action)
354
+ end
355
+
356
+ # Set a custom namespace for the current transaction.
357
+ #
358
+ # When using an integration such as Rails or Sidekiq AppSignal will try to
359
+ # find a appropriate namespace for the transaction.
360
+ #
361
+ # A Rails controller will be automatically put in the "http_request"
362
+ # namespace, while a Sidekiq background job is put in the "background_job"
363
+ # namespace.
364
+ #
365
+ # Note: The "http_request" namespace gets transformed on AppSignal.com to
366
+ # "Web" and "background_job" gets transformed to "Background".
367
+ #
368
+ # If you want to customize the namespace in which transactions appear you
369
+ # can use this method. This overrides the namespace AppSignal uses by
370
+ # default.
371
+ #
372
+ # A common request we've seen is to split the administration panel from the
373
+ # main application.
374
+ #
375
+ # @example create a custom admin namespace
376
+ # class AdminController < ApplicationController
377
+ # before_action :set_appsignal_namespace
378
+ #
379
+ # def set_appsignal_namespace
380
+ # Appsignal.set_namespace("admin")
381
+ # end
382
+ # end
383
+ #
384
+ # @param namespace [String]
385
+ # @return [void]
386
+ # @see Transaction#set_namespace
387
+ # @since 2.2.0
388
+ def set_namespace(namespace)
389
+ return if !active? ||
390
+ Appsignal::Transaction.current.nil? ||
391
+ namespace.nil?
392
+ Appsignal::Transaction.current.set_namespace(namespace)
393
+ end
394
+
395
+ # Set tags on the current transaction.
396
+ #
397
+ # Tags are extra bits of information that are added to transaction and
398
+ # appear on sample details pages on AppSignal.com.
399
+ #
400
+ # @example
401
+ # Appsignal.tag_request(:locale => "en")
402
+ # Appsignal.tag_request("locale" => "en")
403
+ # Appsignal.tag_request("user_id" => 1)
404
+ #
405
+ # @example Nested hashes are not supported
406
+ # # Bad
407
+ # Appsignal.tag_request(:user => { :locale => "en" })
408
+ #
409
+ # @example in a Rails controller
410
+ # class SomeController < ApplicationController
411
+ # before_action :set_appsignal_tags
412
+ #
413
+ # def set_appsignal_tags
414
+ # Appsignal.tag_request(:locale => I18n.locale)
415
+ # end
416
+ # end
417
+ #
418
+ # @param tags [Hash] Collection of tags.
419
+ # @option tags [String, Symbol, Integer] :any
420
+ # The name of the tag as a Symbol.
421
+ # @option tags [String, Symbol, Integer] "any"
422
+ # The name of the tag as a String.
423
+ # @return [void]
424
+ #
425
+ # @see Transaction.set_tags
426
+ # @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
427
+ # Tagging guide
428
+ def tag_request(tags = {})
180
429
  return unless active?
181
430
  transaction = Appsignal::Transaction.current
182
431
  return false unless transaction
183
- transaction.set_tags(params)
432
+ transaction.set_tags(tags)
184
433
  end
185
434
  alias :tag_job :tag_request
186
435
 
436
+ # Instrument helper for AppSignal.
437
+ #
438
+ # For more help, read our custom instrumentation guide, listed under "See
439
+ # also".
440
+ #
441
+ # @example Simple instrumentation
442
+ # Appsignal.instrument("fetch.issue_fetcher") do
443
+ # # To be instrumented code
444
+ # end
445
+ #
446
+ # @example Instrumentation with title and body
447
+ # Appsignal.instrument(
448
+ # "fetch.issue_fetcher",
449
+ # "Fetching issue",
450
+ # "GitHub API"
451
+ # ) do
452
+ # # To be instrumented code
453
+ # end
454
+ #
455
+ # @param name [String] Name of the instrumented event. Read our event
456
+ # naming guide listed under "See also".
457
+ # @param title [String, nil] Human readable name of the event.
458
+ # @param body [String, nil] Value of importance for the event, such as the
459
+ # server against an API call is made.
460
+ # @param body_format [Integer] Enum for the type of event that is
461
+ # instrumented. Accepted values are {EventFormatter::DEFAULT} and
462
+ # {EventFormatter::SQL_BODY_FORMAT}, but we recommend you use
463
+ # {.instrument_sql} instead of {EventFormatter::SQL_BODY_FORMAT}.
464
+ # @yield yields the given block of code instrumented in an AppSignal event.
465
+ # @return [Object] Returns the blocks return value.
466
+ #
467
+ # @see .instrument_sql Specific helper for SQL queries.
468
+ # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
469
+ # AppSignal custom instrumentation guide
470
+ # @see http://docs.appsignal.com/api/event-names.html
471
+ # AppSignal event naming guide
472
+ # @since 1.3.0
187
473
  def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
188
474
  Appsignal::Transaction.current.start_event
189
475
  return_value = yield if block_given?
@@ -191,6 +477,33 @@ module Appsignal
191
477
  return_value
192
478
  end
193
479
 
480
+ # Instrumentation helper for SQL queries.
481
+ #
482
+ # This helper filters out values from SQL queries so you don't have to.
483
+ #
484
+ # @example SQL query instrumentation
485
+ # Appsignal.instrument_sql("perform.query", nil, "SELECT * FROM ...") do
486
+ # # To be instrumented code
487
+ # end
488
+ #
489
+ # @example SQL query instrumentation
490
+ # Appsignal.instrument_sql("perform.query", nil, "WHERE email = 'foo@..'") do
491
+ # # query value will replace 'foo..' with a question mark `?`.
492
+ # end
493
+ #
494
+ # @param name [String] Name of the instrumented event. Read our event
495
+ # naming guide listed under "See also".
496
+ # @param title [String, nil] Human readable name of the event.
497
+ # @param body [String, nil] SQL query that's being executed.
498
+ # @yield yields the given block of code instrumented in an AppSignal event.
499
+ # @return [Object] Returns the blocks return value.
500
+ #
501
+ # @see .instrument
502
+ # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
503
+ # AppSignal custom instrumentation guide
504
+ # @see http://docs.appsignal.com/api/event-names.html
505
+ # AppSignal event naming guide
506
+ # @since 2.0.0
194
507
  def instrument_sql(name, title = nil, body = nil, &block)
195
508
  instrument(name, title, body, Appsignal::EventFormatter::SQL_BODY_FORMAT, &block)
196
509
  end
@@ -225,6 +538,20 @@ module Appsignal
225
538
  Appsignal.logger.warn("Distribution value #{value} for key '#{key}' is too big")
226
539
  end
227
540
 
541
+ # In memory logger used before any logger is started with {.start_logger}.
542
+ #
543
+ # The contents of this logger are flushed to the logger in {.start_logger}.
544
+ #
545
+ # @api private
546
+ # @return [StringIO]
547
+ def in_memory_log
548
+ if defined?(@in_memory_log) && @in_memory_log
549
+ @in_memory_log
550
+ else
551
+ @in_memory_log = StringIO.new
552
+ end
553
+ end
554
+
228
555
  def logger
229
556
  @logger ||= Logger.new(in_memory_log).tap do |l|
230
557
  l.level = Logger::INFO
@@ -232,13 +559,24 @@ module Appsignal
232
559
  end
233
560
  end
234
561
 
562
+ # @api private
235
563
  def log_formatter(prefix = nil)
236
564
  pre = "#{prefix}: " if prefix
237
565
  proc do |severity, datetime, _progname, msg|
238
- "[#{datetime.strftime("%Y-%m-%dT%H:%M:%S")} (process) ##{Process.pid}][#{severity}] #{pre}#{msg}\n"
566
+ "[#{datetime.strftime("%Y-%m-%dT%H:%M:%S")} (process) "\
567
+ "##{Process.pid}][#{severity}] #{pre}#{msg}\n"
239
568
  end
240
569
  end
241
570
 
571
+ # Start the AppSignal logger.
572
+ #
573
+ # Sets the log level and sets the logger. Uses a file-based logger or the
574
+ # STDOUT-based logger. See the `:log` configuration option.
575
+ #
576
+ # @param path_arg [nil] Deprecated param. Use the `:log_path`
577
+ # configuration option instead.
578
+ # @return [void]
579
+ # @since 0.7.0
242
580
  def start_logger(path_arg = nil)
243
581
  if config && config[:log] == "file" && config.log_file_path
244
582
  start_file_logger(config.log_file_path)
@@ -262,27 +600,66 @@ module Appsignal
262
600
  end
263
601
  end
264
602
 
603
+ # Returns if the C-extension was loaded properly.
604
+ #
605
+ # @return [Boolean]
606
+ # @see Extension
607
+ # @since 1.0.0
265
608
  def extension_loaded?
266
609
  !!@extension_loaded
267
610
  end
268
611
 
612
+ # Returns the active state of the AppSignal integration.
613
+ #
614
+ # Conditions apply for AppSignal to be marked as active:
615
+ #
616
+ # - There is a config set on the {.config} attribute.
617
+ # - The set config is active {Config.active?}.
618
+ # - The AppSignal Extension is loaded {.extension_loaded?}.
619
+ #
620
+ # This logic is used within instrument helper such as {.instrument} so it's
621
+ # not necessary to wrap {.instrument} calls with this method.
622
+ #
623
+ # @example Do this
624
+ # Appsignal.instrument(..) do
625
+ # # Do this
626
+ # end
627
+ #
628
+ # @example Don't do this
629
+ # if Appsignal.active?
630
+ # Appsignal.instrument(..) do
631
+ # # Don't do this
632
+ # end
633
+ # end
634
+ #
635
+ # @return [Boolean]
636
+ # @since 0.2.7
269
637
  def active?
270
638
  config && config.active? && extension_loaded?
271
639
  end
272
640
 
273
- def is_ignored_error?(error)
641
+ # @deprecated No replacement
642
+ def is_ignored_error?(error) # rubocop:disable Style/PredicateName
274
643
  Appsignal.config[:ignore_errors].include?(error.class.name)
275
644
  end
276
645
  alias :is_ignored_exception? :is_ignored_error?
277
646
  deprecate :is_ignored_error?, :none, 2017, 3
278
647
 
279
- def is_ignored_action?(action)
648
+ # @deprecated No replacement
649
+ def is_ignored_action?(action) # rubocop:disable Style/PredicateName
280
650
  Appsignal.config[:ignore_actions].include?(action)
281
651
  end
282
652
  deprecate :is_ignored_action?, :none, 2017, 3
283
653
 
284
654
  # Convenience method for skipping instrumentations around a block of code.
285
655
  #
656
+ # @example
657
+ # Appsignal.without_instrumentation do
658
+ # # Complex code here
659
+ # end
660
+ #
661
+ # @yield block of code that shouldn't be instrumented.
662
+ # @return [Object] Returns the return value of the block.
286
663
  # @since 0.8.7
287
664
  def without_instrumentation
288
665
  Appsignal::Transaction.current.pause! if Appsignal::Transaction.current