birling 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem "bundler"
5
+ gem "jeweler"
6
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Scott Tadman, The Working Group.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # birling
2
+
3
+ A replacement for Logger that offers more robust handling of log rotation.
4
+
5
+ The built-in logger supports log rotation on a daily, weekly or monthly basis,
6
+ but not with more fine-grained control. Birling will allow rotation by hour,
7
+ by day, or by an arbitrary amount of time expressed in seconds.
8
+
9
+ Additionally, Birling will automatically remove old log files. These can be
10
+ pruned off by age, by retaining a minimum number of them, or a combination of
11
+ both.
12
+
13
+ ## Example
14
+
15
+ The interface is very similar to the [built in logger](http://rubygems.org/gems/logger)
16
+ facility that ships with Ruby:
17
+
18
+ ```ruby
19
+ logger = Birling::Logger.new('test.log')
20
+
21
+ logger.info("Application starting up.")
22
+ logger.debug("application_init()")
23
+ ```
24
+
25
+ A short-hand method is available:
26
+
27
+ ```ruby
28
+ logger = Birling.open('test.log')
29
+ ```
30
+
31
+ Log rotation parameters are quite flexible. For example, to retain a maximum
32
+ of ten hourly logs:
33
+
34
+ ```ruby
35
+ logger = Birling.open(
36
+ 'test.log',
37
+ :period => :hourly,
38
+ :retain_count => 10
39
+ )
40
+ ```
41
+
42
+ Alternatively the retention period can be expressed in terms of time where
43
+ log files that could have been created by this logger which are older than
44
+ that period will be removed:
45
+
46
+ ```ruby
47
+ logger = Birling.open(
48
+ 'test.log',
49
+ :period => :hourly,
50
+ :retain_period => 10 * 3600
51
+ )
52
+ ```
53
+
54
+ The format of the resulting log-file can be adjusted by supplying a formatter.
55
+ Several arguments passed to the formatter's `call` method, so a `lambda`, a
56
+ `module` or an object instance could be used for this purpose.
57
+
58
+ Example:
59
+
60
+ ```ruby
61
+ logger = Birling.open(
62
+ 'test.log',
63
+ :formatter => lambda { |severity, time, program, message| "#{time}> #{message}\n" }
64
+ )
65
+ ```
66
+
67
+ Note that the formatter is responsible for introducing any line-feeds into
68
+ the resulting output stream. This gives the formatter complete control over
69
+ what is written to the log.
70
+
71
+ ## Limitations
72
+
73
+ The log rotation feature, for reasons that should be obvious, will not work
74
+ on loggers that are created with an existing file-handle. For example, when
75
+ using `STDOUT` the logger will not rotate.
76
+
77
+ ## Copyright
78
+
79
+ Copyright (c) 2011-2012 Scott Tadman, The Working Group Inc.
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ require 'rake'
13
+ require 'jeweler'
14
+
15
+ Jeweler::Tasks.new do |gem|
16
+ gem.name = "birling"
17
+ gem.homepage = "http://github.com/twg/birling"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{Logger with simple log rotation system}
20
+ gem.description = %Q{Mostly drop-in replacement for Logger with a more robust log rotation facility}
21
+ gem.email = "github@tadman.ca"
22
+ gem.authors = [ "Scott Tadman" ]
23
+ end
24
+
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,15 @@
1
+ module Birling::Formatter
2
+ # == Constants ============================================================
3
+
4
+ TIME_FORMAT_DEFAULT = '%Y-%m-%d %H:%M:%S'.freeze
5
+
6
+ # == Module Methods =======================================================
7
+
8
+ def self.call(severity, time, program, message)
9
+ if (program)
10
+ "[#{time.strftime(TIME_FORMAT_DEFAULT)}] <#{program}> #{message}"
11
+ else
12
+ "[#{time.strftime(TIME_FORMAT_DEFAULT)}] #{message}"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ class Birling::Log < File
2
+ # == Constants ============================================================
3
+
4
+ # == Class Methods ========================================================
5
+
6
+ def self.open(log, mode = nil)
7
+ case (log)
8
+ when IO
9
+ yield(log) if (block_given?)
10
+
11
+ log
12
+ when String
13
+ io = new(log, mode || 'a')
14
+
15
+ yield(io) if (block_given?)
16
+
17
+ io
18
+ end
19
+ end
20
+
21
+ # == Instance Methods =====================================================
22
+
23
+ def size
24
+ self.stat.size
25
+ end
26
+
27
+ def create_time
28
+ self.stat.ctime
29
+ end
30
+
31
+ def age(relative_to = nil)
32
+ (relative_to || Time.now) - self.stat.ctime
33
+ end
34
+ end
@@ -0,0 +1,257 @@
1
+ class Birling::Logger
2
+ # == Constants ============================================================
3
+
4
+ # These level constants are the same as the syslog system utility
5
+ SEVERITY = {
6
+ :emergency => EMERGENCY = 0,
7
+ :alert => ALERT = 1,
8
+ :critical => CRITICAL = 2,
9
+ :error => ERROR = 3,
10
+ :warning => WARNING = 4,
11
+ :notice => NOTICE = 5,
12
+ :info => INFO = 6,
13
+ :debug => DEBUG = 7,
14
+ :unknown => UNKNOWN = 999
15
+ }.freeze
16
+
17
+ DEFAULT_SEVERITY = UNKNOWN
18
+
19
+ SEVERITY_LABEL = SEVERITY.invert.freeze
20
+
21
+ PATH_TIME_DEFAULT = {
22
+ :hourly => '%Y%m%d%H'.freeze,
23
+ :daily => '%Y%m%d'.freeze,
24
+ :default => '%s'.freeze
25
+ }.freeze
26
+
27
+ # == Properties ===========================================================
28
+
29
+ attr_reader :severity
30
+ attr_accessor :formatter
31
+ attr_accessor :program
32
+ attr_accessor :time_source
33
+ attr_reader :path
34
+ attr_reader :path_format
35
+ attr_reader :path_time_format
36
+ attr_reader :current_path
37
+ attr_reader :retain_count
38
+ attr_reader :retain_period
39
+ attr_reader :period
40
+ attr_reader :rotation_time
41
+
42
+ # == Class Methods ========================================================
43
+
44
+ def self.severity(value)
45
+ case (value)
46
+ when Symbol
47
+ SEVERITY[value] or DEFAULT_SEVERITY
48
+ when String
49
+ SEVERITY[value.to_sym] or DEFAULT_SEVERITY
50
+ when Fixnum
51
+ SEVERITY_LABEL[value] and value or DEFAULT_SEVERITY
52
+ else
53
+ DEFAULT_SEVERITY
54
+ end
55
+ end
56
+
57
+ # == Instance Methods =====================================================
58
+
59
+ def initialize(log, options = nil)
60
+ @period = (options and options[:period])
61
+ @severity = self.class.severity(options && options[:severity])
62
+ @retain_count = (options and options[:retain_count])
63
+ @retain_period = (options and options[:retain_period])
64
+ @formatter = (options and options[:formatter] or Birling::Formatter)
65
+ @program = (options and options[:program] or nil)
66
+ @time_source = (options and options[:time_source] or Time)
67
+ @path_format = (options and options[:path_format])
68
+
69
+ case (log)
70
+ when IO, StringIO
71
+ @log = log
72
+ when String
73
+ @path = log
74
+ end
75
+
76
+ if (@path and @period)
77
+ @rotation_time = self.next_rotation_time
78
+
79
+ @path_time_format = (PATH_TIME_DEFAULT[@period] or PATH_TIME_DEFAULT[:default])
80
+
81
+ @path_format ||=
82
+ @path.sub(/\.(\w+)$/) do |s|
83
+ '.' + @path_time_format + '.' + $1
84
+ end
85
+ end
86
+
87
+ if (@path and !@log)
88
+ self.log_open!
89
+ end
90
+
91
+ yield(self) if (block_given?)
92
+ end
93
+
94
+ def severity=(value)
95
+ @severity = self.class.severity(value)
96
+ end
97
+
98
+ def can_rotate?
99
+ !!@path
100
+ end
101
+
102
+ def size
103
+ @log and @log.respond_to
104
+ end
105
+
106
+ def retain=(value)
107
+ @retain = value ? value.to_i : nil
108
+
109
+ if (@retain_period and @retain_period <= 0)
110
+ @retain_period = nil
111
+ end
112
+
113
+ @retain_period
114
+ end
115
+
116
+ def log(level, message = nil, program = nil)
117
+ return unless (@log)
118
+
119
+ level = self.class.severity(level)
120
+ program ||= @program
121
+
122
+ self.check_log_rotation!
123
+
124
+ @log.write(@formatter.call(level, @time_source.now, program, message))
125
+ end
126
+ alias_method :add, :log
127
+
128
+ def <<(message)
129
+ return unless (@log)
130
+
131
+ self.check_log_rotation!
132
+
133
+ @log.write(message)
134
+ end
135
+
136
+ SEVERITY.each do |name, level|
137
+ define_method(:"#{name}?") do
138
+ @severity >= level
139
+ end
140
+
141
+ define_method(name) do |message = nil, program = nil|
142
+ return unless (@log and @severity >= level)
143
+
144
+ program ||= @program
145
+
146
+ if (!message and block_given?)
147
+ message = yield
148
+ end
149
+
150
+ self.check_log_rotation!
151
+
152
+ @log.write(@formatter.call(level, @time_source.now, program, message))
153
+ end
154
+ end
155
+
156
+ def close
157
+ return unless (@log)
158
+
159
+ @log.close
160
+ @log = nil
161
+ end
162
+
163
+ def opened?
164
+ !!@log
165
+ end
166
+
167
+ def closed?
168
+ !@log
169
+ end
170
+
171
+ def create_time
172
+ @log and @log.ctime
173
+ end
174
+
175
+ def size
176
+ @log and @log.size
177
+ end
178
+
179
+ def age(relative_to = nil)
180
+ (relative_to || @time_source.now) - @log.ctime
181
+ end
182
+
183
+ protected
184
+ def next_rotation_time
185
+ case (@period)
186
+ when Fixnum, Float
187
+ @time_source.now + @period
188
+ when :daily
189
+ Birling::Support.next_day(@time_source.now)
190
+ when :hourly
191
+ Birling::Support.next_hour(@time_source.now)
192
+ else
193
+ nil
194
+ end
195
+ end
196
+
197
+ def prune_logs!
198
+ return unless (@path and (@retain_period or @retain_count))
199
+
200
+ log_spec = @path.sub(/\.(\w+)$/, '*')
201
+
202
+ logs = (Dir.glob(log_spec) - [ @path ]).collect do |p|
203
+ stat = File.stat(p)
204
+ create_time = (stat and stat.ctime or @time_source.now)
205
+
206
+ [ p, create_time ]
207
+ end.sort_by do |r|
208
+ r[1] || @time_source.now
209
+ end
210
+
211
+ if (@retain_period)
212
+ logs.reject! do |r|
213
+ if (Time.now - r[1] > @retain_period)
214
+ FileUtils.rm_f(r[0])
215
+ end
216
+ end
217
+ end
218
+
219
+ if (@retain_count)
220
+ # The logs array is sorted from oldest to newest, so retaining the N
221
+ # newest entries entails stripping them off the end with pop.
222
+
223
+ logs.pop(@retain_count)
224
+
225
+ FileUtils.rm_f(logs.collect { |r| r[0] })
226
+ end
227
+ end
228
+
229
+ def check_log_rotation!
230
+ return unless (@rotation_time)
231
+
232
+ if (@time_source.now >= @rotation_time)
233
+ self.log_open!
234
+
235
+ @rotation_time = self.next_rotation_time
236
+ end
237
+ end
238
+
239
+ def log_open!
240
+ if (@path_format)
241
+ @current_path = @time_source.now.strftime(@path_format)
242
+
243
+ @log = File.open(@current_path, 'a')
244
+
245
+ if (File.exist?(@path) and File.symlink?(@path))
246
+ File.unlink(@path)
247
+ File.symlink(@path, @current_path)
248
+ end
249
+
250
+ self.prune_logs!
251
+ else
252
+ @current_path = @path
253
+
254
+ @log = File.open(@current_path, 'a')
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,20 @@
1
+ module Birling::Support
2
+ def next_day(time, time_source = nil)
3
+ (time_source || Time).local(
4
+ time.year,
5
+ time.month,
6
+ time.day,
7
+ 23,
8
+ 59,
9
+ 59
10
+ ) + 1
11
+ end
12
+
13
+ def next_hour(time, time_source = nil)
14
+ seconds_left = time.to_i % 3600
15
+
16
+ time + (seconds_left > 0 ? seconds_left : 3600)
17
+ end
18
+
19
+ extend self
20
+ end
data/lib/birling.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Birling
2
+ # == Submodules ===========================================================
3
+
4
+ autoload(:Formatter, 'birling/formatter')
5
+ autoload(:Logger, 'birling/logger')
6
+ autoload(:Support, 'birling/support')
7
+
8
+ # == Module Methods =======================================================
9
+
10
+ def self.open(path, options = nil)
11
+ Birling::Logger.new(path, options)
12
+ end
13
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ require 'fileutils'
13
+ require 'test/unit'
14
+
15
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
17
+
18
+ require 'birling'
19
+
20
+ class Time::Warped < Time
21
+ def self.now
22
+ @now || super
23
+ end
24
+
25
+ def self.now=(value)
26
+ @now = value
27
+ end
28
+ end
29
+
30
+ class Test::Unit::TestCase
31
+ def in_time_zone(zone)
32
+ tz = ENV['TZ']
33
+
34
+ ENV['tz'] = zone
35
+
36
+ yield if (block_given?)
37
+
38
+ ensure
39
+ ENV['tz'] = tz
40
+ end
41
+
42
+ def temp_path(name = nil)
43
+ name ||= begin
44
+ @temp_path_inc ||= 0
45
+
46
+ _name = '%05d.%05d.tmp' % [ @temp_path_inc, $$ ]
47
+ @temp_path_inc += 1
48
+
49
+ _name
50
+ end
51
+
52
+ case (name)
53
+ when Symbol
54
+ name = "#{name}.log"
55
+ end
56
+
57
+ @temp_path ||= File.expand_path('../tmp', File.dirname(__FILE__))
58
+
59
+ full_path = File.expand_path(name, @temp_path)
60
+
61
+ FileUtils::mkdir_p(File.dirname(full_path))
62
+
63
+ if (block_given?)
64
+ begin
65
+ yield(full_path)
66
+ ensure
67
+ remove_spec = File.expand_path('*', @temp_path)
68
+
69
+ FileUtils.rm_f(Dir.glob(remove_spec))
70
+ end
71
+ end
72
+
73
+ full_path
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+
3
+ class TestBirling < Test::Unit::TestCase
4
+ def test_module
5
+ assert Birling
6
+ end
7
+
8
+ def test_open
9
+ _path = nil
10
+
11
+ temp_path do |path|
12
+ _path = path
13
+ log = Birling.open(path)
14
+
15
+ assert_equal Birling::Logger, log.class
16
+
17
+ assert log
18
+ assert File.exist?(path)
19
+
20
+ assert log.opened?
21
+ assert !log.closed?
22
+ assert_equal 0, log.size
23
+
24
+ assert log.debug?
25
+
26
+ log.debug("Test")
27
+
28
+ assert log.size > 0
29
+
30
+ log.close
31
+
32
+ assert !log.opened?
33
+ assert log.closed?
34
+ end
35
+
36
+ assert !File.exist?(_path)
37
+ end
38
+
39
+ def test_time_warped
40
+ _now = Time.now
41
+
42
+ Time::Warped.now = _now
43
+
44
+ assert_not_equal Time.now, Time.now
45
+ assert_equal _now, Time::Warped.now
46
+
47
+ Time::Warped.now = nil
48
+
49
+ assert_not_equal _now, Time::Warped.now
50
+ end
51
+ end
@@ -0,0 +1,203 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+
3
+ class TestBirlingLogger < Test::Unit::TestCase
4
+ def test_defaults
5
+ temp_path do |path|
6
+ log = Birling::Logger.new(path)
7
+
8
+ assert log
9
+
10
+ assert log.opened?
11
+ assert !log.closed?
12
+ assert_equal 0, log.size
13
+ assert Time.now >= log.create_time
14
+
15
+ assert_equal Birling::Formatter, log.formatter
16
+ assert_equal Time, log.time_source
17
+ assert_equal nil, log.period
18
+ end
19
+ end
20
+
21
+ def test_with_handle
22
+ s = StringIO.new
23
+
24
+ log = Birling::Logger.new(s)
25
+
26
+ assert log.opened?
27
+
28
+ assert_equal 0, log.size
29
+ end
30
+
31
+ def test_formatter
32
+ formatter_called = false
33
+ formatter = lambda do |severity, time, program, message|
34
+ formatter_called = true
35
+ message
36
+ end
37
+
38
+ output = StringIO.new
39
+ log = Birling::Logger.new(output, :formatter => formatter)
40
+
41
+ log.debug("Test")
42
+
43
+ assert_equal true, formatter_called
44
+
45
+ output.rewind
46
+ assert_equal "Test", output.read
47
+ end
48
+
49
+ def test_default_level
50
+ temp_path do |path|
51
+ log = Birling::Logger.new(path)
52
+
53
+ assert log
54
+
55
+ assert log.opened?
56
+ assert !log.closed?
57
+ assert_equal 0, log.size
58
+
59
+ assert log.debug?
60
+
61
+ log.debug("Test")
62
+
63
+ current_size = log.size
64
+ assert current_size > 0
65
+ end
66
+ end
67
+
68
+ def test_direct_write
69
+ output = StringIO.new
70
+
71
+ log = Birling::Logger.new(output)
72
+
73
+ log << "TEST"
74
+
75
+ output.rewind
76
+ assert_equal "TEST", output.read
77
+ end
78
+
79
+ def test_level_filter
80
+ output = StringIO.new
81
+
82
+ log = Birling::Logger.new(
83
+ output,
84
+ :formatter => lambda { |s, t, p, m| "#{m}\n" },
85
+ :severity => :info
86
+ )
87
+
88
+ log.debug("DEBUG")
89
+ log.info("INFO")
90
+
91
+ output.rewind
92
+ assert_equal "INFO\n", output.read
93
+ end
94
+
95
+ def test_reopen
96
+ temp_path do |path|
97
+ log = Birling::Logger.new(path)
98
+
99
+ assert log.debug?
100
+
101
+ log.debug("Test")
102
+
103
+ current_size = log.size
104
+ assert current_size > 0
105
+
106
+ create_time = log.create_time
107
+ assert create_time <= Time.now
108
+
109
+ log.close
110
+
111
+ log = Birling::Logger.new(path)
112
+
113
+ assert_equal current_size, log.size
114
+ assert_equal create_time, log.create_time
115
+ end
116
+ end
117
+
118
+ def test_time_source
119
+ temp_path do |path|
120
+ frozen_time = Time.now
121
+ Time::Warped.now = frozen_time
122
+
123
+ logger = Birling::Logger.new(path, :time_source => Time::Warped)
124
+
125
+ assert_equal frozen_time, logger.time_source.now
126
+ end
127
+ end
128
+
129
+ def test_cycling
130
+ temp_path(:cycle) do |path|
131
+ start = Time.now
132
+ Time::Warped.now = start
133
+ logger = Birling::Logger.new(path, :period => 1, :time_source => Time::Warped)
134
+
135
+ assert_equal 1, logger.period
136
+
137
+ current_path = logger.current_path
138
+ assert_equal '%s', logger.path_time_format
139
+
140
+ logger.debug("Test")
141
+
142
+ Time::Warped.now += 1
143
+
144
+ logger.debug("Test")
145
+
146
+ assert_not_equal current_path, logger.current_path
147
+
148
+ current_path = logger.current_path
149
+
150
+ Time::Warped.now += 1
151
+
152
+ logger.debug("Test")
153
+
154
+ assert_not_equal current_path, logger.current_path
155
+ end
156
+ end
157
+
158
+ def test_retain_count
159
+ temp_path(:cycle) do |path|
160
+ start = Time.now
161
+ Time::Warped.now = start
162
+
163
+ retain_count = 10
164
+
165
+ logger = Birling::Logger.new(
166
+ path,
167
+ :period => 1,
168
+ :time_source => Time::Warped,
169
+ :retain_count => retain_count
170
+ )
171
+
172
+ (retain_count + 5).times do |n|
173
+ logger.debug("Test")
174
+ Time::Warped.now += 1
175
+ logger.debug("Test")
176
+ end
177
+
178
+ assert_equal retain_count, Dir.glob(logger.path_format % '*').length
179
+ end
180
+ end
181
+
182
+ def test_retain_period
183
+ temp_path(:cycle) do |path|
184
+ retain_period = 3
185
+
186
+ logger = Birling::Logger.new(
187
+ path,
188
+ :period => 1,
189
+ :retain_period => retain_period
190
+ )
191
+
192
+ finish = Time.now + retain_period + 5
193
+
194
+ while (Time.now < finish)
195
+ logger.debug("Test")
196
+ Time::Warped.now += 1
197
+ logger.debug("Test")
198
+ end
199
+
200
+ assert_equal retain_period + 1, Dir.glob(logger.path_format % '*').length
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+
3
+ class TestBirlingSupport < Test::Unit::TestCase
4
+ def test_next_day_on_dst_flip
5
+ in_time_zone('EST5EDT') do
6
+ time = Time.new(2012, 11, 4)
7
+
8
+ assert_equal time.day, (time + 86400).day
9
+
10
+ next_day = Birling::Support.next_day(time)
11
+
12
+ assert_equal 2012, next_day.year
13
+ assert_equal 11, next_day.month
14
+ assert_equal 5, next_day.day
15
+ assert_equal 0, next_day.hour
16
+ assert_equal 0, next_day.min
17
+ assert_equal 0, next_day.sec
18
+ end
19
+ end
20
+
21
+ def test_hour_day_on_dst_flip
22
+ in_time_zone('EST5EDT') do
23
+ time = Time.new(2012, 11, 4, 0, 59, 59) + 1
24
+
25
+ assert_equal time.hour, (time + 3600).hour
26
+
27
+ next_hour = Birling::Support.next_hour(time)
28
+
29
+ assert_equal 2012, next_hour.year
30
+ assert_equal 11, next_hour.month
31
+ assert_equal 4, next_hour.day
32
+ assert_equal 1, next_hour.hour
33
+ assert_equal 0, next_hour.min
34
+ assert_equal 0, next_hour.sec
35
+
36
+ assert_equal 3600, next_hour - time
37
+ end
38
+ end
39
+
40
+ def test_next_day_at_year_end
41
+ in_time_zone('EST5EDT') do
42
+ time = Time.new(2012, 12, 31)
43
+
44
+ next_day = Birling::Support.next_day(time)
45
+
46
+ assert_equal 2013, next_day.year
47
+ assert_equal 1, next_day.month
48
+ assert_equal 1, next_day.day
49
+ assert_equal 0, next_day.hour
50
+ assert_equal 0, next_day.min
51
+ assert_equal 0, next_day.sec
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: birling
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Tadman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: jeweler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Mostly drop-in replacement for Logger with a more robust log rotation
47
+ facility
48
+ email: github@tadman.ca
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files:
52
+ - LICENSE.txt
53
+ - README.md
54
+ files:
55
+ - .document
56
+ - Gemfile
57
+ - LICENSE.txt
58
+ - README.md
59
+ - Rakefile
60
+ - VERSION
61
+ - lib/birling.rb
62
+ - lib/birling/formatter.rb
63
+ - lib/birling/log.rb
64
+ - lib/birling/logger.rb
65
+ - lib/birling/support.rb
66
+ - test/helper.rb
67
+ - test/test_birling.rb
68
+ - test/test_birling_logger.rb
69
+ - test/test_birling_support.rb
70
+ homepage: http://github.com/twg/birling
71
+ licenses:
72
+ - MIT
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.24
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Logger with simple log rotation system
95
+ test_files: []