appsignal 3.0.13-java → 3.0.17-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,92 @@
1
+ module Appsignal
2
+ class Span
3
+ def initialize(namespace = nil, ext = nil)
4
+ @ext = if ext
5
+ ext
6
+ else
7
+ Appsignal::Extension::Span.root(namespace || "")
8
+ end
9
+ end
10
+
11
+ def child
12
+ Span.new(nil, @ext.child)
13
+ end
14
+
15
+ def name=(value)
16
+ @ext.set_name(value)
17
+ end
18
+
19
+ def add_error(error)
20
+ unless error.is_a?(Exception)
21
+ Appsignal.logger.error "Appsignal::Span#add_error: Cannot add error. " \
22
+ "The given value is not an exception: #{error.inspect}"
23
+ return
24
+ end
25
+ return unless error
26
+
27
+ backtrace = cleaned_backtrace(error.backtrace)
28
+ @ext.add_error(
29
+ error.class.name,
30
+ error.message.to_s,
31
+ backtrace ? Appsignal::Utils::Data.generate(backtrace) : Appsignal::Extension.data_array_new
32
+ )
33
+ end
34
+
35
+ def set_sample_data(key, data)
36
+ return unless key && data && (data.is_a?(Array) || data.is_a?(Hash))
37
+ @ext.set_sample_data(
38
+ key.to_s,
39
+ Appsignal::Utils::Data.generate(data)
40
+ )
41
+ end
42
+
43
+ def []=(key, value)
44
+ case value
45
+ when String
46
+ @ext.set_attribute_string(key.to_s, value)
47
+ when Integer
48
+ begin
49
+ @ext.set_attribute_int(key.to_s, value)
50
+ rescue RangeError
51
+ @ext.set_attribute_string(key.to_s, "bigint:#{value}")
52
+ end
53
+ when TrueClass, FalseClass
54
+ @ext.set_attribute_bool(key.to_s, value)
55
+ when Float
56
+ @ext.set_attribute_double(key.to_s, value)
57
+ else
58
+ raise TypeError, "value needs to be a string, int, bool or float"
59
+ end
60
+ end
61
+
62
+ def to_h
63
+ json = @ext.to_json
64
+ return unless json
65
+ JSON.parse(json)
66
+ end
67
+
68
+ def instrument
69
+ yield self
70
+ ensure
71
+ close
72
+ end
73
+
74
+ def close
75
+ @ext.close
76
+ end
77
+
78
+ def closed?
79
+ to_h.nil?
80
+ end
81
+
82
+ private
83
+
84
+ def cleaned_backtrace(backtrace)
85
+ if defined?(::Rails) && backtrace
86
+ ::Rails.backtrace_cleaner.clean(backtrace, nil)
87
+ else
88
+ backtrace
89
+ end
90
+ end
91
+ end
92
+ end
@@ -333,7 +333,7 @@ module Appsignal
333
333
  backtrace = cleaned_backtrace(error.backtrace)
334
334
  @ext.set_error(
335
335
  error.class.name,
336
- error.message.to_s,
336
+ cleaned_error_message(error),
337
337
  backtrace ? Appsignal::Utils::Data.generate(backtrace) : Appsignal::Extension.data_array_new
338
338
  )
339
339
  end
@@ -533,6 +533,17 @@ module Appsignal
533
533
  end
534
534
  end
535
535
 
536
+ # Clean error messages that are known to potentially contain user data.
537
+ # Returns an unchanged message otherwise.
538
+ def cleaned_error_message(error)
539
+ case error.class.to_s
540
+ when "PG::UniqueViolation"
541
+ error.message.to_s.gsub(/\)=\(.*\)/, ")=(?)")
542
+ else
543
+ error.message.to_s
544
+ end
545
+ end
546
+
536
547
  # Stub that is returned by {Transaction.current} if there is no current
537
548
  # transaction, so that it's still safe to call methods on it if there is no
538
549
  # current transaction.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.0.13".freeze
4
+ VERSION = "3.0.17".freeze
5
5
  end
data/lib/appsignal.rb CHANGED
@@ -116,12 +116,7 @@ module Appsignal
116
116
  )
117
117
 
118
118
  if config.valid?
119
- logger.level =
120
- if config[:debug]
121
- Logger::DEBUG
122
- else
123
- Logger::INFO
124
- end
119
+ logger.level = config.log_level
125
120
  if config.active?
126
121
  logger.info "Starting AppSignal #{Appsignal::VERSION} "\
127
122
  "(#{$PROGRAM_NAME}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})"
@@ -230,12 +225,11 @@ module Appsignal
230
225
  end
231
226
 
232
227
  logger.level =
233
- if config && config[:debug]
234
- Logger::DEBUG
228
+ if config
229
+ config.log_level
235
230
  else
236
- Logger::INFO
231
+ Appsignal::Config::DEFAULT_LOG_LEVEL
237
232
  end
238
-
239
233
  logger << @in_memory_log.string if @in_memory_log
240
234
  end
241
235
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  set -eu
4
4
 
5
+ LINTJE_VERSION="0.6.1"
6
+
5
7
  mkdir -p $HOME/bin
6
8
  cache_key=v1-lintje-$LINTJE_VERSION
7
9
  cache restore $cache_key
@@ -16,3 +18,5 @@ else
16
18
  tar -xz --directory $HOME/bin
17
19
  cache store $cache_key $HOME/bin/lintje
18
20
  fi
21
+
22
+ $HOME/bin/lintje $SEMAPHORE_GIT_COMMIT_RANGE
@@ -115,7 +115,6 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
115
115
  it "logs to the log file" do
116
116
  run
117
117
  log_contents = File.read(config.log_file_path)
118
- p log_contents
119
118
  expect(log_contents).to contains_log :info, "Starting AppSignal diagnose"
120
119
  end
121
120
 
@@ -192,8 +191,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
192
191
 
193
192
  it "outputs version numbers" do
194
193
  expect(output).to include \
195
- "Gem version: #{Appsignal::VERSION}",
196
- "Agent version: #{Appsignal::Extension.agent_version}"
194
+ "Gem version: \"#{Appsignal::VERSION}\"",
195
+ "Agent version: \"#{Appsignal::Extension.agent_version}\""
197
196
  end
198
197
 
199
198
  it "transmits version numbers in report" do
@@ -295,18 +294,18 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
295
294
  "Installation result",
296
295
  " Status: success",
297
296
  "Language details",
298
- " Implementation: #{jruby ? "jruby" : "ruby"}",
299
- " Ruby version: #{"#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}"}",
297
+ " Implementation: \"#{jruby ? "jruby" : "ruby"}\"",
298
+ " Ruby version: \"#{"#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}"}\"",
300
299
  "Download details",
301
- " Download URL: https://",
302
- " Checksum: verified",
300
+ " Download URL: \"https://",
301
+ " Checksum: \"verified\"",
303
302
  "Build details",
304
- " Install time: 20",
305
- " Architecture: #{Appsignal::System.agent_architecture}",
306
- " Target: #{Appsignal::System.agent_platform}",
303
+ " Install time: \"20",
304
+ " Architecture: \"#{Appsignal::System.agent_architecture}\"",
305
+ " Target: \"#{Appsignal::System.agent_platform}\"",
307
306
  " Musl override: false",
308
307
  " Linux ARM override: false",
309
- " Library type: #{jruby ? "dynamic" : "static"}",
308
+ " Library type: \"#{jruby ? "dynamic" : "static"}\"",
310
309
  " Dependencies: {",
311
310
  " Flags: {",
312
311
  "Host details",
@@ -611,9 +610,9 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
611
610
  run
612
611
  expect(output).to include \
613
612
  "Host information",
614
- "Architecture: #{rbconfig["host_cpu"]}",
615
- "Operating System: #{rbconfig["host_os"]}",
616
- "Ruby version: #{language_version}"
613
+ "Architecture: \"#{rbconfig["host_cpu"]}\"",
614
+ "Operating System: \"#{rbconfig["host_os"]}\"",
615
+ "Ruby version: \"#{language_version}\""
617
616
  end
618
617
 
619
618
  context "when on Microsoft Windows" do
@@ -751,13 +750,13 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
751
750
  end
752
751
 
753
752
  it "outputs a warning that no config is loaded" do
754
- expect(output).to include "Environment: \"\"\n#{warning_message}"
753
+ expect(output).to include "environment: \"\"\n#{warning_message}"
755
754
  expect(output).to_not have_color_markers
756
755
  end
757
756
 
758
757
  context "with color", :color => true do
759
758
  it "outputs a warning that no config is loaded in color" do
760
- expect(output).to include "Environment: \"\"\n"
759
+ expect(output).to include "environment: \"\"\n"
761
760
  expect(output).to have_colorized_text :red, warning_message
762
761
  end
763
762
  end
@@ -786,7 +785,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
786
785
  describe "environment" do
787
786
  it "outputs environment" do
788
787
  run
789
- expect(output).to include(%(Environment: "production"))
788
+ expect(output).to include(%(environment: "production"))
790
789
  end
791
790
 
792
791
  context "when the source is a single source" do
@@ -794,7 +793,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
794
793
 
795
794
  it "outputs the label source after the value" do
796
795
  expect(output).to include(
797
- %(Environment: "#{Appsignal.config.env}" (Loaded from: initial)\n)
796
+ %(environment: "#{Appsignal.config.env}" (Loaded from: initial)\n)
798
797
  )
799
798
  end
800
799
  end
@@ -811,7 +810,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
811
810
 
812
811
  it "outputs a list of sources with their values" do
813
812
  expect(output).to include(
814
- %( Environment: "production"\n) +
813
+ %( environment: "production"\n) +
815
814
  %( Sources:\n) +
816
815
  %( initial: "development"\n) +
817
816
  %( env: "production"\n)
@@ -905,7 +904,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
905
904
  before { run_within_dir tmp_dir }
906
905
 
907
906
  it "outputs environment" do
908
- expect(output).to include(%(Environment: "foobar"))
907
+ expect(output).to include(%(environment: "foobar"))
909
908
  end
910
909
 
911
910
  it "outputs config defaults" do
@@ -1008,7 +1007,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
1008
1007
 
1009
1008
  it "outputs failure with status code" do
1010
1009
  expect(output).to include "Validation",
1011
- "Validating Push API key: Failed with status 500\n" +
1010
+ "Validating Push API key: Failed to validate: status 500\n" +
1012
1011
  %("Could not confirm authorization: 500")
1013
1012
  end
1014
1013
 
@@ -1016,14 +1015,19 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
1016
1015
  it "outputs error in color" do
1017
1016
  expect(output).to include "Validation",
1018
1017
  "Validating Push API key: " +
1019
- colorize(%(Failed with status 500\n"Could not confirm authorization: 500"), :red)
1018
+ colorize(
1019
+ "Failed to validate: status 500\n" +
1020
+ %("Could not confirm authorization: 500"),
1021
+ :red
1022
+ )
1020
1023
  end
1021
1024
  end
1022
1025
 
1023
1026
  it "transmits validation in report" do
1024
1027
  expect(received_report).to include(
1025
1028
  "validation" => {
1026
- "push_api_key" => %(Failed with status 500\n\"Could not confirm authorization: 500")
1029
+ "push_api_key" => "Failed to validate: status 500\n" +
1030
+ %("Could not confirm authorization: 500")
1027
1031
  }
1028
1032
  )
1029
1033
  end
@@ -151,33 +151,34 @@ describe Appsignal::Config do
151
151
 
152
152
  it "merges with the default config" do
153
153
  expect(config.config_hash).to eq(
154
+ :active => true,
155
+ :ca_file_path => File.join(resources_dir, "cacert.pem"),
154
156
  :debug => false,
155
- :log => "file",
157
+ :dns_servers => [],
158
+ :enable_allocation_tracking => true,
159
+ :enable_gc_instrumentation => false,
160
+ :enable_host_metrics => true,
161
+ :enable_minutely_probes => true,
162
+ :enable_statsd => true,
163
+ :endpoint => "https://push.appsignal.com",
164
+ :files_world_accessible => true,
165
+ :filter_parameters => [],
166
+ :filter_session_data => [],
156
167
  :ignore_actions => [],
157
168
  :ignore_errors => [],
158
169
  :ignore_namespaces => [],
159
- :filter_parameters => [],
160
- :filter_session_data => [],
161
170
  :instrument_net_http => true,
162
171
  :instrument_redis => true,
163
172
  :instrument_sequel => true,
164
- :skip_session_data => false,
165
- :send_environment_metadata => true,
166
- :send_params => true,
167
- :endpoint => "https://push.appsignal.com",
168
- :push_api_key => "abc",
173
+ :log => "file",
169
174
  :name => "TestApp",
170
- :active => true,
171
- :enable_allocation_tracking => true,
172
- :enable_gc_instrumentation => false,
173
- :enable_host_metrics => true,
174
- :enable_minutely_probes => true,
175
- :ca_file_path => File.join(resources_dir, "cacert.pem"),
176
- :dns_servers => [],
177
- :files_world_accessible => true,
178
- :transaction_debug_mode => false,
175
+ :push_api_key => "abc",
176
+ :request_headers => [],
179
177
  :revision => "v2.5.1",
180
- :request_headers => []
178
+ :send_environment_metadata => true,
179
+ :send_params => true,
180
+ :skip_session_data => false,
181
+ :transaction_debug_mode => false
181
182
  )
182
183
  end
183
184
 
@@ -318,13 +319,15 @@ describe Appsignal::Config do
318
319
  "non-existing-path",
319
320
  "production",
320
321
  :running_in_container => true,
321
- :debug => true
322
+ :debug => true,
323
+ :log_level => "debug"
322
324
  )
323
325
  end
324
326
 
325
327
  it "overrides system detected and defaults config" do
326
328
  expect(config[:running_in_container]).to be_truthy
327
329
  expect(config[:debug]).to be_truthy
330
+ expect(config[:log_level]).to eq("debug")
328
331
  end
329
332
  end
330
333
 
@@ -501,6 +504,7 @@ describe Appsignal::Config do
501
504
  config[:log] = "stdout"
502
505
  config[:log_path] = "/tmp"
503
506
  config[:filter_parameters] = %w[password confirm_password]
507
+ config[:filter_session_data] = %w[key1 key2]
504
508
  config[:running_in_container] = false
505
509
  config[:dns_servers] = ["8.8.8.8", "8.8.4.4"]
506
510
  config[:transaction_debug_mode] = true
@@ -534,6 +538,8 @@ describe Appsignal::Config do
534
538
  expect(ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"]).to eq "true"
535
539
  expect(ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"]).to eq "true"
536
540
  expect(ENV["_APPSIGNAL_SEND_ENVIRONMENT_METADATA"]).to eq "false"
541
+ expect(ENV["_APPSIGNAL_FILTER_PARAMETERS"]).to eq "password,confirm_password"
542
+ expect(ENV["_APPSIGNAL_FILTER_SESSION_DATA"]).to eq "key1,key2"
537
543
  expect(ENV["_APP_REVISION"]).to eq "v2.5.1"
538
544
  expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIR_PATH")
539
545
  expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIRECTORY_PATH")
@@ -780,4 +786,83 @@ describe Appsignal::Config do
780
786
  end
781
787
  end
782
788
  end
789
+
790
+ describe "#log_level" do
791
+ let(:options) { {} }
792
+ let(:config) { described_class.new("", nil, options) }
793
+ subject { config.log_level }
794
+
795
+ context "without any config" do
796
+ it "returns info by default" do
797
+ is_expected.to eq(Logger::INFO)
798
+ end
799
+ end
800
+
801
+ context "with debug set to true" do
802
+ let(:options) { { :debug => true } }
803
+ it { is_expected.to eq(Logger::DEBUG) }
804
+ end
805
+
806
+ context "with transaction_debug_mode set to true" do
807
+ let(:options) { { :transaction_debug_mode => true } }
808
+ it { is_expected.to eq(Logger::DEBUG) }
809
+ end
810
+
811
+ context "with log_level set to error" do
812
+ let(:options) { { :log_level => "error" } }
813
+ it { is_expected.to eq(Logger::ERROR) }
814
+ end
815
+
816
+ context "with log_level set to warn" do
817
+ let(:options) { { :log_level => "warn" } }
818
+ it { is_expected.to eq(Logger::WARN) }
819
+ end
820
+
821
+ context "with log_level set to info" do
822
+ let(:options) { { :log_level => "info" } }
823
+ it { is_expected.to eq(Logger::INFO) }
824
+ end
825
+
826
+ context "with log_level set to debug" do
827
+ let(:options) { { :log_level => "debug" } }
828
+ it { is_expected.to eq(Logger::DEBUG) }
829
+ end
830
+
831
+ context "with log_level set to trace" do
832
+ let(:options) { { :log_level => "trace" } }
833
+ it { is_expected.to eq(Logger::DEBUG) }
834
+ end
835
+
836
+ context "with debug and log_level set" do
837
+ let(:options) { { :log_level => "error", :debug => true } }
838
+
839
+ it "the log_level option is leading" do
840
+ is_expected.to eq(Logger::ERROR)
841
+ end
842
+ end
843
+
844
+ context "with transaction_debug_mode and log_level set" do
845
+ let(:options) { { :log_level => "error", :transaction_debug_mode => true } }
846
+
847
+ it "the log_level option is leading" do
848
+ is_expected.to eq(Logger::ERROR)
849
+ end
850
+ end
851
+
852
+ context "with log level set to an unknown value" do
853
+ let(:options) { { :log_level => "fatal" } }
854
+
855
+ it "prints a warning and doesn't use the log_level" do
856
+ is_expected.to eql(Logger::INFO)
857
+ end
858
+
859
+ context "with debug option set to true" do
860
+ let(:options) { { :log_level => "fatal", :debug => true } }
861
+
862
+ it "prints a warning and sets it to debug" do
863
+ is_expected.to eql(Logger::DEBUG)
864
+ end
865
+ end
866
+ end
867
+ end
783
868
  end
@@ -0,0 +1,23 @@
1
+ describe Appsignal::Hooks::MriHook do
2
+ describe "#dependencies_present?" do
3
+ subject { described_class.new.dependencies_present? }
4
+
5
+ if DependencyHelper.running_jruby?
6
+ it { is_expected.to be_falsy }
7
+ else
8
+ it { is_expected.to be_truthy }
9
+ end
10
+ end
11
+
12
+ unless DependencyHelper.running_jruby?
13
+ context "install" do
14
+ before do
15
+ Appsignal::Hooks.load_hooks
16
+ end
17
+
18
+ it "should be added to minutely probes" do
19
+ expect(Appsignal::Minutely.probes[:mri]).to be Appsignal::Probes::MriProbe
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ describe Appsignal::Probes::MriProbe do
2
+ let(:probe) { described_class.new }
3
+
4
+ describe ".dependencies_present?" do
5
+ if DependencyHelper.running_jruby? || DependencyHelper.running_ruby_2_0?
6
+ it "should not be present" do
7
+ expect(described_class.dependencies_present?).to be_falsy
8
+ end
9
+ else
10
+ it "should be present" do
11
+ expect(described_class.dependencies_present?).to be_truthy
12
+ end
13
+ end
14
+ end
15
+
16
+ unless DependencyHelper.running_jruby? || DependencyHelper.running_ruby_2_0?
17
+ describe "#call" do
18
+ it "should track vm metrics" do
19
+ expect_distribution_value(:class_serial)
20
+ expect_distribution_value(:global_constant_state)
21
+
22
+ probe.call
23
+ end
24
+ end
25
+
26
+ def expect_distribution_value(metric)
27
+ expect(Appsignal).to receive(:add_distribution_value)
28
+ .with("ruby_vm", kind_of(Numeric), :metric => metric)
29
+ .and_call_original
30
+ .once
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,141 @@
1
+ require "appsignal/span"
2
+
3
+ describe Appsignal::Span do
4
+ before :context do
5
+ start_agent
6
+ end
7
+
8
+ let(:namespace) { "web" }
9
+ let(:root) { Appsignal::Span.new(namespace) }
10
+
11
+ describe "creating a span" do
12
+ it "creates an empty span" do
13
+ expect(root.to_h["namespace"]).to eq "web"
14
+ expect(root.to_h["trace_id"].length).to eq 16
15
+ expect(root.to_h["span_id"].length).to eq 8
16
+ expect(root.to_h["parent_span_id"]).to be_empty
17
+ expect(root.to_h["name"]).to be_empty
18
+ expect(root.to_h["start_time"]).to be > 1_600_000_000
19
+ expect(root.to_h["closed"]).to be false
20
+ end
21
+ end
22
+
23
+ describe "#child" do
24
+ let(:child) { root.child }
25
+
26
+ it "creates a child span" do
27
+ expect(child.to_h["namespace"]).to be_empty
28
+ expect(child.to_h["trace_id"].length).to eq 16
29
+ expect(child.to_h["span_id"].length).to eq 8
30
+ expect(child.to_h["parent_span_id"]).to eq root.to_h["span_id"]
31
+ expect(child.to_h["name"]).to be_empty
32
+ expect(child.to_h["start_time"]).to be > 1_600_000_000
33
+ expect(child.to_h["closed"]).to be false
34
+ end
35
+ end
36
+
37
+ describe "#add_error" do
38
+ it "adds an error" do
39
+ begin
40
+ raise "Error"
41
+ rescue => error
42
+ root.add_error(error)
43
+ end
44
+
45
+ error = root.to_h["error"]
46
+ expect(error["name"]).to eq "RuntimeError"
47
+ expect(error["message"]).to eq "Error"
48
+ expect(error["backtrace"]).not_to be_empty
49
+ end
50
+ end
51
+
52
+ describe "set_sample_data" do
53
+ it "sets sample data" do
54
+ root.set_sample_data(:params, "key" => "value")
55
+
56
+ sample_data = root.to_h["sample_data"]
57
+ expect(sample_data["params"]).to eq "{\"key\":\"value\"}"
58
+ end
59
+ end
60
+
61
+ describe "#name=" do
62
+ it "sets the name" do
63
+ root.name = "Span name"
64
+
65
+ expect(root.to_h["name"]).to eq "Span name"
66
+ end
67
+ end
68
+
69
+ describe "#[]=" do
70
+ let(:attributes) { root.to_h["attributes"] }
71
+
72
+ it "sets a string attribute" do
73
+ root["string"] = "attribute"
74
+
75
+ expect(attributes["string"]).to eq "attribute"
76
+ end
77
+
78
+ it "sets an integer attribute" do
79
+ root["integer"] = 1001
80
+
81
+ expect(attributes["integer"]).to eq 1001
82
+ end
83
+
84
+ it "sets a bigint attribute" do
85
+ root["bigint"] = 1 << 64
86
+
87
+ expect(attributes["bigint"]).to eq "bigint:#{1 << 64}"
88
+ end
89
+
90
+ it "sets a boolean attribute" do
91
+ root["true"] = true
92
+ root["false"] = false
93
+
94
+ expect(attributes["true"]).to eq true
95
+ expect(attributes["false"]).to eq false
96
+ end
97
+
98
+ it "sets a float attribute" do
99
+ root["float"] = 10.01
100
+
101
+ expect(attributes["float"]).to eq 10.01
102
+ end
103
+
104
+ it "raises an error for other types" do
105
+ expect do
106
+ root["something"] = Object.new
107
+ end.to raise_error TypeError
108
+ end
109
+ end
110
+
111
+ describe "#instrument" do
112
+ it "closes the span after yielding" do
113
+ root.instrument do
114
+ # Nothing happening
115
+ end
116
+ expect(root.closed?).to eq true
117
+ end
118
+
119
+ context "with an error raised in the passed block" do
120
+ it "closes the span after yielding" do
121
+ expect do
122
+ root.instrument do
123
+ raise ExampleException, "foo"
124
+ end
125
+ end.to raise_error(ExampleException, "foo")
126
+ expect(root.closed?).to eq true
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "#close" do
132
+ it "closes a span" do
133
+ expect(root.closed?).to eq false
134
+
135
+ root.close
136
+
137
+ expect(root.to_h).to be_nil
138
+ expect(root.closed?).to eq true
139
+ end
140
+ end
141
+ end