Skip to content

Commit

Permalink
Report hostname tag for Ruby VM probe metrics
Browse files Browse the repository at this point in the history
The added hostname tag allows us to tell multiple hosts from each other
and graph them separately. If we do not, an app with multiple hosts
would have hosts overwrite one another's metrics. Only the last reported
value would be stored and shown.

I've copied some of the Sidekiq probe's and Puma plugin to determine the
hostname automatically and listen to the AppSignal config in case a
hostname was configured.
  • Loading branch information
tombruijn committed Jul 28, 2022
1 parent 33b1cef commit 31fd19c
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .changesets/track-hostname-tag-for-ruby-vm-metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
bump: "patch"
type: "fix"
---

Add hostname tag for Ruby VM metrics. This allows us to graph every host separately and multiple hosts won't overwrite each other metrics.
19 changes: 18 additions & 1 deletion lib/appsignal/probes/mri.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,24 @@ def call
private

def set_gauge(metric, value, tags = {})
@appsignal.set_gauge(metric, value, tags)
@appsignal.set_gauge(metric, value, { :hostname => hostname }.merge(tags))
end

def hostname
return @hostname if defined?(@hostname)

config = @appsignal.config
@hostname =
if config[:hostname]
config[:hostname]
else
# Auto detect hostname as fallback. May be inaccurate.
Socket.gethostname
end
Appsignal.logger.debug "MRI probe: Using hostname config " \
"option '#{@hostname.inspect}' as hostname"

@hostname
end
end
end
Expand Down
28 changes: 23 additions & 5 deletions spec/lib/appsignal/probes/mri_spec.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
class AppsignalMock
attr_reader :gauges

def initialize
def initialize(hostname: nil)
@hostname = hostname
@gauges = []
end

def config
ConfigHelpers.project_fixture_config.tap do |conf|
conf[:hostname] = @hostname if @hostname
end
end

def set_gauge(*args) # rubocop:disable Naming/AccessorMethodName
@gauges << args
end
end

describe Appsignal::Probes::MriProbe do
let(:appsignal_mock) { AppsignalMock.new }
let(:appsignal_mock) { AppsignalMock.new(:hostname => hostname) }
let(:probe) { described_class.new(appsignal_mock) }

describe ".dependencies_present?" do
Expand All @@ -28,6 +35,8 @@ def set_gauge(*args) # rubocop:disable Naming/AccessorMethodName

unless DependencyHelper.running_jruby? || DependencyHelper.running_ruby_2_0?
describe "#call" do
let(:hostname) { nil }

it "should track vm metrics" do
probe.call
expect_gauge_value("ruby_vm", :tags => { :metric => :class_serial })
Expand Down Expand Up @@ -73,17 +82,26 @@ def set_gauge(*args) # rubocop:disable Naming/AccessorMethodName
expect_gauge_value("heap_slots", :tags => { :metric => :heap_live })
expect_gauge_value("heap_slots", :tags => { :metric => :heap_free })
end

context "with custom hostname" do
let(:hostname) { "my hostname" }

it "reports custom hostname tag value" do
probe.call
expect_gauge_value("heap_slots", :tags => { :metric => :heap_live, :hostname => hostname })
end
end
end
end

def expect_gauge_value(expected_key, expected_value = nil, tags: nil)
expected_tags = tags
def expect_gauge_value(expected_key, expected_value = nil, tags: {})
expected_tags = { :hostname => Socket.gethostname }.merge(tags)
expect(appsignal_mock.gauges).to satisfy do |gauges|
gauges.any? do |distribution_value|
key, value, tags = distribution_value
next unless key == expected_key
next unless expected_value ? expected_value == value : !value.nil?
next if tags && tags != expected_tags
next unless tags == expected_tags

true
end
Expand Down
1 change: 1 addition & 0 deletions spec/support/helpers/config_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def project_fixture_config(env = "production", initial_config = {}, logger = App
config_file
)
end
module_function :project_fixture_config, :project_fixture_path

def start_agent(env = "production")
Appsignal.config = project_fixture_config(env)
Expand Down

0 comments on commit 31fd19c

Please sign in to comment.