sawmill 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +3 -0
- data/README.rdoc +81 -0
- data/Rakefile +147 -0
- data/lib/sawmill/entry.rb +307 -0
- data/lib/sawmill/entry_classifier.rb +75 -0
- data/lib/sawmill/entry_processor/build_records.rb +157 -0
- data/lib/sawmill/entry_processor/conditionals.rb +444 -0
- data/lib/sawmill/entry_processor/filter_basic_fields.rb +145 -0
- data/lib/sawmill/entry_processor/format.rb +228 -0
- data/lib/sawmill/entry_processor/simple_queue.rb +116 -0
- data/lib/sawmill/entry_processor.rb +158 -0
- data/lib/sawmill/errors.rb +66 -0
- data/lib/sawmill/level.rb +264 -0
- data/lib/sawmill/log_record_middleware.rb +93 -0
- data/lib/sawmill/logger.rb +373 -0
- data/lib/sawmill/parser.rb +181 -0
- data/lib/sawmill/record.rb +255 -0
- data/lib/sawmill/record_processor/conditionals.rb +330 -0
- data/lib/sawmill/record_processor/decompose.rb +75 -0
- data/lib/sawmill/record_processor/filter_by_attributes.rb +87 -0
- data/lib/sawmill/record_processor/filter_by_record_id.rb +77 -0
- data/lib/sawmill/record_processor/format.rb +88 -0
- data/lib/sawmill/record_processor/simple_queue.rb +117 -0
- data/lib/sawmill/record_processor.rb +137 -0
- data/lib/sawmill/rotater/base.rb +90 -0
- data/lib/sawmill/rotater/date_based_log_file.rb +145 -0
- data/lib/sawmill/rotater/shifting_log_file.rb +166 -0
- data/lib/sawmill/rotater.rb +236 -0
- data/lib/sawmill/util/queue.rb +138 -0
- data/lib/sawmill/version.rb +47 -0
- data/lib/sawmill.rb +78 -0
- data/tests/tc_entry_processors.rb +138 -0
- data/tests/tc_formatter_parser.rb +144 -0
- data/tests/tc_levels.rb +118 -0
- data/tests/tc_logger.rb +315 -0
- data/tests/tc_record_processors.rb +117 -0
- data/tests/tc_records.rb +206 -0
- metadata +116 -0
data/History.rdoc
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
== Sawmill
|
2
|
+
|
3
|
+
Sawmill is a logging and log analysis system for Ruby.
|
4
|
+
It extends the basic Ruby logging facility with log records and logfile parsing features.
|
5
|
+
|
6
|
+
=== What is it?
|
7
|
+
|
8
|
+
A user reported a bug in your Rails application, and now you have to figure out what happened. You can spend hours poring over megabytes of text log files, stretching your grep-fu to its snapping point... or you can use Sawmill.
|
9
|
+
|
10
|
+
Your CEO wants to know how many people viewed the new product pages last week. You can write yet another arcane sed script looking for that elusive pattern in the log files... or you can use Sawmill.
|
11
|
+
|
12
|
+
Sawmill is an extension to the standard ruby Logger mechanism that enables automated log analysis. Designed especially for web applications using Ruby on Rails and similar frameworks, Sawmill originated in the source to www.geopage.com, where it has been analyzing the production Rails logs for more than three years.
|
13
|
+
|
14
|
+
=== Feature list
|
15
|
+
|
16
|
+
* Drop-in compatible with standard ruby Logger class
|
17
|
+
* Custom log levels
|
18
|
+
* Process log entries as objects
|
19
|
+
* Group log entries into log records with unique record IDs and attributes
|
20
|
+
* Filtering and analysis of log entries and log records
|
21
|
+
* Write logfiles, optionally with one of several logfile rotation mechanisms
|
22
|
+
* Parse logfiles back into log entries and log records
|
23
|
+
* Log analysis framework
|
24
|
+
* Rack middleware for quick setup of web service logs
|
25
|
+
|
26
|
+
=== Requirements
|
27
|
+
|
28
|
+
* Ruby 1.8.6 or later (1.8.7 recommended), Ruby 1.9.1 or later, or JRuby 1.4 or later.
|
29
|
+
* versionomy gem.
|
30
|
+
|
31
|
+
=== Installation
|
32
|
+
|
33
|
+
gem install sawmill
|
34
|
+
|
35
|
+
=== Known issues and limitations
|
36
|
+
|
37
|
+
Sawmill is currently under development, and some features are not yet complete or fully tested.
|
38
|
+
|
39
|
+
=== Development and support
|
40
|
+
|
41
|
+
Documentation is available at http://virtuoso.rubyforge.org/sawmill/README_rdoc.html
|
42
|
+
|
43
|
+
Source code is hosted on Github at http://github.com/dazuma/sawmill
|
44
|
+
|
45
|
+
Report bugs on Github issues at http://github.org/dazuma/sawmill/issues
|
46
|
+
|
47
|
+
Contact the author at dazuma at gmail dot com.
|
48
|
+
|
49
|
+
=== Author / Credits
|
50
|
+
|
51
|
+
Lumber is written by Daniel Azuma (http://www.daniel-azuma.com/).
|
52
|
+
|
53
|
+
== LICENSE:
|
54
|
+
|
55
|
+
Copyright 2009 Daniel Azuma.
|
56
|
+
|
57
|
+
All rights reserved.
|
58
|
+
|
59
|
+
Redistribution and use in source and binary forms, with or without
|
60
|
+
modification, are permitted provided that the following conditions are met:
|
61
|
+
|
62
|
+
* Redistributions of source code must retain the above copyright notice,
|
63
|
+
this list of conditions and the following disclaimer.
|
64
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
65
|
+
this list of conditions and the following disclaimer in the documentation
|
66
|
+
and/or other materials provided with the distribution.
|
67
|
+
* Neither the name of the copyright holder, nor the names of any other
|
68
|
+
contributors to this software, may be used to endorse or promote products
|
69
|
+
derived from this software without specific prior written permission.
|
70
|
+
|
71
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
72
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
73
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
74
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
75
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
76
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
77
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
78
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
79
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
80
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
81
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill Rakefile
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2009 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
|
35
|
+
|
36
|
+
require 'rake'
|
37
|
+
require 'rake/clean'
|
38
|
+
require 'rake/gempackagetask'
|
39
|
+
require 'rake/testtask'
|
40
|
+
require 'rake/rdoctask'
|
41
|
+
require 'rdoc/generator/darkfish'
|
42
|
+
|
43
|
+
require ::File.expand_path("#{::File.dirname(__FILE__)}/lib/sawmill.rb")
|
44
|
+
|
45
|
+
|
46
|
+
# Configuration
|
47
|
+
extra_rdoc_files_ = ['README.rdoc', 'History.rdoc']
|
48
|
+
|
49
|
+
|
50
|
+
# Default task
|
51
|
+
task :default => [:clean, :rdoc, :package, :test]
|
52
|
+
|
53
|
+
|
54
|
+
# Clean task
|
55
|
+
CLEAN.include(['doc', 'pkg'])
|
56
|
+
|
57
|
+
|
58
|
+
# Test task
|
59
|
+
::Rake::TestTask.new('test') do |task_|
|
60
|
+
task_.pattern = 'tests/tc_*.rb'
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# RDoc task
|
65
|
+
::Rake::RDocTask.new do |task_|
|
66
|
+
task_.main = 'README.rdoc'
|
67
|
+
task_.rdoc_files.include(*extra_rdoc_files_)
|
68
|
+
task_.rdoc_files.include('lib/sawmill/**/*.rb')
|
69
|
+
task_.rdoc_dir = 'doc'
|
70
|
+
task_.title = "Sawmill #{::Sawmill::VERSION_STRING} documentation"
|
71
|
+
task_.options << '-f' << 'darkfish'
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Gem task
|
76
|
+
gemspec_ = ::Gem::Specification.new do |s_|
|
77
|
+
s_.name = 'sawmill'
|
78
|
+
s_.summary = 'Sawmill is a logging and log analysis system for Ruby.'
|
79
|
+
s_.version = ::Sawmill::VERSION_STRING
|
80
|
+
s_.author = 'Daniel Azuma'
|
81
|
+
s_.email = 'dazuma@gmail.com'
|
82
|
+
s_.description = 'Sawmill is a logging and log analysis system for Ruby. It extends the basic Ruby logging facility with log records and parsing abilities.'
|
83
|
+
s_.homepage = 'http://github.com/dazuma/sawmill'
|
84
|
+
s_.rubyforge_project = 'virtuoso'
|
85
|
+
s_.required_ruby_version = '>= 1.8.6'
|
86
|
+
s_.files = ::FileList['lib/**/*.rb', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
|
87
|
+
s_.extra_rdoc_files = extra_rdoc_files_
|
88
|
+
s_.has_rdoc = true
|
89
|
+
s_.test_files = ::FileList['tests/tc_*.rb']
|
90
|
+
s_.platform = ::Gem::Platform::RUBY
|
91
|
+
s_.add_dependency('blockenspiel', '>= 0.2.1')
|
92
|
+
s_.add_dependency('versionomy', '>= 0.1.1')
|
93
|
+
end
|
94
|
+
::Rake::GemPackageTask.new(gemspec_) do |task_|
|
95
|
+
task_.need_zip = false
|
96
|
+
task_.need_tar = true
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Publish RDocs
|
101
|
+
desc 'Publishes RDocs to RubyForge'
|
102
|
+
task :publish_rdoc_to_rubyforge => [:rerdoc] do
|
103
|
+
config_ = ::YAML.load(::File.read(::File.expand_path("~/.rubyforge/user-config.yml")))
|
104
|
+
username_ = config_['username']
|
105
|
+
sh "rsync -av --delete doc/ #{username_}@rubyforge.org:/var/www/gforge-projects/virtuoso/sawmill"
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Publish gem
|
110
|
+
task :publish_gem_to_rubyforge => [:package] do |t_|
|
111
|
+
v_ = ::ENV["VERSION"]
|
112
|
+
abort "Must supply VERSION=x.y.z" unless v_
|
113
|
+
if v_ != ::Sawmill::VERSION_STRING
|
114
|
+
abort "Versions don't match: #{v_} vs #{::Sawmill::VERSION_STRING}"
|
115
|
+
end
|
116
|
+
gem_pkg_ = "pkg/sawmill-#{v_}.gem"
|
117
|
+
tgz_pkg_ = "pkg/sawmill-#{v_}.tgz"
|
118
|
+
release_notes_ = ::File.read("README.rdoc").split(/^(==.*)/)[2].strip
|
119
|
+
release_changes_ = ::File.read("History.rdoc").split(/^(===.*)/)[1..2].join.strip
|
120
|
+
|
121
|
+
require 'rubyforge'
|
122
|
+
rf_ = ::RubyForge.new.configure
|
123
|
+
puts "Logging in to RubyForge"
|
124
|
+
rf_.login
|
125
|
+
config_ = rf_.userconfig
|
126
|
+
config_["release_notes"] = release_notes_
|
127
|
+
config_["release_changes"] = release_changes_
|
128
|
+
config_["preformatted"] = true
|
129
|
+
puts "Releasing sawmill #{v_} to RubyForge"
|
130
|
+
rf_.add_release('virtuoso', 'sawmill', v_, gem_pkg_, tgz_pkg_)
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# Publish gem
|
135
|
+
task :publish_gem_to_gemcutter => [:package] do |t_|
|
136
|
+
v_ = ::ENV["VERSION"]
|
137
|
+
abort "Must supply VERSION=x.y.z" unless v_
|
138
|
+
if v_ != ::Sawmill::VERSION_STRING
|
139
|
+
abort "Versions don't match: #{v_} vs #{::Sawmill::VERSION_STRING}"
|
140
|
+
end
|
141
|
+
puts "Releasing sawmill #{v_} to GemCutter"
|
142
|
+
`cd pkg && gem push sawmill-#{v_}.gem`
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# Publish everything
|
147
|
+
task :publish => [:publish_gem_to_gemcutter, :publish_gem_to_rubyforge, :publish_rdoc_to_rubyforge]
|
@@ -0,0 +1,307 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill log entry classes
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2009 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
module Sawmill
|
38
|
+
|
39
|
+
|
40
|
+
# This module is a namespace for log entry classes.
|
41
|
+
|
42
|
+
module Entry
|
43
|
+
|
44
|
+
|
45
|
+
# A log entry that doesn't conform to sawmill's format.
|
46
|
+
|
47
|
+
class UnknownData
|
48
|
+
|
49
|
+
|
50
|
+
def initialize(line_)
|
51
|
+
@line = line_.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Returns <tt>:unknown_data</tt>
|
56
|
+
def type; :unknown_data end
|
57
|
+
|
58
|
+
# The line in the logfile as a raw string
|
59
|
+
attr_reader :line
|
60
|
+
|
61
|
+
|
62
|
+
def to_s # :nodoc:
|
63
|
+
"#{type}: #{@line}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect # :nodoc:
|
67
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} line=#{@line.inspect}>"
|
68
|
+
end
|
69
|
+
|
70
|
+
def eql?(obj_) # :nodoc:
|
71
|
+
obj_.kind_of?(Entry::UnknownData) && obj_.line == @line
|
72
|
+
end
|
73
|
+
|
74
|
+
def ==(obj_) # :nodoc:
|
75
|
+
eql?(obj_)
|
76
|
+
end
|
77
|
+
|
78
|
+
def hash # :nodoc:
|
79
|
+
type.hash ^ @line.hash
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# A log entry containing a standard log message.
|
86
|
+
|
87
|
+
class Message
|
88
|
+
|
89
|
+
|
90
|
+
def initialize(level_, timestamp_, progname_, record_id_, message_)
|
91
|
+
@level = level_
|
92
|
+
@timestamp = timestamp_
|
93
|
+
@progname = progname_.to_s
|
94
|
+
@progname.gsub!(/\s+/, '')
|
95
|
+
@record_id = record_id_.nil? ? nil : record_id_.to_s
|
96
|
+
@record_id.gsub!(/\s+/, '') if @record_id
|
97
|
+
@message = message_.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Returns <tt>:message</tt>
|
102
|
+
def type; :message; end
|
103
|
+
|
104
|
+
# The log level as a Sawmill::Level object
|
105
|
+
attr_reader :level
|
106
|
+
# The timestamp as a Time object
|
107
|
+
attr_reader :timestamp
|
108
|
+
# The progname as a string
|
109
|
+
attr_reader :progname
|
110
|
+
# The record ID as a string
|
111
|
+
attr_reader :record_id
|
112
|
+
# The message as a string
|
113
|
+
attr_reader :message
|
114
|
+
|
115
|
+
|
116
|
+
def to_s # :nodoc:
|
117
|
+
"#{type}: #{@message}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def inspect # :nodoc:
|
121
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} level=#{@level.name} time=#{@timestamp.to_s.inspect} progname=#{@progname.inspect} record_id=#{@record_id.inspect} message=#{@message.inspect}>"
|
122
|
+
end
|
123
|
+
|
124
|
+
def eql?(obj_) # :nodoc:
|
125
|
+
obj_.kind_of?(Entry::Message) && obj_.level == @level && obj_.timestamp == @timestamp && obj_.progname == @progname && obj_.record_id == @record_id && obj_.message == @message
|
126
|
+
end
|
127
|
+
|
128
|
+
def ==(obj_) # :nodoc:
|
129
|
+
eql?(obj_)
|
130
|
+
end
|
131
|
+
|
132
|
+
def hash # :nodoc:
|
133
|
+
type.hash ^ @level.hash ^ @timestamp.hash ^ @progname.hash ^ @record_id.hash ^ @message.hash
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# A log entry signalling the beginning of a log record.
|
141
|
+
|
142
|
+
class BeginRecord
|
143
|
+
|
144
|
+
|
145
|
+
def initialize(level_, timestamp_, progname_, record_id_)
|
146
|
+
@level = level_
|
147
|
+
@timestamp = timestamp_
|
148
|
+
@progname = progname_.to_s
|
149
|
+
@progname.gsub!(/\s+/, '')
|
150
|
+
@record_id = record_id_.nil? ? nil : record_id_.to_s
|
151
|
+
@record_id.gsub!(/\s+/, '') if @record_id
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
# Returns <tt>:begin_record</tt>
|
156
|
+
def type; :begin_record; end
|
157
|
+
|
158
|
+
# The log level as a Sawmill::Level object
|
159
|
+
attr_reader :level
|
160
|
+
# The timestamp as a Time object
|
161
|
+
attr_reader :timestamp
|
162
|
+
# The progname as a string
|
163
|
+
attr_reader :progname
|
164
|
+
# The record ID as a string
|
165
|
+
attr_reader :record_id
|
166
|
+
|
167
|
+
|
168
|
+
def to_s # :nodoc:
|
169
|
+
"#{type}: #{@record_id}"
|
170
|
+
end
|
171
|
+
|
172
|
+
def inspect # :nodoc:
|
173
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} level=#{@level.name} time=#{@timestamp.to_s.inspect} progname=#{@progname.inspect} record_id=#{@record_id.inspect}>"
|
174
|
+
end
|
175
|
+
|
176
|
+
def eql?(obj_) # :nodoc:
|
177
|
+
obj_.kind_of?(Entry::BeginRecord) && obj_.level == @level && obj_.timestamp == @timestamp && obj_.progname == @progname && obj_.record_id == @record_id
|
178
|
+
end
|
179
|
+
|
180
|
+
def ==(obj_) # :nodoc:
|
181
|
+
eql?(obj_)
|
182
|
+
end
|
183
|
+
|
184
|
+
def hash # :nodoc:
|
185
|
+
type.hash ^ @level.hash ^ @timestamp.hash ^ @progname.hash ^ @record_id.hash
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
# A log entry signalling the end of a log record.
|
193
|
+
|
194
|
+
class EndRecord
|
195
|
+
|
196
|
+
|
197
|
+
def initialize(level_, timestamp_, progname_, record_id_)
|
198
|
+
@level = level_
|
199
|
+
@timestamp = timestamp_
|
200
|
+
@progname = progname_.to_s
|
201
|
+
@record_id = record_id_.nil? ? nil : record_id_.to_s
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# Returns <tt>:end_record</tt>
|
206
|
+
def type; :end_record; end
|
207
|
+
|
208
|
+
# The log level as a Sawmill::Level object
|
209
|
+
attr_reader :level
|
210
|
+
# The timestamp as a Time object
|
211
|
+
attr_reader :timestamp
|
212
|
+
# The progname as a string
|
213
|
+
attr_reader :progname
|
214
|
+
# The record ID as a string
|
215
|
+
attr_reader :record_id
|
216
|
+
|
217
|
+
|
218
|
+
def to_s # :nodoc:
|
219
|
+
"#{type}: #{@record_id}"
|
220
|
+
end
|
221
|
+
|
222
|
+
def inspect # :nodoc:
|
223
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} level=#{@level.name} time=#{@timestamp.to_s.inspect} progname=#{@progname.inspect} record_id=#{@record_id.inspect}>"
|
224
|
+
end
|
225
|
+
|
226
|
+
def eql?(obj_) # :nodoc:
|
227
|
+
obj_.kind_of?(Entry::EndRecord) && obj_.level == @level && obj_.timestamp == @timestamp && obj_.progname == @progname && obj_.record_id == @record_id
|
228
|
+
end
|
229
|
+
|
230
|
+
def ==(obj_) # :nodoc:
|
231
|
+
eql?(obj_)
|
232
|
+
end
|
233
|
+
|
234
|
+
def hash # :nodoc:
|
235
|
+
type.hash ^ @level.hash ^ @timestamp.hash ^ @progname.hash ^ @record_id.hash
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
# A log entry containing a log record attribute.
|
243
|
+
|
244
|
+
class Attribute
|
245
|
+
|
246
|
+
|
247
|
+
def initialize(level_, timestamp_, progname_, record_id_, key_, value_, operation_=nil)
|
248
|
+
@level = level_
|
249
|
+
@timestamp = timestamp_
|
250
|
+
@progname = progname_.to_s
|
251
|
+
@progname.gsub!(/\s+/, '')
|
252
|
+
@record_id = record_id_.nil? ? nil : record_id_.to_s
|
253
|
+
@record_id.gsub!(/\s+/, '') if @record_id
|
254
|
+
@key = key_.to_s
|
255
|
+
@key.gsub!(/\s+/, '')
|
256
|
+
@value = value_.to_s
|
257
|
+
@operation = operation_ || :set
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
# Returns <tt>:attribute</tt>
|
262
|
+
def type; :attribute; end
|
263
|
+
|
264
|
+
# The log level as a Sawmill::Level object
|
265
|
+
attr_reader :level
|
266
|
+
# The timestamp as a Time object
|
267
|
+
attr_reader :timestamp
|
268
|
+
# The progname as a string
|
269
|
+
attr_reader :progname
|
270
|
+
# The record ID as a string
|
271
|
+
attr_reader :record_id
|
272
|
+
# The operation, which can be :set, :append, :remove, :unset
|
273
|
+
attr_reader :operation
|
274
|
+
# The attribute key as a string
|
275
|
+
attr_reader :key
|
276
|
+
# The attribute value as a string
|
277
|
+
attr_reader :value
|
278
|
+
|
279
|
+
|
280
|
+
def to_s # :nodoc:
|
281
|
+
"#{type}: #{@key}=#{@value.inspect}"
|
282
|
+
end
|
283
|
+
|
284
|
+
def inspect # :nodoc:
|
285
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} level=#{@level.name} time=#{@timestamp.to_s.inspect} progname=#{@progname.inspect} record_id=#{@record_id.inspect} operation=#{@operation} key=#{@key.inspect} value=#{@value.inspect}>"
|
286
|
+
end
|
287
|
+
|
288
|
+
def eql?(obj_) # :nodoc:
|
289
|
+
obj_.kind_of?(Entry::Attribute) && obj_.level == @level && obj_.timestamp == @timestamp && obj_.progname == @progname && obj_.record_id == @record_id && obj_.key == @key && obj_.value == @value && obj_.operation == @operation
|
290
|
+
end
|
291
|
+
|
292
|
+
def ==(obj_) # :nodoc:
|
293
|
+
eql?(obj_)
|
294
|
+
end
|
295
|
+
|
296
|
+
def hash # :nodoc:
|
297
|
+
type.hash ^ @level.hash ^ @timestamp.hash ^ @progname.hash ^ @record_id.hash ^ @key.hash ^ @value.hash ^ @operation.hash
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill entry sorter utility
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2009 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
module Sawmill
|
38
|
+
|
39
|
+
|
40
|
+
# An object that classifies log entry objects, calling the proper methods
|
41
|
+
# on a Sawmill::EntryProcessor.
|
42
|
+
|
43
|
+
class EntryClassifier
|
44
|
+
|
45
|
+
|
46
|
+
# Create a classifier that sends entries to the given
|
47
|
+
# Sawmill::EntryProcessor.
|
48
|
+
|
49
|
+
def initialize(processor_)
|
50
|
+
@processor = processor_
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Call this method to classify a log entry and send it to the processor.
|
55
|
+
|
56
|
+
def entry(entry_)
|
57
|
+
case entry_.type
|
58
|
+
when :unknown_data
|
59
|
+
@processor.unknown_data(entry_)
|
60
|
+
when :begin_record
|
61
|
+
@processor.begin_record(entry_)
|
62
|
+
when :end_record
|
63
|
+
@processor.end_record(entry_)
|
64
|
+
when :message
|
65
|
+
@processor.message(entry_)
|
66
|
+
when :attribute
|
67
|
+
@processor.attribute(entry_)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|