gltail 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +66 -0
- data/Manifest.txt +40 -0
- data/README +51 -0
- data/README.txt +376 -0
- data/Rakefile +19 -0
- data/TODO +9 -0
- data/bin/gl_tail +109 -0
- data/dist/config.yaml +101 -0
- data/lib/gl_tail.rb +68 -0
- data/lib/gl_tail/activity.rb +145 -0
- data/lib/gl_tail/blob_store.rb +43 -0
- data/lib/gl_tail/block.rb +130 -0
- data/lib/gl_tail/config.rb +187 -0
- data/lib/gl_tail/config/configurable.rb +51 -0
- data/lib/gl_tail/config/yaml_parser.rb +94 -0
- data/lib/gl_tail/element.rb +194 -0
- data/lib/gl_tail/engine.rb +266 -0
- data/lib/gl_tail/font.bin +0 -0
- data/lib/gl_tail/font_store.rb +165 -0
- data/lib/gl_tail/http_helper.rb +58 -0
- data/lib/gl_tail/item.rb +17 -0
- data/lib/gl_tail/parser.rb +46 -0
- data/lib/gl_tail/parsers/apache.rb +56 -0
- data/lib/gl_tail/parsers/iis.rb +47 -0
- data/lib/gl_tail/parsers/mysql.rb +29 -0
- data/lib/gl_tail/parsers/nginx.rb +46 -0
- data/lib/gl_tail/parsers/pix-fwsm.rb +50 -0
- data/lib/gl_tail/parsers/postfix.rb +119 -0
- data/lib/gl_tail/parsers/postgresql.rb +42 -0
- data/lib/gl_tail/parsers/pureftpd.rb +30 -0
- data/lib/gl_tail/parsers/qmail.rb +30 -0
- data/lib/gl_tail/parsers/rails.rb +41 -0
- data/lib/gl_tail/parsers/squid.rb +25 -0
- data/lib/gl_tail/parsers/tshark.rb +25 -0
- data/lib/gl_tail/resolver.rb +69 -0
- data/lib/gl_tail/server.rb +46 -0
- data/lib/gl_tail/sources/base.rb +44 -0
- data/lib/gl_tail/sources/local.rb +12 -0
- data/lib/gl_tail/sources/ssh.rb +112 -0
- data/test/test_gl_tail.rb +0 -0
- metadata +114 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
# gl_tail.rb - OpenGL visualization of your server traffic
|
2
|
+
# Copyright 2007 Erlend Simonsen <mr@fudgie.org>
|
3
|
+
#
|
4
|
+
# Licensed under the GNU General Public License v2 (see LICENSE)
|
5
|
+
#
|
6
|
+
|
7
|
+
class Element
|
8
|
+
attr_accessor :wy, :y, :active, :average_size, :right, :color, :name
|
9
|
+
attr_reader :rate, :messages, :activities, :queue, :updates, :average, :total
|
10
|
+
|
11
|
+
def initialize(block, name, start_position = nil)
|
12
|
+
@block = block
|
13
|
+
|
14
|
+
if name =~ /^\d+.\d+.\d+.\d+$/
|
15
|
+
@name = Resolver.resolv(name, self)
|
16
|
+
else
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
@start_position = start_position.nil? ? -@block.top : start_position
|
21
|
+
|
22
|
+
@queue = []
|
23
|
+
@pending = []
|
24
|
+
@activities = []
|
25
|
+
@messages = 0
|
26
|
+
@rate = 0
|
27
|
+
@total = 0
|
28
|
+
@sum = 0
|
29
|
+
@average = 0.0
|
30
|
+
@last_time = 0
|
31
|
+
@step = 0, @updates = 0
|
32
|
+
@active = false
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_activity(message, color, size, type)
|
36
|
+
@pending.push Item.new(message, size, color, type) if(type != 3)
|
37
|
+
@messages += 1
|
38
|
+
@sum += size
|
39
|
+
@color = color
|
40
|
+
|
41
|
+
if @rate == 0
|
42
|
+
@rate = 1.0 / 60
|
43
|
+
@messages = 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_event(message, color, update_stats)
|
48
|
+
@pending.push Item.new(message, 0.01, color, 2)
|
49
|
+
if update_stats
|
50
|
+
@messages += 1
|
51
|
+
if @rate == 0
|
52
|
+
@rate = 1.0 / 60
|
53
|
+
@messages = 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def update
|
60
|
+
@active = true if @total > 0
|
61
|
+
@updates += 1
|
62
|
+
@rate = (@rate.to_f * 299 + @messages) / 300
|
63
|
+
@messages = 0
|
64
|
+
if @pending.size + @queue.size > 0
|
65
|
+
@total += @pending.size
|
66
|
+
@average = @sum / @total
|
67
|
+
|
68
|
+
@step = 1.0 / (@queue.size + @pending.size) * 1000.0
|
69
|
+
@queue = @queue + @pending
|
70
|
+
if @queue.size == 1
|
71
|
+
@step = rand(1000) * 1.0
|
72
|
+
end
|
73
|
+
@pending = []
|
74
|
+
else
|
75
|
+
@step = 0
|
76
|
+
end
|
77
|
+
@last_time = glutGet(GLUT_ELAPSED_TIME)
|
78
|
+
@last_time += @step if @queue.size == 1
|
79
|
+
# @last_time -= @step if @queue.size != 1
|
80
|
+
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def render(engine, options = { })
|
85
|
+
|
86
|
+
# this used to be in the constructor, couldnt leave it there without using globals
|
87
|
+
if not defined? @x
|
88
|
+
@x = (@block.is_right ? @block.alignment : (@block.alignment - (8.0 / (@block.config.screen.window_width / 2.0)) * (@block.width + 8)))
|
89
|
+
@y = @start_position
|
90
|
+
@z = 0
|
91
|
+
@wy = @start_position
|
92
|
+
|
93
|
+
@color = @block.color.dup if @color.nil? && @block.color
|
94
|
+
@color ||= [1.0, 1.0, 1.0, 1.0]
|
95
|
+
@size = 0.01
|
96
|
+
end
|
97
|
+
|
98
|
+
@wx = @block.is_right ? (@block.alignment - (@block.width+8)*8.0 / (engine.screen.window_width / 2.0)) : @block.alignment
|
99
|
+
|
100
|
+
if(@y == -@block.top)
|
101
|
+
@y = @wy
|
102
|
+
end
|
103
|
+
|
104
|
+
d = @wy - @y
|
105
|
+
if d.abs < 0.001
|
106
|
+
@y = @wy
|
107
|
+
else
|
108
|
+
@y += d / 20
|
109
|
+
end
|
110
|
+
|
111
|
+
d = @wx - @x
|
112
|
+
if d.abs < 0.001
|
113
|
+
@x = @wx
|
114
|
+
else
|
115
|
+
@x += d / 20
|
116
|
+
end
|
117
|
+
|
118
|
+
glPushMatrix()
|
119
|
+
|
120
|
+
glTranslate(@x, @y, @z)
|
121
|
+
|
122
|
+
glColor( (@queue.size > 0 ? (engine.screen.highlight_color || [1.0, 0.0, 0.0, 1.0]) : @color ) )
|
123
|
+
|
124
|
+
case @block.show
|
125
|
+
when 0
|
126
|
+
if @rate < 0.0001
|
127
|
+
txt = " r/m "
|
128
|
+
else
|
129
|
+
txt = "#{sprintf("%7.2f",@rate * 60)} "
|
130
|
+
end
|
131
|
+
when 1
|
132
|
+
if @total == 0
|
133
|
+
txt = " total "
|
134
|
+
else
|
135
|
+
txt = "#{sprintf("%7d",@total)} "
|
136
|
+
end
|
137
|
+
when 2
|
138
|
+
if @average == 0
|
139
|
+
txt = " avg "
|
140
|
+
else
|
141
|
+
txt = "#{sprintf("%7.2f",@average)} "
|
142
|
+
end
|
143
|
+
else
|
144
|
+
raise "unknown block type #{self.inspect}"
|
145
|
+
end
|
146
|
+
|
147
|
+
if @x < 0
|
148
|
+
str = sprintf("%#{@block.width}s %s", @name.length > @block.width ? @name[-@block.width..-1] : @name, txt)
|
149
|
+
else
|
150
|
+
str = sprintf("%s %s", txt, @name[0..@block.width-1])
|
151
|
+
end
|
152
|
+
|
153
|
+
engine.render_string(str)
|
154
|
+
|
155
|
+
glPopMatrix()
|
156
|
+
|
157
|
+
t = glutGet(GLUT_ELAPSED_TIME)
|
158
|
+
while( (@queue.size > 0) && (@last_time < t ) )
|
159
|
+
|
160
|
+
@last_time += @step
|
161
|
+
item = @queue.pop
|
162
|
+
url = item.message
|
163
|
+
color = item.color
|
164
|
+
size = item.size
|
165
|
+
type = item.type
|
166
|
+
|
167
|
+
if type == 2
|
168
|
+
@activities.push Activity.new(url, 0.0 - (0.013 * url.length), engine.screen.top, 0.0, color, size, type)
|
169
|
+
elsif type == 5
|
170
|
+
a = Activity.new(url, 0.0, engine.screen.top, 0.0, color, size, type)
|
171
|
+
a.wx = @wx
|
172
|
+
a.wy = @wy + 0.05
|
173
|
+
@activities.push a
|
174
|
+
elsif type != 4
|
175
|
+
if @x >= 0
|
176
|
+
@activities.push Activity.new(url, (@block.alignment - (@block.width+8)*8.0 / (engine.screen.window_width / 2.0)), @y + engine.screen.line_size/2, @z, color, size, type)
|
177
|
+
else
|
178
|
+
@activities.push Activity.new(url, (@block.alignment + (@block.width+8)*8.0 / (engine.screen.window_width / 2.0) ), @y + engine.screen.line_size/2, @z, color, size, type)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
@activities.each do |a|
|
184
|
+
if a.x > 1.0 || a.x < -1.0 || a.y < -(engine.screen.aspect*1.5)
|
185
|
+
@activities.delete a
|
186
|
+
else
|
187
|
+
a.wy = @wy + 0.005 if(a.type == 5 && @wy != a.wy)
|
188
|
+
a.render(engine)
|
189
|
+
engine.stats[1] += 1
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
|
2
|
+
include Gl
|
3
|
+
include Glut
|
4
|
+
|
5
|
+
module GlTail
|
6
|
+
class Engine
|
7
|
+
|
8
|
+
def render_string(string)
|
9
|
+
FontStore.render_string(self, string)
|
10
|
+
end
|
11
|
+
|
12
|
+
def screen
|
13
|
+
@config.screen
|
14
|
+
end
|
15
|
+
|
16
|
+
def char_size
|
17
|
+
@char_size ||= (8.0 / (@config.screen.window_width / 2.0))
|
18
|
+
end
|
19
|
+
|
20
|
+
def line_size
|
21
|
+
@config.screen.line_size
|
22
|
+
end
|
23
|
+
|
24
|
+
def highlight_color
|
25
|
+
@config.screen.highlight_color
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset_stats
|
29
|
+
@stats = [0, 0]
|
30
|
+
end
|
31
|
+
|
32
|
+
def stats
|
33
|
+
@stats
|
34
|
+
end
|
35
|
+
|
36
|
+
def draw
|
37
|
+
@render_time ||= 0
|
38
|
+
@t = Time.new
|
39
|
+
|
40
|
+
glClear(GL_COLOR_BUFFER_BIT);
|
41
|
+
# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
42
|
+
|
43
|
+
glPushMatrix()
|
44
|
+
|
45
|
+
positions = Hash.new
|
46
|
+
|
47
|
+
reset_stats
|
48
|
+
|
49
|
+
glPushMatrix()
|
50
|
+
|
51
|
+
glColor([0.15, 0.15, 0.15, 1.0])
|
52
|
+
glBegin(GL_QUADS)
|
53
|
+
glNormal3f(1.0, 1.0, 0.0)
|
54
|
+
|
55
|
+
glVertex3f(@left_left, @config.screen.aspect, 0.0)
|
56
|
+
glVertex3f(@left_right, @config.screen.aspect, 0.0)
|
57
|
+
glVertex3f(@left_right, -@config.screen.aspect, 0.0)
|
58
|
+
glVertex3f(@left_left, -@config.screen.aspect, 0.0)
|
59
|
+
|
60
|
+
glVertex3f(@right_left, @config.screen.aspect, 0.0)
|
61
|
+
glVertex3f(@right_right, @config.screen.aspect, 0.0)
|
62
|
+
glVertex3f(@right_right, -@config.screen.aspect, 0.0)
|
63
|
+
glVertex3f(@right_left, -@config.screen.aspect, 0.0)
|
64
|
+
|
65
|
+
glEnd()
|
66
|
+
glPopMatrix()
|
67
|
+
|
68
|
+
# TODO: do we really need to sort every block on every draw?!
|
69
|
+
# Nope. But it was a hash, so keeping order was a bit hard.
|
70
|
+
@config.blocks.sort { |k,v| k.order <=> v.order}.each do |block|
|
71
|
+
# glPushMatrix + glTranslate3f to render each element relativ to its containing block instead of the screen?
|
72
|
+
positions[block.is_right] = block.render(self, positions[block.is_right] || 0 )
|
73
|
+
end
|
74
|
+
|
75
|
+
glPopMatrix()
|
76
|
+
|
77
|
+
@frames += 1
|
78
|
+
t = glutGet(GLUT_ELAPSED_TIME)
|
79
|
+
if t - @t0 >= 5000
|
80
|
+
seconds = (t - @t0) / 1000.0
|
81
|
+
$FPS = @frames / seconds
|
82
|
+
printf("%d frames in %6.3f seconds = %6.3f FPS\n",
|
83
|
+
@frames, seconds, $FPS) if $VRB > 0
|
84
|
+
@t0, @frames = t, 0
|
85
|
+
puts "Elements[#{stats[0]}], Activities[#{stats[1]}], Blobs[#{BlobStore.used}/#{BlobStore.size}]" if $VRB > 0
|
86
|
+
end
|
87
|
+
@render_time = (Time.new - @t)
|
88
|
+
end
|
89
|
+
|
90
|
+
def idle
|
91
|
+
@last_run ||= Time.new
|
92
|
+
@last_run_time ||= 0
|
93
|
+
delta = (Time.new - @last_run) - @last_run_time
|
94
|
+
if @config.screen.wanted_fps > 0 && delta < (1000.0/(@config.screen.wanted_fps*1000.0))
|
95
|
+
sleep((1000.0/(@config.screen.wanted_fps*1000.0) - delta))
|
96
|
+
end
|
97
|
+
@last_run = Time.new
|
98
|
+
glutPostRedisplay()
|
99
|
+
glutSwapBuffers()
|
100
|
+
do_process
|
101
|
+
@last_run_time = (@last_run_time.to_f * (@config.screen.wanted_fps-1.0) + (Time.new - @last_run).to_f) / @config.screen.wanted_fps.to_f if @config.screen.wanted_fps > 0
|
102
|
+
end
|
103
|
+
|
104
|
+
def timer(value)
|
105
|
+
glutTimerFunc(15, method(:timer).to_proc, 0)
|
106
|
+
# t = glutGet(GLUT_ELAPSED_TIME)
|
107
|
+
glutPostRedisplay()
|
108
|
+
glutSwapBuffers()
|
109
|
+
do_process
|
110
|
+
# t = glutGet(GLUT_ELAPSED_TIME) - t
|
111
|
+
# t = 14 if t > 14
|
112
|
+
end
|
113
|
+
|
114
|
+
# Change view angle, exit upon ESC
|
115
|
+
def key(k, x, y)
|
116
|
+
case k
|
117
|
+
when 27 # Escape
|
118
|
+
exit
|
119
|
+
when 32 # Space
|
120
|
+
@config.screen.bounce ||= false
|
121
|
+
@config.screen.bounce = !@config.screen.bounce
|
122
|
+
when 102 #f
|
123
|
+
@config.screen.wanted_fps = case @config.screen.wanted_fps
|
124
|
+
when 0
|
125
|
+
60
|
126
|
+
when 60
|
127
|
+
50
|
128
|
+
when 50
|
129
|
+
45
|
130
|
+
when 45
|
131
|
+
30
|
132
|
+
when 30
|
133
|
+
25
|
134
|
+
when 25
|
135
|
+
20
|
136
|
+
when 20
|
137
|
+
0
|
138
|
+
end
|
139
|
+
puts "WANTED_FPS[#{@config.screen.wanted_fps}]"
|
140
|
+
when 98
|
141
|
+
@config.screen.mode = 1 - @config.screen.mode.to_i
|
142
|
+
BlobStore.empty
|
143
|
+
end
|
144
|
+
puts "Keypress: #{k}"
|
145
|
+
glutPostRedisplay()
|
146
|
+
end
|
147
|
+
|
148
|
+
# Change view angle
|
149
|
+
def special(k, x, y)
|
150
|
+
glutPostRedisplay()
|
151
|
+
end
|
152
|
+
|
153
|
+
# New window size or exposure
|
154
|
+
def reshape(width, height)
|
155
|
+
@config.reshape(width, height)
|
156
|
+
|
157
|
+
# reset char size
|
158
|
+
@char_size = nil
|
159
|
+
|
160
|
+
@left_left = @config.screen.left.alignment + char_size * (@config.screen.left.size + 1)
|
161
|
+
@left_right = @config.screen.left.alignment + char_size * (@config.screen.left.size + 8)
|
162
|
+
|
163
|
+
@right_left = @config.screen.right.alignment - char_size * (@config.screen.right.size + 1)
|
164
|
+
@right_right = @config.screen.right.alignment - char_size * (@config.screen.right.size + 8)
|
165
|
+
|
166
|
+
glViewport(0, 0, width, height)
|
167
|
+
glMatrixMode(GL_PROJECTION)
|
168
|
+
glLoadIdentity()
|
169
|
+
|
170
|
+
# glFrustum(-1.0, 1.0, -@config.screen.aspect, @config.screen.aspect, 5.0, 60.0)
|
171
|
+
glOrtho(-1.0, 1.0, -@config.screen.aspect, @config.screen.aspect, -1.0, 1.0)
|
172
|
+
|
173
|
+
@config.screen.line_size = @config.screen.aspect * 2 / (@config.screen.window_height/13.0)
|
174
|
+
@config.screen.top = @config.screen.aspect - @config.screen.line_size
|
175
|
+
|
176
|
+
puts "Reshape: #{width}x#{height} = #{@config.screen.aspect}/#{@config.screen.line_size}" if $VRB > 0
|
177
|
+
|
178
|
+
glMatrixMode(GL_MODELVIEW)
|
179
|
+
glLoadIdentity()
|
180
|
+
glTranslate(0.0, 0.0, 0.0)
|
181
|
+
|
182
|
+
BlobStore.empty # Flush cached objects to recreate with correct size
|
183
|
+
end
|
184
|
+
|
185
|
+
def visible(vis)
|
186
|
+
# glutIdleFunc((vis == GLUT_VISIBLE ? method(:idle).to_proc : nil))
|
187
|
+
end
|
188
|
+
|
189
|
+
def mouse(button, state, x, y)
|
190
|
+
@mouse = state
|
191
|
+
@x0, @y0 = x, y
|
192
|
+
end
|
193
|
+
|
194
|
+
def motion(x, y)
|
195
|
+
if @mouse == GLUT_DOWN then
|
196
|
+
end
|
197
|
+
@x0, @y0 = x, y
|
198
|
+
end
|
199
|
+
|
200
|
+
def initialize(config)
|
201
|
+
@config = config
|
202
|
+
|
203
|
+
@frames = 0
|
204
|
+
@t0 = 0
|
205
|
+
@left_left = @left_right = @right_left = @right_right = 0.0 # TODO: Why is draw called before these are set by reshape?
|
206
|
+
end
|
207
|
+
|
208
|
+
def start
|
209
|
+
glutInit()
|
210
|
+
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE)
|
211
|
+
|
212
|
+
glutInitWindowPosition(0, 0)
|
213
|
+
glutInitWindowSize(@config.screen.window_width, @config.screen.window_height)
|
214
|
+
glutCreateWindow('glTail')
|
215
|
+
|
216
|
+
glutDisplayFunc(method(:draw).to_proc)
|
217
|
+
glutReshapeFunc(method(:reshape).to_proc)
|
218
|
+
glutKeyboardFunc(method(:key).to_proc)
|
219
|
+
glutSpecialFunc(method(:special).to_proc)
|
220
|
+
glutVisibilityFunc(method(:visible).to_proc)
|
221
|
+
glutMouseFunc(method(:mouse).to_proc)
|
222
|
+
glutMotionFunc(method(:motion).to_proc)
|
223
|
+
|
224
|
+
glutIdleFunc(method(:idle).to_proc)
|
225
|
+
# glutTimerFunc(33, method(:timer).to_proc, 0)
|
226
|
+
|
227
|
+
glLightfv(GL_LIGHT0, GL_POSITION, [5.0, 5.0, 0.0, 0.0])
|
228
|
+
glLightfv(GL_LIGHT0, GL_AMBIENT, [0,0,0,1])
|
229
|
+
glDisable(GL_CULL_FACE)
|
230
|
+
glEnable(GL_LIGHTING)
|
231
|
+
glEnable(GL_LIGHT0)
|
232
|
+
glEnable(GL_TEXTURE_2D)
|
233
|
+
# glShadeModel(GL_FLAT)
|
234
|
+
glDisable(GL_DEPTH_TEST)
|
235
|
+
glDisable(GL_NORMALIZE)
|
236
|
+
# glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
|
237
|
+
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST )
|
238
|
+
|
239
|
+
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
|
240
|
+
glEnable(GL_COLOR_MATERIAL)
|
241
|
+
glEnable(GL_NORMALIZE)
|
242
|
+
FontStore.generate_font
|
243
|
+
|
244
|
+
@since = glutGet(GLUT_ELAPSED_TIME)
|
245
|
+
|
246
|
+
@config.init
|
247
|
+
|
248
|
+
glutMainLoop()
|
249
|
+
end
|
250
|
+
|
251
|
+
def do_process
|
252
|
+
active = @config.do_process
|
253
|
+
|
254
|
+
if active >= 0
|
255
|
+
|
256
|
+
if glutGet(GLUT_ELAPSED_TIME) - @since >= 1000
|
257
|
+
@since = glutGet(GLUT_ELAPSED_TIME)
|
258
|
+
@config.update
|
259
|
+
BlobStore.prune
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
self
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|