barnes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []