diff --git a/.changesets/report-gauge-delta-value-for-gc_count.md b/.changesets/report-gauge-delta-value-for-gc_count.md new file mode 100644 index 000000000..6fd6463ff --- /dev/null +++ b/.changesets/report-gauge-delta-value-for-gc_count.md @@ -0,0 +1,6 @@ +--- +bump: "patch" +type: "change" +--- + +Report gauge delta value for Garbage Collection counts. This reports a more user friendly metric that doesn't always goes up until the app restarts or gets a new deploy. diff --git a/lib/appsignal/probes/mri.rb b/lib/appsignal/probes/mri.rb index 9dac7f765..fd236455b 100644 --- a/lib/appsignal/probes/mri.rb +++ b/lib/appsignal/probes/mri.rb @@ -46,9 +46,18 @@ def call @appsignal.set_gauge("allocated_objects", allocated_objects) end - @appsignal.add_distribution_value("gc_count", GC.count, :metric => :gc_count) - @appsignal.add_distribution_value("gc_count", gc_stats[:minor_gc_count], :metric => :minor_gc_count) - @appsignal.add_distribution_value("gc_count", gc_stats[:major_gc_count], :metric => :major_gc_count) + gc_count = gauge_delta(:gc_count, GC.count) + if gc_count + @appsignal.add_distribution_value("gc_count", gc_count, :metric => :gc_count) + end + minor_gc_count = gauge_delta(:minor_gc_count, gc_stats[:minor_gc_count]) + if minor_gc_count + @appsignal.add_distribution_value("gc_count", minor_gc_count, :metric => :minor_gc_count) + end + major_gc_count = gauge_delta(:major_gc_count, gc_stats[:major_gc_count]) + if major_gc_count + @appsignal.add_distribution_value("gc_count", major_gc_count, :metric => :major_gc_count) + end @appsignal.add_distribution_value("heap_slots", gc_stats[:heap_live_slots] || gc_stats[:heap_live_slot], :metric => :heap_live) @appsignal.add_distribution_value("heap_slots", gc_stats[:heap_free_slots] || gc_stats[:heap_free_slot], :metric => :heap_free) diff --git a/spec/lib/appsignal/probes/mri_spec.rb b/spec/lib/appsignal/probes/mri_spec.rb index 76e2b32d6..21e53f71c 100644 --- a/spec/lib/appsignal/probes/mri_spec.rb +++ b/spec/lib/appsignal/probes/mri_spec.rb @@ -49,11 +49,17 @@ def set_gauge(*args) # rubocop:disable Naming/AccessorMethodName expect_gauge_value("gc_total_time") end - it "tracks GC runs" do + it "tracks GC run count" do + expect(GC).to receive(:count).and_return(10, 15) + expect(GC).to receive(:stat).and_return( + { :minor_gc_count => 10, :major_gc_count => 10 }, + :minor_gc_count => 16, :major_gc_count => 17 + ) probe.call - expect_distribution_value("gc_count", :gc_count) - expect_distribution_value("gc_count", :major_gc_count) - expect_distribution_value("gc_count", :minor_gc_count) + probe.call + expect_distribution_value("gc_count", :gc_count, 5) + expect_distribution_value("gc_count", :minor_gc_count, 6) + expect_distribution_value("gc_count", :major_gc_count, 7) end it "tracks object allocation" do @@ -75,11 +81,15 @@ def set_gauge(*args) # rubocop:disable Naming/AccessorMethodName end end - def expect_distribution_value(expected_key, metric) + def expect_distribution_value(expected_key, metric, expected_value = nil) expect(appsignal_mock.distribution_values).to satisfy do |distribution_values| distribution_values.any? do |distribution_value| key, value, metadata = distribution_value - key == expected_key && !value.nil? && metadata == { :metric => metric } + next unless key == expected_key + next unless expected_value ? expected_value == value : !value.nil? + next unless metadata == { :metric => metric } + + true end end end