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,43 @@
|
|
1
|
+
class BlobStore
|
2
|
+
@store = { }
|
3
|
+
@used_keys = { }
|
4
|
+
def self.get(key)
|
5
|
+
@used_keys[key] = true
|
6
|
+
return @store[key]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.put(key, ob)
|
10
|
+
@store[key] = ob
|
11
|
+
@used_keys[key] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.has(key)
|
15
|
+
@store.include? key
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.size
|
19
|
+
@store.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.used
|
23
|
+
@used_keys.size
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.prune
|
27
|
+
@store.keys.each do |k|
|
28
|
+
unless @used_keys.include? k
|
29
|
+
glDeleteLists(@store[k], 1)
|
30
|
+
@store.delete k
|
31
|
+
end
|
32
|
+
end
|
33
|
+
@used_keys = { }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.empty
|
37
|
+
@store.keys.each do |k|
|
38
|
+
glDeleteLists(@store[k], 1)
|
39
|
+
@store.delete k
|
40
|
+
end
|
41
|
+
@used_keys = { }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,130 @@
|
|
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 Block
|
8
|
+
include GlTail::Configurable
|
9
|
+
|
10
|
+
attr_reader :name, :bottom_position
|
11
|
+
|
12
|
+
config_attribute :color, "FIXME: add description", :type => :color
|
13
|
+
config_attribute :order, "FIXME"
|
14
|
+
config_attribute :size, "FIXME"
|
15
|
+
config_attribute :auto_clean
|
16
|
+
|
17
|
+
attr_accessor :column
|
18
|
+
attr_reader :config
|
19
|
+
|
20
|
+
def initialize(config, name)
|
21
|
+
@config = config
|
22
|
+
@name = name
|
23
|
+
|
24
|
+
@size = 10
|
25
|
+
@auto_clean = true
|
26
|
+
@order = 100
|
27
|
+
# @color = [1.0, 1.0, 1.0, 1.0]
|
28
|
+
|
29
|
+
@show = 0
|
30
|
+
|
31
|
+
@header = Element.new(self, @name.upcase)
|
32
|
+
@header.color = [1.0, 1.0, 1.0, 1.0]
|
33
|
+
|
34
|
+
@elements = { }
|
35
|
+
@bottom_position = -@config.screen.top
|
36
|
+
end
|
37
|
+
|
38
|
+
def show=(value)
|
39
|
+
@show = case value
|
40
|
+
when 'rate' then 0
|
41
|
+
when 'total' then 1
|
42
|
+
when 'average' then 2
|
43
|
+
else
|
44
|
+
0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :show
|
49
|
+
|
50
|
+
def top
|
51
|
+
@config.screen.top
|
52
|
+
end
|
53
|
+
|
54
|
+
def line_size
|
55
|
+
@config.screen.line_size
|
56
|
+
end
|
57
|
+
|
58
|
+
def is_right
|
59
|
+
column.is_right
|
60
|
+
end
|
61
|
+
|
62
|
+
def alignment
|
63
|
+
column.alignment
|
64
|
+
end
|
65
|
+
|
66
|
+
def position
|
67
|
+
column.position
|
68
|
+
end
|
69
|
+
|
70
|
+
def width
|
71
|
+
column.size
|
72
|
+
end
|
73
|
+
|
74
|
+
def render(engine, num)
|
75
|
+
return num if @elements.size == 0
|
76
|
+
|
77
|
+
@header.wy = top - (num * line_size)
|
78
|
+
# @header.y = @header.wy if @header.y == -$CONFIG.top
|
79
|
+
@header.render(engine)
|
80
|
+
num += 1
|
81
|
+
|
82
|
+
sorted = case @show
|
83
|
+
when 0: @elements.values.sort { |k,v| v.rate <=> k.rate}[0..@size-1]
|
84
|
+
when 1: @elements.values.sort { |k,v| v.total <=> k.total}[0..@size-1]
|
85
|
+
when 2: @elements.values.sort { |k,v| v.average <=> k.average}[0..@size-1]
|
86
|
+
end
|
87
|
+
|
88
|
+
sorted.each do |e|
|
89
|
+
e.wy = top - (num * line_size)
|
90
|
+
e.render(engine)
|
91
|
+
engine.stats[0] += 1
|
92
|
+
if e.rate <= 0.0001 && e.active && e.updates > 59 && @auto_clean
|
93
|
+
@elements.delete(e.name)
|
94
|
+
end
|
95
|
+
num += 1
|
96
|
+
end
|
97
|
+
|
98
|
+
(@elements.values - sorted).each do |e|
|
99
|
+
engine.stats[0] += 1
|
100
|
+
e.activities.each do |a|
|
101
|
+
a.render(engine)
|
102
|
+
if a.x > 1.0 || a.x < -1.0 || a.y > @config.screen.aspect
|
103
|
+
e.activities.delete a
|
104
|
+
end
|
105
|
+
end
|
106
|
+
if e.activities.size == 0 && @auto_clean && e.updates > 59
|
107
|
+
@elements.delete(e.name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
@elements.delete_if { |k,v| (!sorted.include? v) && v.active && v.activities.size == 0 && v.updates > 29} if @auto_clean
|
111
|
+
@bottom_position = top - ((sorted.size > 0 ? (num-1) : num) * line_size)
|
112
|
+
num + 1
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_activity(options = { })
|
116
|
+
x = @elements[options[:name]] ||= Element.new(self, options[:name])
|
117
|
+
x.add_activity(options[:message], @color || options[:color] , options[:size] || 0.01, options[:type] || 0 )
|
118
|
+
end
|
119
|
+
|
120
|
+
def add_event(options = { })
|
121
|
+
x = @elements[options[:name]] ||= Element.new(self, options[:name])
|
122
|
+
x.add_event(options[:message], options[:color] || @color, options[:update_stats] || false)
|
123
|
+
end
|
124
|
+
|
125
|
+
def update
|
126
|
+
@elements.each_value do |e|
|
127
|
+
e.update
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
|
2
|
+
module GlTail
|
3
|
+
CONFIG_OPTIONS = { }
|
4
|
+
|
5
|
+
COLORS = {
|
6
|
+
'white' => %w{ 255 255 255 255 },
|
7
|
+
'red' => %w{ 255 0 0 255 },
|
8
|
+
'green' => %w{ 0 255 0 255 },
|
9
|
+
'blue' => %w{ 0 0 255 255 },
|
10
|
+
'yellow' => %w{ 255 255 0 255 },
|
11
|
+
'cyan' => %w{ 0 255 255 255 },
|
12
|
+
'magenta' => %w{ 255 0 255 255 },
|
13
|
+
|
14
|
+
'purple' => %w{ 128 0 255 255 },
|
15
|
+
'orange' => %w{ 255 128 0 255 },
|
16
|
+
'pink' => %w{ 255 0 128 255 },
|
17
|
+
}
|
18
|
+
|
19
|
+
class Screen
|
20
|
+
include Configurable
|
21
|
+
|
22
|
+
config_attribute :wanted_fps, "FIXME: add description"
|
23
|
+
|
24
|
+
config_attribute :min_blob_size, "Minimum size of activity indicators [0.0 - 1.0]"
|
25
|
+
config_attribute :max_blob_size, "Maximum size of activity indicators [0.0 - 1.0]"
|
26
|
+
|
27
|
+
# shortcut to set these via dimensions
|
28
|
+
config_attribute :window_width, "Width of GlTail window"
|
29
|
+
config_attribute :window_height, "Height of GlTail window"
|
30
|
+
|
31
|
+
config_attribute :mode, "FIXME"
|
32
|
+
config_attribute :bounce, "FIXME"
|
33
|
+
|
34
|
+
config_attribute :highlight_color, "FIXME: add description", :type => :color
|
35
|
+
|
36
|
+
attr_accessor :aspect, :line_size, :top, :bitmap_mode
|
37
|
+
|
38
|
+
def initialize(config)
|
39
|
+
@config = config
|
40
|
+
|
41
|
+
@wanted_fps = 0
|
42
|
+
@aspect = 0.6
|
43
|
+
@bounce = false
|
44
|
+
|
45
|
+
@top = 0.9
|
46
|
+
@line_size = 0.03
|
47
|
+
@bitmap_mode = 0
|
48
|
+
@mode = 0
|
49
|
+
@highlight_color = [1.0, 0.0, 0.0, 1.0]
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_dim(x, y)
|
53
|
+
self.window_width = x.to_i
|
54
|
+
self.window_height = y.to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def dimensions=(dim)
|
59
|
+
if dim.is_a?(String)
|
60
|
+
case dim
|
61
|
+
when /^(\d+)x(\d+)$/
|
62
|
+
set_dim $1, $2
|
63
|
+
when /^(\d+), (\d+)$/
|
64
|
+
set_dim $1, $2
|
65
|
+
else
|
66
|
+
raise "dimensions not understood #{dim}, please use <width>x<height> or <width>, <height>"
|
67
|
+
end
|
68
|
+
else
|
69
|
+
raise "what are you #{dim.inspect}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def right
|
74
|
+
@right ||= Column.new(@config, :right)
|
75
|
+
end
|
76
|
+
|
77
|
+
def left
|
78
|
+
@left ||= Column.new(@config, :left)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class Column
|
84
|
+
include Configurable
|
85
|
+
|
86
|
+
config_attribute :size, "FIXME: add description"
|
87
|
+
config_attribute :alignment, "FIXME: add description"
|
88
|
+
|
89
|
+
def initialize(config, which)
|
90
|
+
@config = config
|
91
|
+
@which = which
|
92
|
+
end
|
93
|
+
|
94
|
+
def is_right
|
95
|
+
@which == :right
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Config
|
100
|
+
class << self
|
101
|
+
def parse_yaml(file)
|
102
|
+
require 'yaml'
|
103
|
+
|
104
|
+
YamlParser.new(file).apply(self.new)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_reader :sources
|
109
|
+
|
110
|
+
def initialize
|
111
|
+
@sources = []
|
112
|
+
@blocks = []
|
113
|
+
@max_size = 1.0
|
114
|
+
end
|
115
|
+
|
116
|
+
def screen
|
117
|
+
@screen ||= Screen.new(self)
|
118
|
+
end
|
119
|
+
|
120
|
+
def blocks
|
121
|
+
@blocks
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_block(name)
|
125
|
+
@blocks << b = Block.new(self, name)
|
126
|
+
b
|
127
|
+
end
|
128
|
+
|
129
|
+
def reshape(w, h)
|
130
|
+
screen.aspect = h.to_f / w.to_f
|
131
|
+
screen.window_width, screen.window_height = w, h
|
132
|
+
end
|
133
|
+
|
134
|
+
def do_process
|
135
|
+
active = 0
|
136
|
+
|
137
|
+
sources.each do |it|
|
138
|
+
active += 1
|
139
|
+
it.process
|
140
|
+
end
|
141
|
+
|
142
|
+
active
|
143
|
+
end
|
144
|
+
|
145
|
+
def update
|
146
|
+
sources.each { |it| it.update }
|
147
|
+
blocks.each { |it| it.update }
|
148
|
+
@max_size = @max_size * 0.99 if(@max_size * 0.99 > 1.0)
|
149
|
+
end
|
150
|
+
|
151
|
+
def init
|
152
|
+
sources.each { |it| it.init }
|
153
|
+
|
154
|
+
@blocks_by_name = {}
|
155
|
+
|
156
|
+
blocks.each do |it|
|
157
|
+
@blocks_by_name[it.name] = it
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#block, message, size
|
162
|
+
def add_activity(source, options = { })
|
163
|
+
size = screen.min_blob_size
|
164
|
+
if options[:size]
|
165
|
+
size = options[:size].to_f
|
166
|
+
@max_size = size if size > @max_size
|
167
|
+
size = screen.min_blob_size + ((size / @max_size) * (screen.max_blob_size - screen.min_blob_size))
|
168
|
+
options[:size] = size
|
169
|
+
end
|
170
|
+
|
171
|
+
if block = @blocks_by_name[options[:block]]
|
172
|
+
block.add_activity({
|
173
|
+
:name => source.name,
|
174
|
+
:color => source.color,
|
175
|
+
:size => screen.min_blob_size
|
176
|
+
}.update(options) )
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
#block, message
|
181
|
+
def add_event(source, options = { })
|
182
|
+
if block = @blocks_by_name[options[:block]]
|
183
|
+
block.add_event( { :name => source.name, :color => source.color, :size => screen.min_blob_size}.update(options) )
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GlTail
|
2
|
+
|
3
|
+
module Configurable
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def config_attribute(id, description = "", opts = {})
|
7
|
+
|
8
|
+
rewrite_method = case opts[:type]
|
9
|
+
when :color
|
10
|
+
"config_rewrite_color"
|
11
|
+
else
|
12
|
+
""
|
13
|
+
end
|
14
|
+
|
15
|
+
self.class_eval %{
|
16
|
+
def #{id}=(value)
|
17
|
+
@#{id} = #{rewrite_method} value
|
18
|
+
end
|
19
|
+
|
20
|
+
def #{id}
|
21
|
+
@#{id}
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
doc = GlTail::CONFIG_OPTIONS[self.to_s] ||= {}
|
26
|
+
doc[id] = {
|
27
|
+
:description => description,
|
28
|
+
}.update(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def config_rewrite_color(v)
|
34
|
+
case v
|
35
|
+
when /(.+),(.+),(.+),(.+)/
|
36
|
+
value = v.split(',')
|
37
|
+
else
|
38
|
+
value = GlTail::COLORS[v.downcase]
|
39
|
+
unless value
|
40
|
+
raise SyntaxError, "You must use either [#{GlTail::COLORS.keys.sort.join('|')}] or a color in RGBA format."
|
41
|
+
end
|
42
|
+
value.map! { |x| x.to_i / 255.0 }
|
43
|
+
end
|
44
|
+
value.map {|x| x.to_f }
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.included(receiver)
|
48
|
+
receiver.extend ClassMethods
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module GlTail
|
4
|
+
class YamlParser
|
5
|
+
attr_reader :yaml
|
6
|
+
|
7
|
+
def initialize file
|
8
|
+
file ||= "config.yaml"
|
9
|
+
@yaml = YAML.load_file(file)
|
10
|
+
end
|
11
|
+
|
12
|
+
def apply(config)
|
13
|
+
@config = config
|
14
|
+
|
15
|
+
@left = Hash.new
|
16
|
+
@right = Hash.new
|
17
|
+
@blocks = Array.new
|
18
|
+
|
19
|
+
parse_servers
|
20
|
+
parse_config
|
21
|
+
parse_resolver
|
22
|
+
|
23
|
+
@config
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_resolver
|
27
|
+
if x = self.yaml['resolver']
|
28
|
+
x.each do |k, v|
|
29
|
+
apply_value(Resolver.instance, k, v )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_servers
|
35
|
+
|
36
|
+
self.yaml['servers'].each do |server|
|
37
|
+
|
38
|
+
src = GlTail::Source::SSH.new(@config)
|
39
|
+
|
40
|
+
src.name = server.shift
|
41
|
+
|
42
|
+
apply_values(src, server.shift)
|
43
|
+
|
44
|
+
@config.sources << src
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def apply_values(target, hash)
|
49
|
+
hash.each do |key, value|
|
50
|
+
apply_value(target, key, value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def apply_value(target, key, value)
|
55
|
+
begin
|
56
|
+
target.send("#{key}=", value)
|
57
|
+
rescue
|
58
|
+
puts "FAILED TO APPLY #{key}=#{value} TO #{target}"
|
59
|
+
raise
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_config
|
64
|
+
self.yaml['config'].each do |key, config|
|
65
|
+
screen = @config.screen
|
66
|
+
|
67
|
+
case config
|
68
|
+
when Hash
|
69
|
+
if key =~ /(left|right)_column/
|
70
|
+
column = screen.send($1)
|
71
|
+
blocks = config.delete('blocks')
|
72
|
+
|
73
|
+
apply_values(column, config)
|
74
|
+
add_blocks(column, blocks)
|
75
|
+
else
|
76
|
+
target = screen.send(key)
|
77
|
+
apply_values(target, config)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
apply_value(screen, key, config)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_blocks(column, hash)
|
86
|
+
hash.each do |key, config|
|
87
|
+
block = @config.add_block(key)
|
88
|
+
block.column = column
|
89
|
+
|
90
|
+
apply_values(block, config)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|