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 CHANGED
@@ -2,3 +2,4 @@ pkg
2
2
  doc
3
3
  Manifest
4
4
  .swp
5
+ tmp/*
@@ -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
@@ -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.6
1
+ 0.1.7
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{colortail}
8
- s.version = "0.1.6"
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-04-26}
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
- files = opt[:files]
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
- @match_group = config.load_opts(options[:group])
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
- @match_group = Array.new
32
- @match_group.push( 'default' => [] )
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
- # Before we go any further, check for existance of files
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 @match_group.class == Array
66
- @match_group.each do |matcher|
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( @match_group )
69
+ logger.add_color_matcher( match_group )
71
70
  end
72
-
73
- # Create a thread for each file
74
- threads = []
75
- files.each do |file|
76
- threads[files.index(file)] = Thread.new {
77
- tailer = ColorTail::TailFile.new( file )
78
-
79
- # First display the last 10 lines of each file in ARGV
80
- tailer.interval = 10
81
- tailer.backward( 10 )
82
-
83
- # Tail the file and show new lines
84
- tailer.tail { |line| logger.log( file, line ) }
85
- }
86
- threads[files.index(file)].run
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.each do |thread|
104
- thread.kill
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
- @config = File.read(conf)
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, groupings)
14
- if groupings.class == Hash
15
- if groupings.has_key?( group )
16
- return groupings[group]
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
- if File.exists?(@config_file)
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.on_tail( '-h', '--help', 'Display this help screen' ) do
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.6
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-04-26 00:00:00 -04:00
12
+ date: 2010-05-09 00:00:00 -04:00
13
13
  default_executable: colortail
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency