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,150 @@
1
+ # encoding: UTF-8
2
+
3
+ describe Appsignal::Utils do
4
+ describe ".data_generate" do
5
+ subject { Appsignal::Utils.data_generate(body) }
6
+
7
+ context "with a valid hash body" do
8
+ let(:body) do
9
+ {
10
+ "the" => "payload",
11
+ "int" => 1, # Fixnum
12
+ "int61" => 1 << 61, # Fixnum
13
+ "int62" => 1 << 62, # Bignum, this one still works
14
+ "int63" => 1 << 63, # Bignum, turnover point for C, too big for long
15
+ "int64" => 1 << 64, # Bignum
16
+ "float" => 1.0,
17
+ 1 => true,
18
+ nil => "test",
19
+ :foo => [1, 2, "three", { "foo" => "bar" }],
20
+ "bar" => nil,
21
+ "baz" => { "foo" => "bʊr", "arr" => [1, 2] }
22
+ }
23
+ end
24
+
25
+ it { is_expected.to eq Appsignal::Utils.data_generate(body) }
26
+ it { is_expected.to_not eq Appsignal::Utils.data_generate({}) }
27
+
28
+ describe "#to_s" do
29
+ it "returns a serialized hash" do
30
+ expect(subject.to_s).to eq %({"":"test",) +
31
+ %("1":true,) +
32
+ %("bar":null,) +
33
+ %("baz":{"arr":[1,2],"foo":"bʊr"},) +
34
+ %("float":1.0,) +
35
+ %("foo":[1,2,"three",{"foo":"bar"}],) +
36
+ %("int":1,) +
37
+ %("int61":#{1 << 61},) +
38
+ %("int62":#{1 << 62},) +
39
+ %("int63":"bigint:#{1 << 63}",) +
40
+ %("int64":"bigint:#{1 << 64}",) +
41
+ %("the":"payload"})
42
+ end
43
+ end
44
+ end
45
+
46
+ context "with a valid array body" do
47
+ let(:body) do
48
+ [
49
+ nil,
50
+ true,
51
+ false,
52
+ "string",
53
+ 1, # Fixnum
54
+ 1.0, # Float
55
+ 1 << 61, # Fixnum
56
+ 1 << 62, # Bignum, this one still works
57
+ 1 << 63, # Bignum, turnover point for C, too big for long
58
+ 1 << 64, # Bignum
59
+ { "arr" => [1, 2, "three"], "foo" => "bʊr" }
60
+ ]
61
+ end
62
+
63
+ it { is_expected.to eq Appsignal::Utils.data_generate(body) }
64
+ it { is_expected.to_not eq Appsignal::Utils.data_generate({}) }
65
+
66
+ describe "#to_s" do
67
+ it "returns a serialized array" do
68
+ expect(subject.to_s).to eq %([null,) +
69
+ %(true,) +
70
+ %(false,) +
71
+ %(\"string\",) +
72
+ %(1,) +
73
+ %(1.0,) +
74
+ %(#{1 << 61},) +
75
+ %(#{1 << 62},) +
76
+ %("bigint:#{1 << 63}",) +
77
+ %("bigint:#{1 << 64}",) +
78
+ %({\"arr\":[1,2,\"three\"],\"foo\":\"bʊr\"}])
79
+ end
80
+ end
81
+ end
82
+
83
+ context "with a body that contains strings with invalid utf-8 content" do
84
+ let(:string_with_invalid_utf8) { [0x61, 0x61, 0x85].pack("c*") }
85
+ let(:body) do
86
+ {
87
+ "field_one" => [0x61, 0x61].pack("c*"),
88
+ :field_two => string_with_invalid_utf8,
89
+ "field_three" => [
90
+ "one", string_with_invalid_utf8
91
+ ],
92
+ "field_four" => {
93
+ "one" => string_with_invalid_utf8
94
+ }
95
+ }
96
+ end
97
+
98
+ describe "#to_s" do
99
+ it { expect(subject.to_s).to eq %({"field_four":{"one":"aa�"},"field_one":"aa","field_three":["one","aa�"],"field_two":"aa�"}) }
100
+ end
101
+ end
102
+
103
+ context "with an invalid body" do
104
+ let(:body) { "body" }
105
+
106
+ it "should raise a type error" do
107
+ expect do
108
+ subject
109
+ end.to raise_error TypeError
110
+ end
111
+ end
112
+ end
113
+
114
+ describe ".json_generate" do
115
+ subject { Appsignal::Utils.json_generate(body) }
116
+
117
+ context "with a valid body" do
118
+ let(:body) do
119
+ {
120
+ "the" => "payload",
121
+ 1 => true,
122
+ nil => "test",
123
+ :foo => [1, 2, "three"],
124
+ "bar" => nil,
125
+ "baz" => { "foo" => "bar" }
126
+ }
127
+ end
128
+
129
+ it { is_expected.to eq %({"the":"payload","1":true,"":"test","foo":[1,2,"three"],"bar":null,"baz":{"foo":"bar"}}) }
130
+ end
131
+
132
+ context "with a body that contains strings with invalid utf-8 content" do
133
+ let(:string_with_invalid_utf8) { [0x61, 0x61, 0x85].pack("c*") }
134
+ let(:body) do
135
+ {
136
+ "field_one" => [0x61, 0x61].pack("c*"),
137
+ :field_two => string_with_invalid_utf8,
138
+ "field_three" => [
139
+ "one", string_with_invalid_utf8
140
+ ],
141
+ "field_four" => {
142
+ "one" => string_with_invalid_utf8
143
+ }
144
+ }
145
+ end
146
+
147
+ it { is_expected.to eq %({"field_one":"aa","field_two":"aa�","field_three":["one","aa�"],"field_four":{"one":"aa�"}}) }
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,1049 @@
1
+ describe Appsignal do
2
+ before do
3
+ # Make sure we have a clean state because we want to test
4
+ # initialization here.
5
+ Appsignal.config = nil
6
+ Appsignal.extensions.clear
7
+ end
8
+
9
+ let(:transaction) { http_request_transaction }
10
+
11
+ describe ".config=" do
12
+ it "should set the config" do
13
+ config = project_fixture_config
14
+ expect(Appsignal.logger).to_not receive(:level=)
15
+
16
+ Appsignal.config = config
17
+ expect(Appsignal.config).to eq config
18
+ end
19
+ end
20
+
21
+ describe ".extensions" do
22
+ it "should keep a list of extensions" do
23
+ expect(Appsignal.extensions).to be_empty
24
+ Appsignal.extensions << Appsignal::MockExtension
25
+ expect(Appsignal.extensions.size).to eq(1)
26
+ end
27
+ end
28
+
29
+ describe ".start" do
30
+ context "with no config set beforehand" do
31
+ it "should do nothing when config is not set and there is no valid config in the env" do
32
+ expect(Appsignal.logger).to receive(:error).with(
33
+ "Push api key not set after loading config"
34
+ ).once
35
+ expect(Appsignal.logger).to receive(:error).with(
36
+ "Not starting, no valid config for this environment"
37
+ ).once
38
+ expect(Appsignal::Extension).to_not receive(:start)
39
+ Appsignal.start
40
+ end
41
+
42
+ it "should create a config from the env" do
43
+ ENV["APPSIGNAL_PUSH_API_KEY"] = "something"
44
+ expect(Appsignal::Extension).to receive(:start)
45
+ expect(Appsignal.logger).not_to receive(:error)
46
+ silence { Appsignal.start }
47
+ expect(Appsignal.config[:push_api_key]).to eq("something")
48
+ end
49
+ end
50
+
51
+ context "when config is loaded" do
52
+ before { Appsignal.config = project_fixture_config }
53
+
54
+ it "should initialize logging" do
55
+ Appsignal.start
56
+ expect(Appsignal.logger.level).to eq Logger::INFO
57
+ end
58
+
59
+ it "should start native" do
60
+ expect(Appsignal::Extension).to receive(:start)
61
+ Appsignal.start
62
+ end
63
+
64
+ it "should initialize formatters" do
65
+ expect(Appsignal::EventFormatter).to receive(:initialize_formatters)
66
+ Appsignal.start
67
+ end
68
+
69
+ context "with an extension" do
70
+ before { Appsignal.extensions << Appsignal::MockExtension }
71
+
72
+ it "should call the extension's initializer" do
73
+ expect(Appsignal::MockExtension).to receive(:initializer)
74
+ Appsignal.start
75
+ end
76
+ end
77
+
78
+ context "when allocation tracking and gc instrumentation have been enabled" do
79
+ before do
80
+ allow(GC::Profiler).to receive(:enable)
81
+ Appsignal.config.config_hash[:enable_allocation_tracking] = true
82
+ Appsignal.config.config_hash[:enable_gc_instrumentation] = true
83
+ end
84
+
85
+ it "should enable Ruby's GC::Profiler" do
86
+ expect(GC::Profiler).to receive(:enable)
87
+ Appsignal.start
88
+ end
89
+
90
+ unless Appsignal::System.jruby?
91
+ it "installs the allocation event hook" do
92
+ expect(Appsignal::Extension).to receive(:install_allocation_event_hook)
93
+ Appsignal.start
94
+ end
95
+ end
96
+
97
+ it "should add the gc probe to minutely" do
98
+ expect(Appsignal::Minutely).to receive(:add_gc_probe)
99
+ Appsignal.start
100
+ end
101
+ end
102
+
103
+ context "when allocation tracking and gc instrumentation have been disabled" do
104
+ before do
105
+ Appsignal.config.config_hash[:enable_allocation_tracking] = false
106
+ Appsignal.config.config_hash[:enable_gc_instrumentation] = false
107
+ end
108
+
109
+ it "should not enable Ruby's GC::Profiler" do
110
+ expect(GC::Profiler).not_to receive(:enable)
111
+ Appsignal.start
112
+ end
113
+
114
+ it "should not install the allocation event hook" do
115
+ expect(Appsignal::Minutely).not_to receive(:install_allocation_event_hook)
116
+ Appsignal.start
117
+ end
118
+
119
+ it "should not add the gc probe to minutely" do
120
+ expect(Appsignal::Minutely).not_to receive(:add_gc_probe)
121
+ Appsignal.start
122
+ end
123
+ end
124
+
125
+ context "when minutely metrics has been enabled" do
126
+ before do
127
+ Appsignal.config.config_hash[:enable_minutely_probes] = true
128
+ end
129
+
130
+ it "should start minutely" do
131
+ expect(Appsignal::Minutely).to receive(:start)
132
+ Appsignal.start
133
+ end
134
+ end
135
+
136
+ context "when minutely metrics has been disabled" do
137
+ before do
138
+ Appsignal.config.config_hash[:enable_minutely_probes] = false
139
+ end
140
+
141
+ it "should not start minutely" do
142
+ expect(Appsignal::Minutely).to_not receive(:start)
143
+ Appsignal.start
144
+ end
145
+ end
146
+ end
147
+
148
+ context "with debug logging" do
149
+ before { Appsignal.config = project_fixture_config("test") }
150
+
151
+ it "should change the log level" do
152
+ Appsignal.start
153
+ expect(Appsignal.logger.level).to eq Logger::DEBUG
154
+ end
155
+ end
156
+ end
157
+
158
+ describe ".forked" do
159
+ context "when not active" do
160
+ it "should should do nothing" do
161
+ expect(Appsignal::Extension).to_not receive(:start)
162
+
163
+ Appsignal.forked
164
+ end
165
+ end
166
+
167
+ context "when active" do
168
+ before do
169
+ Appsignal.config = project_fixture_config
170
+ end
171
+
172
+ it "should resubscribe and start the extension" do
173
+ expect(Appsignal).to receive(:start_logger)
174
+ expect(Appsignal::Extension).to receive(:start)
175
+
176
+ Appsignal.forked
177
+ end
178
+ end
179
+ end
180
+
181
+ describe ".stop" do
182
+ it "should call stop on the extension" do
183
+ expect(Appsignal.logger).to receive(:debug).with("Stopping appsignal")
184
+ expect(Appsignal::Extension).to receive(:stop)
185
+ Appsignal.stop
186
+ expect(Appsignal.active?).to be_falsy
187
+ end
188
+
189
+ context "with context specified" do
190
+ it "should log the context" do
191
+ expect(Appsignal.logger).to receive(:debug).with("Stopping appsignal (something)")
192
+ expect(Appsignal::Extension).to receive(:stop)
193
+ Appsignal.stop("something")
194
+ expect(Appsignal.active?).to be_falsy
195
+ end
196
+ end
197
+ end
198
+
199
+ describe ".active?" do
200
+ subject { Appsignal.active? }
201
+
202
+ context "without config" do
203
+ before do
204
+ Appsignal.config = nil
205
+ end
206
+
207
+ it { is_expected.to be_falsy }
208
+ end
209
+
210
+ context "with inactive config" do
211
+ before do
212
+ Appsignal.config = project_fixture_config("nonsense")
213
+ end
214
+
215
+ it { is_expected.to be_falsy }
216
+ end
217
+
218
+ context "with active config" do
219
+ before do
220
+ Appsignal.config = project_fixture_config
221
+ end
222
+
223
+ it { is_expected.to be_truthy }
224
+ end
225
+ end
226
+
227
+ describe ".add_exception" do
228
+ it "should alias this method" do
229
+ expect(Appsignal).to respond_to(:add_exception)
230
+ end
231
+ end
232
+
233
+ describe ".get_server_state" do
234
+ it "should call server state on the extension" do
235
+ expect(Appsignal::Extension).to receive(:get_server_state).with("key")
236
+
237
+ Appsignal.get_server_state("key")
238
+ end
239
+
240
+ it "should get nil by default" do
241
+ expect(Appsignal.get_server_state("key")).to be_nil
242
+ end
243
+ end
244
+
245
+ context "not active" do
246
+ describe ".monitor_transaction" do
247
+ it "should do nothing but still yield the block" do
248
+ expect(Appsignal::Transaction).to_not receive(:create)
249
+ expect(Appsignal).to_not receive(:instrument)
250
+ object = double
251
+ expect(object).to receive(:some_method).and_return(1)
252
+
253
+ expect do
254
+ expect(Appsignal.monitor_transaction("perform_job.nothing") do
255
+ object.some_method
256
+ end).to eq 1
257
+ end.to_not raise_error
258
+ end
259
+ end
260
+
261
+ describe ".listen_for_error" do
262
+ it "does not record anyhing" do
263
+ error = RuntimeError.new("specific error")
264
+ expect do
265
+ Appsignal.listen_for_error do
266
+ raise error
267
+ end
268
+ end.to raise_error(error)
269
+ end
270
+ end
271
+
272
+ describe ".send_error" do
273
+ it "should do nothing" do
274
+ expect do
275
+ Appsignal.send_error(RuntimeError.new)
276
+ end.to_not raise_error
277
+ end
278
+ end
279
+
280
+ describe ".set_error" do
281
+ it "should do nothing" do
282
+ expect do
283
+ Appsignal.set_error(RuntimeError.new)
284
+ end.to_not raise_error
285
+ end
286
+ end
287
+
288
+ describe ".set_namespace" do
289
+ it "should do nothing" do
290
+ expect do
291
+ Appsignal.set_namespace("custom")
292
+ end.to_not raise_error
293
+ end
294
+ end
295
+
296
+ describe ".tag_request" do
297
+ it "should do nothing" do
298
+ expect do
299
+ Appsignal.tag_request(:tag => "tag")
300
+ end.to_not raise_error
301
+ end
302
+ end
303
+ end
304
+
305
+ context "with config and started" do
306
+ before do
307
+ Appsignal.config = project_fixture_config
308
+ Appsignal.start
309
+ end
310
+
311
+ describe ".monitor_transaction" do
312
+ context "with a successful call" do
313
+ it "should instrument and complete for a background job" do
314
+ expect(Appsignal).to receive(:instrument).with(
315
+ "perform_job.something"
316
+ ).and_yield
317
+ expect(Appsignal::Transaction).to receive(:complete_current!)
318
+ object = double
319
+ expect(object).to receive(:some_method).and_return(1)
320
+
321
+ expect(Appsignal.monitor_transaction(
322
+ "perform_job.something",
323
+ background_env_with_data
324
+ ) do
325
+ current = Appsignal::Transaction.current
326
+ expect(current.namespace).to eq Appsignal::Transaction::BACKGROUND_JOB
327
+ expect(current.request).to be_a(Appsignal::Transaction::GenericRequest)
328
+ object.some_method
329
+ end).to eq 1
330
+ end
331
+
332
+ it "should instrument and complete for a http request" do
333
+ expect(Appsignal).to receive(:instrument).with(
334
+ "process_action.something"
335
+ ).and_yield
336
+ expect(Appsignal::Transaction).to receive(:complete_current!)
337
+ object = double
338
+ expect(object).to receive(:some_method)
339
+
340
+ Appsignal.monitor_transaction(
341
+ "process_action.something",
342
+ http_request_env_with_data
343
+ ) do
344
+ current = Appsignal::Transaction.current
345
+ expect(current.namespace).to eq Appsignal::Transaction::HTTP_REQUEST
346
+ expect(current.request).to be_a(::Rack::Request)
347
+ object.some_method
348
+ end
349
+ end
350
+ end
351
+
352
+ context "with an erroring call" do
353
+ let(:error) { ExampleException.new }
354
+
355
+ it "should add the error to the current transaction and complete" do
356
+ expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
357
+ expect(Appsignal::Transaction).to receive(:complete_current!)
358
+
359
+ expect do
360
+ Appsignal.monitor_transaction("perform_job.something") do
361
+ raise error
362
+ end
363
+ end.to raise_error(error)
364
+ end
365
+ end
366
+ end
367
+
368
+ describe ".monitor_single_transaction" do
369
+ context "with a successful call" do
370
+ it "should call monitor_transaction and stop" do
371
+ expect(Appsignal).to receive(:monitor_transaction).with(
372
+ "perform_job.something",
373
+ :key => :value
374
+ ).and_yield
375
+ expect(Appsignal).to receive(:stop)
376
+
377
+ Appsignal.monitor_single_transaction("perform_job.something", :key => :value) do
378
+ # nothing
379
+ end
380
+ end
381
+ end
382
+
383
+ context "with an erroring call" do
384
+ let(:error) { ExampleException.new }
385
+
386
+ it "should call monitor_transaction and stop and then raise the error" do
387
+ expect(Appsignal).to receive(:monitor_transaction).with(
388
+ "perform_job.something",
389
+ :key => :value
390
+ ).and_yield
391
+ expect(Appsignal).to receive(:stop)
392
+
393
+ expect do
394
+ Appsignal.monitor_single_transaction("perform_job.something", :key => :value) do
395
+ raise error
396
+ end
397
+ end.to raise_error(error)
398
+ end
399
+ end
400
+ end
401
+
402
+ describe ".tag_request" do
403
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
404
+
405
+ context "with transaction" do
406
+ let(:transaction) { double }
407
+ it "should call set_tags on transaction" do
408
+ expect(transaction).to receive(:set_tags).with("a" => "b")
409
+ end
410
+
411
+ after { Appsignal.tag_request("a" => "b") }
412
+ end
413
+
414
+ context "without transaction" do
415
+ let(:transaction) { nil }
416
+
417
+ it "should call set_tags on transaction" do
418
+ expect(Appsignal.tag_request).to be_falsy
419
+ end
420
+ end
421
+
422
+ it "should also listen to tag_job" do
423
+ expect(Appsignal).to respond_to(:tag_job)
424
+ end
425
+ end
426
+
427
+ describe "custom stats" do
428
+ describe ".set_gauge" do
429
+ it "should call set_gauge on the extension with a string key and float" do
430
+ expect(Appsignal::Extension).to receive(:set_gauge).with("key", 0.1)
431
+ Appsignal.set_gauge("key", 0.1)
432
+ end
433
+
434
+ it "should call set_gauge on the extension with a symbol key and int" do
435
+ expect(Appsignal::Extension).to receive(:set_gauge).with("key", 1.0)
436
+ Appsignal.set_gauge(:key, 1)
437
+ end
438
+
439
+ it "should not raise an exception when out of range" do
440
+ expect(Appsignal::Extension).to receive(:set_gauge).with("key", 10).and_raise(RangeError)
441
+ expect(Appsignal.logger).to receive(:warn).with("Gauge value 10 for key 'key' is too big")
442
+ expect do
443
+ Appsignal.set_gauge("key", 10)
444
+ end.to_not raise_error
445
+ end
446
+ end
447
+
448
+ describe ".set_host_gauge" do
449
+ it "should call set_host_gauge on the extension with a string key and float" do
450
+ expect(Appsignal::Extension).to receive(:set_host_gauge).with("key", 0.1)
451
+ Appsignal.set_host_gauge("key", 0.1)
452
+ end
453
+
454
+ it "should call set_host_gauge on the extension with a symbol key and int" do
455
+ expect(Appsignal::Extension).to receive(:set_host_gauge).with("key", 1.0)
456
+ Appsignal.set_host_gauge(:key, 1)
457
+ end
458
+
459
+ it "should not raise an exception when out of range" do
460
+ expect(Appsignal::Extension).to receive(:set_host_gauge).with("key", 10).and_raise(RangeError)
461
+ expect(Appsignal.logger).to receive(:warn).with("Host gauge value 10 for key 'key' is too big")
462
+ expect do
463
+ Appsignal.set_host_gauge("key", 10)
464
+ end.to_not raise_error
465
+ end
466
+ end
467
+
468
+ describe ".set_process_gauge" do
469
+ it "should call set_process_gauge on the extension with a string key and float" do
470
+ expect(Appsignal::Extension).to receive(:set_process_gauge).with("key", 0.1)
471
+ Appsignal.set_process_gauge("key", 0.1)
472
+ end
473
+
474
+ it "should call set_process_gauge on the extension with a symbol key and int" do
475
+ expect(Appsignal::Extension).to receive(:set_process_gauge).with("key", 1.0)
476
+ Appsignal.set_process_gauge(:key, 1)
477
+ end
478
+
479
+ it "should not raise an exception when out of range" do
480
+ expect(Appsignal::Extension).to receive(:set_process_gauge).with("key", 10).and_raise(RangeError)
481
+ expect(Appsignal.logger).to receive(:warn).with("Process gauge value 10 for key 'key' is too big")
482
+ expect do
483
+ Appsignal.set_process_gauge("key", 10)
484
+ end.to_not raise_error
485
+ end
486
+ end
487
+
488
+ describe ".increment_counter" do
489
+ it "should call increment_counter on the extension with a string key" do
490
+ expect(Appsignal::Extension).to receive(:increment_counter).with("key", 1)
491
+ Appsignal.increment_counter("key")
492
+ end
493
+
494
+ it "should call increment_counter on the extension with a symbol key" do
495
+ expect(Appsignal::Extension).to receive(:increment_counter).with("key", 1)
496
+ Appsignal.increment_counter(:key)
497
+ end
498
+
499
+ it "should call increment_counter on the extension with a count" do
500
+ expect(Appsignal::Extension).to receive(:increment_counter).with("key", 5)
501
+ Appsignal.increment_counter("key", 5)
502
+ end
503
+
504
+ it "should not raise an exception when out of range" do
505
+ expect(Appsignal::Extension).to receive(:increment_counter).with("key", 10).and_raise(RangeError)
506
+ expect(Appsignal.logger).to receive(:warn).with("Counter value 10 for key 'key' is too big")
507
+ expect do
508
+ Appsignal.increment_counter("key", 10)
509
+ end.to_not raise_error
510
+ end
511
+ end
512
+
513
+ describe ".add_distribution_value" do
514
+ it "should call add_distribution_value on the extension with a string key and float" do
515
+ expect(Appsignal::Extension).to receive(:add_distribution_value).with("key", 0.1)
516
+ Appsignal.add_distribution_value("key", 0.1)
517
+ end
518
+
519
+ it "should call add_distribution_value on the extension with a symbol key and int" do
520
+ expect(Appsignal::Extension).to receive(:add_distribution_value).with("key", 1.0)
521
+ Appsignal.add_distribution_value(:key, 1)
522
+ end
523
+
524
+ it "should not raise an exception when out of range" do
525
+ expect(Appsignal::Extension).to receive(:add_distribution_value).with("key", 10).and_raise(RangeError)
526
+ expect(Appsignal.logger).to receive(:warn).with("Distribution value 10 for key 'key' is too big")
527
+ expect do
528
+ Appsignal.add_distribution_value("key", 10)
529
+ end.to_not raise_error
530
+ end
531
+ end
532
+ end
533
+
534
+ describe ".logger" do
535
+ subject { Appsignal.logger }
536
+
537
+ it { is_expected.to be_a Logger }
538
+ end
539
+
540
+ describe ".start_logger" do
541
+ let(:out_stream) { std_stream }
542
+ let(:output) { out_stream.read }
543
+ let(:log_path) { File.join(tmp_dir, "log") }
544
+ let(:log_file) { File.join(log_path, "appsignal.log") }
545
+
546
+ before do
547
+ FileUtils.mkdir_p(log_path)
548
+
549
+ Appsignal.logger.error("Log in memory")
550
+ Appsignal.config = project_fixture_config(
551
+ "production",
552
+ :log_path => log_path
553
+ )
554
+ end
555
+ after { FileUtils.rm_rf(log_path) }
556
+
557
+ context "when the log path is writable" do
558
+ context "when the log file is writable" do
559
+ let(:log_file_contents) { File.open(log_file).read }
560
+
561
+ before do
562
+ capture_stdout(out_stream) do
563
+ Appsignal.start_logger
564
+ Appsignal.logger.error("Log to file")
565
+ end
566
+ end
567
+
568
+ it "logs to file" do
569
+ expect(File.exist?(log_file)).to be_truthy
570
+ expect(log_file_contents).to include "[ERROR] Log to file"
571
+ expect(output).to be_empty
572
+ end
573
+
574
+ it "amends in memory log to log file" do
575
+ expect(log_file_contents).to include "[ERROR] appsignal: Log in memory"
576
+ end
577
+ end
578
+
579
+ context "when the log file is not writable" do
580
+ before do
581
+ FileUtils.touch log_file
582
+ FileUtils.chmod 0o444, log_file
583
+
584
+ capture_stdout(out_stream) do
585
+ Appsignal.start_logger
586
+ Appsignal.logger.error("Log to not writable log file")
587
+ end
588
+ end
589
+
590
+ it "logs to stdout" do
591
+ expect(File.writable?(log_file)).to be_falsy
592
+ expect(output).to include "[ERROR] appsignal: Log to not writable log file"
593
+ end
594
+
595
+ it "amends in memory log to stdout" do
596
+ expect(output).to include "[ERROR] appsignal: Log in memory"
597
+ end
598
+
599
+ it "outputs a warning" do
600
+ expect(output).to include \
601
+ "[WARN] appsignal: Unable to start logger with log path '#{log_file}'.",
602
+ "[WARN] appsignal: Permission denied"
603
+ end
604
+ end
605
+ end
606
+
607
+ context "when the log path and fallback path are not writable" do
608
+ before do
609
+ FileUtils.chmod 0o444, log_path
610
+ FileUtils.chmod 0o444, Appsignal::Config.system_tmp_dir
611
+
612
+ capture_stdout(out_stream) do
613
+ Appsignal.start_logger
614
+ Appsignal.logger.error("Log to not writable log path")
615
+ end
616
+ end
617
+ after do
618
+ FileUtils.chmod 0o755, Appsignal::Config.system_tmp_dir
619
+ end
620
+
621
+ it "logs to stdout" do
622
+ expect(File.writable?(log_path)).to be_falsy
623
+ expect(output).to include "[ERROR] appsignal: Log to not writable log path"
624
+ end
625
+
626
+ it "amends in memory log to stdout" do
627
+ expect(output).to include "[ERROR] appsignal: Log in memory"
628
+ end
629
+
630
+ it "outputs a warning" do
631
+ expect(output).to include \
632
+ "appsignal: Unable to log to '#{log_path}' "\
633
+ "or the '#{Appsignal::Config.system_tmp_dir}' fallback."
634
+ end
635
+ end
636
+
637
+ context "when on Heroku" do
638
+ before do
639
+ capture_stdout(out_stream) do
640
+ Appsignal.start_logger
641
+ Appsignal.logger.error("Log to stdout")
642
+ end
643
+ end
644
+ around { |example| recognize_as_heroku { example.run } }
645
+
646
+ it "logs to stdout" do
647
+ expect(output).to include "[ERROR] appsignal: Log to stdout"
648
+ end
649
+
650
+ it "amends in memory log to stdout" do
651
+ expect(output).to include "[ERROR] appsignal: Log in memory"
652
+ end
653
+ end
654
+
655
+ describe "#logger#level" do
656
+ subject { Appsignal.logger.level }
657
+
658
+ context "when there is no config" do
659
+ before do
660
+ Appsignal.config = nil
661
+ capture_stdout(out_stream) do
662
+ Appsignal.start_logger
663
+ end
664
+ end
665
+
666
+ it "sets the log level to info" do
667
+ expect(subject).to eq Logger::INFO
668
+ end
669
+ end
670
+
671
+ context "when there is a config" do
672
+ context "when log level is configured to debug" do
673
+ before do
674
+ Appsignal.config.config_hash[:debug] = true
675
+ capture_stdout(out_stream) do
676
+ Appsignal.start_logger
677
+ end
678
+ end
679
+
680
+ it "sets the log level to debug" do
681
+ expect(subject).to eq Logger::DEBUG
682
+ end
683
+ end
684
+ end
685
+ end
686
+ end
687
+
688
+ describe ".log_formatter" do
689
+ subject { Appsignal.log_formatter.call("Debug", Time.parse("2015-07-08"), nil, "log line") }
690
+
691
+ it "formats a log" do
692
+ expect(subject).to eq "[2015-07-08T00:00:00 (process) ##{Process.pid}][Debug] log line\n"
693
+ end
694
+
695
+ context "with prefix" do
696
+ subject do
697
+ Appsignal.log_formatter("prefix").call("Debug", Time.parse("2015-07-08"), nil, "log line")
698
+ end
699
+
700
+ it "adds a prefix" do
701
+ expect(subject)
702
+ .to eq "[2015-07-08T00:00:00 (process) ##{Process.pid}][Debug] prefix: log line\n"
703
+ end
704
+ end
705
+ end
706
+
707
+ describe ".config" do
708
+ subject { Appsignal.config }
709
+
710
+ it { is_expected.to be_a Appsignal::Config }
711
+ it "should return configuration" do
712
+ expect(subject[:endpoint]).to eq "https://push.appsignal.com"
713
+ end
714
+ end
715
+
716
+ describe ".send_error" do
717
+ let(:transaction) do
718
+ Appsignal::Transaction.new(
719
+ SecureRandom.uuid,
720
+ Appsignal::Transaction::HTTP_REQUEST,
721
+ Appsignal::Transaction::GenericRequest.new({})
722
+ )
723
+ end
724
+ let(:error) { ExampleException.new }
725
+
726
+ it "sends the error to AppSignal" do
727
+ expect(Appsignal::Transaction).to receive(:new).with(
728
+ kind_of(String),
729
+ Appsignal::Transaction::HTTP_REQUEST,
730
+ kind_of(Appsignal::Transaction::GenericRequest)
731
+ ).and_return(transaction)
732
+ expect(transaction).to receive(:set_error).with(error)
733
+ expect(transaction).to_not receive(:set_tags)
734
+ expect(transaction).to receive(:complete)
735
+
736
+ Appsignal.send_error(error)
737
+ end
738
+
739
+ context "when given error is not an Exception" do
740
+ let(:error) { double }
741
+
742
+ it "logs an error message" do
743
+ expect(Appsignal.logger).to receive(:error)
744
+ .with("Can't send error, given value is not an exception")
745
+ end
746
+
747
+ it "does not send the error" do
748
+ expect(Appsignal::Transaction).to_not receive(:create)
749
+ end
750
+
751
+ after { Appsignal.send_error(error) }
752
+ end
753
+
754
+ context "with tags" do
755
+ let(:tags) { { :a => "a", :b => "b" } }
756
+ before do
757
+ allow(Appsignal::Transaction).to receive(:new).and_return(transaction)
758
+ end
759
+
760
+ it "tags the request before sending it" do
761
+ expect(transaction).to receive(:set_tags).with(tags).and_call_original
762
+ expect(transaction).to receive(:complete)
763
+
764
+ Appsignal.send_error(error, tags)
765
+ end
766
+ end
767
+
768
+ context "with namespace" do
769
+ let(:namespace) { "admin" }
770
+
771
+ it "sets the namespace on the transaction" do
772
+ expect(Appsignal::Transaction).to receive(:new).with(
773
+ kind_of(String),
774
+ "admin",
775
+ kind_of(Appsignal::Transaction::GenericRequest)
776
+ ).and_call_original
777
+ end
778
+
779
+ after { Appsignal.send_error(error, nil, namespace) }
780
+ end
781
+ end
782
+
783
+ describe ".listen_for_error" do
784
+ it "records the error and re-raise it" do
785
+ expect(Appsignal).to receive(:send_error).with(
786
+ kind_of(ExampleException),
787
+ nil,
788
+ Appsignal::Transaction::HTTP_REQUEST
789
+ )
790
+ expect do
791
+ Appsignal.listen_for_error do
792
+ raise ExampleException, "I am an exception"
793
+ end
794
+ end.to raise_error(ExampleException, "I am an exception")
795
+ end
796
+
797
+ context "with tags" do
798
+ it "adds tags to the transaction" do
799
+ expect(Appsignal).to receive(:send_error).with(
800
+ kind_of(ExampleException),
801
+ { "foo" => "bar" },
802
+ Appsignal::Transaction::HTTP_REQUEST
803
+ )
804
+ expect do
805
+ Appsignal.listen_for_error("foo" => "bar") do
806
+ raise ExampleException, "I am an exception"
807
+ end
808
+ end.to raise_error(ExampleException, "I am an exception")
809
+ end
810
+ end
811
+
812
+ context "with a custom namespace" do
813
+ it "adds the namespace to the transaction" do
814
+ expect(Appsignal).to receive(:send_error).with(
815
+ kind_of(ExampleException),
816
+ nil,
817
+ "custom_namespace"
818
+ )
819
+ expect do
820
+ Appsignal.listen_for_error(nil, "custom_namespace") do
821
+ raise ExampleException, "I am an exception"
822
+ end
823
+ end.to raise_error(ExampleException, "I am an exception")
824
+ end
825
+ end
826
+ end
827
+
828
+ describe ".set_error" do
829
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
830
+ let(:error) { RuntimeError.new("I am an exception") }
831
+
832
+ context "when there is an active transaction" do
833
+ it "adds the error to the active transaction" do
834
+ expect(transaction).to receive(:set_error).with(error)
835
+ expect(transaction).to_not receive(:set_tags)
836
+ expect(transaction).to_not receive(:set_namespace)
837
+
838
+ Appsignal.set_error(error)
839
+ end
840
+
841
+ context "when the error is nil" do
842
+ it "does nothing" do
843
+ expect(transaction).to_not receive(:set_error)
844
+ expect(transaction).to_not receive(:set_tags)
845
+ expect(transaction).to_not receive(:set_namespace)
846
+
847
+ Appsignal.set_error(nil)
848
+ end
849
+ end
850
+
851
+ context "with tags" do
852
+ let(:tags) { { "foo" => "bar" } }
853
+
854
+ it "sets the tags on the transaction" do
855
+ expect(transaction).to receive(:set_error).with(error)
856
+ expect(transaction).to receive(:set_tags).with(tags)
857
+ expect(transaction).to_not receive(:set_namespace)
858
+
859
+ Appsignal.set_error(error, tags)
860
+ end
861
+ end
862
+
863
+ context "with namespace" do
864
+ let(:namespace) { "admin" }
865
+
866
+ it "sets the namespace on the transaction" do
867
+ expect(transaction).to receive(:set_error).with(error)
868
+ expect(transaction).to_not receive(:set_tags)
869
+ expect(transaction).to receive(:set_namespace).with(namespace)
870
+
871
+ Appsignal.set_error(error, nil, namespace)
872
+ end
873
+ end
874
+ end
875
+
876
+ context "when there is no active transaction" do
877
+ it "does nothing" do
878
+ allow(Appsignal::Transaction).to receive(:current).and_return(nil)
879
+
880
+ expect(transaction).to_not receive(:set_error)
881
+
882
+ Appsignal.set_error(error)
883
+ end
884
+ end
885
+ end
886
+
887
+ describe ".set_action" do
888
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
889
+
890
+ it "should set the namespace to the current transaction" do
891
+ expect(transaction).to receive(:set_action).with("custom")
892
+
893
+ Appsignal.set_action("custom")
894
+ end
895
+
896
+ it "should do nothing if there is no current transaction" do
897
+ allow(Appsignal::Transaction).to receive(:current).and_return(nil)
898
+
899
+ expect(transaction).to_not receive(:set_action)
900
+
901
+ Appsignal.set_action("custom")
902
+ end
903
+
904
+ it "should do nothing if the error is nil" do
905
+ expect(transaction).to_not receive(:set_action)
906
+
907
+ Appsignal.set_action(nil)
908
+ end
909
+ end
910
+
911
+ describe ".set_namespace" do
912
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
913
+
914
+ it "should set the namespace to the current transaction" do
915
+ expect(transaction).to receive(:set_namespace).with("custom")
916
+
917
+ Appsignal.set_namespace("custom")
918
+ end
919
+
920
+ it "should do nothing if there is no current transaction" do
921
+ allow(Appsignal::Transaction).to receive(:current).and_return(nil)
922
+
923
+ expect(transaction).to_not receive(:set_namespace)
924
+
925
+ Appsignal.set_namespace("custom")
926
+ end
927
+
928
+ it "should do nothing if the error is nil" do
929
+ expect(transaction).to_not receive(:set_namespace)
930
+
931
+ Appsignal.set_namespace(nil)
932
+ end
933
+ end
934
+
935
+ describe ".instrument" do
936
+ it_behaves_like "instrument helper" do
937
+ let(:instrumenter) { Appsignal }
938
+ before do
939
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
940
+ end
941
+ end
942
+ end
943
+
944
+ describe ".instrument_sql" do
945
+ before do
946
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
947
+ end
948
+
949
+ it "creates an SQL event on the transaction" do
950
+ expect(transaction).to receive(:start_event)
951
+ expect(transaction).to receive(:finish_event)
952
+ .with("name", "title", "body", Appsignal::EventFormatter::SQL_BODY_FORMAT)
953
+
954
+ result = Appsignal.instrument_sql "name", "title", "body" do
955
+ "return value"
956
+ end
957
+ expect(result).to eq "return value"
958
+ end
959
+ end
960
+
961
+ describe ".without_instrumentation" do
962
+ let(:transaction) { double }
963
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
964
+
965
+ it "should pause and unpause the transaction around the block" do
966
+ expect(transaction).to receive(:pause!)
967
+ expect(transaction).to receive(:resume!)
968
+ end
969
+
970
+ context "without transaction" do
971
+ let(:transaction) { nil }
972
+
973
+ it "should not crash" do
974
+ # just execute the after block
975
+ end
976
+ end
977
+
978
+ after do
979
+ Appsignal.without_instrumentation do
980
+ # nothing
981
+ end
982
+ end
983
+ end
984
+
985
+ describe ".is_ignored_error?" do
986
+ let(:error) { StandardError.new }
987
+ let(:err_stream) { std_stream }
988
+ let(:stderr) { err_stream.read }
989
+ before do
990
+ allow(Appsignal).to receive(:config).and_return(:ignore_errors => ["StandardError"])
991
+ end
992
+
993
+ subject do
994
+ capture_std_streams(std_stream, err_stream) do
995
+ Appsignal.is_ignored_error?(error)
996
+ end
997
+ end
998
+
999
+ it "should return true if it's in the ignored list" do
1000
+ is_expected.to be_truthy
1001
+ end
1002
+
1003
+ it "outputs deprecated warning" do
1004
+ subject
1005
+ expect(stderr).to include("Appsignal.is_ignored_error? is deprecated with no replacement.")
1006
+ end
1007
+
1008
+ context "when error is not in the ignored list" do
1009
+ let(:error) { Object.new }
1010
+
1011
+ it "should return false" do
1012
+ is_expected.to be_falsy
1013
+ end
1014
+ end
1015
+ end
1016
+
1017
+ describe ".is_ignored_action?" do
1018
+ let(:action) { "TestController#isup" }
1019
+ let(:err_stream) { std_stream }
1020
+ let(:stderr) { err_stream.read }
1021
+ before do
1022
+ allow(Appsignal).to receive(:config).and_return(:ignore_actions => "TestController#isup")
1023
+ end
1024
+
1025
+ subject do
1026
+ capture_std_streams(std_stream, err_stream) do
1027
+ Appsignal.is_ignored_action?(action)
1028
+ end
1029
+ end
1030
+
1031
+ it "should return true if it's in the ignored list" do
1032
+ is_expected.to be_truthy
1033
+ end
1034
+
1035
+ it "outputs deprecated warning" do
1036
+ subject
1037
+ expect(stderr).to include("Appsignal.is_ignored_action? is deprecated with no replacement.")
1038
+ end
1039
+
1040
+ context "when action is not in the ingore list" do
1041
+ let(:action) { "TestController#other_action" }
1042
+
1043
+ it "should return false" do
1044
+ is_expected.to be_falsy
1045
+ end
1046
+ end
1047
+ end
1048
+ end
1049
+ end