Skip to content
This repository has been archived by the owner on Nov 4, 2020. It is now read-only.

Commit

Permalink
Add GC stats to the api
Browse files Browse the repository at this point in the history
This PR introduce GC stats in the form of `collection_time` and `collection_count`, it takes the configured gc and categorize them
into old and young.

Example output:

```
gc: {
  collectors: {
    young: {
      collection_time_in_millis: 22101,
      collection_count: 5452
    },
    old: {
      collection_time_in_millis: 57,
      collection_count: 1
    }
  }
}
```

Fixes: elastic#5730

Fixes elastic#5788
  • Loading branch information
ph committed Aug 22, 2016
1 parent cf3a424 commit 5d4a3ae
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 22 deletions.
7 changes: 6 additions & 1 deletion logstash-core/lib/logstash/api/commands/stats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def jvm
:count,
:peak_count
),
:mem => memory
:mem => memory,
:gc => gc
}
end

Expand Down Expand Up @@ -59,6 +60,10 @@ def memory
}
end

def gc
service.get_shallow(:jvm, :gc)
end

def hot_threads(options={})
HotThreadsReport.new(self, options)
end
Expand Down
2 changes: 1 addition & 1 deletion logstash-core/lib/logstash/api/modules/node_stats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class NodeStats < ::LogStash::Api::Modules::Base
payload = {
:jvm => jvm_payload,
:process => process_payload,
:pipeline => pipeline_payload
:pipeline => pipeline_payload,
}
respond_with(payload, {:filter => params["filter"]})
end
Expand Down
53 changes: 43 additions & 10 deletions logstash-core/lib/logstash/instrument/periodic_poller/jvm.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@

# encoding: utf-8
require "logstash/instrument/periodic_poller/base"
require 'jrmonitor'
require "jrmonitor"
require "set"

java_import 'java.lang.management.ManagementFactory'
java_import 'java.lang.management.OperatingSystemMXBean'
java_import 'java.lang.management.GarbageCollectorMXBean'
java_import 'com.sun.management.UnixOperatingSystemMXBean'
java_import 'javax.management.MBeanServer'
java_import 'javax.management.ObjectName'
java_import 'javax.management.AttributeList'
java_import 'javax.naming.directory.Attribute'


module LogStash module Instrument module PeriodicPoller
class JVM < Base
class GarbageCollectorName
YOUNG_GC_NAMES = Set.new(["Copy", "PS Scavenge", "ParNew", "G1 Young Generation"])
OLD_GC_NAMES = Set.new(["MarkSweepCompact", "PS MarkSweep", "ConcurrentMarkSweep", "G1 Old Generation"])

YOUNG = :young
OLD = :old

def self.get(gc_name)
if YOUNG_GC_NAMES.include?(gc_name)
YOUNG
elsif(OLD_GC_NAMES.include?(gc_name))
OLD
end
end
end

attr_reader :metric

Expand All @@ -22,31 +40,46 @@ def initialize(metric, options = {})
end

def collect
raw = JRMonitor.memory.generate
raw = JRMonitor.memory.generate
collect_heap_metrics(raw)
collect_non_heap_metrics(raw)
collect_pools_metrics(raw)
collect_threads_metrics
collect_process_metrics
collect_gc_stats
end

private

def collect_threads_metrics
def collect_gc_stats
garbage_collectors = ManagementFactory.getGarbageCollectorMXBeans()

garbage_collectors.each do |collector|
name = GarbageCollectorName.get(collector.getName())
if name.nil?
logger.error("Unknown garbage collector name", :name => name)
else
metric.gauge([:jvm, :gc, :collectors, name], :collection_count, collector.getCollectionCount())
metric.gauge([:jvm, :gc, :collectors, name], :collection_time_in_millis, collector.getCollectionTime())
end
end
end

def collect_threads_metrics
threads = JRMonitor.threads.generate

current = threads.count
if @peak_threads.nil? || @peak_threads < current
@peak_threads = current
end
metric.gauge([:jvm, :threads], :count, threads.count)
end

metric.gauge([:jvm, :threads], :count, threads.count)
metric.gauge([:jvm, :threads], :peak_count, @peak_threads)
end

def collect_process_metrics
process_metrics = JRMonitor.process.generate

path = [:jvm, :process]


Expand Down Expand Up @@ -91,6 +124,7 @@ def collect_pools_metrics(data)
end
end


def build_pools_metrics(data)
heap = data["heap"]
old = {}
Expand Down Expand Up @@ -129,9 +163,8 @@ def default_information_accumulator
:committed_in_bytes => 0,
:max_in_bytes => 0,
:peak_used_in_bytes => 0,
:peak_max_in_bytes => 0
:peak_max_in_bytes => 0
}
end

end
end; end; end
12 changes: 12 additions & 0 deletions logstash-core/spec/api/lib/api/node_stats_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
"count"=>Numeric,
"peak_count"=>Numeric
},
"gc" => {
"collectors" => {
"young" => {
"collection_count" => Numeric,
"collection_time_in_millis" => Numeric
},
"old" => {
"collection_count" => Numeric,
"collection_time_in_millis" => Numeric
}
}
},
"mem" => {
"heap_used_in_bytes" => Numeric,
"heap_used_percent" => Numeric,
Expand Down
47 changes: 37 additions & 10 deletions logstash-core/spec/logstash/instrument/periodic_poller/jvm_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,67 @@
require "logstash/instrument/periodic_poller/jvm"
require "logstash/instrument/collector"

describe LogStash::Instrument::PeriodicPoller::JVM::GarbageCollectorName do
subject { LogStash::Instrument::PeriodicPoller::JVM::GarbageCollectorName }

context "when the gc is of young type" do
LogStash::Instrument::PeriodicPoller::JVM::GarbageCollectorName::YOUNG_GC_NAMES.each do |name|
it "returns young for #{name}" do
expect(subject.get(name)).to eq(:young)
end
end
end

context "when the gc is of old type" do
LogStash::Instrument::PeriodicPoller::JVM::GarbageCollectorName::OLD_GC_NAMES.each do |name|
it "returns old for #{name}" do
expect(subject.get(name)).to eq(:old)
end
end
end

it "returns `nil` when we dont know the gc name" do
expect(subject.get("UNKNOWN GC")).to be_nil
end
end

describe LogStash::Instrument::PeriodicPoller::JVM do
let(:metric) { LogStash::Instrument::Metric.new(LogStash::Instrument::Collector.new) }
let(:options) { {} }
subject(:jvm) { described_class.new(metric, options) }

it "should initialize cleanly" do
expect { jvm }.not_to raise_error
end

describe "collections" do
subject(:collection) { jvm.collect }

it "should run cleanly" do
expect { collection }.not_to raise_error
end

describe "metrics" do
before(:each) { jvm.collect }
let(:snapshot_store) { metric.collector.snapshot_metric.metric_store }
subject(:jvm_metrics) { snapshot_store.get_shallow(:jvm, :process) }
subject(:jvm_metrics) { snapshot_store.get_shallow(:jvm) }

# Make looking up metric paths easy when given varargs of keys
# e.g. mval(:parent, :child)
def mval(*metric_path)
metric_path.reduce(jvm_metrics) {|acc,k| acc[k]}.value
end
end

[
:max_file_descriptors,
:open_file_descriptors,
:peak_open_file_descriptors,
[:mem, :total_virtual_in_bytes],
[:cpu, :total_in_millis],
[:cpu, :percent]
[:process, :max_file_descriptors],
[:process, :open_file_descriptors],
[:process, :peak_open_file_descriptors],
[:process, :mem, :total_virtual_in_bytes],
[:process, :cpu, :total_in_millis],
[:process, :cpu, :percent],
[:gc, :collectors, :young, :collection_count],
[:gc, :collectors, :young, :collection_time_in_millis],
[:gc, :collectors, :old, :collection_count],
[:gc, :collectors, :old, :collection_time_in_millis]
].each do |path|
path = Array(path)
it "should have a value for #{path} that is Numeric" do
Expand Down

0 comments on commit 5d4a3ae

Please sign in to comment.