process-instrumentation 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +88 -0
- data/Rakefile +16 -0
- data/bin/console +6 -0
- data/bin/setup +8 -0
- data/example.gif +0 -0
- data/exe/instrument +9 -0
- data/instrumentation.gemspec +34 -0
- data/lib/assets/application.js +87 -0
- data/lib/assets/chart.min.js +15 -0
- data/lib/assets/styles.css +40 -0
- data/lib/instrumentation/bounded_array.rb +26 -0
- data/lib/instrumentation/load_average.rb +33 -0
- data/lib/instrumentation/memory.rb +25 -0
- data/lib/instrumentation/rack_app.rb +22 -0
- data/lib/instrumentation/report.rb +61 -0
- data/lib/instrumentation/version.rb +3 -0
- data/lib/instrumentation/view.rb +17 -0
- data/lib/instrumentation/webserver.rb +36 -0
- data/lib/instrumentation.rb +45 -0
- data/lib/templates/index.erb +31 -0
- metadata +182 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Instrumentation
|
2
|
+
# Reads RSS memory from the PID specified
|
3
|
+
class Memory
|
4
|
+
def initialize(pid)
|
5
|
+
@pid = pid
|
6
|
+
end
|
7
|
+
|
8
|
+
def read
|
9
|
+
case system
|
10
|
+
when :mac_os
|
11
|
+
rss_via_ps
|
12
|
+
else
|
13
|
+
raise "Unknown system #{system.inspect}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def system
|
18
|
+
:mac_os
|
19
|
+
end
|
20
|
+
|
21
|
+
def rss_via_ps
|
22
|
+
`ps -o rss= -p #{@pid}`.strip.to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'tubesock'
|
2
|
+
|
3
|
+
module Instrumentation
|
4
|
+
# Main Rack application that serves the `websocket` data or the index page
|
5
|
+
class RackApp
|
6
|
+
def initialize(report)
|
7
|
+
@report = report
|
8
|
+
@view = Instrumentation::View.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
if env['HTTP_UPGRADE'] == 'websocket'
|
13
|
+
socket = Tubesock.hijack(env)
|
14
|
+
socket.listen
|
15
|
+
@report.socket = socket
|
16
|
+
[-1, {}, []]
|
17
|
+
else
|
18
|
+
['200', { 'Content-Type' => 'text/html' }, [@view.render]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Instrumentation
|
2
|
+
# Reads data from system and process information and writes it to
|
3
|
+
# websocket
|
4
|
+
class Report
|
5
|
+
attr_reader :memory, :loadavg
|
6
|
+
attr_accessor :socket
|
7
|
+
|
8
|
+
def initialize(pid)
|
9
|
+
@pid = pid
|
10
|
+
@sleep = 1
|
11
|
+
@memory = BoundedArray.new(300)
|
12
|
+
@loadavg = BoundedArray.new(300)
|
13
|
+
@socket = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
@thread = Thread.new do
|
18
|
+
loop do
|
19
|
+
read_data
|
20
|
+
send_data if socket
|
21
|
+
sleep @sleep
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_data
|
27
|
+
now = Time.now.strftime('%FT%T')
|
28
|
+
@memory = @memory << [now, read_memory]
|
29
|
+
@loadavg = @loadavg << [now, read_loadavg]
|
30
|
+
rescue => error
|
31
|
+
puts "Error when reading data: #{error.inspect}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_data
|
35
|
+
socket.send_data(event(:memory, @memory.items))
|
36
|
+
socket.send_data(event(:loadavg, @loadavg.items))
|
37
|
+
rescue => error
|
38
|
+
puts "Error when sending data: #{error.inspect}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def event(name, data)
|
42
|
+
{ data_type: name, data: data }.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_memory
|
46
|
+
Memory.new(@pid).read
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_loadavg
|
50
|
+
LoadAverage.new.read[:one]
|
51
|
+
end
|
52
|
+
|
53
|
+
def join
|
54
|
+
@thread.join
|
55
|
+
end
|
56
|
+
|
57
|
+
def shutdown
|
58
|
+
@thread.kill
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Instrumentation
|
2
|
+
# Handles rendering of ERB templates and asset loading
|
3
|
+
class View
|
4
|
+
include Erb::View
|
5
|
+
template :index
|
6
|
+
|
7
|
+
def asset(path)
|
8
|
+
File.read(asset_root.join('lib/assets', path))
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def asset_root
|
14
|
+
Instrumentation.root
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Instrumentation
|
2
|
+
# Webserver using `puma` that handles requests for stats
|
3
|
+
class Webserver
|
4
|
+
def initialize
|
5
|
+
@worker = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(app, opts = {})
|
9
|
+
config = build_config(opts.merge(app: app))
|
10
|
+
|
11
|
+
@launcher = Puma::Launcher.new(config, events: Puma::Events.stdio)
|
12
|
+
@worker = Thread.new { @launcher.run }
|
13
|
+
end
|
14
|
+
|
15
|
+
def join
|
16
|
+
@worker.join
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop
|
20
|
+
@launcher.stop
|
21
|
+
@worker.kill
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build_config(opts)
|
27
|
+
Puma::Configuration.new do |c|
|
28
|
+
host = opts.fetch(:host, Puma::Configuration::DefaultTCPHost)
|
29
|
+
port = opts.fetch(:port, Puma::Configuration::DefaultTCPPort)
|
30
|
+
|
31
|
+
c.port port, host
|
32
|
+
c.app opts.fetch(:app)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'instrumentation/version'
|
2
|
+
require 'pathname'
|
3
|
+
require 'json'
|
4
|
+
require 'rack'
|
5
|
+
require 'puma'
|
6
|
+
require 'erb/view'
|
7
|
+
|
8
|
+
Erb.root = Pathname.new(__FILE__).join('..', '..').join('lib/templates')
|
9
|
+
|
10
|
+
# Instrumentation
|
11
|
+
#
|
12
|
+
# Start a server by calling:
|
13
|
+
# Instrumentation.start_server(Process.pid)
|
14
|
+
#
|
15
|
+
# By default the server is started on http://localhost:8080
|
16
|
+
module Instrumentation
|
17
|
+
def start_server(pid:, port: 8080)
|
18
|
+
report = Report.new(pid)
|
19
|
+
app = RackApp.new(report)
|
20
|
+
server = Webserver.new
|
21
|
+
|
22
|
+
report.start
|
23
|
+
server.run(app, port: port)
|
24
|
+
|
25
|
+
[server, report].map(&:join)
|
26
|
+
rescue Interrupt => _
|
27
|
+
print "\n=> Shutting down instrumentation.\n"
|
28
|
+
report.shutdown
|
29
|
+
server.stop
|
30
|
+
end
|
31
|
+
|
32
|
+
def root
|
33
|
+
Pathname.new(__FILE__).join('..', '..')
|
34
|
+
end
|
35
|
+
|
36
|
+
module_function :start_server, :root
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'instrumentation/bounded_array'
|
40
|
+
require 'instrumentation/memory'
|
41
|
+
require 'instrumentation/load_average'
|
42
|
+
require 'instrumentation/rack_app'
|
43
|
+
require 'instrumentation/report'
|
44
|
+
require 'instrumentation/view'
|
45
|
+
require 'instrumentation/webserver'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<style type="text/css"><%= asset('styles.css') %></style>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
<div class="content">
|
8
|
+
<div class="container">
|
9
|
+
<div class="stats-column">
|
10
|
+
<h5>Memory Usage (RSS)</h5>
|
11
|
+
<h1 id="last-memory">-</h1>
|
12
|
+
</div>
|
13
|
+
<div class="chart-container">
|
14
|
+
<canvas id="memory" width="1000" height="300"></canvas>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="container">
|
19
|
+
<div class="stats-column">
|
20
|
+
<h5>One Minute Load Avg</h5>
|
21
|
+
<h1 id="last-loadavg">-</h1>
|
22
|
+
</div>
|
23
|
+
<div class="chart-container">
|
24
|
+
<canvas id="loadavg" width="1000" height="300"></canvas>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<script charset="utf-8"><%= asset('chart.min.js') %></script>
|
30
|
+
<script charset="utf-8"><%= asset('application.js') %></script>
|
31
|
+
</body>
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: process-instrumentation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Felipe Philipp
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '11.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '11.0'
|
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.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-rg
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.0.1
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.0.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tubesock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.2.7
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.2.7
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: puma
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.6.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.6.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: erb-view
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.1'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.1'
|
125
|
+
description: Monitor process memory and CPU over time
|
126
|
+
email:
|
127
|
+
- felipeelias@gmail.com
|
128
|
+
executables:
|
129
|
+
- instrument
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- ".gitignore"
|
134
|
+
- ".travis.yml"
|
135
|
+
- CODE_OF_CONDUCT.md
|
136
|
+
- Gemfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- bin/console
|
141
|
+
- bin/setup
|
142
|
+
- example.gif
|
143
|
+
- exe/instrument
|
144
|
+
- instrumentation.gemspec
|
145
|
+
- lib/assets/application.js
|
146
|
+
- lib/assets/chart.min.js
|
147
|
+
- lib/assets/styles.css
|
148
|
+
- lib/instrumentation.rb
|
149
|
+
- lib/instrumentation/bounded_array.rb
|
150
|
+
- lib/instrumentation/load_average.rb
|
151
|
+
- lib/instrumentation/memory.rb
|
152
|
+
- lib/instrumentation/rack_app.rb
|
153
|
+
- lib/instrumentation/report.rb
|
154
|
+
- lib/instrumentation/version.rb
|
155
|
+
- lib/instrumentation/view.rb
|
156
|
+
- lib/instrumentation/webserver.rb
|
157
|
+
- lib/templates/index.erb
|
158
|
+
homepage: https://github.com/felipeelias/instrumentation
|
159
|
+
licenses:
|
160
|
+
- MIT
|
161
|
+
metadata: {}
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
requirements: []
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 2.5.1
|
179
|
+
signing_key:
|
180
|
+
specification_version: 4
|
181
|
+
summary: Monitor process stats over time
|
182
|
+
test_files: []
|