sawmill 0.0.1 → 0.0.2

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 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