sawmill 0.0.1
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.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
@@ -0,0 +1,66 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill error 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 is a namespace for errors that can be thrown by Sawmill.
|
41
|
+
|
42
|
+
module Errors
|
43
|
+
|
44
|
+
|
45
|
+
# Base class for all Sawmill exceptions
|
46
|
+
|
47
|
+
class SawmillError < ::RuntimeError
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Tried to create an illegal record
|
52
|
+
|
53
|
+
class IllegalRecordError < SawmillError
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Tried to log an entry with an unknown level code
|
58
|
+
|
59
|
+
class UnknownLevelError < SawmillError
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill level class
|
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
|
+
# Level objects represent logging levels, sometimes known as severities.
|
41
|
+
#
|
42
|
+
# A level object has a name and a numeric value. The name controls how the
|
43
|
+
# level is represented in a logfile. The value indicates its severity rank
|
44
|
+
# compared to other levels.
|
45
|
+
#
|
46
|
+
# Levels are organized into groups. Levels are comparable with one another
|
47
|
+
# if they are part of the same group.
|
48
|
+
|
49
|
+
class Level
|
50
|
+
|
51
|
+
|
52
|
+
def initialize(group_, name_, value_) # :nodoc:
|
53
|
+
@group = group_
|
54
|
+
@name = name_
|
55
|
+
@value = value_
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# The LevelGroup of which this Level is a member
|
60
|
+
attr_reader :group
|
61
|
+
|
62
|
+
# The name of the level, as a string.
|
63
|
+
attr_reader :name
|
64
|
+
|
65
|
+
# The numeric value of the level.
|
66
|
+
attr_reader :value
|
67
|
+
|
68
|
+
|
69
|
+
# Compare this level with another level of the same group.
|
70
|
+
def <=>(obj_)
|
71
|
+
if obj_.respond_to?(:value) && obj_.respond_to?(:group)
|
72
|
+
@group == obj_.group ? @value <=> obj_.value : nil
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Returns the name.
|
80
|
+
def to_s
|
81
|
+
@name
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def inspect # :nodoc:
|
86
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} name=#{@name.inspect} value=#{@value}>"
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
include ::Comparable
|
91
|
+
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# A level group is a group of related levels that can be ordered and used
|
97
|
+
# in a log. A given log is always associated with exactly one group, which
|
98
|
+
# controls what levels are available for log entries.
|
99
|
+
#
|
100
|
+
# Normally, you will use Sawmill::STANDARD_LEVELS, which defines levels
|
101
|
+
# corresponding to the ones available in the classic ruby logger class.
|
102
|
+
# However, this class is available to define custom level hierarchies.
|
103
|
+
|
104
|
+
class LevelGroup
|
105
|
+
|
106
|
+
|
107
|
+
# Create a level group.
|
108
|
+
# You must provide a block that calls methods of
|
109
|
+
# Sawmill::LevelGroup::Builder to define the levels in the group.
|
110
|
+
|
111
|
+
def initialize(&block_)
|
112
|
+
@level_order = []
|
113
|
+
@level_names = {}
|
114
|
+
@level_methods = {}
|
115
|
+
@default = nil
|
116
|
+
::Blockenspiel.invoke(block_, Builder.new(self))
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def inspect # :nodoc:
|
121
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} levels=[#{@level_order.map{|lvl_| lvl_.name.inspect}.join(',')}]>"
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Return the default level, the one used when no level is specified.
|
126
|
+
|
127
|
+
def default
|
128
|
+
@default ||= highest
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# Return the lowest level in the group.
|
133
|
+
|
134
|
+
def lowest
|
135
|
+
@level_order.first
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Return the highest level in the group.
|
140
|
+
|
141
|
+
def highest
|
142
|
+
@level_order.last
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# Return the length of the longest name in the group.
|
147
|
+
|
148
|
+
def column_width
|
149
|
+
@level_order.inject(0) do |width_, level_|
|
150
|
+
w_ = level_.name.size
|
151
|
+
w_ > width_ ? w_ : width_
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# Look up a level by a logger method name.
|
157
|
+
|
158
|
+
def lookup_method(method_name_)
|
159
|
+
@level_methods[method_name_.to_sym]
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# Get a level in this group.
|
164
|
+
#
|
165
|
+
# You may pass either an integer value, a level name, a level object,
|
166
|
+
# or nil. If you pass nil, the default level is returned. Otherwise,
|
167
|
+
# the level corresponding to the given parameter is returned. If no
|
168
|
+
# level in this group corresponds to the parameter, nil is returned.
|
169
|
+
|
170
|
+
def get(name_)
|
171
|
+
case name_
|
172
|
+
when ::Integer
|
173
|
+
@level_order[name_]
|
174
|
+
when Level
|
175
|
+
@level_order[name_.value] == name_ ? name_ : nil
|
176
|
+
when ::Symbol, ::String
|
177
|
+
@level_names[name_.to_sym]
|
178
|
+
when nil
|
179
|
+
default
|
180
|
+
else
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def _add(name_, opts_={}) # :nodoc:
|
187
|
+
name_ = name_.to_sym
|
188
|
+
default_ = opts_[:default]
|
189
|
+
methods_ = opts_[:methods] || []
|
190
|
+
methods_ = [methods_] unless methods_.kind_of?(::Array)
|
191
|
+
if @level_names.include?(name_)
|
192
|
+
raise ::ArgumentError, "Name #{name_} already taken"
|
193
|
+
end
|
194
|
+
value_ = @level_order.size
|
195
|
+
level_ = Level.new(self, name_, value_)
|
196
|
+
if default_
|
197
|
+
if @default
|
198
|
+
raise ::ArgumentError, "A default level is already specified"
|
199
|
+
else
|
200
|
+
@default = level_
|
201
|
+
end
|
202
|
+
end
|
203
|
+
@level_order << level_
|
204
|
+
@level_names[name_] = level_
|
205
|
+
methods_.each do |method_|
|
206
|
+
method_ = method_.to_sym
|
207
|
+
if @level_methods.include?(method_)
|
208
|
+
raise ::ArgumentError, "Method #{method_} already taken"
|
209
|
+
else
|
210
|
+
@level_methods[method_] = level_
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
# You may call methods of this object in the block passed to
|
217
|
+
# Sawmill::LevelGroup#new.
|
218
|
+
|
219
|
+
class Builder
|
220
|
+
|
221
|
+
include ::Blockenspiel::DSL
|
222
|
+
|
223
|
+
|
224
|
+
def initialize(group_) # :nodoc:
|
225
|
+
@group = group_
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
# Add a level to this group. The level is assigned the next value in
|
230
|
+
# sequence, and the given name.
|
231
|
+
#
|
232
|
+
# You may also provide these options:
|
233
|
+
#
|
234
|
+
# <tt>:default</tt>::
|
235
|
+
# If set to true, this level is made the default.
|
236
|
+
# <tt>:methods</tt>::
|
237
|
+
# If set to an array of strings or methods, those method names are
|
238
|
+
# mapped to this level. You may then use those methods in the
|
239
|
+
# Sawmill::Logger class as a shortcut for creating log messages with
|
240
|
+
# this level.
|
241
|
+
|
242
|
+
def add(name_, opts_={})
|
243
|
+
@group._add(name_, opts_)
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
# A LevelGroup that corresponds to the classic ruby logger levels.
|
253
|
+
|
254
|
+
STANDARD_LEVELS = LevelGroup.new do
|
255
|
+
add(:DEBUG, :methods => 'debug')
|
256
|
+
add(:INFO, :methods => 'info', :default => true)
|
257
|
+
add(:WARN, :methods => 'warn')
|
258
|
+
add(:ERROR, :methods => 'error')
|
259
|
+
add(:FATAL, :methods => 'fatal')
|
260
|
+
add(:ANY, :methods => ['any', 'unknown'])
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill logger class
|
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
|
+
# A Rack middleware that starts and ends a log record.
|
41
|
+
# Insert this in your Rack stack to wrap requests in a log record.
|
42
|
+
|
43
|
+
class LogRecordMiddleware
|
44
|
+
|
45
|
+
|
46
|
+
# Create a middleware object for Rack.
|
47
|
+
#
|
48
|
+
# If you do not provide a logger object, one will be generated for you
|
49
|
+
# that simply logs to STDOUT.
|
50
|
+
#
|
51
|
+
# Recognized options include:
|
52
|
+
#
|
53
|
+
# <tt>:request_id_key</tt>::
|
54
|
+
# The name of a rack environment key where the record ID should be
|
55
|
+
# stored. If not specified, defaults to "sawmill.request_id".
|
56
|
+
# <tt>:start_time_attribute</tt>::
|
57
|
+
# If present, logs an attribute with this name with the starting
|
58
|
+
# timestamp for the request. If absent, does not log this attribute.
|
59
|
+
# <tt>:end_time_attribute</tt>::
|
60
|
+
# If present, logs an attribute with this name with the ending
|
61
|
+
# timestamp for the request. If absent, does not log this attribute.
|
62
|
+
|
63
|
+
def initialize(app_, logger_=nil, opts_={})
|
64
|
+
@app = app_
|
65
|
+
@logger = logger_ || Logger.new(:progname => 'rack', :processor => ::Sawmill::Formatter.new(STDOUT))
|
66
|
+
@request_id_key = opts_[:request_id_key] || 'sawmill.request_id'
|
67
|
+
@start_time_attribute = opts_[:start_time_attribute]
|
68
|
+
@end_time_attribute = opts_[:end_time_attribute]
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def call(env_)
|
73
|
+
env_[@request_id_key] = @logger.begin_record
|
74
|
+
if @start_time_attribute
|
75
|
+
time_ = Time.now.utc
|
76
|
+
@logger.set_attribute(@start_time_attribute, time_.strftime('%Y-%m-%dT%H:%M:%S.') + ('%06d' % time_.usec) + 'Z')
|
77
|
+
end
|
78
|
+
begin
|
79
|
+
return @app.call(env_)
|
80
|
+
ensure
|
81
|
+
if @end_time_attribute
|
82
|
+
time_ = Time.now.utc
|
83
|
+
@logger.set_attribute(@end_time_attribute, time_.strftime('%Y-%m-%dT%H:%M:%S.') + ('%06d' % time_.usec) + 'Z')
|
84
|
+
end
|
85
|
+
@logger.end_record
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
end
|