gltail 0.0.7
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.
- 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
|