sawmill 0.0.2 → 0.0.3
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 +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
|