appsignal 3.0.0.beta.1-java → 3.0.3-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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +1 -1
  3. data/.semaphore/semaphore.yml +88 -88
  4. data/CHANGELOG.md +41 -1
  5. data/Rakefile +12 -4
  6. data/appsignal.gemspec +7 -5
  7. data/build_matrix.yml +11 -11
  8. data/ext/agent.yml +17 -17
  9. data/gemfiles/no_dependencies.gemfile +0 -7
  10. data/lib/appsignal.rb +1 -2
  11. data/lib/appsignal/config.rb +1 -1
  12. data/lib/appsignal/extension.rb +50 -0
  13. data/lib/appsignal/helpers/instrumentation.rb +69 -5
  14. data/lib/appsignal/hooks.rb +16 -0
  15. data/lib/appsignal/hooks/action_cable.rb +10 -2
  16. data/lib/appsignal/hooks/sidekiq.rb +9 -142
  17. data/lib/appsignal/integrations/object.rb +21 -43
  18. data/lib/appsignal/integrations/railtie.rb +0 -4
  19. data/lib/appsignal/integrations/sidekiq.rb +171 -0
  20. data/lib/appsignal/minutely.rb +6 -0
  21. data/lib/appsignal/transaction.rb +2 -2
  22. data/lib/appsignal/version.rb +1 -1
  23. data/spec/lib/appsignal/config_spec.rb +2 -0
  24. data/spec/lib/appsignal/extension_install_failure_spec.rb +0 -7
  25. data/spec/lib/appsignal/extension_spec.rb +43 -9
  26. data/spec/lib/appsignal/hooks/action_cable_spec.rb +88 -0
  27. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +60 -458
  28. data/spec/lib/appsignal/hooks_spec.rb +41 -0
  29. data/spec/lib/appsignal/integrations/object_spec.rb +91 -4
  30. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +524 -0
  31. data/spec/lib/appsignal/transaction_spec.rb +17 -0
  32. data/spec/lib/appsignal/utils/data_spec.rb +133 -87
  33. data/spec/lib/appsignal_spec.rb +162 -47
  34. data/spec/lib/puma/appsignal_spec.rb +28 -0
  35. data/spec/spec_helper.rb +22 -0
  36. data/spec/support/testing.rb +11 -1
  37. metadata +9 -8
  38. data/gemfiles/rails-4.0.gemfile +0 -6
  39. data/gemfiles/rails-4.1.gemfile +0 -6
@@ -246,6 +246,23 @@ describe Appsignal::Transaction do
246
246
  expect(transaction.ext).to_not be_nil
247
247
  end
248
248
 
249
+ context "when extension is not loaded", :extension_installation_failure do
250
+ around do |example|
251
+ Appsignal::Testing.without_testing { example.run }
252
+ end
253
+
254
+ it "does not error on missing extension method calls" do
255
+ expect(transaction.ext).to be_kind_of(Appsignal::Extension::MockTransaction)
256
+ transaction.start_event
257
+ transaction.finish_event(
258
+ "name",
259
+ "title",
260
+ "body",
261
+ Appsignal::EventFormatter::DEFAULT
262
+ )
263
+ end
264
+ end
265
+
249
266
  it "sets the transaction id" do
250
267
  expect(transaction.transaction_id).to eq "1"
251
268
  end
@@ -4,110 +4,156 @@ describe Appsignal::Utils::Data do
4
4
  describe ".generate" do
5
5
  subject { Appsignal::Utils::Data.generate(body) }
6
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
- }
7
+ context "when extension is not loaded", :extension_installation_failure do
8
+ around do |example|
9
+ Appsignal::Testing.without_testing { example.run }
23
10
  end
24
11
 
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"})
12
+ context "with valid hash body" do
13
+ let(:body) { hash_body }
14
+
15
+ it "does not error and returns MockData class" do
16
+ expect(subject).to be_kind_of(Appsignal::Extension::MockData)
17
+ expect(subject.to_s).to eql("{}")
42
18
  end
43
19
  end
44
- end
45
20
 
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
- ]
21
+ context "with valid array body" do
22
+ let(:body) { array_body }
23
+
24
+ it "does not error and returns MockData class" do
25
+ expect(subject).to be_kind_of(Appsignal::Extension::MockData)
26
+ expect(subject.to_s).to eql("{}")
27
+ end
61
28
  end
62
29
 
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\"}])
30
+ context "with an invalid body" do
31
+ let(:body) { "body" }
32
+
33
+ it "raise a type error" do
34
+ expect do
35
+ subject
36
+ end.to raise_error TypeError
79
37
  end
80
38
  end
81
39
  end
82
40
 
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
- }
41
+ context "when extension is loaded" do
42
+ context "with a valid hash body" do
43
+ let(:body) { hash_body }
44
+
45
+ it "returns a valid Data object" do
46
+ is_expected.to eq Appsignal::Utils::Data.generate(body)
47
+ is_expected.to_not eq Appsignal::Utils::Data.generate({})
48
+ end
49
+
50
+ describe "#to_s" do
51
+ it "returns a serialized hash" do
52
+ expect(subject.to_s).to eq %({"":"test",) +
53
+ %("1":true,) +
54
+ %("bar":null,) +
55
+ %("baz":{"arr":[1,2],"foo":"bʊr"},) +
56
+ %("float":1.0,) +
57
+ %("foo":[1,2,"three",{"foo":"bar"}],) +
58
+ %("int":1,) +
59
+ %("int61":#{1 << 61},) +
60
+ %("int62":#{1 << 62},) +
61
+ %("int63":"bigint:#{1 << 63}",) +
62
+ %("int64":"bigint:#{1 << 64}",) +
63
+ %("the":"payload"})
64
+ end
65
+ end
66
+ end
67
+
68
+ context "with a valid array body" do
69
+ let(:body) { array_body }
70
+
71
+ it "returns a valid Data object" do
72
+ is_expected.to eq Appsignal::Utils::Data.generate(body)
73
+ is_expected.to_not eq Appsignal::Utils::Data.generate({})
74
+ end
75
+
76
+ describe "#to_s" do
77
+ it "returns a serialized array" do
78
+ expect(subject.to_s).to eq %([null,) +
79
+ %(true,) +
80
+ %(false,) +
81
+ %(\"string\",) +
82
+ %(1,) +
83
+ %(1.0,) +
84
+ %(#{1 << 61},) +
85
+ %(#{1 << 62},) +
86
+ %("bigint:#{1 << 63}",) +
87
+ %("bigint:#{1 << 64}",) +
88
+ %({\"arr\":[1,2,\"three\"],\"foo\":\"bʊr\"}])
89
+ end
90
+ end
96
91
  end
97
92
 
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�"}) }
93
+ context "with a body that contains strings with invalid utf-8 content" do
94
+ let(:string_with_invalid_utf8) { [0x61, 0x61, 0x85].pack("c*") }
95
+ let(:body) do
96
+ {
97
+ "field_one" => [0x61, 0x61].pack("c*"),
98
+ :field_two => string_with_invalid_utf8,
99
+ "field_three" => [
100
+ "one", string_with_invalid_utf8
101
+ ],
102
+ "field_four" => {
103
+ "one" => string_with_invalid_utf8
104
+ }
105
+ }
106
+ end
107
+
108
+ describe "#to_s" do
109
+ it "returns a JSON representation in a String" do
110
+ expect(subject.to_s).to eq %({"field_four":{"one":"aa�"},"field_one":"aa","field_three":["one","aa�"],"field_two":"aa�"})
111
+ end
112
+ end
100
113
  end
101
- end
102
114
 
103
- context "with an invalid body" do
104
- let(:body) { "body" }
115
+ context "with an invalid body" do
116
+ let(:body) { "body" }
105
117
 
106
- it "should raise a type error" do
107
- expect do
108
- subject
109
- end.to raise_error TypeError
118
+ it "raises a type error" do
119
+ expect do
120
+ subject
121
+ end.to raise_error TypeError
122
+ end
110
123
  end
111
124
  end
112
125
  end
126
+
127
+ def hash_body
128
+ {
129
+ "the" => "payload",
130
+ "int" => 1, # Fixnum
131
+ "int61" => 1 << 61, # Fixnum
132
+ "int62" => 1 << 62, # Bignum, this one still works
133
+ "int63" => 1 << 63, # Bignum, turnover point for C, too big for long
134
+ "int64" => 1 << 64, # Bignum
135
+ "float" => 1.0,
136
+ 1 => true,
137
+ nil => "test",
138
+ :foo => [1, 2, "three", { "foo" => "bar" }],
139
+ "bar" => nil,
140
+ "baz" => { "foo" => "bʊr", "arr" => [1, 2] }
141
+ }
142
+ end
143
+
144
+ def array_body
145
+ [
146
+ nil,
147
+ true,
148
+ false,
149
+ "string",
150
+ 1, # Fixnum
151
+ 1.0, # Float
152
+ 1 << 61, # Fixnum
153
+ 1 << 62, # Bignum, this one still works
154
+ 1 << 63, # Bignum, turnover point for C, too big for long
155
+ 1 << 64, # Bignum
156
+ { "arr" => [1, 2, "three"], "foo" => "bʊr" }
157
+ ]
158
+ end
113
159
  end
@@ -686,6 +686,9 @@ describe Appsignal do
686
686
  )
687
687
  end
688
688
  let(:error) { ExampleException.new }
689
+ let(:err_stream) { std_stream }
690
+ let(:stderr) { err_stream.read }
691
+ around { |example| keep_transactions { example.run } }
689
692
 
690
693
  it "sends the error to AppSignal" do
691
694
  expect(Appsignal::Transaction).to receive(:new).with(
@@ -719,30 +722,60 @@ describe Appsignal do
719
722
 
720
723
  context "with tags" do
721
724
  let(:tags) { { :a => "a", :b => "b" } }
722
- before do
723
- allow(Appsignal::Transaction).to receive(:new).and_return(transaction)
724
- end
725
725
 
726
- it "tags the request before sending it" do
727
- expect(transaction).to receive(:set_tags).with(tags).and_call_original
728
- expect(transaction).to receive(:complete)
726
+ it "prints a deprecation warning and tags the transaction" do
727
+ logs = capture_logs do
728
+ expect do
729
+ capture_std_streams(std_stream, err_stream) do
730
+ Appsignal.send_error(error, tags)
731
+ end
732
+ end.to change { created_transactions.count }.by(1)
733
+ end
734
+
735
+ transaction = last_transaction
736
+ transaction_hash = transaction.to_h
737
+ expect(transaction_hash).to include(
738
+ "sample_data" => hash_including(
739
+ "tags" => { "a" => "a", "b" => "b" }
740
+ )
741
+ )
729
742
 
730
- Appsignal.send_error(error, tags)
743
+ message = "The tags argument for `Appsignal.send_error` is deprecated. " \
744
+ "Please use the block method to set tags instead.\n\n" \
745
+ " Appsignal.send_error(error) do |transaction|\n" \
746
+ " transaction.set_tags(#{tags.inspect})\n" \
747
+ " end\n\n" \
748
+ "Appsignal.send_error called on location: #{__FILE__}:"
749
+ expect(stderr).to include("appsignal WARNING: #{message}")
750
+ expect(logs).to include(message)
731
751
  end
732
752
  end
733
753
 
734
754
  context "with namespace" do
735
755
  let(:namespace) { "admin" }
736
756
 
737
- it "sets the namespace on the transaction" do
738
- expect(Appsignal::Transaction).to receive(:new).with(
739
- kind_of(String),
740
- "admin",
741
- kind_of(Appsignal::Transaction::GenericRequest)
742
- ).and_call_original
743
- end
757
+ it "prints a deprecation warning and sets the namespace on the transaction" do
758
+ logs = capture_logs do
759
+ expect do
760
+ capture_std_streams(std_stream, err_stream) do
761
+ Appsignal.send_error(error, nil, namespace)
762
+ end
763
+ end.to change { created_transactions.count }.by(1)
764
+ end
765
+
766
+ transaction = last_transaction
767
+ transaction_hash = transaction.to_h
768
+ expect(transaction_hash).to include("namespace" => namespace)
744
769
 
745
- after { Appsignal.send_error(error, nil, namespace) }
770
+ message = "The namespace argument for `Appsignal.send_error` is deprecated. " \
771
+ "Please use the block method to set the namespace instead.\n\n" \
772
+ " Appsignal.send_error(error) do |transaction|\n" \
773
+ " transaction.set_namespace(#{namespace.inspect})\n" \
774
+ " end\n\n" \
775
+ "Appsignal.send_error called on location: #{__FILE__}:"
776
+ expect(stderr).to include("appsignal WARNING: #{message}")
777
+ expect(logs).to include(message)
778
+ end
746
779
  end
747
780
 
748
781
  context "when given a block" do
@@ -769,53 +802,85 @@ describe Appsignal do
769
802
  end
770
803
 
771
804
  describe ".listen_for_error" do
805
+ around { |example| keep_transactions { example.run } }
806
+
772
807
  it "records the error and re-raise it" do
773
- expect(Appsignal).to receive(:send_error).with(
774
- kind_of(ExampleException),
775
- nil,
776
- Appsignal::Transaction::HTTP_REQUEST
777
- )
778
808
  expect do
779
- Appsignal.listen_for_error do
780
- raise ExampleException, "I am an exception"
781
- end
782
- end.to raise_error(ExampleException, "I am an exception")
809
+ expect do
810
+ Appsignal.listen_for_error do
811
+ raise ExampleException, "I am an exception"
812
+ end
813
+ end.to raise_error(ExampleException, "I am an exception")
814
+ end.to change { created_transactions.count }.by(1)
815
+
816
+ expect(last_transaction.to_h).to include(
817
+ "error" => {
818
+ "name" => "ExampleException",
819
+ "message" => "I am an exception",
820
+ "backtrace" => kind_of(String)
821
+ },
822
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST, # Default namespace
823
+ "sample_data" => hash_including(
824
+ "tags" => {}
825
+ )
826
+ )
783
827
  end
784
828
 
785
829
  context "with tags" do
786
830
  it "adds tags to the transaction" do
787
- expect(Appsignal).to receive(:send_error).with(
788
- kind_of(ExampleException),
789
- { "foo" => "bar" },
790
- Appsignal::Transaction::HTTP_REQUEST
791
- )
792
831
  expect do
793
- Appsignal.listen_for_error("foo" => "bar") do
794
- raise ExampleException, "I am an exception"
795
- end
796
- end.to raise_error(ExampleException, "I am an exception")
832
+ expect do
833
+ Appsignal.listen_for_error("foo" => "bar") do
834
+ raise ExampleException, "I am an exception"
835
+ end
836
+ end.to raise_error(ExampleException, "I am an exception")
837
+ end.to change { created_transactions.count }.by(1)
838
+
839
+ expect(last_transaction.to_h).to include(
840
+ "error" => {
841
+ "name" => "ExampleException",
842
+ "message" => "I am an exception",
843
+ "backtrace" => kind_of(String)
844
+ },
845
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST, # Default namespace
846
+ "sample_data" => hash_including(
847
+ "tags" => { "foo" => "bar" }
848
+ )
849
+ )
797
850
  end
798
851
  end
799
852
 
800
853
  context "with a custom namespace" do
801
854
  it "adds the namespace to the transaction" do
802
- expect(Appsignal).to receive(:send_error).with(
803
- kind_of(ExampleException),
804
- nil,
805
- "custom_namespace"
806
- )
807
855
  expect do
808
- Appsignal.listen_for_error(nil, "custom_namespace") do
809
- raise ExampleException, "I am an exception"
810
- end
811
- end.to raise_error(ExampleException, "I am an exception")
856
+ expect do
857
+ Appsignal.listen_for_error(nil, "custom_namespace") do
858
+ raise ExampleException, "I am an exception"
859
+ end
860
+ end.to raise_error(ExampleException, "I am an exception")
861
+ end.to change { created_transactions.count }.by(1)
862
+
863
+ expect(last_transaction.to_h).to include(
864
+ "error" => {
865
+ "name" => "ExampleException",
866
+ "message" => "I am an exception",
867
+ "backtrace" => kind_of(String)
868
+ },
869
+ "namespace" => "custom_namespace",
870
+ "sample_data" => hash_including(
871
+ "tags" => {}
872
+ )
873
+ )
812
874
  end
813
875
  end
814
876
  end
815
877
 
816
878
  describe ".set_error" do
879
+ let(:err_stream) { std_stream }
880
+ let(:stderr) { err_stream.read }
881
+ let(:error) { ExampleException.new("I am an exception") }
817
882
  before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
818
- let(:error) { RuntimeError.new("I am an exception") }
883
+ around { |example| keep_transactions { example.run } }
819
884
 
820
885
  context "when there is an active transaction" do
821
886
  it "adds the error to the active transaction" do
@@ -845,24 +910,74 @@ describe Appsignal do
845
910
  context "with tags" do
846
911
  let(:tags) { { "foo" => "bar" } }
847
912
 
848
- it "sets the tags on the transaction" do
913
+ it "prints a deprecation warning and tags the transaction" do
849
914
  expect(transaction).to receive(:set_error).with(error)
850
915
  expect(transaction).to receive(:set_tags).with(tags)
851
916
  expect(transaction).to_not receive(:set_namespace)
852
917
 
853
- Appsignal.set_error(error, tags)
918
+ logs = capture_logs do
919
+ capture_std_streams(std_stream, err_stream) do
920
+ Appsignal.set_error(error, tags)
921
+ end
922
+ end
923
+
924
+ message = "The tags argument for `Appsignal.set_error` is deprecated. " \
925
+ "Please use the block method to set tags instead.\n\n" \
926
+ " Appsignal.set_error(error) do |transaction|\n" \
927
+ " transaction.set_tags(#{tags.inspect})\n" \
928
+ " end\n\n" \
929
+ "Appsignal.set_error called on location: #{__FILE__}:"
930
+ expect(stderr).to include("appsignal WARNING: #{message}")
931
+ expect(logs).to include(message)
854
932
  end
855
933
  end
856
934
 
857
935
  context "with namespace" do
858
936
  let(:namespace) { "admin" }
859
937
 
860
- it "sets the namespace on the transaction" do
938
+ it "prints a deprecation warning andsets the namespace on the transaction" do
861
939
  expect(transaction).to receive(:set_error).with(error)
862
940
  expect(transaction).to_not receive(:set_tags)
863
941
  expect(transaction).to receive(:set_namespace).with(namespace)
864
942
 
865
- Appsignal.set_error(error, nil, namespace)
943
+ logs = capture_logs do
944
+ capture_std_streams(std_stream, err_stream) do
945
+ Appsignal.set_error(error, nil, namespace)
946
+ end
947
+ end
948
+
949
+ message = "The namespace argument for `Appsignal.set_error` is deprecated. " \
950
+ "Please use the block method to set the namespace instead.\n\n" \
951
+ " Appsignal.set_error(error) do |transaction|\n" \
952
+ " transaction.set_namespace(#{namespace.inspect})\n" \
953
+ " end\n\n" \
954
+ "Appsignal.set_error called on location: #{__FILE__}:"
955
+ expect(stderr).to include("appsignal WARNING: #{message}")
956
+ expect(logs).to include(message)
957
+ end
958
+ end
959
+
960
+ context "when given a block" do
961
+ it "yields the transaction and allows additional metadata to be set" do
962
+ captured_transaction = nil
963
+ keep_transactions do
964
+ Appsignal.set_error(StandardError.new("my_error")) do |transaction|
965
+ captured_transaction = transaction
966
+ transaction.set_action("my_action")
967
+ transaction.set_namespace("my_namespace")
968
+ end
969
+ end
970
+
971
+ expect(transaction).to eql(captured_transaction)
972
+ expect(captured_transaction.to_h).to include(
973
+ "namespace" => "my_namespace",
974
+ "action" => "my_action",
975
+ "error" => {
976
+ "name" => "StandardError",
977
+ "message" => "my_error",
978
+ "backtrace" => kind_of(String)
979
+ }
980
+ )
866
981
  end
867
982
  end
868
983
  end