sawmill 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +17 -0
- data/lib/sawmill/entry.rb +17 -1
- data/lib/sawmill/entry_processor/build_records.rb +10 -7
- data/lib/sawmill/entry_processor/compile_report.rb +115 -0
- data/lib/sawmill/entry_processor/conditionals.rb +22 -23
- data/lib/sawmill/entry_processor/count_entries.rb +112 -0
- data/lib/sawmill/entry_processor/{filter_basic_fields.rb → filter_by_basic_fields.rb} +3 -3
- data/lib/sawmill/entry_processor/filter_by_block.rb +96 -0
- data/lib/sawmill/entry_processor/format.rb +9 -1
- data/lib/sawmill/entry_processor/simple_queue.rb +2 -1
- data/lib/sawmill/entry_processor.rb +68 -5
- data/lib/sawmill/errors.rb +7 -0
- data/lib/sawmill/interface.rb +324 -0
- data/lib/sawmill/logger.rb +8 -7
- data/lib/sawmill/record.rb +14 -0
- data/lib/sawmill/record_processor/compile_report.rb +113 -0
- data/lib/sawmill/record_processor/conditionals.rb +12 -13
- data/lib/sawmill/record_processor/count_records.rb +84 -0
- data/lib/sawmill/record_processor/decompose.rb +2 -2
- data/lib/sawmill/record_processor/filter_by_attributes.rb +2 -1
- data/lib/sawmill/record_processor/filter_by_block.rb +95 -0
- data/lib/sawmill/record_processor/filter_by_record_id.rb +2 -1
- data/lib/sawmill/record_processor/format.rb +8 -2
- data/lib/sawmill/record_processor/simple_queue.rb +2 -1
- data/lib/sawmill/record_processor.rb +69 -5
- data/lib/sawmill/rotater/date_based_log_file.rb +8 -8
- data/lib/sawmill/rotater/shifting_log_file.rb +7 -6
- data/lib/sawmill/util/processor_tools.rb +71 -0
- data/lib/sawmill/version.rb +1 -3
- data/lib/sawmill.rb +9 -1
- data/tests/tc_entry_processors.rb +7 -7
- data/tests/tc_reports.rb +101 -0
- metadata +13 -3
@@ -103,15 +103,35 @@ module Sawmill
|
|
103
103
|
end
|
104
104
|
|
105
105
|
|
106
|
-
# Close down the processor
|
107
|
-
#
|
108
|
-
|
109
|
-
|
106
|
+
# Close down the processor, perform any finishing tasks, and return
|
107
|
+
# any final calculated value.
|
108
|
+
#
|
109
|
+
# After this is called, the processor should ignore any further entries.
|
110
|
+
#
|
111
|
+
# The return value can be used to communicate a final computed value,
|
112
|
+
# analysis report, or other data back to the caller. It may also be
|
113
|
+
# nil, signalling no finish value.
|
114
|
+
#
|
115
|
+
# Note that some processors function to multiplex other processors. In
|
116
|
+
# such a case, their finish value needs to be an aggregate of the
|
117
|
+
# values returned by their descendants. To handle these cases, we
|
118
|
+
# define a protocol for finish values. A finish value may be nil, an
|
119
|
+
# Array, or another kind of object. Nil means "no value" and thus can
|
120
|
+
# be ignored by a processor that aggregates other values. An Array
|
121
|
+
# indicates an aggregation; if finish returns an array, it is _always_
|
122
|
+
# an aggregation of actual values. Any other kind of object is to be
|
123
|
+
# interpreted as a single value. This means that if you want to
|
124
|
+
# actually return an array _as_ a value, you must wrap it in another
|
125
|
+
# array, indicating "an array of one finish value, and that finish
|
126
|
+
# value also happens to be an array itself".
|
127
|
+
|
128
|
+
def finish
|
129
|
+
nil
|
110
130
|
end
|
111
131
|
|
112
132
|
|
113
133
|
def self.inherited(subclass_) # :nodoc:
|
114
|
-
if subclass_.name =~ /^Sawmill::EntryProcessor::(
|
134
|
+
if subclass_.name =~ /^Sawmill::EntryProcessor::([^:]+)$/
|
115
135
|
name_ = $1
|
116
136
|
Builder.class_eval do
|
117
137
|
define_method(name_) do |*args_|
|
@@ -122,6 +142,37 @@ module Sawmill
|
|
122
142
|
end
|
123
143
|
|
124
144
|
|
145
|
+
# Add a method to the processor building DSL. You may call this method
|
146
|
+
# in the DSL to create an instance of this entry processor.
|
147
|
+
# You must pass a method name that begins with a lower-case letter or
|
148
|
+
# underscore.
|
149
|
+
#
|
150
|
+
# Processors that subclass Sawmill::EntryProcessor::Base and live in
|
151
|
+
# the Sawmill::EntryProcessor namespace will have their class name
|
152
|
+
# automatically added to the DSL. This method is primarily for other
|
153
|
+
# processors that do not live in that module namespace.
|
154
|
+
#
|
155
|
+
# See Sawmill::EntryProcessor#build for more information.
|
156
|
+
#
|
157
|
+
# Raises Sawmill::Errors::DSLMethodError if the given name is already
|
158
|
+
# taken.
|
159
|
+
|
160
|
+
def self.add_dsl_method(name_)
|
161
|
+
klass_ = self
|
162
|
+
if name_.to_s !~ /^[a-z_]/
|
163
|
+
raise ::ArgumentError, "Method name must begin with a lower-case letter or underscore"
|
164
|
+
end
|
165
|
+
if Builder.method_defined?(name_)
|
166
|
+
raise Errors::DSLMethodError, "Method #{name_} already defined"
|
167
|
+
end
|
168
|
+
Builder.class_eval do
|
169
|
+
define_method(name_) do |*args_|
|
170
|
+
klass_.new(*args_)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
125
176
|
private
|
126
177
|
|
127
178
|
def _interpret_processor_array(param_) # :nodoc:
|
@@ -146,6 +197,18 @@ module Sawmill
|
|
146
197
|
# A convenience DSL for building sets of processors. This is typically
|
147
198
|
# useful for constructing if-expressions using the boolean operation
|
148
199
|
# processors.
|
200
|
+
#
|
201
|
+
# Every entry processor that lives in the Sawmill::EntryProcessor
|
202
|
+
# module and subclasses Sawmill::EntryProcessor::Base can be
|
203
|
+
# instantiated by using its name as a function call. Other processors
|
204
|
+
# may also add themselves to the DSL by calling
|
205
|
+
# Sawmill::EntryProcessor::Base#add_dsl_method.
|
206
|
+
#
|
207
|
+
# For example:
|
208
|
+
#
|
209
|
+
# Sawmill::EntryProcessor.build do
|
210
|
+
# If(FilterByBasicFields(:level => :WARN), Format(STDOUT))
|
211
|
+
# end
|
149
212
|
|
150
213
|
def self.build(&block_)
|
151
214
|
::Blockenspiel.invoke(block_, Builder.new)
|
data/lib/sawmill/errors.rb
CHANGED
@@ -0,0 +1,324 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill convenience interface
|
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
|
+
# This module is a namespace for Sawmill.
|
38
|
+
#
|
39
|
+
# It also contains some convenience class methods.
|
40
|
+
|
41
|
+
module Sawmill
|
42
|
+
|
43
|
+
class << self
|
44
|
+
|
45
|
+
|
46
|
+
# Creates a new logger that writes to a single logfile.
|
47
|
+
# You may provide either the path to the logfile, or an IO object to
|
48
|
+
# write to, such as STDOUT.
|
49
|
+
#
|
50
|
+
# You may pass the same options taken by Sawmill::Logger#new and
|
51
|
+
# Sawmill::EntryProcessor::Format#new, which are:
|
52
|
+
#
|
53
|
+
# <tt>:levels</tt>::
|
54
|
+
# Use a custom Sawmill::LevelGroup. Normally, you should leave this
|
55
|
+
# set to the default, which is Sawmill::STANDARD_LEVELS.
|
56
|
+
# <tt>:level</tt>::
|
57
|
+
# Default level to use for log messages when no level is explicitly
|
58
|
+
# provided. By default, this is set to the level group's default,
|
59
|
+
# which in the case of the standard levels is :INFO.
|
60
|
+
# <tt>:attribute_level</tt>::
|
61
|
+
# Default level to use for attributes when no level is explicitly
|
62
|
+
# provided. By default, this is set to the level group's highest,
|
63
|
+
# level, which in the case of the standard levels is :ANY.
|
64
|
+
# <tt>:progname</tt>::
|
65
|
+
# Progname to use in log messages. Default is "sawmill".
|
66
|
+
# <tt>:record_progname</tt>::
|
67
|
+
# Progname to use in special log entries dealing with log records
|
68
|
+
# (i.e. record delimiters and attribute messages). Default is the
|
69
|
+
# same as the normal progname setting.
|
70
|
+
# <tt>:record_id_generator</tt>::
|
71
|
+
# A proc that generates and returns a new record ID if one is not
|
72
|
+
# explicitly passed into begin_record. If you do not provide a
|
73
|
+
# generator, the default one is used, which generates an ID using the
|
74
|
+
# variant 4 (random) UUID standard.
|
75
|
+
# <tt>:include_id</tt>::
|
76
|
+
# Write the record ID in every log entry. Default is false.
|
77
|
+
# <tt>:fractional_second_digits</tt>::
|
78
|
+
# Number of digits of fractional seconds to write in timestamps.
|
79
|
+
# Default is 2. Accepted values are 0 to 6.
|
80
|
+
# <tt>:level_width</tt>::
|
81
|
+
# Column width of the level field.
|
82
|
+
# <tt>:local_time</tt>::
|
83
|
+
# If true, outputs local time with the timezone offset indicator.
|
84
|
+
# If false (the default), outputs UTC.
|
85
|
+
# <tt>:iso_8601_time</tt>::
|
86
|
+
# If true, outputs time in strict ISO 8601 format.
|
87
|
+
# If false (the default), outputs a slightly more readable format.
|
88
|
+
|
89
|
+
def simple_logger(filepath_=::STDOUT, opts_={})
|
90
|
+
if filepath_.kind_of?(::String)
|
91
|
+
io_ = ::File.open(filepath_)
|
92
|
+
elsif filepath_.respond_to?(:write) && filepath_.respond_to?(:close)
|
93
|
+
io_ = filepath_
|
94
|
+
else
|
95
|
+
raise ::ArgumentError, "You must pass a file path or an IO object"
|
96
|
+
end
|
97
|
+
processor_ = EntryProcessor::Format.new(io_, opts_)
|
98
|
+
Logger.new(opts_.merge(:processor => processor_))
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Creates a new logger that writes to a logfile that rotates
|
103
|
+
# automatically by "shifting". This is a standard rotation strategy
|
104
|
+
# used by many unix tools.
|
105
|
+
#
|
106
|
+
# You must provide the logfile path, a shifting period, and a maximum
|
107
|
+
# file size.
|
108
|
+
#
|
109
|
+
# The period specifies how often to rotate the logfile. Possible values
|
110
|
+
# include <tt>:yearly</tt>, <tt>:monthly</tt>, <tt>:daily</tt>, and
|
111
|
+
# <tt>:hourly</tt>. You may also specify an integer value, which is
|
112
|
+
# interpreted as a number of seconds. Finally, you may pass nil to
|
113
|
+
# disable checking of the file's age. If you do pass nil, you should
|
114
|
+
# provide a maximum size.
|
115
|
+
#
|
116
|
+
# The maximum size is the maximum file size in bytes. You may provide
|
117
|
+
# a number, or nil to disable checking of the file size.
|
118
|
+
#
|
119
|
+
# You may pass the same options taken by Sawmill::Logger#new,
|
120
|
+
# Sawmill::EntryProcessor::Format#new, Sawmill::Rotater#new, and
|
121
|
+
# Sawmill::Rotater::ShiftingLogFile#new, which are:
|
122
|
+
#
|
123
|
+
# <tt>:levels</tt>::
|
124
|
+
# Use a custom Sawmill::LevelGroup. Normally, you should leave this
|
125
|
+
# set to the default, which is Sawmill::STANDARD_LEVELS.
|
126
|
+
# <tt>:level</tt>::
|
127
|
+
# Default level to use for log messages when no level is explicitly
|
128
|
+
# provided. By default, this is set to the level group's default,
|
129
|
+
# which in the case of the standard levels is :INFO.
|
130
|
+
# <tt>:attribute_level</tt>::
|
131
|
+
# Default level to use for attributes when no level is explicitly
|
132
|
+
# provided. By default, this is set to the level group's highest,
|
133
|
+
# level, which in the case of the standard levels is :ANY.
|
134
|
+
# <tt>:progname</tt>::
|
135
|
+
# Progname to use in log messages. Default is "sawmill".
|
136
|
+
# <tt>:record_progname</tt>::
|
137
|
+
# Progname to use in special log entries dealing with log records
|
138
|
+
# (i.e. record delimiters and attribute messages). Default is the
|
139
|
+
# same as the normal progname setting.
|
140
|
+
# <tt>:record_id_generator</tt>::
|
141
|
+
# A proc that generates and returns a new record ID if one is not
|
142
|
+
# explicitly passed into begin_record. If you do not provide a
|
143
|
+
# generator, the default one is used, which generates an ID using the
|
144
|
+
# variant 4 (random) UUID standard.
|
145
|
+
# <tt>:include_id</tt>::
|
146
|
+
# Write the record ID in every log entry. Default is false.
|
147
|
+
# <tt>:fractional_second_digits</tt>::
|
148
|
+
# Number of digits of fractional seconds to write in timestamps.
|
149
|
+
# Default is 2. Accepted values are 0 to 6.
|
150
|
+
# <tt>:level_width</tt>::
|
151
|
+
# Column width of the level field.
|
152
|
+
# <tt>:local_time</tt>::
|
153
|
+
# If true, outputs local time with the timezone offset indicator.
|
154
|
+
# If false (the default), outputs UTC.
|
155
|
+
# <tt>:iso_8601_time</tt>::
|
156
|
+
# If true, outputs time in strict ISO 8601 format.
|
157
|
+
# If false (the default), outputs a slightly more readable format.
|
158
|
+
# <tt>:omit_directives</tt>::
|
159
|
+
# If true, omit standard logfile directives. Default is false.
|
160
|
+
# <tt>:basedir</tt>::
|
161
|
+
# The base directory used if the filepath is a relative path.
|
162
|
+
# If not specified, the current working directory is used.
|
163
|
+
# <tt>:history_size</tt>::
|
164
|
+
# The maximum number of old logfiles (files with indexes) to
|
165
|
+
# keep. Files beyond this history size will be automatically
|
166
|
+
# deleted. Default is 1. This value must be at least 1.
|
167
|
+
|
168
|
+
def shifting_logfile(filepath_, period_, max_size_, opts_={})
|
169
|
+
rotater_ = Rotater.new(Rotater::ShiftingLogFile, opts_.merge(:filepath => filepath_,
|
170
|
+
:max_logfile_size => max_size_, :shift_period => period_))
|
171
|
+
processor_ = EntryProcessor::Format.new(rotater_, opts_)
|
172
|
+
Logger.new(opts_.merge(:processor => processor_))
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Creates a new logger that writes to a logfile that rotates
|
177
|
+
# automatically by tagging filenames with a datestamp.
|
178
|
+
#
|
179
|
+
# You must provide the file path prefix, and a turnover frequency.
|
180
|
+
# Possible values for the turnover frequency are <tt>:yearly</tt>,
|
181
|
+
# <tt>:monthly</tt>, <tt>:daily</tt>, <tt>:hourly</tt>, and
|
182
|
+
# <tt>:never</tt>.
|
183
|
+
#
|
184
|
+
# You may pass the same options taken by Sawmill::Logger#new,
|
185
|
+
# Sawmill::EntryProcessor::Format#new, Sawmill::Rotater#new, and
|
186
|
+
# Sawmill::Rotater::DateBasedLogFile#new, which are:
|
187
|
+
#
|
188
|
+
# <tt>:levels</tt>::
|
189
|
+
# Use a custom Sawmill::LevelGroup. Normally, you should leave this
|
190
|
+
# set to the default, which is Sawmill::STANDARD_LEVELS.
|
191
|
+
# <tt>:level</tt>::
|
192
|
+
# Default level to use for log messages when no level is explicitly
|
193
|
+
# provided. By default, this is set to the level group's default,
|
194
|
+
# which in the case of the standard levels is :INFO.
|
195
|
+
# <tt>:attribute_level</tt>::
|
196
|
+
# Default level to use for attributes when no level is explicitly
|
197
|
+
# provided. By default, this is set to the level group's highest,
|
198
|
+
# level, which in the case of the standard levels is :ANY.
|
199
|
+
# <tt>:progname</tt>::
|
200
|
+
# Progname to use in log messages. Default is "sawmill".
|
201
|
+
# <tt>:record_progname</tt>::
|
202
|
+
# Progname to use in special log entries dealing with log records
|
203
|
+
# (i.e. record delimiters and attribute messages). Default is the
|
204
|
+
# same as the normal progname setting.
|
205
|
+
# <tt>:record_id_generator</tt>::
|
206
|
+
# A proc that generates and returns a new record ID if one is not
|
207
|
+
# explicitly passed into begin_record. If you do not provide a
|
208
|
+
# generator, the default one is used, which generates an ID using the
|
209
|
+
# variant 4 (random) UUID standard.
|
210
|
+
# <tt>:include_id</tt>::
|
211
|
+
# Write the record ID in every log entry. Default is false.
|
212
|
+
# <tt>:fractional_second_digits</tt>::
|
213
|
+
# Number of digits of fractional seconds to write in timestamps.
|
214
|
+
# Default is 2. Accepted values are 0 to 6.
|
215
|
+
# <tt>:level_width</tt>::
|
216
|
+
# Column width of the level field.
|
217
|
+
# <tt>:local_time</tt>::
|
218
|
+
# If true, outputs local time with the timezone offset indicator.
|
219
|
+
# If false (the default), outputs UTC.
|
220
|
+
# <tt>:iso_8601_time</tt>::
|
221
|
+
# If true, outputs time in strict ISO 8601 format.
|
222
|
+
# If false (the default), outputs a slightly more readable format.
|
223
|
+
# <tt>:omit_directives</tt>::
|
224
|
+
# If true, omit standard logfile directives. Default is false.
|
225
|
+
# <tt>:basedir</tt>::
|
226
|
+
# The base directory used if the filepath is a relative path.
|
227
|
+
# If not specified, the current working directory is used.
|
228
|
+
# <tt>:suffix</tt>::
|
229
|
+
# The logfile name prefix.
|
230
|
+
# In the filename "rails.2009-10-11.log", the suffix is ".log".
|
231
|
+
# If not specified, defaults to ".log".
|
232
|
+
# <tt>:local_datestamps</tt>::
|
233
|
+
# If true, use the local timezone to create datestamps.
|
234
|
+
# The default is to use UTC.
|
235
|
+
|
236
|
+
def date_based_logfile(filepath_, frequency_, opts_={})
|
237
|
+
rotater_ = Rotater.new(Rotater::DateBasedLogFile, opts_.merge(:prefix => filepath_,
|
238
|
+
:turnover_frequency => frequency_))
|
239
|
+
processor_ = EntryProcessor::Format.new(rotater_, opts_)
|
240
|
+
Logger.new(opts_.merge(:processor => processor_))
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
# Open one or more log files and run them through an entry processor.
|
245
|
+
# The processor is built on the fly using the EntryProcessor DSL.
|
246
|
+
# See EntryProcessor#build for more details.
|
247
|
+
#
|
248
|
+
# You may pass the same options taken by Sawmill::MultiParser#new,
|
249
|
+
# which are:
|
250
|
+
#
|
251
|
+
# <tt>:levels</tt>::
|
252
|
+
# Sawmill::LevelGroup to use to parse log levels.
|
253
|
+
# If not specified, Sawmill::STANDARD_LEVELS is used by default.
|
254
|
+
# <tt>:emit_incomplete_records_at_eof</tt>::
|
255
|
+
# If set to true, causes any incomplete log records to be emitted
|
256
|
+
# in their incomplete state when EOF is reached on all streams.
|
257
|
+
|
258
|
+
def open_entries(globs_, opts_={}, &block_)
|
259
|
+
processor_ = EntryProcessor.build(&block_)
|
260
|
+
open_files(globs_, processor_, opts_.merge(:finish => true))
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
# Open one or more log files and run them through a record processor.
|
265
|
+
# The processor is built on the fly using the RecordProcessor DSL.
|
266
|
+
# See RecordProcessor#build for more details.
|
267
|
+
#
|
268
|
+
# You may pass the same options taken by Sawmill::MultiParser#new,
|
269
|
+
# which are:
|
270
|
+
#
|
271
|
+
# <tt>:levels</tt>::
|
272
|
+
# Sawmill::LevelGroup to use to parse log levels.
|
273
|
+
# If not specified, Sawmill::STANDARD_LEVELS is used by default.
|
274
|
+
# <tt>:emit_incomplete_records_at_eof</tt>::
|
275
|
+
# If set to true, causes any incomplete log records to be emitted
|
276
|
+
# in their incomplete state when EOF is reached on all streams.
|
277
|
+
|
278
|
+
def open_records(globs_, opts_={}, &block_)
|
279
|
+
processor_ = RecordProcessor.build(&block_)
|
280
|
+
open_files(globs_, processor_, opts_.merge(:finish => true))
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
# Open one or more log files and run them through the given
|
285
|
+
# EntryProcessor or RecordProcessor.
|
286
|
+
#
|
287
|
+
# You may pass the same options taken by Sawmill::MultiParser#new,
|
288
|
+
# which are:
|
289
|
+
#
|
290
|
+
# <tt>:levels</tt>::
|
291
|
+
# Sawmill::LevelGroup to use to parse log levels.
|
292
|
+
# If not specified, Sawmill::STANDARD_LEVELS is used by default.
|
293
|
+
# <tt>:emit_incomplete_records_at_eof</tt>::
|
294
|
+
# If set to true, causes any incomplete log records to be emitted
|
295
|
+
# in their incomplete state when EOF is reached on all streams.
|
296
|
+
# <tt>:finish</tt>::
|
297
|
+
# If set to true, the "finish" method is called on the processor
|
298
|
+
# after all files have been parsed, and the return value is returned.
|
299
|
+
# Otherwise, the processor is left open and nil is returned.
|
300
|
+
|
301
|
+
def open_files(globs_, processor_, opts_={})
|
302
|
+
io_array_ = []
|
303
|
+
globs_ = [globs_] unless globs_.kind_of?(::Array)
|
304
|
+
begin
|
305
|
+
globs_.each do |glob_|
|
306
|
+
::Dir.glob(glob_).each do |path_|
|
307
|
+
io_ = ::File.open(path_)
|
308
|
+
io_ = ::Zlib::GzipReader.new(io_) if path_ =~ /\.gz$/
|
309
|
+
io_array_ << io_
|
310
|
+
end
|
311
|
+
end
|
312
|
+
MultiParser.new(io_array_, processor_, opts_).parse_all
|
313
|
+
ensure
|
314
|
+
io_array_.each do |io_|
|
315
|
+
io_.close rescue nil
|
316
|
+
end
|
317
|
+
end
|
318
|
+
opts_[:finish] ? processor_.finish : nil
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
data/lib/sawmill/logger.rb
CHANGED
@@ -89,7 +89,7 @@ module Sawmill
|
|
89
89
|
@attribute_level = @levels.highest
|
90
90
|
end
|
91
91
|
@progname = opts_[:progname] || 'sawmill'
|
92
|
-
@record_progname = opts_[:record_progname]
|
92
|
+
@record_progname = opts_[:record_progname]
|
93
93
|
@record_id_generator = opts_[:record_id_generator] || Logger._get_default_record_id_generator
|
94
94
|
@processor = opts_[:processor] || Formatter.new(::STDOUT)
|
95
95
|
@current_record_id = nil
|
@@ -162,7 +162,7 @@ module Sawmill
|
|
162
162
|
def begin_record(id_=nil)
|
163
163
|
end_record if @current_record_id
|
164
164
|
@current_record_id = (id_ || @record_id_generator.call).to_s
|
165
|
-
@processor.begin_record(Entry::BeginRecord.new(@levels.highest, ::Time.now, @record_progname, @current_record_id))
|
165
|
+
@processor.begin_record(Entry::BeginRecord.new(@levels.highest, ::Time.now, @record_progname || @progname, @current_record_id))
|
166
166
|
@current_record_id
|
167
167
|
end
|
168
168
|
|
@@ -181,7 +181,7 @@ module Sawmill
|
|
181
181
|
|
182
182
|
def end_record
|
183
183
|
if @current_record_id
|
184
|
-
@processor.end_record(Entry::EndRecord.new(@levels.highest, ::Time.now, @record_progname, @current_record_id))
|
184
|
+
@processor.end_record(Entry::EndRecord.new(@levels.highest, ::Time.now, @record_progname || @progname, @current_record_id))
|
185
185
|
id_ = @current_record_id
|
186
186
|
@current_record_id = nil
|
187
187
|
id_
|
@@ -210,7 +210,7 @@ module Sawmill
|
|
210
210
|
end
|
211
211
|
end
|
212
212
|
return true if level_obj_ < @level
|
213
|
-
@processor.attribute(Entry::Attribute.new(level_obj_, ::Time.now, progname_ || @record_progname, @current_record_id, key_, value_, operation_))
|
213
|
+
@processor.attribute(Entry::Attribute.new(level_obj_, ::Time.now, progname_ || @record_progname || @progname, @current_record_id, key_, value_, operation_))
|
214
214
|
true
|
215
215
|
end
|
216
216
|
|
@@ -233,11 +233,12 @@ module Sawmill
|
|
233
233
|
end
|
234
234
|
|
235
235
|
|
236
|
-
# Close the logger by
|
237
|
-
# emitting log entries.
|
236
|
+
# Close the logger by finishing the log entry processor to which it is
|
237
|
+
# emitting log entries. Returns the value returned by the processor's
|
238
|
+
# finish method.
|
238
239
|
|
239
240
|
def close
|
240
|
-
@processor.
|
241
|
+
@processor.finish
|
241
242
|
end
|
242
243
|
|
243
244
|
|
data/lib/sawmill/record.rb
CHANGED
@@ -72,6 +72,7 @@ module Sawmill
|
|
72
72
|
@message_count = 0
|
73
73
|
@entries = []
|
74
74
|
@attributes = {}
|
75
|
+
@computations = {}
|
75
76
|
if entries_ && entries_.size > 0
|
76
77
|
entries_.each do |entry_|
|
77
78
|
add_entry(entry_)
|
@@ -259,6 +260,19 @@ module Sawmill
|
|
259
260
|
end
|
260
261
|
|
261
262
|
|
263
|
+
# Compute and cache a value.
|
264
|
+
# This is a convenient way for RecordProcessor objects to share
|
265
|
+
# computed information about a record.
|
266
|
+
#
|
267
|
+
# Returns the computed value with the given key.
|
268
|
+
# If the given key has not been computed yet, computes it by
|
269
|
+
# calling the given block and passing self.
|
270
|
+
|
271
|
+
def compute(key_)
|
272
|
+
@computations[key_] ||= yield self
|
273
|
+
end
|
274
|
+
|
275
|
+
|
262
276
|
end
|
263
277
|
|
264
278
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill record processor that generates reports
|
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
|
+
module RecordProcessor
|
41
|
+
|
42
|
+
|
43
|
+
# This processor collects and formats reports from descendant
|
44
|
+
# record processors.
|
45
|
+
|
46
|
+
class CompileReport < All
|
47
|
+
|
48
|
+
|
49
|
+
# Create a report collection.
|
50
|
+
#
|
51
|
+
# Recognized options include:
|
52
|
+
#
|
53
|
+
# <tt>:postprocessor</tt>::
|
54
|
+
# Postprocessor proc for individual reports.
|
55
|
+
# <tt>:separator</tt>::
|
56
|
+
# Separator string for reports. Default is a single newline.
|
57
|
+
# <tt>:header</tt>::
|
58
|
+
# Header string for the final compiled report.
|
59
|
+
# Default is the empty string.
|
60
|
+
# <tt>:footer</tt>::
|
61
|
+
# Footer string for the final compiled report.
|
62
|
+
# Default is the empty string.
|
63
|
+
|
64
|
+
def initialize(*children_)
|
65
|
+
opts_ = children_.last.kind_of?(::Hash) ? children_.pop : {}
|
66
|
+
@postprocessor = opts_[:postprocessor]
|
67
|
+
@separator = opts_[:separator] || "\n"
|
68
|
+
@header = opts_[:header] || ''
|
69
|
+
@footer = opts_[:footer] || ''
|
70
|
+
super(*children_)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# Separator string to be inserted between individual reports.
|
75
|
+
attr_accessor :separator
|
76
|
+
|
77
|
+
# Header string for the final compiled report.
|
78
|
+
attr_accessor :header
|
79
|
+
|
80
|
+
# Footer string for the final compiled report.
|
81
|
+
attr_accessor :footer
|
82
|
+
|
83
|
+
|
84
|
+
# Provide a postprocessor block for individual report values.
|
85
|
+
# This block should take a single parameter and return a string
|
86
|
+
# that should be included in the compiled report. It may also
|
87
|
+
# return nil to indicate that the data should not be included.
|
88
|
+
|
89
|
+
def to_postprocess_value(&block_)
|
90
|
+
@postprocessor = block_
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# On finish, this processor calls finish on its descendants, converts
|
95
|
+
# their values into strings and compiles them into a report. It then
|
96
|
+
# returns that report as a string.
|
97
|
+
|
98
|
+
def finish
|
99
|
+
values_ = super || []
|
100
|
+
values_ = [values_] unless values_.kind_of?(::Array)
|
101
|
+
values_.map!{ |val_| @postprocessor.call(val_) } if @postprocessor
|
102
|
+
values_.compact!
|
103
|
+
"#{@header}#{values_.join(@separator)}#{@footer}"
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
end
|