sawmill 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.rdoc +3 -0
- data/README.rdoc +81 -0
- data/Rakefile +147 -0
- data/lib/sawmill/entry.rb +307 -0
- data/lib/sawmill/entry_classifier.rb +75 -0
- data/lib/sawmill/entry_processor/build_records.rb +157 -0
- data/lib/sawmill/entry_processor/conditionals.rb +444 -0
- data/lib/sawmill/entry_processor/filter_basic_fields.rb +145 -0
- data/lib/sawmill/entry_processor/format.rb +228 -0
- data/lib/sawmill/entry_processor/simple_queue.rb +116 -0
- data/lib/sawmill/entry_processor.rb +158 -0
- data/lib/sawmill/errors.rb +66 -0
- data/lib/sawmill/level.rb +264 -0
- data/lib/sawmill/log_record_middleware.rb +93 -0
- data/lib/sawmill/logger.rb +373 -0
- data/lib/sawmill/parser.rb +181 -0
- data/lib/sawmill/record.rb +255 -0
- data/lib/sawmill/record_processor/conditionals.rb +330 -0
- data/lib/sawmill/record_processor/decompose.rb +75 -0
- data/lib/sawmill/record_processor/filter_by_attributes.rb +87 -0
- data/lib/sawmill/record_processor/filter_by_record_id.rb +77 -0
- data/lib/sawmill/record_processor/format.rb +88 -0
- data/lib/sawmill/record_processor/simple_queue.rb +117 -0
- data/lib/sawmill/record_processor.rb +137 -0
- data/lib/sawmill/rotater/base.rb +90 -0
- data/lib/sawmill/rotater/date_based_log_file.rb +145 -0
- data/lib/sawmill/rotater/shifting_log_file.rb +166 -0
- data/lib/sawmill/rotater.rb +236 -0
- data/lib/sawmill/util/queue.rb +138 -0
- data/lib/sawmill/version.rb +47 -0
- data/lib/sawmill.rb +78 -0
- data/tests/tc_entry_processors.rb +138 -0
- data/tests/tc_formatter_parser.rb +144 -0
- data/tests/tc_levels.rb +118 -0
- data/tests/tc_logger.rb +315 -0
- data/tests/tc_record_processors.rb +117 -0
- data/tests/tc_records.rb +206 -0
- metadata +116 -0
@@ -0,0 +1,145 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill formatter 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
|
+
class Rotater
|
40
|
+
|
41
|
+
|
42
|
+
# A rotation strategy that produces log files with the date stamp in the
|
43
|
+
# file name. For example, you could set up an hourly log rotation that
|
44
|
+
# produces the following files:
|
45
|
+
#
|
46
|
+
# rails.2009-10-09-22.log
|
47
|
+
# rails.2009-10-09-23.log
|
48
|
+
# rails.2009-10-10-00.log
|
49
|
+
# rails.2009-10-10-01.log
|
50
|
+
# etc...
|
51
|
+
#
|
52
|
+
# The exact format depends on the rotation frequency, which could be
|
53
|
+
# anywhere from yearly to hourly. For settings less frequent than
|
54
|
+
# hourly, fewer fields will appear in the date stamp portion of the
|
55
|
+
# file name.
|
56
|
+
|
57
|
+
class DateBasedLogFile
|
58
|
+
|
59
|
+
|
60
|
+
# Create a new date-based log file rotation strategy.
|
61
|
+
#
|
62
|
+
# Recognized options include:
|
63
|
+
#
|
64
|
+
# <tt>:turnover_frequency</tt>::
|
65
|
+
# How often the log files should turn over. Allowed values are:
|
66
|
+
# <tt>:yearly</tt>, <tt>:monthly</tt>, <tt>:daily</tt>,
|
67
|
+
# <tt>:hourly</tt>, and <tt>:never</tt>.
|
68
|
+
# <tt>:dirname</tt>::
|
69
|
+
# The directory for the logfiles to be output.
|
70
|
+
# If not specified, the current working directory is used.
|
71
|
+
# <tt>:prefix</tt>::
|
72
|
+
# The logfile name prefix.
|
73
|
+
# In the filename "rails.2009-10-11.log", the prefix is "rails".
|
74
|
+
# If not specified, defaults to "sawmill".
|
75
|
+
# <tt>:suffix</tt>::
|
76
|
+
# The logfile name prefix.
|
77
|
+
# In the filename "rails.2009-10-11.log", the suffix is ".log".
|
78
|
+
# If not specified, defaults to ".log".
|
79
|
+
# <tt>:local_timezone</tt>::
|
80
|
+
# If true, use the local timezone to create datestamps.
|
81
|
+
# The default is to use UTC.
|
82
|
+
|
83
|
+
def initialize(options_)
|
84
|
+
@turnover_frequency = options_[:turnover_frequency] || :none
|
85
|
+
dirname_ = options_[:dirname] || Dir.getwd
|
86
|
+
@prefix = File.join(dirname_, options_[:prefix] || 'sawmill')
|
87
|
+
@suffix = options_[:suffix] || '.log'
|
88
|
+
@local_timezone = options_[:local_timezone]
|
89
|
+
@date_pattern =
|
90
|
+
case @turnover_frequency
|
91
|
+
when :yearly then "%Y"
|
92
|
+
when :monthly then "%Y-%m"
|
93
|
+
when :daily then "%Y-%m-%d"
|
94
|
+
when :hourly then "%Y-%m-%d-%H"
|
95
|
+
else nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Implements the rotation strategy contract.
|
101
|
+
|
102
|
+
def preferred_handle
|
103
|
+
if @date_pattern
|
104
|
+
time_ = Time.now
|
105
|
+
time_.utc unless @local_timezone
|
106
|
+
time_.strftime(@date_pattern)
|
107
|
+
else
|
108
|
+
''
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# Implements the rotation strategy contract.
|
114
|
+
|
115
|
+
def open_handle(handle_)
|
116
|
+
if @date_pattern
|
117
|
+
path_ = "#{@prefix}.#{handle_}#{@suffix}"
|
118
|
+
else
|
119
|
+
path_ = @prefix+@suffix
|
120
|
+
end
|
121
|
+
file_ = File.open(path_, File::CREAT | File::WRONLY | File::APPEND)
|
122
|
+
file_.sync = true
|
123
|
+
file_
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# Implements the rotation strategy contract.
|
128
|
+
|
129
|
+
def close_handle(handle_, io_)
|
130
|
+
io_.close
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# Implements the rotation strategy contract.
|
135
|
+
|
136
|
+
def before_write
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill formatter 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
|
+
class Rotater
|
40
|
+
|
41
|
+
|
42
|
+
# A rotation strategy that "shifts" log files by appending index numbers
|
43
|
+
# to the filename when the file reaches a certain size or age. So when
|
44
|
+
# the file "foo.log" is ready to rotate, it is renamed "foo.log.0", and
|
45
|
+
# a new "foo.log" is started. When that one is ready to rotate, the
|
46
|
+
# oldest "foo.log.0" is shifted down to "foo.log.1", "foo.log" is
|
47
|
+
# renamed to "foo.log.0", and a new "foo.log" is started. So the oldest
|
48
|
+
# logfile is always the one with the largest number suffix, and the
|
49
|
+
# file currently being written to has no suffix.
|
50
|
+
# This is a common rotation strategy for many unix tools.
|
51
|
+
|
52
|
+
class ShiftingLogFile
|
53
|
+
|
54
|
+
|
55
|
+
# Create a new shifting log file rotation strategy.
|
56
|
+
#
|
57
|
+
# Recognized options include:
|
58
|
+
#
|
59
|
+
# <tt>:dirname</tt>::
|
60
|
+
# The directory for the logfiles to be output.
|
61
|
+
# If not specified, the current working directory is used.
|
62
|
+
# <tt>:filename</tt>::
|
63
|
+
# The logfile name.
|
64
|
+
# If not specified, defaults to "sawmill.log".
|
65
|
+
# <tt>:max_logfile_size</tt>::
|
66
|
+
# A logfile will try to rotate once it has reached this size in
|
67
|
+
# bytes. If not specified, the file size is not checked.
|
68
|
+
# <tt>:shift_period</tt>::
|
69
|
+
# A logfile will try to rotate once it has been in service for
|
70
|
+
# this many seconds. This parameter also recognizes the values
|
71
|
+
# <tt>:yearly</tt>, <tt>:monthly</tt>, <tt>:daily</tt>,
|
72
|
+
# and <tt>:hourly</tt>. If not specified, the file's age is
|
73
|
+
# not checked.
|
74
|
+
# <tt>:history_size</tt>::
|
75
|
+
# The maximum number of old logfiles (files with indexes) to
|
76
|
+
# keep. Files beyond this history size will be automatically
|
77
|
+
# deleted. Default is 1. This value must be at least 1.
|
78
|
+
|
79
|
+
def initialize(options_)
|
80
|
+
@max_logfile_size = options_[:max_logfile_size]
|
81
|
+
@shift_period = options_[:shift_period]
|
82
|
+
case @shift_period
|
83
|
+
when :yearly
|
84
|
+
@shift_period = 60*60*24*365
|
85
|
+
when :monthly
|
86
|
+
@shift_period = 60*60*24*30
|
87
|
+
when :daily
|
88
|
+
@shift_period = 60*60*24
|
89
|
+
when :hourly
|
90
|
+
@shift_period = 60*60
|
91
|
+
end
|
92
|
+
@history_size = options_[:history_size].to_i
|
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')
|
96
|
+
@preferred_handle = 0
|
97
|
+
@open_handles = {}
|
98
|
+
@last_shift = Time.now
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Implements the rotation strategy contract.
|
103
|
+
|
104
|
+
def preferred_handle
|
105
|
+
@preferred_handle
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Implements the rotation strategy contract.
|
110
|
+
|
111
|
+
def open_handle(handle_)
|
112
|
+
if handle_ == @preferred_handle
|
113
|
+
path_ = @normal_path
|
114
|
+
else
|
115
|
+
path_ = "#{@normal_path}.#{@preferred_handle-handle_-1}"
|
116
|
+
end
|
117
|
+
file_ = File.open(path_, File::CREAT | File::WRONLY | File::APPEND)
|
118
|
+
file_.sync = true
|
119
|
+
@open_handles[handle_] = true
|
120
|
+
file_
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Implements the rotation strategy contract.
|
125
|
+
|
126
|
+
def close_handle(handle_, io_)
|
127
|
+
io_.close
|
128
|
+
if @preferred_handle - handle_ > @history_size
|
129
|
+
File.delete("#{@normal_path}.#{@preferred_handle-handle_-1}") rescue nil
|
130
|
+
end
|
131
|
+
@open_handles.delete(handle_)
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
# Implements the rotation strategy contract.
|
137
|
+
|
138
|
+
def before_write
|
139
|
+
return unless @max_logfile_size || @shift_period
|
140
|
+
turnover_ = false
|
141
|
+
if @max_logfile_size && File.file?(@normal_path) && File.size(@normal_path) > @max_logfile_size
|
142
|
+
turnover_ = true
|
143
|
+
end
|
144
|
+
if @shift_period && (Time.now - @last_shift) > @shift_period
|
145
|
+
turnover_ = true
|
146
|
+
end
|
147
|
+
if turnover_
|
148
|
+
max_ = @preferred_handle - @open_handles.keys.min + 1
|
149
|
+
max_ = @history_size if max_ < @history_size
|
150
|
+
File.delete("#{@normal_path}.#{max_-1}") rescue nil
|
151
|
+
(max_-1).downto(1) do |index_|
|
152
|
+
File.rename("#{@normal_path}.#{index_-1}", "#{@normal_path}.#{index_}") rescue nil
|
153
|
+
end
|
154
|
+
File.rename("#{@normal_path}", "#{@normal_path}.0") rescue nil
|
155
|
+
@preferred_handle += 1
|
156
|
+
@last_shift = Time.now
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill log rotation 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
|
+
require 'thread'
|
38
|
+
|
39
|
+
|
40
|
+
module Sawmill
|
41
|
+
|
42
|
+
|
43
|
+
# The Sawmill Rotater provides log rotation services for logfile
|
44
|
+
# formatting, supporting several different log rotation strategies.
|
45
|
+
#
|
46
|
+
# The formatter implemented by Sawmill::EntryProcessor::Format already
|
47
|
+
# recognizes a Rotater as a supported destination, and automatically
|
48
|
+
# interfaces with it to ensure that log records aren't split into
|
49
|
+
# multiple files by rotation.
|
50
|
+
#
|
51
|
+
# You may also interface with a rotater manually. The core of rotater
|
52
|
+
# usage is the Channel, which lets you ensure that groups of log
|
53
|
+
# entries end up in the same log file regardless of log rotation.
|
54
|
+
# Generally, to use a rotater, you obtain one or more channels and
|
55
|
+
# write formatted entries to them, either telling them explicitly
|
56
|
+
# where the allowable file breaks are, or by letting the rotater
|
57
|
+
# break the file anywhere it wants. See the create_channel method
|
58
|
+
# and the Channel object for more details.
|
59
|
+
|
60
|
+
class Rotater
|
61
|
+
|
62
|
+
|
63
|
+
# Create a rotater using the given rotation strategy.
|
64
|
+
# See Sawmill::Rotater::DateBasedLogFile and
|
65
|
+
# Sawmill::Rotater::ShiftingLogFile for examples of common strategies.
|
66
|
+
#
|
67
|
+
# The rotation strategy can be passed as an object or as a class with a
|
68
|
+
# set of options that will be used to instantiate the strategy.
|
69
|
+
# In addition to those options, the following options are recognized:
|
70
|
+
#
|
71
|
+
# <tt>:omit_directives</tt>::
|
72
|
+
# If true, omit standard logfile directives. Default is false.
|
73
|
+
|
74
|
+
def initialize(io_manager_, opts_={})
|
75
|
+
@omit_directives = opts_.delete(:omit_directives)
|
76
|
+
if io_manager_.kind_of?(Class)
|
77
|
+
@io_manager = io_manager_.new(opts_)
|
78
|
+
else
|
79
|
+
@io_manager = io_manager_
|
80
|
+
end
|
81
|
+
@handles ||= {}
|
82
|
+
@mutex ||= Monitor.new
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Create a new Channel for this Rotater. See Sawmill::Rotater::Channel
|
87
|
+
# for details on the Channel object.
|
88
|
+
#
|
89
|
+
# The following options are recognized:
|
90
|
+
#
|
91
|
+
# <tt>:auto_rotate</tt>::
|
92
|
+
# Put the channel in auto-rotate mode. In this mode, the rotater is
|
93
|
+
# allowed to rotate the logfile at any time for that channel. It is
|
94
|
+
# the equivalent of calling check_rotate on the channel after every
|
95
|
+
# write. Default is false.
|
96
|
+
|
97
|
+
def create_channel(opts_={})
|
98
|
+
Channel.new(self, opts_)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def _obtain_handle # :nodoc:
|
103
|
+
handle_ = @io_manager.preferred_handle
|
104
|
+
if @handles.include?(handle_)
|
105
|
+
@handles[handle_][2] += 1
|
106
|
+
else
|
107
|
+
io_ = @io_manager.open_handle(handle_)
|
108
|
+
unless @omit_directives
|
109
|
+
io_.write("# sawmill_format: version=1\n")
|
110
|
+
end
|
111
|
+
@handles[handle_] = [handle_, io_, 1]
|
112
|
+
end
|
113
|
+
handle_
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def _release_handle(handle_) # :nodoc:
|
118
|
+
info_ = @handles[handle_]
|
119
|
+
info_[2] -= 1
|
120
|
+
if info_[2] == 0
|
121
|
+
@io_manager.close_handle(handle_, info_[1])
|
122
|
+
@handles.delete(handle_)
|
123
|
+
end
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def _check_rotate_handle(handle_) # :nodoc:
|
129
|
+
if handle_ != @io_manager.preferred_handle
|
130
|
+
_release_handle(handle_)
|
131
|
+
_obtain_handle
|
132
|
+
else
|
133
|
+
handle_
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def _do_open # :nodoc:
|
139
|
+
@mutex.synchronize do
|
140
|
+
_obtain_handle
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def _do_write(handle_, str_, auto_rotate_) # :nodoc:
|
146
|
+
@mutex.synchronize do
|
147
|
+
@io_manager.before_write
|
148
|
+
if auto_rotate_
|
149
|
+
handle_ = _check_rotate_handle(handle_)
|
150
|
+
end
|
151
|
+
info_ = @handles[handle_]
|
152
|
+
info_[1].write(str_)
|
153
|
+
handle_
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
def _do_close(handle_) # :nodoc:
|
159
|
+
@mutex.synchronize do
|
160
|
+
_release_handle(handle_)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
def _do_check_rotate(handle_) # :nodoc:
|
166
|
+
@mutex.synchronize do
|
167
|
+
_check_rotate_handle(handle_)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
# A channel is a lightweight object that responds to the write and close
|
173
|
+
# methods; that is, it is sufficient for Sawmill::Formatter.
|
174
|
+
#
|
175
|
+
# When a channel is opened, it locks down a path to the logfile and
|
176
|
+
# ensures that the logfile will not rotate out from under it; that is,
|
177
|
+
# writes to a channel are ensured to end up in the same physical file.
|
178
|
+
#
|
179
|
+
# You may choose, at intervals, to explicitly tell the channel that it
|
180
|
+
# is okay to rotate the logfile now, by calling check_rotate.
|
181
|
+
#
|
182
|
+
# You must close a channel when you are done with it. Closing a channel
|
183
|
+
# does not close the underlying logfile, but instead tells the rotater
|
184
|
+
# that you are done with this channel and that the logfile is free to
|
185
|
+
# rotate independent of it.
|
186
|
+
#
|
187
|
+
# You may have any number of channels open at any time, each on a
|
188
|
+
# different rotation schedule. Each may possibly be writing to different
|
189
|
+
# files in the rotation at any time, but this is all done automatically
|
190
|
+
# behind the scenes.
|
191
|
+
|
192
|
+
class Channel
|
193
|
+
|
194
|
+
def initialize(rotater_, opts_={}) # :nodoc:
|
195
|
+
@rotater = rotater_
|
196
|
+
@auto_rotate = opts_[:auto_rotate]
|
197
|
+
@io_handle = @rotater._do_open
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
# Write a string to this channel.
|
202
|
+
|
203
|
+
def write(str_)
|
204
|
+
if @io_handle
|
205
|
+
@rotater._do_write(@io_handle, str_, @auto_rotate)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# Close this channel, telling the rotater that this channel no longer
|
211
|
+
# needs to constrain the log rotation.
|
212
|
+
|
213
|
+
def close
|
214
|
+
if @io_handle
|
215
|
+
@rotater._do_close(@io_handle)
|
216
|
+
@io_handle = nil
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
# Manually tell the rotater that this channel is at a stopping point
|
222
|
+
# and that the log file may rotate at this time.
|
223
|
+
|
224
|
+
def check_rotate
|
225
|
+
if @io_handle
|
226
|
+
@io_handle = @rotater._do_check_rotate(@io_handle)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill entry stream processor 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
|
+
module Sawmill
|
38
|
+
|
39
|
+
module Util
|
40
|
+
|
41
|
+
|
42
|
+
# A simple queue that optionally provides an upper size limit.
|
43
|
+
|
44
|
+
class Queue
|
45
|
+
|
46
|
+
|
47
|
+
# Recognized options include:
|
48
|
+
#
|
49
|
+
# <tt>:limit</tt>::
|
50
|
+
# Size limit for the queue. If not specified, the queue can grow
|
51
|
+
# arbitrarily large.
|
52
|
+
# <tt>:drop_oldest</tt>::
|
53
|
+
# If set to true, then when an item is added to a full queue, the
|
54
|
+
# oldest item is dropped. If set to false or not specified, then
|
55
|
+
# the new item is not added.
|
56
|
+
|
57
|
+
def initialize(opts_={})
|
58
|
+
limit_ = opts_[:limit]
|
59
|
+
@buffer = limit_ ? ::Array.new(limit_) : []
|
60
|
+
@push_ptr = limit_ ? 0 : nil
|
61
|
+
@pop_ptr = nil
|
62
|
+
@drop_oldest = limit_ && opts_[:drop_oldest]
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Attempt to push an item on the queue.
|
67
|
+
#
|
68
|
+
# If the queue is full, then the behavior is determined by the
|
69
|
+
# :drop_oldest setting provided to the constructor.
|
70
|
+
#
|
71
|
+
# Returns true if the object was pushed on the queue, or false if the
|
72
|
+
# queue was full.
|
73
|
+
|
74
|
+
def enqueue(object_)
|
75
|
+
result_ = true
|
76
|
+
if @push_ptr
|
77
|
+
if @pop_ptr == @push_ptr
|
78
|
+
if @drop_oldest
|
79
|
+
@pop_ptr += 1
|
80
|
+
@pop_ptr = 0 if @pop_ptr == @buffer.size
|
81
|
+
result_ = false
|
82
|
+
else
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
elsif @pop_ptr.nil?
|
86
|
+
@pop_ptr = @push_ptr
|
87
|
+
end
|
88
|
+
@buffer[@push_ptr] = object_
|
89
|
+
@push_ptr += 1
|
90
|
+
@push_ptr = 0 if @push_ptr == @buffer.size
|
91
|
+
else
|
92
|
+
@buffer.push(object_)
|
93
|
+
end
|
94
|
+
result_
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Return the oldest item in the queue, or nil if the queue is empty.
|
99
|
+
|
100
|
+
def dequeue
|
101
|
+
if @push_ptr
|
102
|
+
if @pop_ptr
|
103
|
+
object_ = @buffer[@pop_ptr]
|
104
|
+
@pop_ptr += 1
|
105
|
+
@pop_ptr = 0 if @pop_ptr == @buffer.size
|
106
|
+
@pop_ptr = nil if @pop_ptr == @push_ptr
|
107
|
+
object_
|
108
|
+
else
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
else
|
112
|
+
@buffer.shift
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Return the size of the queue, which is 0 if the queue is empty.
|
118
|
+
|
119
|
+
def size
|
120
|
+
if @push_ptr
|
121
|
+
if @pop_ptr
|
122
|
+
value_ = @push_ptr - @pop_ptr
|
123
|
+
value_ > 0 ? value_ : value_ + @buffer.size
|
124
|
+
else
|
125
|
+
0
|
126
|
+
end
|
127
|
+
else
|
128
|
+
@buffer.size
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|