logmerge 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ All original code copyright 2005 Eric Hodel, The Robot Co-op. All rights
2
+ reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+ 3. Neither the names of the authors nor the names of their contributors
14
+ may be used to endorse or promote products derived from this software
15
+ without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
18
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
21
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
23
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ Manifest.txt
3
+ README
4
+ Rakefile
5
+ bin/ip2name
6
+ bin/logmerge
7
+ lib/logmerge.rb
8
+ lib/logmerge/merger.rb
9
+ lib/logmerge/resolv.rb
10
+ lib/logmerge/resolver.rb
11
+ resolv_test
12
+ test/access.log
13
+ test/test_merger.rb
14
+ test/test_resolver.rb
data/README ADDED
@@ -0,0 +1,35 @@
1
+ = logmerge
2
+
3
+ Rubyforge Project:
4
+
5
+ http://rubyforge.org/projects/rctools/
6
+
7
+ == About
8
+
9
+ Logmerge contains two utilities logmerge and ip2name. logmerge merges Apache
10
+ access logs into one log ordered by date. ip2name performs DNS lookups on
11
+ Apache access logs using multiple threads and Ruby's DNS resolver library to
12
+ speed through log files.
13
+
14
+ == Using logmerge
15
+
16
+ Simply pass in all the logs you want to merge on the command line. logmerge
17
+ gives you the merged log on STDOUT.
18
+
19
+ logmerge hal/access.log nestor/access.log herbie/access.log > merged.log
20
+
21
+ Note that logmerge expects the input log files to be ordered by date.
22
+
23
+ == Using ip2name
24
+
25
+ Simply pass in the log files you want to perform DNS lookups on the command
26
+ line or via STDIN. ip2name gives you the looked-up log lines on STDOUT.
27
+
28
+ ip2name < merged.log > resolved.log
29
+
30
+ ip2name merged.log > resolved.log
31
+
32
+ In order to speed DNS lookups, ip2name creates a .name_cache file in the
33
+ current directory. ip2name uses double the DNS record TTL value so
34
+ rapidly-changing names may not be correctly resolved.
35
+
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ $VERBOSE = nil
7
+
8
+ spec = Gem::Specification.new do |s|
9
+ s.name = 'logmerge'
10
+ s.version = '1.0.0'
11
+ s.summary = 'Resolves IP addresses and merges Apache access logs.'
12
+ s.author = 'Eric Hodel'
13
+ s.email = 'eric@robotcoop.com'
14
+
15
+ s.files = File.read('Manifest.txt').split($/)
16
+ s.require_path = 'lib'
17
+
18
+ s.executables = %w[ip2name logmerge]
19
+ end
20
+
21
+ desc 'Run tests'
22
+ task :default => [ :test ]
23
+
24
+ Rake::TestTask.new('test') do |t|
25
+ t.libs << 'test'
26
+ t.verbose = true
27
+ end
28
+
29
+ desc 'Update Manifest.txt'
30
+ task :update_manifest do
31
+ sh "find . -type f | sed -e 's%./%%' | egrep -v 'svn|swp|~' | egrep -v '^(doc|pkg)/' | sort > Manifest.txt"
32
+ end
33
+
34
+ desc 'Generate RDoc'
35
+ Rake::RDocTask.new :rdoc do |rd|
36
+ rd.rdoc_dir = 'doc'
37
+ rd.rdoc_files.add 'lib', 'README', 'LICENSE'
38
+ rd.rdoc_files.exclude '*/resolv.rb' # WTF doesn't this work?
39
+ rd.main = 'README'
40
+ rd.options << '-d' if `which dot` =~ /\/dot/
41
+ end
42
+
43
+ desc 'Generate RDoc for dev.robotcoop.com'
44
+ Rake::RDocTask.new :dev_rdoc do |rd|
45
+ rd.rdoc_dir = '../../../www/trunk/dev/html/Tools/logmerge'
46
+ rd.rdoc_files.add 'lib', 'README', 'LICENSE'
47
+ rd.main = 'README'
48
+ rd.options << '-d' if `which dot` =~ /\/dot/
49
+ end
50
+
51
+ desc 'Build Gem'
52
+ Rake::GemPackageTask.new spec do |pkg|
53
+ pkg.need_tar = true
54
+ end
55
+
56
+ desc 'Clean up'
57
+ task :clean => [ :clobber_rdoc, :clobber_package ]
58
+
59
+ desc 'Clean up'
60
+ task :clobber => [ :clean ]
61
+
62
+ # vim: syntax=Ruby
63
+
data/bin/ip2name ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'logmerge'
4
+
5
+ Thread.abort_on_exception = true
6
+
7
+ LogMerge::IPResolver.resolve
8
+
data/bin/logmerge ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'logmerge'
4
+
5
+ LogMerge::Merger.merge
6
+
@@ -0,0 +1,74 @@
1
+ $TESTING = defined? $TESTING
2
+
3
+ require 'logmerge'
4
+
5
+ ##
6
+ # Merges multiple Apache log files by date.
7
+
8
+ class LogMerge::Merger
9
+
10
+ ##
11
+ # Merges +files+ into +output+. If +files+ is nil, ARGV is used.
12
+
13
+ def self.merge(output = STDOUT, *files)
14
+ files = ARGV if files.empty?
15
+ merger = LogMerge::Merger.new files.map { |file| File.open file }
16
+ merger.merge output
17
+ end
18
+
19
+ ##
20
+ # Creates a new Merger that will operate on an array of IO-like objects
21
+ # +streams+.
22
+
23
+ def initialize(streams)
24
+ @streams = streams
25
+ @buf = []
26
+
27
+ @streams.each_index { |slot| buf_fill slot }
28
+
29
+ @all_closed = @streams.empty?
30
+ end
31
+
32
+ ##
33
+ # Are all the streams closed?
34
+
35
+ def all_closed?
36
+ @all_closed
37
+ end
38
+
39
+ ##
40
+ # Adds a line to the buffer from input strem +slot+.
41
+
42
+ def buf_fill(slot)
43
+ line = @streams[slot].gets
44
+
45
+ if line.nil? and @streams[slot].eof? then
46
+ @streams.delete_at slot
47
+ @buf.delete_at slot
48
+ elsif line.nil? then
49
+ raise "WTF? line.nil? and not eof?"
50
+ else
51
+ line =~ / \[(.+?)\] /
52
+ time = Time.parse($1.sub(':', ' ').gsub('/', '-')).to_i
53
+ @buf[slot] = [time, line]
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Merges streams into +out_stream+.
59
+
60
+ def merge(out_stream)
61
+ index, min, line = nil
62
+
63
+ until @streams.empty? do
64
+ min = @buf.min { |a,b| a.first <=> b.first}
65
+ slot = @buf.index min
66
+ buf_fill slot
67
+ out_stream.puts min.last
68
+ end
69
+
70
+ @all_closed = true
71
+ end
72
+
73
+ end
74
+