space_observatory 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +25 -0
- data/lib/space_observatory.rb +1 -1
- data/lib/space_observatory/base_station.rb +30 -52
- data/lib/space_observatory/rack_middleware.rb +119 -0
- data/lib/space_observatory/railtie.rb +31 -0
- data/lib/space_observatory/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbfdd79f8b24c39b374ca7bd7f23540ea545e602
|
4
|
+
data.tar.gz: 05c51b5ed6e113c65617c1bb74d0120ddcdcc542
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d48ce0353e37319ee548bda34aa224e75edbeb69c18b41838676cff0635c8c88f5db33f7a3eee2c00dd4e11f4b6dca280f9f735dd88dc0d15fece64ec5ac213f
|
7
|
+
data.tar.gz: cdb858c3109a0405c8e498419f13c9dbbe8901598f5f13efd6fb51fb96fd12a1191ddc9381a81b6f1bb984f280e28f07e166eca208cfeb95044a8b4440640ae5
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -7,13 +7,38 @@ This is an easy add-on for you to observe your ObjectSpace.
|
|
7
7
|
- Pure ruby. NO C extensions.
|
8
8
|
- Drop-in design that requires NO modifications to your program.
|
9
9
|
- Minimal overhead. Does all jobs except data collection in a separate process.
|
10
|
+
- Native support Rails, Sinatra, and non-Web daemons.
|
10
11
|
|
11
12
|
## Installation
|
12
13
|
|
13
14
|
As usual.
|
14
15
|
|
16
|
+
**NOTE** however that as of this writing, WEBrick do not support websockets (necessary for this lib). You need a websockets-aware rack handler like Puma, Passanger, whatever.
|
17
|
+
|
15
18
|
## Usage
|
16
19
|
|
20
|
+
### on Rails
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'space_observatory', require: 'space_observatory/railtie'
|
24
|
+
```
|
25
|
+
|
26
|
+
And you are done.
|
27
|
+
|
28
|
+
### Pure Rack / Sinatra / Padrino etc
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'space_observatory', require: 'space_observatory/rack_middleware'
|
32
|
+
```
|
33
|
+
|
34
|
+
Also in your `config.ru` file add:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
use SpaceObservatory::RackMiddleware
|
38
|
+
```
|
39
|
+
|
40
|
+
### Versatile use case including non-Web daemons
|
41
|
+
|
17
42
|
```bash
|
18
43
|
bundle exec with-space-observatory.rb --port=1234 -- ruby bin/rails server --port=5678
|
19
44
|
```
|
data/lib/space_observatory.rb
CHANGED
@@ -22,13 +22,13 @@
|
|
22
22
|
# SOFTWARE.
|
23
23
|
|
24
24
|
require 'rubygems'
|
25
|
-
require '
|
26
|
-
require 'sync'
|
25
|
+
require 'bundler/setup'
|
27
26
|
require 'rack'
|
28
27
|
require 'slop'
|
29
28
|
require_relative '../space_observatory'
|
29
|
+
require_relative 'rack_middleware'
|
30
30
|
|
31
|
-
class SpaceObservatory::BaseStation
|
31
|
+
class SpaceObservatory::BaseStation < SpaceObservatory::RackMiddleware
|
32
32
|
# Fork a base station process
|
33
33
|
# @param [Array] argv The ::ARGV
|
34
34
|
# @return [IO, IO, Integer, Array] child's socket, pid, and argv.
|
@@ -44,7 +44,6 @@ class SpaceObservatory::BaseStation
|
|
44
44
|
def self.construct
|
45
45
|
obj = new ARGV, STDIN, STDOUT
|
46
46
|
if obj.need_rackup?
|
47
|
-
obj.start_collector
|
48
47
|
obj.rackup
|
49
48
|
else
|
50
49
|
obj.banner
|
@@ -74,17 +73,18 @@ class SpaceObservatory::BaseStation
|
|
74
73
|
@stdout.sync = true # we need line IO
|
75
74
|
@opts, @argw = self.class.parse argv
|
76
75
|
@argw.shift if @argw.first == '--'
|
77
|
-
|
78
|
-
@
|
79
|
-
@jsons = Array.new
|
80
|
-
@rwlock = Sync.new
|
81
|
-
@expires = @opts['ttl'] || 60 # 300 # 3600
|
76
|
+
|
77
|
+
super nil, '/', @opts['ttl'] || 60 # 300 # 3600
|
82
78
|
end
|
83
79
|
|
84
80
|
def rackup
|
85
81
|
Rack::Handler::WEBrick.run self, Port: @opts['port']
|
86
82
|
end
|
87
83
|
|
84
|
+
def need_rackup?
|
85
|
+
not @opts['h'] and not @argw.empty?
|
86
|
+
end
|
87
|
+
|
88
88
|
def banner
|
89
89
|
@stdout.puts "space_observatory norun"
|
90
90
|
@stdout.puts @opts.help if @opts['h']
|
@@ -92,13 +92,28 @@ class SpaceObservatory::BaseStation
|
|
92
92
|
@stdout.close_write
|
93
93
|
end
|
94
94
|
|
95
|
+
private
|
96
|
+
|
95
97
|
def start_collector
|
98
|
+
retun unless need_rackup?
|
99
|
+
|
100
|
+
Thread.start do
|
101
|
+
loop do
|
102
|
+
env = @queue.deq # block here
|
103
|
+
@mutex.synchronize do
|
104
|
+
next if Time.now - @started < @expires
|
105
|
+
@stdout.puts "space_observatory probe"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
96
110
|
Thread.start do
|
97
111
|
# needs handshale
|
98
112
|
@stdout.puts "space_observatory ok"
|
99
113
|
@stdin.gets # wait execve(2)
|
100
114
|
@stdout.puts "space_observatory setup"
|
101
|
-
|
115
|
+
|
116
|
+
tmp = Tempfile.new ''
|
102
117
|
while line = @stdin.gets
|
103
118
|
case line
|
104
119
|
when "space_observatory projectile_eof\n"
|
@@ -107,57 +122,20 @@ class SpaceObservatory::BaseStation
|
|
107
122
|
return
|
108
123
|
when "space_observatory begin_objspace\n"
|
109
124
|
@started = Time.now
|
110
|
-
@
|
111
|
-
|
125
|
+
@mutex.lock
|
126
|
+
tmp.truncate 0
|
112
127
|
when "space_observatory end_objspace\n"
|
113
|
-
|
128
|
+
cook tmp
|
129
|
+
@mutex.unlock
|
114
130
|
@finished = Time.now
|
115
131
|
STDERR.printf "took %fsec\n", (@finished - @started).to_f
|
116
132
|
when /\A{/o
|
117
133
|
# This is the output of ObjectSpace.dump_all
|
118
|
-
|
134
|
+
tmp.write line
|
119
135
|
else
|
120
136
|
raise "TBW: #{line}"
|
121
137
|
end
|
122
138
|
end
|
123
139
|
end
|
124
140
|
end
|
125
|
-
|
126
|
-
def need_rackup?
|
127
|
-
not @opts['h'] and not @argw.empty?
|
128
|
-
end
|
129
|
-
|
130
|
-
def call env
|
131
|
-
# TODO: more "proper" JSON
|
132
|
-
enum = Enumerator.new do |y|
|
133
|
-
@rwlock.synchronize Sync::EX do
|
134
|
-
if Time.now - @started >= @expires
|
135
|
-
@stdout.puts "space_observatory probe"
|
136
|
-
IO.select [@stdin], [], [], 10
|
137
|
-
@started = Time.now # prevent further probe
|
138
|
-
end
|
139
|
-
end
|
140
|
-
Thread.pass
|
141
|
-
@rwlock.synchronize Sync::SH do
|
142
|
-
id = @finished.to_i
|
143
|
-
y.yield <<-"]"
|
144
|
-
{
|
145
|
-
"jsonrpc" : "2.0",
|
146
|
-
"id" : #{id},
|
147
|
-
"result" : [
|
148
|
-
]
|
149
|
-
@jsons.each_with_index do |i, j|
|
150
|
-
y.yield "," unless j.zero?
|
151
|
-
y.yield i
|
152
|
-
end
|
153
|
-
y.yield "]\n}\n"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
return [
|
158
|
-
200,
|
159
|
-
{ 'Content-Type' => 'application/json' },
|
160
|
-
enum
|
161
|
-
]
|
162
|
-
end
|
163
141
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- coding: utf-8; mode: ruby; ruby-indent-level: 2 -*-
|
3
|
+
#
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
|
24
|
+
require 'objspace'
|
25
|
+
require 'thread'
|
26
|
+
require 'tempfile'
|
27
|
+
require 'open3'
|
28
|
+
require 'rubygems'
|
29
|
+
require 'bundler/setup'
|
30
|
+
require_relative '../space_observatory'
|
31
|
+
|
32
|
+
class SpaceObservatory::RackMiddleware
|
33
|
+
|
34
|
+
def initialize app, path = '/space', expires = 60 # 300 # 3600
|
35
|
+
@app = app
|
36
|
+
@path = path
|
37
|
+
@expires = expires
|
38
|
+
@mutex = Mutex.new
|
39
|
+
@queue = Queue.new
|
40
|
+
@latest = nil
|
41
|
+
@started = Time.at 0
|
42
|
+
@finished = Time.at 0
|
43
|
+
@thread = start_collector
|
44
|
+
end
|
45
|
+
|
46
|
+
def call env
|
47
|
+
return @app.call env if @app and env['PATH_INFO'] != @path
|
48
|
+
|
49
|
+
@queue.enq env
|
50
|
+
hdr = {
|
51
|
+
'Connection' => 'close',
|
52
|
+
'Content-Type' => 'application/json',
|
53
|
+
'rack.hijack' => lambda do |fp|
|
54
|
+
Thread.start do
|
55
|
+
begin
|
56
|
+
Thread.pass until @latest
|
57
|
+
@mutex.synchronize do
|
58
|
+
@latest.rewind
|
59
|
+
IO.copy_stream @latest, fp
|
60
|
+
end
|
61
|
+
rescue Errno::ENOTCONN
|
62
|
+
ensure
|
63
|
+
fp.close
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
}
|
68
|
+
return [ 200, hdr, nil ]
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def start_collector
|
74
|
+
Thread.start do
|
75
|
+
loop do
|
76
|
+
env = @queue.deq # block here
|
77
|
+
@mutex.synchronize do
|
78
|
+
next if Time.now - @started < @expires
|
79
|
+
|
80
|
+
@started = Time.now # prevent further probe
|
81
|
+
env['rack.errors'] << "Collection happen at #@started\n"
|
82
|
+
tmp1 = ObjectSpace.dump_all output: :file
|
83
|
+
cook tmp1
|
84
|
+
tmp1.close
|
85
|
+
@finished = Time.now
|
86
|
+
env['rack.errors'] << "Collection done in #{@finished - @started} secs.\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def cook io
|
93
|
+
@latest ||= Tempfile.new ''
|
94
|
+
@latest.rewind
|
95
|
+
@latest.truncate 0
|
96
|
+
@latest.print <<-"end".gsub(/^\s+(\S)/, '\\1').gsub(/[\r\n]/, "\r\n")
|
97
|
+
HTTP/1.1 200 OK
|
98
|
+
Content-Type: application/json
|
99
|
+
Connection: close
|
100
|
+
|
101
|
+
end
|
102
|
+
@latest.puts <<-"]".gsub(/^\s+/, '')
|
103
|
+
{
|
104
|
+
"jsonrpc" : "2.0",
|
105
|
+
"id" : #{@started.to_i},
|
106
|
+
"result" : [
|
107
|
+
]
|
108
|
+
# This subprocessing is CHAPER than gsub-ing ourselves because
|
109
|
+
# gsub creates TONS of garbage string objects, which would be
|
110
|
+
# counted in the next ObjectSpace.dump_all call.
|
111
|
+
#
|
112
|
+
# Also note that this can totally be avoided if
|
113
|
+
# ObjectSpace.dump_all have generated valid JSON from the outset.
|
114
|
+
Open3.pipeline_r "cat #{io.path}", 'sed s/$/,/' do |r,|
|
115
|
+
IO.copy_stream r, @latest
|
116
|
+
end
|
117
|
+
@latest.print "]\n}\n"
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- coding: utf-8; mode: ruby; ruby-indent-level: 2 -*-
|
3
|
+
#
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require_relative 'rack_middleware'
|
26
|
+
|
27
|
+
class SpaceObservatory::Railtie < ::Rails::Railtie
|
28
|
+
initializer "space_observatory.install_middleware" do |app|
|
29
|
+
app.config.middleware.insert_before "ActionDispatch::Static", "SpaceObservatory::RackMiddleware"
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: space_observatory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Urabe, Shyouhei
|
8
8
|
autorequire:
|
9
9
|
bindir: exec
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -168,6 +168,8 @@ files:
|
|
168
168
|
- exec/with-space-observatory.rb
|
169
169
|
- lib/space_observatory.rb
|
170
170
|
- lib/space_observatory/base_station.rb
|
171
|
+
- lib/space_observatory/rack_middleware.rb
|
172
|
+
- lib/space_observatory/railtie.rb
|
171
173
|
- lib/space_observatory/version.rb
|
172
174
|
- space_observatory.gemspec
|
173
175
|
homepage: https://github.com/shyouhei/space_observatory
|