barnes 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f7dd4e395ed1fe8dd518e79a0cc8b99029074d2c0eae7c775b198a5e4e142f91
4
+ data.tar.gz: d1b447e714af1bd2893e24f7d659c641a2bb6481049c636856241dae280b1b21
5
+ SHA512:
6
+ metadata.gz: 452478b7f41883a87f5564b98dc15bf5ea3fffeed42ed1ebb65ba4e5703aef0e3666c47446ff07edc774619d00e284d8de0b8e6159bfad543cc3dbac485fd6fd
7
+ data.tar.gz: 7010aec4b79e73221b848519077f220da2d30a8fd29a9de7164c89f72665f251ad9c5b0306d657fb528ef94b7d98e04a505bea6c1e0e4d6f94f33a2572819a39
@@ -0,0 +1 @@
1
+ require 'barnes'
@@ -0,0 +1,40 @@
1
+ module Barnes
2
+ DEFAULT_INTERVAL = 10
3
+ DEFAULT_AGGREGATION_PERIOD = 60
4
+ DEFAULT_STATSD = :default
5
+ DEFAULT_PANELS = []
6
+
7
+
8
+ # Starts the reporting client
9
+ #
10
+ # Arguments:
11
+ #
12
+ # - interval: How often, in seconds, to instrument and report
13
+ # - aggregation_period: The minimal aggregation period in use, in seconds.
14
+ # - statsd: The statsd reporter. This should be an instance of statsd-ruby
15
+ # - panels: The instrumentation "panels" in use. See `resource_usage.rb` for
16
+ # an example panel, which is the default if none are provided.
17
+ def self.start(interval: DEFAULT_INTERVAL, aggregation_period: DEFAULT_AGGREGATION_PERIOD, statsd: DEFAULT_STATSD, panels: DEFAULT_PANELS)
18
+ require 'statsd'
19
+ statsd_client = statsd
20
+ panels = panels
21
+ sample_rate = interval.to_f / aggregation_period.to_f
22
+
23
+ if statsd_client == :default && ENV["PORT"]
24
+ statsd_client = Statsd.new('127.0.0.1', ENV["PORT"])
25
+ end
26
+
27
+ if statsd_client && statsd_client != :default
28
+ reporter = Barnes::Reporter.new(statsd: statsd_client, sample_rate: sample_rate)
29
+
30
+ unless panels.length > 0
31
+ panels << Barnes::ResourceUsage.new(sample_rate)
32
+ end
33
+
34
+ Periodic.new reporter: reporter, sample_rate: sample_rate, panels: panels
35
+ end
36
+ end
37
+ end
38
+
39
+ require 'barnes/periodic'
40
+ require 'barnes/railtie' if defined? ::Rails::Railtie
@@ -0,0 +1,3 @@
1
+ module Barnes
2
+ STATE, COUNTERS, GAUGES = 'barnes.state', 'barnes.counters', 'barnes.gauges'
3
+ end
@@ -0,0 +1,30 @@
1
+ module Barnes
2
+ module Instruments
3
+ # Tracks out of band GCs that occurred *since* the last request.
4
+ class GctoolsOobgc
5
+ def start!(state)
6
+ state[:oobgc] = current
7
+ end
8
+
9
+ def instrument!(state, counters, gauges)
10
+ last = state[:oobgc]
11
+ cur = state[:oobgc] = current
12
+
13
+ counters.update \
14
+ :'OOBGC.count' => cur[:count] - last[:count],
15
+ :'OOBGC.major_count' => cur[:major] - last[:major],
16
+ :'OOBGC.minor_count' => cur[:minor] - last[:minor],
17
+ :'OOBGC.sweep_count' => cur[:sweep] - last[:sweep]
18
+ end
19
+
20
+ private def current
21
+ {
22
+ :count => GC::OOB.stat(:count).to_i,
23
+ :major => GC::OOB.stat(:major).to_i,
24
+ :minor => GC::OOB.stat(:minor).to_i,
25
+ :sweep => GC::OOB.stat(:sweep).to_i
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module Barnes
2
+ module Instruments
3
+ class ObjectSpaceCounter
4
+ def instrument!(state, counters, gauges)
5
+ ObjectSpace.count_objects.each do |type, count|
6
+ gauges[:"Objects.#{type}"] = count
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ module Barnes
2
+ module Instruments
3
+ class Ruby18GC
4
+ def initialize
5
+ GC.enable_stats
6
+ end
7
+
8
+ def start!(state)
9
+ state[:ruby18_gc] = current
10
+ end
11
+
12
+ def instrument!(state, counters, gauges)
13
+ last = state[:ruby18_gc]
14
+ cur = state[:ruby18_gc] = current
15
+
16
+ counters.update \
17
+ :'GC.count' => cur[:gc_count] - before[:gc_count],
18
+ :'GC.time' => cur[:gc_time] - before[:gc_time],
19
+ :'GC.memory' => cur[:gc_memory] - before[:gc_memory],
20
+ :'GC.allocated_objects' => cur[:objects] - before[:objects]
21
+
22
+ gauges[:'Objects.live'] = ObjectSpace.live_objects
23
+ gauges[:'GC.growth'] = GC.growth
24
+ end
25
+
26
+ private def current
27
+ {
28
+ :objects => ObjectSpace.allocated_objects,
29
+ :gc_count => GC.collections,
30
+ :gc_time => GC.time,
31
+ :gc_memory => GC.allocated_size
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,87 @@
1
+ # A note on GAUGE_COUNTERS.
2
+ #
3
+ # The sample_rate argument allows for the parameterization
4
+ # of instruments that decide to report data as gauges, that
5
+ # would typically be reported as counters.
6
+ #
7
+ # Aggregating counters is typically done simply with the `+`
8
+ # operator, which doesn't preserve the number of unique
9
+ # reporters that contributed to the count, or allow for one
10
+ # to learn the *average* of the counts posted.
11
+ #
12
+ # A gauge is typically aggregated by simply *replacing* the
13
+ # previous value, however, some systems do *more* with gauges
14
+ # when aggregating across multiple sources of that gauge, like,
15
+ # average, or compute stdev.
16
+ #
17
+ # This is problematic, however, when a gauge is being used as
18
+ # a counter, to preserve the average / stdev computational
19
+ # properties from above, because the interval that the gauge
20
+ # is being read it, affects the derivative of the increasing
21
+ # count. Instead of the derivative over 60s, the derivative is
22
+ # taken every 10s, giving us a derivative value that's approximately
23
+ # 1/6th of the actual derivative over 60s.
24
+ #
25
+ # We compensate for this by allowing Instruments to correct for
26
+ # this, and ensure that, even though it's an estimate, the data
27
+ # is scaled appropriately to the target aggregation interval, not
28
+ # just the collection interval.
29
+
30
+ module Barnes
31
+ module Instruments
32
+ class RubyGC
33
+ COUNTERS = {
34
+ :count => :'GC.count',
35
+ :major_gc_count => :'GC.major_count',
36
+ :minor_gc_count => :'GC.minor_gc_count' }
37
+
38
+ GAUGE_COUNTERS = {}
39
+
40
+ # Detect Ruby 2.1 vs 2.2 GC.stat naming
41
+ begin
42
+ GC.stat :total_allocated_objects
43
+ rescue ArgumentError
44
+ GAUGE_COUNTERS.update \
45
+ :total_allocated_object => :'GC.total_allocated_objects',
46
+ :total_freed_object => :'GC.total_freed_objects'
47
+ else
48
+ GAUGE_COUNTERS.update \
49
+ :total_allocated_objects => :'GC.total_allocated_objects',
50
+ :total_freed_objects => :'GC.total_freed_objects'
51
+ end
52
+
53
+ def initialize(sample_rate)
54
+ # see header for an explanation of how this sample_rate is used
55
+ @sample_rate = sample_rate
56
+ end
57
+
58
+ def start!(state)
59
+ state[:ruby_gc] = GC.stat
60
+ end
61
+
62
+ def instrument!(state, counters, gauges)
63
+ last = state[:ruby_gc]
64
+ cur = state[:ruby_gc] = GC.stat
65
+
66
+ COUNTERS.each do |stat, metric|
67
+ counters[metric] = cur[stat] - last[stat] if cur.include? stat
68
+ end
69
+
70
+ # special treatment gauges
71
+ GAUGE_COUNTERS.each do |stat, metric|
72
+ if cur.include? stat
73
+ val = cur[stat] - last[stat] if cur.include? stat
74
+ gauges[metric] = val * (1/@sample_rate)
75
+ end
76
+ end
77
+
78
+ # the rest of the gauges
79
+ cur.each do |k, v|
80
+ unless GAUGE_COUNTERS.include? k
81
+ gauges[:"GC.#{k}"] = v
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,65 @@
1
+ module Barnes
2
+ module Instruments
3
+ class Stopwatch
4
+ def initialize(timepiece = Timepiece)
5
+ @timepiece = timepiece
6
+ @has_cpu_time = timepiece.respond_to?(:cpu)
7
+ end
8
+
9
+ def start!(state)
10
+ state[:stopwatch] = current
11
+ end
12
+
13
+ def instrument!(state, counters, gauges)
14
+ last = state[:stopwatch]
15
+ wall_elapsed = @timepiece.wall - last[:wall]
16
+ counters[:'Time.wall'] = wall_elapsed
17
+
18
+ if @has_cpu_time
19
+ cpu_elapsed = @timepiece.cpu - last[:cpu]
20
+ idle_elapsed = wall_elapsed - cpu_elapsed
21
+
22
+ counters[:'Time.cpu'] = cpu_elapsed
23
+ counters[:'Time.idle'] = idle_elapsed
24
+
25
+ if wall_elapsed == 0
26
+ counters[:'Time.pct.cpu'] = 0
27
+ counters[:'Time.pct.idle'] = 0
28
+ else
29
+ counters[:'Time.pct.cpu'] = 100.0 * cpu_elapsed / wall_elapsed
30
+ counters[:'Time.pct.idle'] = 100.0 * idle_elapsed / wall_elapsed
31
+ end
32
+ end
33
+
34
+ state[:stopwatch] = current
35
+ end
36
+
37
+ private def current
38
+ state = {
39
+ :wall => @timepiece.wall,
40
+ }
41
+ state[:cpu] = @timepiece.cpu if @has_cpu_time
42
+ state
43
+ end
44
+ end
45
+
46
+ module Timepiece
47
+ def self.wall
48
+ ::Time.now.to_f * 1000
49
+ end
50
+
51
+ # Ruby 2.1+
52
+ if Process.respond_to?(:clock_gettime)
53
+ def self.cpu
54
+ Process.clock_gettime Process::CLOCK_PROCESS_CPUTIME_ID, :float_millisecond
55
+ end
56
+
57
+ # ruby-prof installed
58
+ elsif defined? RubyProf::Measure::ProcessTime
59
+ def self.cpu
60
+ RubyProf::Measure::Process.measure * 1000
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,27 @@
1
+ module Barnes
2
+ class Panel
3
+ def initialize
4
+ @instruments = []
5
+ end
6
+
7
+ # Add an instrument to the Panel
8
+ def instrument(instrument)
9
+ @instruments << instrument
10
+ end
11
+
12
+ # Initialize the state of each instrument in the panel.
13
+ def start!(state)
14
+ @instruments.each do |ins|
15
+ ins.start! state if ins.respond_to?(:start!)
16
+ end
17
+ end
18
+
19
+ # Read the values of each instrument into counter_readings,
20
+ # and gauge_readings. May have side effects on all arguments.
21
+ def instrument!(state, counter_readings, gauge_readings)
22
+ @instruments.each do |ins|
23
+ ins.instrument! state, counter_readings, gauge_readings
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ require 'barnes/consts'
2
+
3
+ module Barnes
4
+ # The periodic class is used to send occasional metrics
5
+ # to a reporting instance of `Barnes::Reporter` at a semi-regular
6
+ # rate.
7
+ class Periodic
8
+ def initialize(reporter:, sample_rate: 1, panels: [])
9
+ @reporter = reporter
10
+ @reporter.sample_rate = sample_rate
11
+
12
+ # compute interval based on a 60s reporting phase.
13
+ @interval = sample_rate * 60.0
14
+ @panels = panels
15
+
16
+ @thread = Thread.new {
17
+ Thread.current[:barnes_state] = {}
18
+
19
+ @panels.each do |panel|
20
+ panel.start! Thread.current[:barnes_state]
21
+ end
22
+
23
+ loop do
24
+ begin
25
+ sleep @interval
26
+
27
+ # read the current values
28
+ env = {
29
+ STATE => Thread.current[:barnes_state],
30
+ COUNTERS => {},
31
+ GAUGES => {}
32
+ }
33
+
34
+ @panels.each do |panel|
35
+ panel.instrument! env[STATE], env[COUNTERS], env[GAUGES]
36
+ end
37
+ @reporter.report env
38
+ end
39
+ end
40
+ }
41
+ @thread.abort_on_exception = true
42
+ end
43
+
44
+ def stop
45
+ @thread.exit
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ require 'rails/railtie'
2
+ require 'barnes/reporter'
3
+ require 'barnes/resource_usage'
4
+
5
+ module Barnes
6
+ # Automatically configures barnes to run with
7
+ # rails 3, 4, and 5. Configuration can be changed
8
+ # in the application.rb. For example
9
+ #
10
+ # module YourApp
11
+ # class Application < Rails::Application
12
+ # config.barnes[:interval] = 20
13
+ #
14
+ class Railtie < ::Rails::Railtie
15
+ config.barnes = {
16
+ interval: DEFAULT_INTERVAL,
17
+ aggregation_period: DEFAULT_AGGREGATION_PERIOD,
18
+ statsd: DEFAULT_STATSD,
19
+ panels: DEFAULT_PANELS,
20
+ }
21
+
22
+ initializer 'barnes' do |app|
23
+ Barnes.start(config.barnes)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ module Barnes
2
+ # The reporter is used to send stats to the server.
3
+ #
4
+ # Example:
5
+ #
6
+ # statsd = Statsd.new('127.0.0.1', "8125")
7
+ # reporter = Reporter.new(statsd: , sample_rate: 10)
8
+ # reporter.report_statsd('barnes.counters' => {"hello" => 2})
9
+ class Reporter
10
+ attr_accessor :statsd, :sample_rate
11
+
12
+ def initialize(statsd: , sample_rate:)
13
+ @statsd = statsd
14
+ @sample_rate = sample_rate.to_f
15
+
16
+ if @statsd.respond_to?(:easy)
17
+ @statsd_method = statsd.method(:easy)
18
+ else
19
+ @statsd_method = statsd.method(:batch)
20
+ end
21
+ end
22
+
23
+ def report(env)
24
+ report_statsd env if @statsd
25
+ end
26
+
27
+ def report_statsd(env)
28
+ @statsd_method.call do |statsd|
29
+ env[Barnes::COUNTERS].each do |metric, value|
30
+ statsd.count(:"Rack.Server.All.#{metric}", value, @sample_rate)
31
+ end
32
+
33
+ # for :gauge, use sample rate of 1, since gauges in statsd have no sampling characteristics.
34
+ env[Barnes::GAUGES].each do |metric, value|
35
+ statsd.gauge(:"Rack.Server.All.#{metric}", value, 1.0)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'barnes/panel'
2
+
3
+ module Barnes
4
+ class ResourceUsage < Panel
5
+ def initialize(sample_rate)
6
+ super()
7
+
8
+ require 'barnes/instruments/stopwatch'
9
+ instrument Barnes::Instruments::Stopwatch.new
10
+
11
+ if GC.respond_to? :enable_stats
12
+ require 'barnes/instruments/ree_gc'
13
+ instrument Barnes::Instruments::Ruby18GC.new
14
+ end
15
+
16
+ # Ruby 1.9+
17
+ if ObjectSpace.respond_to? :count_objects
18
+ require 'barnes/instruments/object_space_counter'
19
+ instrument Barnes::Instruments::ObjectSpaceCounter.new
20
+ end
21
+
22
+ # Ruby 1.9+
23
+ if GC.respond_to?(:stat)
24
+ require 'barnes/instruments/ruby_gc'
25
+ instrument Barnes::Instruments::RubyGC.new(sample_rate)
26
+ end
27
+
28
+ # Ruby 2.1+ with https://github.com/tmm1/gctools
29
+ if defined? GC::OOB
30
+ require 'barnes/instruments/gctools_oobgc'
31
+ instrument Barnes::Instruments::GctoolsOobgc.new
32
+ end
33
+ end
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barnes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - APG
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: statsd-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.3'
55
+ description: Report GC usage data to StatsD.
56
+ email: agwozdziewycz@salesforce.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - "/Users/schneems/Documents/projects/barnes/init.rb"
62
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes.rb"
63
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/consts.rb"
64
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/instruments/gctools_oobgc.rb"
65
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/instruments/object_space_counter.rb"
66
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/instruments/ree_gc.rb"
67
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/instruments/ruby_gc.rb"
68
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/instruments/stopwatch.rb"
69
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/panel.rb"
70
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/periodic.rb"
71
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/railtie.rb"
72
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/reporter.rb"
73
+ - "/Users/schneems/Documents/projects/barnes/lib/barnes/resource_usage.rb"
74
+ homepage: https://github.com/heroku/barnes
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 2.2.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.6.14
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Ruby GC stats => StatsD
98
+ test_files: []