colortail 0.1.6 → 0.1.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/.gitignore +1 -0
- data/Changelog.markdown +7 -0
- data/README.markdown +16 -2
- data/VERSION +1 -1
- data/colortail.gemspec +2 -2
- data/lib/colortail/application.rb +134 -42
- data/lib/colortail/configuration.rb +33 -11
- data/test/colortail/test_application.rb +12 -0
- metadata +2 -2
data/.gitignore
CHANGED
data/Changelog.markdown
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
### colortail 0.1.7 2010-05-10
|
2
|
+
|
3
|
+
* FEATURE: Color groupings can now be applied on a per file basis (#10)
|
4
|
+
* FEATURE: Can now pipe input into colortail (#7)
|
5
|
+
* FEATURE: Filename prefixes are now available (-F) (#9)
|
6
|
+
* BUGFIX: Removed "No such group" on match listing (#5)
|
7
|
+
|
1
8
|
### colortail 0.1.6 2010-04-26
|
2
9
|
|
3
10
|
* BUGFIX: Lots of bugfixes as a result of finally adding testing
|
data/README.markdown
CHANGED
@@ -65,9 +65,23 @@ Using ColorTail is similar to using tail. The main assumption is that you will a
|
|
65
65
|
|
66
66
|
#### Tailing with groups
|
67
67
|
|
68
|
-
The command below will tail the **/var/log/messages** file using the syslog group. The example config [examples/colortail.rb](http://www.codaset.com/elubow/colortail/source/master/blob/examples/colortail.rb) shows a _syslog_ grouping that is used in command below:
|
68
|
+
The command below will tail the **/var/log/messages** file using the syslog group. The example config [examples/colortail.rb](http://www.codaset.com/elubow/colortail/source/master/blob/examples/colortail.rb) shows a _syslog_ grouping that is used in command below (the below 2 commands are equivilent):
|
69
69
|
|
70
|
-
# colortail -g syslog /var/log/messages
|
70
|
+
# colortail -g syslog /var/log/messages
|
71
|
+
# cat /var/log/messages | colortail -g syslog
|
72
|
+
|
73
|
+
#### Tailing multiple files
|
74
|
+
|
75
|
+
To tail multiple files can be confusing, especially when you don't know which file you are seeing. Use the **-F** option to show the filenames at the beginning of each colored line.
|
76
|
+
|
77
|
+
# colortail -F -g syslog /var/log/messages /var/log/secure.log
|
78
|
+
|
79
|
+
#### Tailing multiple files using different color groups
|
80
|
+
|
81
|
+
You can also tail multiple files using different color groups. Currently, the separater is *#*. If no grouping is specified with the file or the grouping specified doesn't exist, colortail will default to the one specied on the command line.
|
82
|
+
|
83
|
+
# colortail /var/log/messages#syslog /var/log/secure.log#otherlog
|
84
|
+
# colortail -g syslog /var/log/messags#nosuchgroup /var/log/secure.log#secure
|
71
85
|
|
72
86
|
## Caveats and Intended Behaviors ##
|
73
87
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.7
|
data/colortail.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{colortail}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Eric Lubow"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-05-09}
|
13
13
|
s.default_executable = %q{colortail}
|
14
14
|
s.description = %q{Tail a file and color lines based on regular expressions within that line. By setting up multiple expression and color groups in the configuration file, you can apply highlighting to a file while its being tailed.}
|
15
15
|
s.email = %q{eric@lubow.org}
|
@@ -2,9 +2,16 @@ module ColorTail
|
|
2
2
|
|
3
3
|
class Application
|
4
4
|
class << self
|
5
|
+
def version
|
6
|
+
version = File.read("#{File.join(File.dirname(__FILE__), '..')}/../VERSION").chomp!
|
7
|
+
"#{File.basename($0)} v#{version}"
|
8
|
+
end
|
9
|
+
|
5
10
|
def run!(*arguments)
|
11
|
+
require 'fcntl'
|
12
|
+
|
6
13
|
opt = ColorTail::Options.new(arguments)
|
7
|
-
|
14
|
+
filelist = opt[:files]
|
8
15
|
options = opt[:options]
|
9
16
|
|
10
17
|
# Deal with any/all options issues
|
@@ -15,9 +22,16 @@ module ColorTail
|
|
15
22
|
|
16
23
|
# Show the help menu if desired
|
17
24
|
if options[:help]
|
25
|
+
$stderr.puts version()
|
18
26
|
$stderr.puts opt.opts
|
19
27
|
return 1
|
20
28
|
end
|
29
|
+
|
30
|
+
# Show the version
|
31
|
+
if options[:version]
|
32
|
+
$stderr.puts version()
|
33
|
+
return 1
|
34
|
+
end
|
21
35
|
|
22
36
|
|
23
37
|
# The meat of the program
|
@@ -25,11 +39,11 @@ module ColorTail
|
|
25
39
|
# Read the config file if it exists
|
26
40
|
if File.exists?(options[:conf])
|
27
41
|
config = ColorTail::Configuration.new(options[:conf])
|
28
|
-
|
42
|
+
match_group = config.load_opts(options[:group])
|
29
43
|
else
|
30
44
|
# Create this to ensure we always have a value for this array
|
31
|
-
|
32
|
-
|
45
|
+
match_group = Array.new
|
46
|
+
match_group.push( 'default' => [] )
|
33
47
|
end
|
34
48
|
|
35
49
|
# Display the list of available groups and exit
|
@@ -42,48 +56,116 @@ module ColorTail
|
|
42
56
|
end
|
43
57
|
return 1
|
44
58
|
end
|
45
|
-
|
46
|
-
#
|
47
|
-
@files_exist = false
|
48
|
-
files.each do |file|
|
49
|
-
if File.exists?(file)
|
50
|
-
@files_exist = true
|
51
|
-
break
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# If we have no files, tell them and show the help
|
56
|
-
unless @files_exist
|
57
|
-
$stderr.puts "Please check to make sure the files exist ..."
|
58
|
-
$stderr.puts opt.opts
|
59
|
-
return 1
|
60
|
-
end
|
61
|
-
|
59
|
+
|
60
|
+
# Create the default logging object
|
62
61
|
logger = ColorTail::Colorize.new()
|
63
62
|
|
64
63
|
# Add the color match array if we aren't using the default
|
65
|
-
if
|
66
|
-
|
64
|
+
if match_group.class == Array
|
65
|
+
match_group.each do |matcher|
|
67
66
|
logger.add_color_matcher( matcher )
|
68
67
|
end
|
69
68
|
else
|
70
|
-
logger.add_color_matcher(
|
69
|
+
logger.add_color_matcher( match_group )
|
71
70
|
end
|
72
|
-
|
73
|
-
# Create
|
74
|
-
threads =
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
71
|
+
|
72
|
+
# Create an empty array of threads
|
73
|
+
threads = Array.new
|
74
|
+
|
75
|
+
# Set $stdin to be non-blocking
|
76
|
+
$stdin.fcntl(Fcntl::F_SETFL,Fcntl::O_NONBLOCK)
|
77
|
+
|
78
|
+
begin
|
79
|
+
threads[0] = Thread.new {
|
80
|
+
begin
|
81
|
+
$stdin.each_line do |line|
|
82
|
+
if options[:filename_prefix]
|
83
|
+
logger.log( nil, line, "STDIN: " )
|
84
|
+
else
|
85
|
+
logger.log( nil, line )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
rescue Errno::EAGAIN
|
89
|
+
# Remove this thread since we won't be reading from $stdin
|
90
|
+
threads.delete(0)
|
91
|
+
end
|
92
|
+
}.run
|
93
|
+
|
94
|
+
# Check to see if there are files in the files array
|
95
|
+
if filelist.size > 0
|
96
|
+
# Before we go any further, check for existance of files
|
97
|
+
@files_exist = false
|
98
|
+
files = Hash.new
|
99
|
+
filelist.each do |file|
|
100
|
+
filename = String.new
|
101
|
+
group = String.new
|
102
|
+
filename,group = file.split('#')
|
103
|
+
if group.nil?
|
104
|
+
files[filename] = options[:group]
|
105
|
+
else
|
106
|
+
files[filename] = group
|
107
|
+
end
|
108
|
+
|
109
|
+
# Check for individual files and ignore file if doesn't exist
|
110
|
+
if File.exists?(filename)
|
111
|
+
@files_exist = true
|
112
|
+
else
|
113
|
+
$stderr.puts("#{filename} does not exist, skipping...")
|
114
|
+
filelist.delete(file)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# If we have no files, tell them and show the help
|
119
|
+
if !@files_exist and (threads.class == Array.class and threads.size <= 2)
|
120
|
+
$stderr.puts "Please check to make sure the files exist ..."
|
121
|
+
$stderr.puts opt.opts
|
122
|
+
return 1
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create a thread for each file
|
126
|
+
files.each_pair do |filename, grouping|
|
127
|
+
threads.push Thread.new {
|
128
|
+
# Figure out if grouping exists, if not fall back to global default
|
129
|
+
if config.group_exists?(grouping)
|
130
|
+
# Create a new per file logger object per file
|
131
|
+
file_logger = ColorTail::Colorize.new()
|
132
|
+
|
133
|
+
# Redefine match_group since we have a new grouping
|
134
|
+
match_group = config.load_opts(grouping)
|
135
|
+
|
136
|
+
# Add the color match array if we aren't using the default
|
137
|
+
if match_group.class == Array
|
138
|
+
match_group.each do |matcher|
|
139
|
+
file_logger.add_color_matcher( matcher )
|
140
|
+
end
|
141
|
+
else
|
142
|
+
file_logger.add_color_matcher( match_group )
|
143
|
+
end
|
144
|
+
|
145
|
+
# There is no such group, so we fall back to the default here
|
146
|
+
else
|
147
|
+
file_logger = logger
|
148
|
+
end
|
149
|
+
|
150
|
+
# Create the new tailer object
|
151
|
+
tailer = ColorTail::TailFile.new( filename )
|
152
|
+
|
153
|
+
# First display the last 10 lines of each file in ARGV
|
154
|
+
tailer.interval = 10
|
155
|
+
tailer.backward( 10 )
|
156
|
+
|
157
|
+
# Tail the file and show new lines
|
158
|
+
tailer.tail do |line|
|
159
|
+
if options[:filename_prefix]
|
160
|
+
file_logger.log( filename, line, "#{filename}: ")
|
161
|
+
else
|
162
|
+
file_logger.log( filename, line )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
}.run
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
87
169
|
end
|
88
170
|
|
89
171
|
# Let the threads do their real work
|
@@ -93,15 +175,22 @@ module ColorTail
|
|
93
175
|
|
94
176
|
# If we get a CTRL-C, catch it (rescue) and send it for cleanup
|
95
177
|
rescue Interrupt
|
96
|
-
thread_cleanup(threads)
|
178
|
+
thread_cleanup(threads) if defined? threads
|
179
|
+
rescue NoInputError
|
180
|
+
$stderr.puts "Please enter a file to tail..."
|
181
|
+
$stderr.puts opt.opts
|
182
|
+
return 1
|
97
183
|
end
|
98
184
|
|
185
|
+
# We should never make it here, but just in case...
|
99
186
|
return 0
|
100
187
|
end
|
101
188
|
|
102
189
|
def thread_cleanup(threads)
|
103
|
-
threads.
|
104
|
-
thread
|
190
|
+
if threads.class == Array.class and threads.size > 0
|
191
|
+
threads.each do |thread|
|
192
|
+
thread.kill
|
193
|
+
end
|
105
194
|
end
|
106
195
|
$stderr.puts "Terminating..."
|
107
196
|
exit
|
@@ -113,6 +202,9 @@ module ColorTail
|
|
113
202
|
end
|
114
203
|
|
115
204
|
|
205
|
+
class NoInputError < StandardError
|
206
|
+
end
|
207
|
+
|
116
208
|
class FileDoesNotExist < StandardError
|
117
209
|
end
|
118
210
|
|
@@ -4,16 +4,17 @@ module ColorTail
|
|
4
4
|
def initialize(conf)
|
5
5
|
@config_file = conf
|
6
6
|
if File.exists?(conf)
|
7
|
-
@
|
7
|
+
load @config_file
|
8
8
|
else
|
9
9
|
raise FileDoesNotExist, "Config file #{@config_file} cannot be found."
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def colorit(group
|
14
|
-
if
|
15
|
-
|
16
|
-
|
13
|
+
def colorit(group)
|
14
|
+
if Groupings.class == Hash
|
15
|
+
Groupings["default"] = [] unless Groupings.has_key?('default')
|
16
|
+
if Groupings.has_key?( group )
|
17
|
+
return Groupings[group]
|
17
18
|
else
|
18
19
|
$stderr.puts "No such group '#{group}', falling back to default."
|
19
20
|
return "'default' => []"
|
@@ -27,11 +28,8 @@ module ColorTail
|
|
27
28
|
# isn't available in the configuration object until after a new object
|
28
29
|
# has been instantiated.
|
29
30
|
def load_opts(group)
|
30
|
-
colorset =
|
31
|
-
|
32
|
-
load @config_file
|
33
|
-
colorset = self.colorit( group, Groupings )
|
34
|
-
end
|
31
|
+
colorset = Array.new
|
32
|
+
colorset = self.colorit( group )
|
35
33
|
return colorset
|
36
34
|
end
|
37
35
|
|
@@ -40,6 +38,10 @@ module ColorTail
|
|
40
38
|
puts " * #{group}"
|
41
39
|
end
|
42
40
|
end
|
41
|
+
|
42
|
+
def group_exists?(key)
|
43
|
+
Groupings.has_key?(key) ? true : false
|
44
|
+
end
|
43
45
|
end
|
44
46
|
|
45
47
|
class ComplexRecord < StandardError
|
@@ -65,6 +67,11 @@ module ColorTail
|
|
65
67
|
o.on( '-g', '--group <group>', 'Specify the color grouping to use for these files' ) do |group|
|
66
68
|
options[:group] = group
|
67
69
|
end
|
70
|
+
|
71
|
+
options[:filename_prefix] = false
|
72
|
+
o.on( '-F', '--filename_prefix', 'Prefix each colored line with it\'s filename') do
|
73
|
+
options[:filename_prefix] = true
|
74
|
+
end
|
68
75
|
|
69
76
|
options[:list] = false
|
70
77
|
o.on( '-l', '--list', 'List all the available color groupings' ) do |group|
|
@@ -81,11 +88,26 @@ module ColorTail
|
|
81
88
|
raise FileDoesNotExist, "Config file #{file} cannot be found."
|
82
89
|
end
|
83
90
|
end
|
91
|
+
|
92
|
+
o.on('-V', '--version', "Display version information") do
|
93
|
+
options[:version] = true
|
94
|
+
end
|
84
95
|
|
85
96
|
options[:help] = false
|
86
|
-
o.
|
97
|
+
o.on( '-h', '--help', 'Display this help screen' ) do
|
87
98
|
options[:help] = true
|
88
99
|
end
|
100
|
+
|
101
|
+
o.separator ""
|
102
|
+
o.separator "Examples:"
|
103
|
+
o.separator " Tail messages and mail log through STDIN with syslog group:"
|
104
|
+
o.separator " cat /var/log/maillog | #{File.basename($0)} -g syslog /var/log/messages"
|
105
|
+
|
106
|
+
o.separator " Tail messages with syslog group and maillog with mail group:"
|
107
|
+
o.separator " #{File.basename($0)} /var/log/messages#syslog /var/log/messages#mail"
|
108
|
+
|
109
|
+
o.separator " Tail messages with syslog group and maillog with mail group with line prefix:"
|
110
|
+
o.separator " #{File.basename($0)} -F /var/log/messages#syslog /var/log/messages#mail"
|
89
111
|
end
|
90
112
|
|
91
113
|
begin
|
@@ -38,6 +38,18 @@ module TestColortail
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
context "With '-V' option and no files" do
|
42
|
+
setup do
|
43
|
+
ARGV.clear
|
44
|
+
ARGV.push("-V")
|
45
|
+
end
|
46
|
+
|
47
|
+
should "show the version string" do
|
48
|
+
ColorTail::Application.run!(*ARGV)
|
49
|
+
assert_match /.*\ v/, $stderr.string
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
41
53
|
context "With '-h' option and no files" do
|
42
54
|
setup do
|
43
55
|
ARGV.clear
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: colortail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Lubow
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-05-09 00:00:00 -04:00
|
13
13
|
default_executable: colortail
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|