sawmill 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.0.2 / 2009-10-28
2
+
3
+ * Added multi-parser utility.
4
+ * Fixed several bugs involving parsing and formatting.
5
+ * Logger uses activesupport's SecureRandom on Ruby 1.8 if available.
6
+
1
7
  === 0.0.1 / 2009-10-28
2
8
 
3
9
  * Initial test release
data/Rakefile CHANGED
@@ -80,7 +80,7 @@ gemspec_ = ::Gem::Specification.new do |s_|
80
80
  s_.author = 'Daniel Azuma'
81
81
  s_.email = 'dazuma@gmail.com'
82
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'
83
+ s_.homepage = 'http://virtuoso.rubyforge.org/sawmill'
84
84
  s_.rubyforge_project = 'virtuoso'
85
85
  s_.required_ruby_version = '>= 1.8.6'
86
86
  s_.files = ::FileList['lib/**/*.rb', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
@@ -88,8 +88,8 @@ gemspec_ = ::Gem::Specification.new do |s_|
88
88
  s_.has_rdoc = true
89
89
  s_.test_files = ::FileList['tests/tc_*.rb']
90
90
  s_.platform = ::Gem::Platform::RUBY
91
- s_.add_dependency('blockenspiel', '>= 0.2.1')
92
- s_.add_dependency('versionomy', '>= 0.1.1')
91
+ s_.add_dependency('blockenspiel', '>= 0.2.2')
92
+ s_.add_dependency('versionomy', '>= 0.1.2')
93
93
  end
94
94
  ::Rake::GemPackageTask.new(gemspec_) do |task_|
95
95
  task_.need_zip = false
@@ -106,8 +106,8 @@ task :publish_rdoc_to_rubyforge => [:rerdoc] do
106
106
  end
107
107
 
108
108
 
109
- # Publish gem
110
- task :publish_gem_to_rubyforge => [:package] do |t_|
109
+ # Release gem ro rubyforge
110
+ task :release_gem_to_rubyforge => [:package] do |t_|
111
111
  v_ = ::ENV["VERSION"]
112
112
  abort "Must supply VERSION=x.y.z" unless v_
113
113
  if v_ != ::Sawmill::VERSION_STRING
@@ -131,8 +131,8 @@ task :publish_gem_to_rubyforge => [:package] do |t_|
131
131
  end
132
132
 
133
133
 
134
- # Publish gem
135
- task :publish_gem_to_gemcutter => [:package] do |t_|
134
+ # Release gem to gemcutter
135
+ task :release_gem_to_gemcutter => [:package] do |t_|
136
136
  v_ = ::ENV["VERSION"]
137
137
  abort "Must supply VERSION=x.y.z" unless v_
138
138
  if v_ != ::Sawmill::VERSION_STRING
@@ -144,4 +144,4 @@ end
144
144
 
145
145
 
146
146
  # Publish everything
147
- task :publish => [:publish_gem_to_gemcutter, :publish_gem_to_rubyforge, :publish_rdoc_to_rubyforge]
147
+ task :release => [:release_gem_to_gemcutter, :release_gem_to_rubyforge, :publish_rdoc_to_rubyforge]
@@ -119,7 +119,7 @@ module Sawmill
119
119
  def _check_filter(entry_) # :nodoc:
120
120
  if @level
121
121
  level_ = entry_.level
122
- if @level.kind_of?(::Sawmill::Level)
122
+ if @level.kind_of?(Level)
123
123
  check_level_ = @level
124
124
  if level_.group != check_level_.group
125
125
  return false unless @accept_incomparable_levels
@@ -74,7 +74,7 @@ module Sawmill
74
74
  elsif destination_.respond_to?(:close) && destination_.respond_to?(:write)
75
75
  @io = destination_
76
76
  else
77
- raise ArgumentError, "Unknown destination type"
77
+ raise ::ArgumentError, "Unknown destination type"
78
78
  end
79
79
  @include_id = opts_[:include_id]
80
80
  @fractional_second_digits = (opts_[:fractional_second_digits] || 2).to_i
@@ -139,7 +139,7 @@ module Sawmill
139
139
 
140
140
  def unknown_data(entry_)
141
141
  return false unless @io || @rotater
142
- _write_str(entry_.line+"\n", entry_.record_id)
142
+ _write_str(entry_.line+"\n", nil)
143
143
  true
144
144
  end
145
145
 
@@ -70,6 +70,13 @@ module Sawmill
70
70
  end
71
71
 
72
72
 
73
+ # Return an array of the contents of the queue, in order.
74
+
75
+ def dequeue_all
76
+ @queue.dequeue_all
77
+ end
78
+
79
+
73
80
  # Return the size of the queue, which is 0 if the queue is empty.
74
81
 
75
82
  def size
@@ -130,12 +130,12 @@ module Sawmill
130
130
 
131
131
  def _interpret_processor(param_) # :nodoc:
132
132
  case param_
133
- when Class
133
+ when ::Class
134
134
  param_.new
135
135
  when Base
136
136
  param_
137
137
  else
138
- raise ArgumentError, "Unknown processor object of type #{param_.class.name}"
138
+ raise ::ArgumentError, "Unknown processor object of type #{param_.class.name}"
139
139
  end
140
140
  end
141
141
 
@@ -62,7 +62,7 @@ module Sawmill
62
62
 
63
63
  def initialize(app_, logger_=nil, opts_={})
64
64
  @app = app_
65
- @logger = logger_ || Logger.new(:progname => 'rack', :processor => ::Sawmill::Formatter.new(STDOUT))
65
+ @logger = logger_ || Logger.new(:progname => 'rack', :processor => Formatter.new(::STDOUT))
66
66
  @request_id_key = opts_[:request_id_key] || 'sawmill.request_id'
67
67
  @start_time_attribute = opts_[:start_time_attribute]
68
68
  @end_time_attribute = opts_[:end_time_attribute]
@@ -72,14 +72,14 @@ module Sawmill
72
72
  def call(env_)
73
73
  env_[@request_id_key] = @logger.begin_record
74
74
  if @start_time_attribute
75
- time_ = Time.now.utc
75
+ time_ = ::Time.now.utc
76
76
  @logger.set_attribute(@start_time_attribute, time_.strftime('%Y-%m-%dT%H:%M:%S.') + ('%06d' % time_.usec) + 'Z')
77
77
  end
78
78
  begin
79
79
  return @app.call(env_)
80
80
  ensure
81
81
  if @end_time_attribute
82
- time_ = Time.now.utc
82
+ time_ = ::Time.now.utc
83
83
  @logger.set_attribute(@end_time_attribute, time_.strftime('%Y-%m-%dT%H:%M:%S.') + ('%06d' % time_.usec) + 'Z')
84
84
  end
85
85
  @logger.end_record
@@ -34,8 +34,9 @@
34
34
  ;
35
35
 
36
36
 
37
- if RUBY_VERSION >= '1.9'
37
+ begin
38
38
  require 'securerandom'
39
+ rescue ::LoadError
39
40
  end
40
41
 
41
42
 
@@ -80,7 +81,7 @@ module Sawmill
80
81
  # If not specified, log entries are written out to STDOUT.
81
82
 
82
83
  def initialize(opts_={})
83
- @levels = opts_[:levels] || ::Sawmill::STANDARD_LEVELS
84
+ @levels = opts_[:levels] || STANDARD_LEVELS
84
85
  @level = @levels.get(opts_[:level])
85
86
  if opts_.include?(:attribute_level)
86
87
  @attribute_level = @levels.get(opts_[:attribute_level])
@@ -89,8 +90,8 @@ module Sawmill
89
90
  end
90
91
  @progname = opts_[:progname] || 'sawmill'
91
92
  @record_progname = opts_[:record_progname] || @progname
92
- @record_id_generator = opts_[:record_id_generator] || _get_default_record_id_generator
93
- @processor = opts_[:processor] || ::Sawmill::Formatter.new(STDOUT)
93
+ @record_id_generator = opts_[:record_id_generator] || Logger._get_default_record_id_generator
94
+ @processor = opts_[:processor] || Formatter.new(::STDOUT)
94
95
  @current_record_id = nil
95
96
  end
96
97
 
@@ -122,7 +123,7 @@ module Sawmill
122
123
  else
123
124
  message_ = message_.inspect
124
125
  end
125
- @processor.message(Entry::Message.new(level_obj_, Time.now, progname_, @current_record_id, message_))
126
+ @processor.message(Entry::Message.new(level_obj_, ::Time.now, progname_, @current_record_id, message_))
126
127
  true
127
128
  end
128
129
  alias_method :log, :add
@@ -161,7 +162,7 @@ module Sawmill
161
162
  def begin_record(id_=nil)
162
163
  end_record if @current_record_id
163
164
  @current_record_id = (id_ || @record_id_generator.call).to_s
164
- @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, @current_record_id))
165
166
  @current_record_id
166
167
  end
167
168
 
@@ -180,7 +181,7 @@ module Sawmill
180
181
 
181
182
  def end_record
182
183
  if @current_record_id
183
- @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, @current_record_id))
184
185
  id_ = @current_record_id
185
186
  @current_record_id = nil
186
187
  id_
@@ -209,7 +210,7 @@ module Sawmill
209
210
  end
210
211
  end
211
212
  return true if level_obj_ < @level
212
- @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, @current_record_id, key_, value_, operation_))
213
214
  true
214
215
  end
215
216
 
@@ -288,7 +289,7 @@ module Sawmill
288
289
  # a default generator which uses the variant 4 (random) UUID standard.
289
290
 
290
291
  def to_generate_record_id(&block_)
291
- @record_id_generator = block_ || _get_default_record_id_generator
292
+ @record_id_generator = block_ || Logger._get_default_record_id_generator
292
293
  end
293
294
 
294
295
 
@@ -339,21 +340,23 @@ module Sawmill
339
340
  end
340
341
 
341
342
 
342
- def _get_default_record_id_generator # :nodoc:
343
- if RUBY_VERSION >= '1.9'
344
- lambda do
345
- uuid_ = SecureRandom.hex(32)
346
- uuid_[12] = '4'
347
- uuid_[16] = (uuid_[16,1].to_i(16)&3|8).to_s(16)
348
- uuid_.insert(8, '-')
349
- uuid_.insert(13, '-')
350
- uuid_.insert(18, '-')
351
- uuid_.insert(23, '-')
352
- uuid_
343
+ def self._get_default_record_id_generator # :nodoc:
344
+ unless @_default_generator
345
+ if defined?(::SecureRandom)
346
+ def self._random_hex32
347
+ ::SecureRandom.hex(32)
348
+ end
349
+ elsif defined?(::ActiveSupport::SecureRandom)
350
+ def self._random_hex32
351
+ ::ActiveSupport::SecureRandom.hex(32)
352
+ end
353
+ else
354
+ def self._random_hex32
355
+ ::Kernel.rand(0x100000000000000000000000000000000).to_s(16).rjust(32, '0')
356
+ end
353
357
  end
354
- else
355
- lambda do
356
- uuid_ = Kernel.rand(0x100000000000000000000000000000000).to_s(16).rjust(32, '0')
358
+ @_default_generator = ::Proc.new do
359
+ uuid_ = _random_hex32
357
360
  uuid_[12] = '4'
358
361
  uuid_[16] = (uuid_[16,1].to_i(16)&3|8).to_s(16)
359
362
  uuid_.insert(8, '-')
@@ -363,8 +366,8 @@ module Sawmill
363
366
  uuid_
364
367
  end
365
368
  end
369
+ @_default_generator
366
370
  end
367
- private :_get_default_record_id_generator
368
371
 
369
372
 
370
373
  end
@@ -0,0 +1,127 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill multi-stream parser 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
+ # A logfile parser that parses log entries from multiple logfile streams,
41
+ # sorts by timestamp, and sends them to a processor.
42
+
43
+ class MultiParser
44
+
45
+
46
+ # Create a new parser that reads from the given streams.
47
+ #
48
+ # You should provide a processor to receive the data from the logfile.
49
+ # The processor may be either an entry processor or a record processor.
50
+ # You may also pass nil for the processor. In this case, the generated
51
+ # log entries will not be sent to a processor but will still be returned
52
+ # by the parse_one_entry method.
53
+ #
54
+ # Recognized options include:
55
+ #
56
+ # <tt>:levels</tt>
57
+ # Sawmill::LevelGroup to use to parse log levels.
58
+ # If not specified, Sawmill::STANDARD_LEVELS is used by default.
59
+ # <tt>:emit_incomplete_records_at_eof</tt>
60
+ # If set to true, causes any incomplete log records to be emitted
61
+ # in their incomplete state when EOF is reached on all streams.
62
+
63
+ def initialize(io_array_, processor_, opts_={})
64
+ @emit_incomplete_records_at_eof = opts_.delete(:emit_incomplete_records_at_eof)
65
+ @heap = Util::Heap.new{ |a_, b_| a_[1].timestamp <=> b_[1].timestamp }
66
+ @queue = Util::Queue.new
67
+ io_array_.each{ |io_| _enqueue(Parser.new(io_, nil, opts_)) }
68
+ @processor = nil
69
+ if processor_.respond_to?(:record) && processor_.respond_to?(:extra_entry)
70
+ @processor = RecordBuilder.new(processor_)
71
+ elsif processor_.respond_to?(:begin_record) && processor_.respond_to?(:end_record)
72
+ @processor = processor_
73
+ end
74
+ @classifier = @processor ? EntryClassifier.new(@processor) : nil
75
+ end
76
+
77
+
78
+ # Parse one log entry from the streams and emit it to the processor.
79
+ # Also returns the log entry.
80
+ # Returns nil if EOF has been reached on all streams.
81
+
82
+ def parse_one_entry
83
+ entry_ = @queue.dequeue
84
+ unless entry_
85
+ data_ = @heap.remove
86
+ if data_
87
+ _enqueue(data_[0])
88
+ entry_ = data_[1]
89
+ else
90
+ if @emit_incomplete_records_at_eof && @processor.respond_to?(:emit_incomplete_records)
91
+ @processor.emit_incomplete_records
92
+ end
93
+ end
94
+ end
95
+ @classifier.entry(entry_) if entry_
96
+ entry_
97
+ end
98
+
99
+
100
+ # Parse until EOF is reached on all streams, and emit the log
101
+ # entries to the processor.
102
+
103
+ def parse_all
104
+ while parse_one_entry; end
105
+ end
106
+
107
+
108
+ private
109
+
110
+ def _enqueue(parser_) # :nodoc:
111
+ loop do
112
+ entry_ = parser_.parse_one_entry
113
+ return unless entry_
114
+ if entry_.type == :unknown_data
115
+ @queue.enqueue(entry_)
116
+ else
117
+ @heap << [parser_, entry_]
118
+ return
119
+ end
120
+ end
121
+ end
122
+
123
+
124
+ end
125
+
126
+
127
+ end
@@ -62,21 +62,20 @@ module Sawmill
62
62
  # <tt>:levels</tt>
63
63
  # Sawmill::LevelGroup to use to parse log levels.
64
64
  # If not specified, Sawmill::STANDARD_LEVELS is used by default.
65
- # <tt>:emit_incomplete_records_on_complete</tt>
65
+ # <tt>:emit_incomplete_records_at_eof</tt>
66
66
  # If set to true, causes any incomplete log records to be emitted
67
67
  # in their incomplete state when EOF is reached.
68
68
 
69
69
  def initialize(io_, processor_, opts_={})
70
70
  @io = io_
71
- @record_processor = nil
72
71
  @processor = nil
73
72
  if processor_.respond_to?(:record) && processor_.respond_to?(:extra_entry)
74
73
  @processor = RecordBuilder.new(processor_)
75
74
  elsif processor_.respond_to?(:begin_record) && processor_.respond_to?(:end_record)
76
75
  @processor = processor_
77
76
  end
78
- @levels = opts_[:levels] || ::Sawmill::STANDARD_LEVELS
79
- @emit_incomplete_records_on_complete = opts_[:emit_incomplete_records_on_complete]
77
+ @levels = opts_[:levels] || STANDARD_LEVELS
78
+ @emit_incomplete_records_at_eof = opts_[:emit_incomplete_records_at_eof]
80
79
  @current_record_id = nil
81
80
  @parser_directives = {}
82
81
  end
@@ -155,11 +154,11 @@ module Sawmill
155
154
  if str_ =~ DIRECTIVE_REGEXP
156
155
  @parser_directives[$1] = $2
157
156
  end
158
- entry_ = Entry::UnknownData.new(str_)
157
+ entry_ = Entry::UnknownData.new(str_.chomp)
159
158
  @processor.unknown_data(entry_) if @processor.respond_to?(:unknown_data)
160
159
  end
161
160
  else
162
- if @emit_incomplete_records_on_complete && @processor.respond_to?(:emit_incomplete_records)
161
+ if @emit_incomplete_records_at_eof && @processor.respond_to?(:emit_incomplete_records)
163
162
  @processor.emit_incomplete_records
164
163
  end
165
164
  end
@@ -171,7 +170,7 @@ module Sawmill
171
170
  # entries to the processor.
172
171
 
173
172
  def parse_all
174
- while process_one_entry; end
173
+ while parse_one_entry; end
175
174
  end
176
175
 
177
176
 
@@ -89,6 +89,16 @@ module Sawmill
89
89
  end
90
90
 
91
91
 
92
+ def eql?(obj_) # :nodoc:
93
+ return false unless obj_.kind_of?(Record)
94
+ return @entries == obj_.instance_variable_get(:@entries)
95
+ end
96
+
97
+ def ==(obj_) # :nodoc:
98
+ eql?(obj_)
99
+ end
100
+
101
+
92
102
  # Append a log entry to this record.
93
103
  #
94
104
  # Entries must be added in order. Raises Errors::IllegalRecordError if
@@ -132,9 +142,9 @@ module Sawmill
132
142
  when :append
133
143
  val_ = @attributes[entry_.key]
134
144
  case val_
135
- when Array
145
+ when ::Array
136
146
  val_ << entry_.value
137
- when String
147
+ when ::String
138
148
  @attributes[entry_.key] = [val_, entry_.value]
139
149
  when nil
140
150
  @attributes[entry_.key] = [entry_.value]
@@ -49,7 +49,7 @@ module Sawmill
49
49
 
50
50
  def initialize(processor_, opts_={})
51
51
  @processor = processor_
52
- @classifier = ::Sawmill::EntryClassifier.new(processor_)
52
+ @classifier = EntryClassifier.new(processor_)
53
53
  end
54
54
 
55
55
 
@@ -61,9 +61,9 @@ module Sawmill
61
61
  @attributes.each do |key_, value_|
62
62
  record_value_ = record_.attribute(key_.to_s)
63
63
  case record_value_
64
- when Array
64
+ when ::Array
65
65
  return false unless record_value_.find{ |rval_| value_ === rval_ }
66
- when String
66
+ when ::String
67
67
  return false unless value_ === record_value_
68
68
  when nil
69
69
  return false unless value_.nil?
@@ -73,6 +73,13 @@ module Sawmill
73
73
  end
74
74
 
75
75
 
76
+ # Return an array of the contents of the record queue, in order.
77
+
78
+ def dequeue_all
79
+ @queue.dequeue_all
80
+ end
81
+
82
+
76
83
  # Return the number of records in the record queue.
77
84
 
78
85
  def size
@@ -88,6 +95,13 @@ module Sawmill
88
95
  end
89
96
 
90
97
 
98
+ # Return an array of the contents of the extra entry queue, in order.
99
+
100
+ def dequeue_all_extra_entries
101
+ @extra_entries_queue.dequeue_all
102
+ end
103
+
104
+
91
105
  # Return the number of entries in the extra entry queue.
92
106
 
93
107
  def extra_entries_size
@@ -109,12 +109,12 @@ module Sawmill
109
109
 
110
110
  def _interpret_processor(param_) # :nodoc:
111
111
  case param_
112
- when Class
112
+ when ::Class
113
113
  param_.new
114
114
  when Base
115
115
  param_
116
116
  else
117
- raise ArgumentError, "Unknown processor object of type #{param_.class.name}"
117
+ raise ::ArgumentError, "Unknown processor object of type #{param_.class.name}"
118
118
  end
119
119
  end
120
120
 
@@ -63,7 +63,7 @@ module Sawmill
63
63
  # closed in the meantime.
64
64
 
65
65
  def open_handle(handle_)
66
- STDOUT
66
+ ::STDOUT
67
67
  end
68
68
 
69
69
 
@@ -82,8 +82,8 @@ module Sawmill
82
82
 
83
83
  def initialize(options_)
84
84
  @turnover_frequency = options_[:turnover_frequency] || :none
85
- dirname_ = options_[:dirname] || Dir.getwd
86
- @prefix = File.join(dirname_, options_[:prefix] || 'sawmill')
85
+ dirname_ = options_[:dirname] || ::Dir.getwd
86
+ @prefix = ::File.join(dirname_, options_[:prefix] || 'sawmill')
87
87
  @suffix = options_[:suffix] || '.log'
88
88
  @local_timezone = options_[:local_timezone]
89
89
  @date_pattern =
@@ -101,7 +101,7 @@ module Sawmill
101
101
 
102
102
  def preferred_handle
103
103
  if @date_pattern
104
- time_ = Time.now
104
+ time_ = ::Time.now
105
105
  time_.utc unless @local_timezone
106
106
  time_.strftime(@date_pattern)
107
107
  else
@@ -118,7 +118,7 @@ module Sawmill
118
118
  else
119
119
  path_ = @prefix+@suffix
120
120
  end
121
- file_ = File.open(path_, File::CREAT | File::WRONLY | File::APPEND)
121
+ file_ = ::File.open(path_, ::File::CREAT | ::File::WRONLY | ::File::APPEND)
122
122
  file_.sync = true
123
123
  file_
124
124
  end
@@ -91,11 +91,11 @@ module Sawmill
91
91
  end
92
92
  @history_size = options_[:history_size].to_i
93
93
  @history_size = 1 if @history_size < 1 && (@max_logfile_size || @shift_period)
94
- dirname_ = options_[:dirname] || Dir.getwd
95
- @normal_path = File.join(dirname_, options_[:filename] || 'sawmill.log')
94
+ dirname_ = options_[:dirname] || ::Dir.getwd
95
+ @normal_path = ::File.join(dirname_, options_[:filename] || 'sawmill.log')
96
96
  @preferred_handle = 0
97
97
  @open_handles = {}
98
- @last_shift = Time.now
98
+ @last_shift = ::Time.now
99
99
  end
100
100
 
101
101
 
@@ -114,7 +114,7 @@ module Sawmill
114
114
  else
115
115
  path_ = "#{@normal_path}.#{@preferred_handle-handle_-1}"
116
116
  end
117
- file_ = File.open(path_, File::CREAT | File::WRONLY | File::APPEND)
117
+ file_ = ::File.open(path_, ::File::CREAT | ::File::WRONLY | ::File::APPEND)
118
118
  file_.sync = true
119
119
  @open_handles[handle_] = true
120
120
  file_
@@ -126,7 +126,7 @@ module Sawmill
126
126
  def close_handle(handle_, io_)
127
127
  io_.close
128
128
  if @preferred_handle - handle_ > @history_size
129
- File.delete("#{@normal_path}.#{@preferred_handle-handle_-1}") rescue nil
129
+ ::File.delete("#{@normal_path}.#{@preferred_handle-handle_-1}") rescue nil
130
130
  end
131
131
  @open_handles.delete(handle_)
132
132
  nil
@@ -138,22 +138,22 @@ module Sawmill
138
138
  def before_write
139
139
  return unless @max_logfile_size || @shift_period
140
140
  turnover_ = false
141
- if @max_logfile_size && File.file?(@normal_path) && File.size(@normal_path) > @max_logfile_size
141
+ if @max_logfile_size && ::File.file?(@normal_path) && ::File.size(@normal_path) > @max_logfile_size
142
142
  turnover_ = true
143
143
  end
144
- if @shift_period && (Time.now - @last_shift) > @shift_period
144
+ if @shift_period && (::Time.now - @last_shift) > @shift_period
145
145
  turnover_ = true
146
146
  end
147
147
  if turnover_
148
148
  max_ = @preferred_handle - @open_handles.keys.min + 1
149
149
  max_ = @history_size if max_ < @history_size
150
- File.delete("#{@normal_path}.#{max_-1}") rescue nil
150
+ ::File.delete("#{@normal_path}.#{max_-1}") rescue nil
151
151
  (max_-1).downto(1) do |index_|
152
- File.rename("#{@normal_path}.#{index_-1}", "#{@normal_path}.#{index_}") rescue nil
152
+ ::File.rename("#{@normal_path}.#{index_-1}", "#{@normal_path}.#{index_}") rescue nil
153
153
  end
154
- File.rename("#{@normal_path}", "#{@normal_path}.0") rescue nil
154
+ ::File.rename("#{@normal_path}", "#{@normal_path}.0") rescue nil
155
155
  @preferred_handle += 1
156
- @last_shift = Time.now
156
+ @last_shift = ::Time.now
157
157
  end
158
158
  end
159
159
 
@@ -73,13 +73,13 @@ module Sawmill
73
73
 
74
74
  def initialize(io_manager_, opts_={})
75
75
  @omit_directives = opts_.delete(:omit_directives)
76
- if io_manager_.kind_of?(Class)
76
+ if io_manager_.kind_of?(::Class)
77
77
  @io_manager = io_manager_.new(opts_)
78
78
  else
79
79
  @io_manager = io_manager_
80
80
  end
81
81
  @handles ||= {}
82
- @mutex ||= Monitor.new
82
+ @mutex ||= ::Monitor.new
83
83
  end
84
84
 
85
85
 
@@ -0,0 +1,153 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill heap 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
+ module Util
40
+
41
+
42
+ # A simple heap class.
43
+
44
+ class Heap
45
+
46
+
47
+ # Create a new heap.
48
+
49
+ def initialize(data_=nil, &block_)
50
+ @_heap = data_ || []
51
+ @_comparator = block_ || ::Proc.new{ |a_,b_| a_ <=> b_ }
52
+ end
53
+
54
+
55
+ def merge(enum_)
56
+ enum_.each{ |value_| add(value_) }
57
+ self
58
+ end
59
+
60
+
61
+ def add(value_)
62
+ @_heap << value_
63
+ _sift_up(@_heap.length-1)
64
+ self
65
+ end
66
+
67
+
68
+ def <<(value_)
69
+ add(value_)
70
+ end
71
+
72
+
73
+ def remove
74
+ ret_ = @_heap[0]
75
+ if @_heap.length > 1
76
+ @_heap[0] = @_heap.pop
77
+ _sift_down(0)
78
+ else
79
+ @_heap.clear
80
+ end
81
+ ret_
82
+ end
83
+
84
+
85
+ def peek
86
+ @_heap[0]
87
+ end
88
+
89
+
90
+ def size
91
+ @_heap.size
92
+ end
93
+
94
+
95
+ def empty?
96
+ @_heap.empty?
97
+ end
98
+
99
+
100
+ def clear
101
+ @_heap.clear
102
+ end
103
+
104
+
105
+ def each!
106
+ while !empty?
107
+ yield(remove)
108
+ end
109
+ end
110
+
111
+
112
+ private
113
+
114
+ def _sift_up(start_) # :nodoc:
115
+ while start_ > 0
116
+ parent_ = (start_ + 1) / 2 - 1
117
+ if @_comparator.call(@_heap[start_], @_heap[parent_]) < 0
118
+ @_heap[start_], @_heap[parent_] = @_heap[parent_], @_heap[start_]
119
+ start_ = parent_
120
+ else
121
+ return start_
122
+ end
123
+ end
124
+ end
125
+
126
+
127
+ def _sift_down(start_) # :nodoc:
128
+ length_ = self.size
129
+ while length_ >= (child2_ = (start_ + 1) * 2)
130
+ child1_ = child2_-1
131
+ if length_ <= child2_
132
+ earliest_child_ = child1_
133
+ elsif @_comparator.call(@_heap[child1_], @_heap[child2_]) < 0
134
+ earliest_child_ = child1_
135
+ else
136
+ earliest_child_ = child2_
137
+ end
138
+ if @_comparator.call(@_heap[start_], @_heap[earliest_child_]) < 0
139
+ return start_
140
+ else
141
+ @_heap[start_], @_heap[earliest_child_] = @_heap[earliest_child_], @_heap[start_]
142
+ start_ = earliest_child_
143
+ end
144
+ end
145
+ end
146
+
147
+
148
+ end
149
+
150
+
151
+ end
152
+
153
+ end
@@ -1,6 +1,6 @@
1
1
  # -----------------------------------------------------------------------------
2
2
  #
3
- # Sawmill entry stream processor interface
3
+ # Sawmill queue utility
4
4
  #
5
5
  # -----------------------------------------------------------------------------
6
6
  # Copyright 2009 Daniel Azuma
@@ -101,6 +101,7 @@ module Sawmill
101
101
  if @push_ptr
102
102
  if @pop_ptr
103
103
  object_ = @buffer[@pop_ptr]
104
+ @buffer[@pop_ptr] = nil
104
105
  @pop_ptr += 1
105
106
  @pop_ptr = 0 if @pop_ptr == @buffer.size
106
107
  @pop_ptr = nil if @pop_ptr == @push_ptr
@@ -114,6 +115,31 @@ module Sawmill
114
115
  end
115
116
 
116
117
 
118
+ # Return an array of the contents of the queue, in order.
119
+
120
+ def dequeue_all
121
+ if @push_ptr
122
+ if @pop_ptr
123
+ if @pop_ptr < @push_ptr
124
+ ret_ = @buffer[@pop_ptr..@push_ptr-1]
125
+ else
126
+ ret_ = @buffer[@pop_ptr..-1] + @buffer[0..@push_ptr-1]
127
+ end
128
+ @buffer.fill(nil)
129
+ @push_ptr = 0
130
+ @pop_ptr = nil
131
+ ret_
132
+ else
133
+ []
134
+ end
135
+ else
136
+ ret_ = @buffer
137
+ @buffer = []
138
+ ret_
139
+ end
140
+ end
141
+
142
+
117
143
  # Return the size of the queue, which is 0 if the queue is empty.
118
144
 
119
145
  def size
@@ -39,7 +39,7 @@
39
39
  module Sawmill
40
40
 
41
41
  # Current gem version, as a frozen string.
42
- VERSION_STRING = '0.0.1'.freeze
42
+ VERSION_STRING = '0.0.2'.freeze
43
43
 
44
44
  # Current gem version, as a Versionomy::Value.
45
45
  VERSION = ::Versionomy.parse(VERSION_STRING, :standard)
data/lib/sawmill.rb CHANGED
@@ -49,6 +49,7 @@ dir_ = ::File.expand_path('sawmill', ::File.dirname(__FILE__))
49
49
  includes_ = [
50
50
  'version',
51
51
  'util/queue',
52
+ 'util/heap',
52
53
  'errors',
53
54
  'level',
54
55
  'entry',
@@ -68,6 +69,7 @@ includes_ = [
68
69
  'record_processor/decompose',
69
70
  'record_processor/format',
70
71
  'parser',
72
+ 'multi_parser',
71
73
  'logger',
72
74
  'rotater',
73
75
  'rotater/base',
@@ -0,0 +1,88 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill: tests multi-parser
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 'test/unit'
37
+ require 'stringio'
38
+ require ::File.expand_path("#{::File.dirname(__FILE__)}/../lib/sawmill.rb")
39
+
40
+
41
+ module Sawmill
42
+ module Tests # :nodoc:
43
+
44
+ class TestMultiParser < ::Test::Unit::TestCase # :nodoc:
45
+
46
+
47
+ def setup
48
+ @levels = ::Sawmill::STANDARD_LEVELS
49
+ end
50
+
51
+
52
+ def _get_io_array(entry_groups_)
53
+ strings_ = []
54
+ entry_groups_.each do |entries_|
55
+ stringio_ = ::StringIO.new
56
+ formatter_ = ::Sawmill::EntryClassifier.new(::Sawmill::Formatter.new(stringio_, :fractional_second_digits => 6))
57
+ entries_.each do |entry_|
58
+ formatter_.entry(entry_)
59
+ end
60
+ strings_ << stringio_.string
61
+ end
62
+ strings_.map{ |str_| ::StringIO.new(str_) }
63
+ end
64
+
65
+
66
+ # Test interleaved entries including unknown data entries.
67
+ # Makes sure they come out in the right order.
68
+
69
+ def test_interleaved_entries
70
+ base_time_ = Time.now.utc
71
+ entries_ = []
72
+ 2.times do |i_|
73
+ entries_ << ::Sawmill::Entry::UnknownData.new("Unknown #{i_}")
74
+ end
75
+ 4.times do |i_|
76
+ entries_ << ::Sawmill::Entry::Message.new(@levels.get(:INFO), base_time_+i_, 'rails', nil, "Hello #{i_}")
77
+ end
78
+ io_array_ = _get_io_array([[entries_[0], entries_[2], entries_[5]], [entries_[3], entries_[4], entries_[1]]])
79
+ queue_ = ::Sawmill::EntryProcessor::SimpleQueue.new
80
+ ::Sawmill::MultiParser.new(io_array_, queue_).parse_all
81
+ assert_equal([entries_[0], entries_[2], entries_[3], entries_[4], entries_[1], entries_[5]], queue_.dequeue_all)
82
+ end
83
+
84
+
85
+ end
86
+
87
+ end
88
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sawmill
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.2.1
23
+ version: 0.2.2
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: versionomy
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.1
33
+ version: 0.1.2
34
34
  version:
35
35
  description: Sawmill is a logging and log analysis system for Ruby. It extends the basic Ruby logging facility with log records and parsing abilities.
36
36
  email: dazuma@gmail.com
@@ -54,6 +54,7 @@ files:
54
54
  - lib/sawmill/level.rb
55
55
  - lib/sawmill/log_record_middleware.rb
56
56
  - lib/sawmill/logger.rb
57
+ - lib/sawmill/multi_parser.rb
57
58
  - lib/sawmill/parser.rb
58
59
  - lib/sawmill/record.rb
59
60
  - lib/sawmill/record_processor/conditionals.rb
@@ -67,6 +68,7 @@ files:
67
68
  - lib/sawmill/rotater/date_based_log_file.rb
68
69
  - lib/sawmill/rotater/shifting_log_file.rb
69
70
  - lib/sawmill/rotater.rb
71
+ - lib/sawmill/util/heap.rb
70
72
  - lib/sawmill/util/queue.rb
71
73
  - lib/sawmill/version.rb
72
74
  - lib/sawmill.rb
@@ -74,13 +76,14 @@ files:
74
76
  - tests/tc_formatter_parser.rb
75
77
  - tests/tc_levels.rb
76
78
  - tests/tc_logger.rb
79
+ - tests/tc_multi_parser.rb
77
80
  - tests/tc_record_processors.rb
78
81
  - tests/tc_records.rb
79
82
  - History.rdoc
80
83
  - README.rdoc
81
84
  - Rakefile
82
85
  has_rdoc: true
83
- homepage: http://github.com/dazuma/sawmill
86
+ homepage: http://virtuoso.rubyforge.org/sawmill
84
87
  licenses: []
85
88
 
86
89
  post_install_message:
@@ -112,5 +115,6 @@ test_files:
112
115
  - tests/tc_formatter_parser.rb
113
116
  - tests/tc_levels.rb
114
117
  - tests/tc_logger.rb
118
+ - tests/tc_multi_parser.rb
115
119
  - tests/tc_record_processors.rb
116
120
  - tests/tc_records.rb