chrono_logger 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e4231d70e04d8cc920687cb1f31c11f6bba61cb8
4
- data.tar.gz: 7e3697d47afa57f7b22d99e169ce64cf85b1e588
3
+ metadata.gz: e4685d19b4094e975b08164b4041c82d20a0d843
4
+ data.tar.gz: 87f197078fead7232cc75b576a5eee36def4b0dd
5
5
  SHA512:
6
- metadata.gz: d9398a7c7046b9e171f499ee3a2dd8638ae964d660133b884c4bdf905d2c2f024fe4159130fc60f2b375bdd1d9727a631ddd1c8f903adac40582cf384f63be5e
7
- data.tar.gz: 6292f9aa38ea3f72b72d50c6ffe3cdea0fdaf443a36b6737da812e3e80fff042360d2e91753bbc66ef18838ca12ef29ac0524029440d478a4b7ff5e1941a6f7f
6
+ metadata.gz: 6c453b8b694c821b515c7b40a4b8641e2fad33295676b11894aa6e6efedc995dffc1dd91ff83b5ee0a07e554a2b7fa943e1f173e0f0f540bcb334ec45535d57c
7
+ data.tar.gz: 53db7768b5ecfbafded33375ccf583faed190bf2997c78cb25ebb2e41e0db00189d1737c039b254ea762b678b9b7a7501d94b74122962065b8fcbe733af676f2
data/.gitignore CHANGED
@@ -13,3 +13,4 @@
13
13
  *.a
14
14
  mkmf.log
15
15
  /vendor/bundle
16
+ .ruby-version
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ChronoLogger
2
2
 
3
- TODO: Write a gem description
3
+ A lock-free logger with timebased file rotation.
4
4
 
5
5
  ## Installation
6
6
 
@@ -20,11 +20,22 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- TODO: Write usage instructions here
23
+ Same interfaces ruby's stdlib `Logger` except for `new` method.
24
+
25
+ ```
26
+ require 'chrono_logger'
27
+
28
+ logger = ChronoLogger.new('development.%Y%m%d')
29
+
30
+ logger.error("Enjoy")
31
+ logger.warn("logging!")
32
+ logger.info("Enjoy")
33
+ logger.debug("programming!")
34
+ ```
24
35
 
25
36
  ## Contributing
26
37
 
27
- 1. Fork it ( https://github.com/[my-github-username]/chrono_logger/fork )
38
+ 1. Fork it ( https://github.com/ma2gedev/chrono_logger/fork )
28
39
  2. Create your feature branch (`git checkout -b my-new-feature`)
29
40
  3. Commit your changes (`git commit -am 'Add some feature'`)
30
41
  4. Push to the branch (`git push origin my-new-feature`)
data/Rakefile CHANGED
@@ -3,6 +3,7 @@ require "rake/testtask"
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test"
6
+ t.test_files = Dir["test/**/test_*.rb"]
6
7
  end
7
8
 
8
9
  task :default => :test
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["takayuki.1229@gmail.com"]
11
11
  spec.summary = %q{A lock-free logger with timebased file rotation.}
12
12
  spec.description = %q{A lock-free logger with timebased file rotation.}
13
- spec.homepage = ""
13
+ spec.homepage = "https://github.com/ma2gedev/chrono_logger"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency "minitest"
23
+ spec.add_development_dependency "test-unit"
24
24
  spec.add_development_dependency "pry"
25
25
  spec.add_development_dependency "delorean"
26
26
  spec.add_development_dependency "parallel"
@@ -1,5 +1,5 @@
1
1
  require 'logger'
2
2
 
3
3
  class ChronoLogger < Logger
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
data/lib/chrono_logger.rb CHANGED
@@ -30,7 +30,7 @@ class ChronoLogger < Logger
30
30
  end
31
31
 
32
32
  def write(message)
33
- if @dev.respond_to?(:stat)
33
+ if @pattern && @dev.respond_to?(:stat)
34
34
  begin
35
35
  check_shift_log
36
36
  rescue
@@ -61,10 +61,7 @@ class ChronoLogger < Logger
61
61
  def create_logfile(filename)
62
62
  begin
63
63
  logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL))
64
- logdev.flock(File::LOCK_EX)
65
64
  logdev.sync = true
66
- add_log_header(logdev)
67
- logdev.flock(File::LOCK_UN)
68
65
  rescue Errno::EEXIST
69
66
  # file is created by another process
70
67
  logdev = open_logfile(filename)
@@ -73,12 +70,6 @@ class ChronoLogger < Logger
73
70
  logdev
74
71
  end
75
72
 
76
- def add_log_header(file)
77
- file.write(
78
- "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
79
- ) if file.size == 0
80
- end
81
-
82
73
  def check_shift_log
83
74
  unless Time.now.strftime(@pattern) == @filename
84
75
  new_filename = Time.now.strftime(@pattern)
@@ -0,0 +1,153 @@
1
+ # coding: US-ASCII
2
+ require 'helper'
3
+ require 'tempfile'
4
+ require 'tmpdir'
5
+ require 'parallel'
6
+ require 'delorean'
7
+ require 'pry'
8
+
9
+ class TestLogDevice < Test::Unit::TestCase
10
+ class LogExcnRaiser
11
+ def write(*arg)
12
+ raise 'disk is full'
13
+ end
14
+
15
+ def close
16
+ end
17
+ end
18
+
19
+ def setup
20
+ @tempfile = Tempfile.new("logger")
21
+ @tempfile.close
22
+ @format = [@tempfile.path, '%Y%m%d'].join
23
+ @filename = Time.now.strftime(@format)
24
+ File.unlink(@tempfile.path)
25
+ end
26
+
27
+ def teardown
28
+ @tempfile.close(true)
29
+ end
30
+
31
+ def d(format, opt = {})
32
+ ChronoLogger::TimeBasedLogDevice.new(format, opt)
33
+ end
34
+
35
+ def test_initialize
36
+ logdev = d(STDERR)
37
+ assert_equal(STDERR, logdev.dev)
38
+ assert_nil(logdev.filename)
39
+ assert_raises(TypeError) do
40
+ d(nil)
41
+ end
42
+ #
43
+ logdev = d(@format)
44
+ begin
45
+ assert(File.exist?(@filename))
46
+ assert(logdev.dev.sync)
47
+ assert_equal(@filename, logdev.filename)
48
+ logdev.write('hello')
49
+ ensure
50
+ logdev.close
51
+ end
52
+ # create logfile whitch is already exist.
53
+ logdev = d(@format)
54
+ begin
55
+ logdev.write('world')
56
+ logfile = File.read(@filename)
57
+ assert_equal(1, logfile.split(/\n/).size)
58
+ assert_match(/^helloworld$/, logfile)
59
+ ensure
60
+ logdev.close
61
+ end
62
+ end
63
+
64
+ def test_write
65
+ r, w = IO.pipe
66
+ logdev = d(w)
67
+ logdev.write("msg2\n\n")
68
+ IO.select([r], nil, nil, 0.1)
69
+ w.close
70
+ msg = r.read
71
+ r.close
72
+ assert_equal("msg2\n\n", msg)
73
+ #
74
+ logdev = d(LogExcnRaiser.new)
75
+ class << (stderr = '')
76
+ alias write <<
77
+ end
78
+ $stderr, stderr = stderr, $stderr
79
+ begin
80
+ assert_nothing_raised do
81
+ logdev.write('hello')
82
+ end
83
+ ensure
84
+ logdev.close
85
+ $stderr, stderr = stderr, $stderr
86
+ end
87
+ assert_equal "log writing failed. disk is full\n", stderr
88
+ end
89
+
90
+ def test_close
91
+ r, w = IO.pipe
92
+ logdev = d(w)
93
+ logdev.write("msg2\n\n")
94
+ IO.select([r], nil, nil, 0.1)
95
+ assert(!w.closed?)
96
+ logdev.close
97
+ assert(w.closed?)
98
+ r.close
99
+ end
100
+
101
+ def test_shifting_age_in_multiprocess
102
+ old_log = [@tempfile.path, '20150122'].join
103
+ new_log = [@tempfile.path, '20150123'].join
104
+ $stderr, stderr = StringIO.new, $stderr
105
+ begin
106
+ Delorean.time_travel_to '2015-01-22 23:59:59'
107
+ logger = ChronoLogger.new(@format)
108
+ Parallel.map(['a', 'b'], :in_processes => 2) do |letter|
109
+ 5000.times do
110
+ logger.info letter * 5000
111
+ end
112
+ end
113
+ assert_no_match(/log shifting failed/, $stderr.string)
114
+ assert_no_match(/log writing failed/, $stderr.string)
115
+ assert { File.exist?(old_log) }
116
+ assert { File.exist?(new_log) }
117
+ ensure
118
+ $stderr, stderr = stderr, $stderr
119
+ Delorean.back_to_the_present
120
+ File.unlink(old_log)
121
+ File.unlink(new_log)
122
+ end
123
+ end
124
+
125
+ def test_shifting_midnight
126
+ Dir.mktmpdir do |tmpdir|
127
+ log = "log20140102"
128
+ old_log = File.join(tmpdir, log)
129
+ begin
130
+ File.open(old_log, "w") {}
131
+ File.utime(*[Time.mktime(2014, 1, 1, 23, 59, 59)]*2, old_log)
132
+
133
+ Delorean.time_travel_to '2014-01-02 23:59:59'
134
+ dev = ChronoLogger::TimeBasedLogDevice.new(File.join(tmpdir, "log%Y%m%d"))
135
+ dev.write("#{Time.now} hello-1\n")
136
+
137
+ Delorean.time_travel_to '2014-01-03 00:00:01'
138
+ dev.write("#{Time.now} hello-2\n")
139
+ ensure
140
+ Delorean.back_to_the_present
141
+ dev.close
142
+ end
143
+
144
+ cont = File.read(old_log)
145
+ assert_match(/hello-1/, cont)
146
+ assert_not_match(/hello-2/, cont)
147
+ new_log = File.join(tmpdir, "log20140103")
148
+ bug = '[GH-539]'
149
+ assert { File.exist?(new_log) }
150
+ assert_match(/hello-2/, File.read(new_log), bug)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,243 @@
1
+ # coding: US-ASCII
2
+ require 'helper'
3
+ require 'tempfile'
4
+
5
+ class TestLogger < Test::Unit::TestCase
6
+ include ChronoLogger::Severity
7
+
8
+ def setup
9
+ @logger = ChronoLogger.new(nil)
10
+ end
11
+
12
+ class Log
13
+ attr_reader :label, :datetime, :pid, :severity, :progname, :msg
14
+ def initialize(line)
15
+ /\A(\w+), \[([^#]*)#(\d+)\]\s+(\w+) -- (\w*): ([\x0-\xff]*)/ =~ line
16
+ @label, @datetime, @pid, @severity, @progname, @msg = $1, $2, $3, $4, $5, $6
17
+ end
18
+ end
19
+
20
+ def log_add(logger, severity, msg, progname = nil, &block)
21
+ log(logger, :add, severity, msg, progname, &block)
22
+ end
23
+
24
+ def log(logger, msg_id, *arg, &block)
25
+ Log.new(log_raw(logger, msg_id, *arg, &block))
26
+ end
27
+
28
+ def log_raw(logger, msg_id, *arg, &block)
29
+ Tempfile.create(File.basename(__FILE__) + '.log') {|logdev|
30
+ logger.instance_eval { @logdev = ChronoLogger::TimeBasedLogDevice.new(logdev) }
31
+ logger.__send__(msg_id, *arg, &block)
32
+ logdev.rewind
33
+ logdev.read
34
+ }
35
+ end
36
+
37
+ def test_level
38
+ @logger.level = UNKNOWN
39
+ assert_equal(UNKNOWN, @logger.level)
40
+ @logger.level = INFO
41
+ assert_equal(INFO, @logger.level)
42
+ @logger.sev_threshold = ERROR
43
+ assert_equal(ERROR, @logger.sev_threshold)
44
+ @logger.sev_threshold = WARN
45
+ assert_equal(WARN, @logger.sev_threshold)
46
+ assert_equal(WARN, @logger.level)
47
+
48
+ @logger.level = DEBUG
49
+ assert(@logger.debug?)
50
+ assert(@logger.info?)
51
+ @logger.level = INFO
52
+ assert(!@logger.debug?)
53
+ assert(@logger.info?)
54
+ assert(@logger.warn?)
55
+ @logger.level = WARN
56
+ assert(!@logger.info?)
57
+ assert(@logger.warn?)
58
+ assert(@logger.error?)
59
+ @logger.level = ERROR
60
+ assert(!@logger.warn?)
61
+ assert(@logger.error?)
62
+ assert(@logger.fatal?)
63
+ @logger.level = FATAL
64
+ assert(!@logger.error?)
65
+ assert(@logger.fatal?)
66
+ @logger.level = UNKNOWN
67
+ assert(!@logger.error?)
68
+ assert(!@logger.fatal?)
69
+ end
70
+
71
+ def test_progname
72
+ assert_nil(@logger.progname)
73
+ @logger.progname = "name"
74
+ assert_equal("name", @logger.progname)
75
+ end
76
+
77
+ def test_datetime_format
78
+ dummy = STDERR
79
+ logger = ChronoLogger.new(dummy)
80
+ log = log_add(logger, INFO, "foo")
81
+ assert_match(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\s*\d+ $/, log.datetime)
82
+ logger.datetime_format = "%d%b%Y@%H:%M:%S"
83
+ log = log_add(logger, INFO, "foo")
84
+ assert_match(/^\d\d\w\w\w\d\d\d\d@\d\d:\d\d:\d\d$/, log.datetime)
85
+ logger.datetime_format = ""
86
+ log = log_add(logger, INFO, "foo")
87
+ assert_match(/^$/, log.datetime)
88
+ end
89
+
90
+ def test_formatter
91
+ dummy = STDERR
92
+ logger = ChronoLogger.new(dummy)
93
+ # default
94
+ log = log(logger, :info, "foo")
95
+ assert_equal("foo\n", log.msg)
96
+ # config
97
+ logger.formatter = proc { |severity, timestamp, progname, msg|
98
+ "#{severity}:#{msg}\n\n"
99
+ }
100
+ line = log_raw(logger, :info, "foo")
101
+ assert_equal("INFO:foo\n\n", line)
102
+ # recover
103
+ logger.formatter = nil
104
+ log = log(logger, :info, "foo")
105
+ assert_equal("foo\n", log.msg)
106
+ # again
107
+ o = Object.new
108
+ def o.call(severity, timestamp, progname, msg)
109
+ "<<#{severity}-#{msg}>>\n"
110
+ end
111
+ logger.formatter = o
112
+ line = log_raw(logger, :info, "foo")
113
+ assert_equal("<""<INFO-foo>>\n", line)
114
+ end
115
+
116
+ def test_initialize
117
+ logger = ChronoLogger.new(STDERR)
118
+ assert_nil(logger.progname)
119
+ assert_equal(DEBUG, logger.level)
120
+ assert_nil(logger.datetime_format)
121
+ end
122
+
123
+ def test_add
124
+ logger = ChronoLogger.new(nil)
125
+ logger.progname = "my_progname"
126
+ assert(logger.add(INFO))
127
+ log = log_add(logger, nil, "msg")
128
+ assert_equal("ANY", log.severity)
129
+ assert_equal("my_progname", log.progname)
130
+ logger.level = WARN
131
+ assert(logger.log(INFO))
132
+ assert_nil(log_add(logger, INFO, "msg").msg)
133
+ log = log_add(logger, WARN, nil) { "msg" }
134
+ assert_equal("msg\n", log.msg)
135
+ log = log_add(logger, WARN, "") { "msg" }
136
+ assert_equal("\n", log.msg)
137
+ assert_equal("my_progname", log.progname)
138
+ log = log_add(logger, WARN, nil, "progname?")
139
+ assert_equal("progname?\n", log.msg)
140
+ assert_equal("my_progname", log.progname)
141
+ end
142
+
143
+ def test_level_log
144
+ logger = ChronoLogger.new(nil)
145
+ logger.progname = "my_progname"
146
+ log = log(logger, :debug, "custom_progname") { "msg" }
147
+ assert_equal("msg\n", log.msg)
148
+ assert_equal("custom_progname", log.progname)
149
+ assert_equal("DEBUG", log.severity)
150
+ assert_equal("D", log.label)
151
+ #
152
+ log = log(logger, :debug) { "msg_block" }
153
+ assert_equal("msg_block\n", log.msg)
154
+ assert_equal("my_progname", log.progname)
155
+ log = log(logger, :debug, "msg_inline")
156
+ assert_equal("msg_inline\n", log.msg)
157
+ assert_equal("my_progname", log.progname)
158
+ #
159
+ log = log(logger, :info, "custom_progname") { "msg" }
160
+ assert_equal("msg\n", log.msg)
161
+ assert_equal("custom_progname", log.progname)
162
+ assert_equal("INFO", log.severity)
163
+ assert_equal("I", log.label)
164
+ #
165
+ log = log(logger, :warn, "custom_progname") { "msg" }
166
+ assert_equal("msg\n", log.msg)
167
+ assert_equal("custom_progname", log.progname)
168
+ assert_equal("WARN", log.severity)
169
+ assert_equal("W", log.label)
170
+ #
171
+ log = log(logger, :error, "custom_progname") { "msg" }
172
+ assert_equal("msg\n", log.msg)
173
+ assert_equal("custom_progname", log.progname)
174
+ assert_equal("ERROR", log.severity)
175
+ assert_equal("E", log.label)
176
+ #
177
+ log = log(logger, :fatal, "custom_progname") { "msg" }
178
+ assert_equal("msg\n", log.msg)
179
+ assert_equal("custom_progname", log.progname)
180
+ assert_equal("FATAL", log.severity)
181
+ assert_equal("F", log.label)
182
+ #
183
+ log = log(logger, :unknown, "custom_progname") { "msg" }
184
+ assert_equal("msg\n", log.msg)
185
+ assert_equal("custom_progname", log.progname)
186
+ assert_equal("ANY", log.severity)
187
+ assert_equal("A", log.label)
188
+ end
189
+
190
+ def test_close
191
+ r, w = IO.pipe
192
+ assert(!w.closed?)
193
+ logger = ChronoLogger.new(w)
194
+ logger.close
195
+ assert(w.closed?)
196
+ r.close
197
+ end
198
+
199
+ class MyError < StandardError
200
+ end
201
+
202
+ class MyMsg
203
+ def inspect
204
+ "my_msg"
205
+ end
206
+ end
207
+
208
+ def test_format
209
+ logger = ChronoLogger.new(nil)
210
+ log = log_add(logger, INFO, "msg\n")
211
+ assert_equal("msg\n\n", log.msg)
212
+ begin
213
+ raise MyError.new("excn")
214
+ rescue MyError => e
215
+ log = log_add(logger, INFO, e)
216
+ assert_match(/^excn \(TestLogger::MyError\)/, log.msg)
217
+ # expects backtrace is dumped across multi lines. 10 might be changed.
218
+ assert(log.msg.split(/\n/).size >= 10)
219
+ end
220
+ log = log_add(logger, INFO, MyMsg.new)
221
+ assert_equal("my_msg\n", log.msg)
222
+ end
223
+
224
+ def test_lshift
225
+ r, w = IO.pipe
226
+ logger = ChronoLogger.new(w)
227
+ logger << "msg"
228
+ read_ready, = IO.select([r], nil, nil, 0.1)
229
+ w.close
230
+ msg = r.read
231
+ r.close
232
+ assert_equal("msg", msg)
233
+ #
234
+ r, w = IO.pipe
235
+ logger = ChronoLogger.new(w)
236
+ logger << "msg2\n\n"
237
+ read_ready, = IO.select([r], nil, nil, 0.1)
238
+ w.close
239
+ msg = r.read
240
+ r.close
241
+ assert_equal("msg2\n\n", msg)
242
+ end
243
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ class TestLoggerSeverity < Test::Unit::TestCase
4
+ def test_enum
5
+ logger_levels = ChronoLogger.constants
6
+ levels = ["WARN", "UNKNOWN", "INFO", "FATAL", "DEBUG", "ERROR"]
7
+ ChronoLogger::Severity.constants.each do |level|
8
+ assert(levels.include?(level.to_s))
9
+ assert(logger_levels.include?(level))
10
+ end
11
+ assert_equal(levels.size, ChronoLogger::Severity.constants.size)
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
2
  require 'chrono_logger'
3
3
 
4
- require 'minitest/autorun'
4
+ require 'test/unit'
@@ -1,11 +1,7 @@
1
- require 'minitest_helper'
1
+ require 'helper'
2
2
 
3
- class TestChronoLogger < MiniTest::Unit::TestCase
3
+ class TestChronoLogger < Test::Unit::TestCase
4
4
  def test_that_it_has_a_version_number
5
5
  refute_nil ::ChronoLogger::VERSION
6
6
  end
7
-
8
- def test_it_does_something_useful
9
- assert false
10
- end
11
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_logger
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
  - Takayuki Matsubara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-21 00:00:00.000000000 Z
11
+ date: 2015-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: test-unit
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -110,9 +110,12 @@ files:
110
110
  - chrono_logger.gemspec
111
111
  - lib/chrono_logger.rb
112
112
  - lib/chrono_logger/version.rb
113
- - test/minitest_helper.rb
113
+ - test/from_ruby_repo/test_logdevice.rb
114
+ - test/from_ruby_repo/test_logger.rb
115
+ - test/from_ruby_repo/test_severity.rb
116
+ - test/helper.rb
114
117
  - test/test_chrono_logger.rb
115
- homepage: ''
118
+ homepage: https://github.com/ma2gedev/chrono_logger
116
119
  licenses:
117
120
  - MIT
118
121
  metadata: {}
@@ -137,5 +140,8 @@ signing_key:
137
140
  specification_version: 4
138
141
  summary: A lock-free logger with timebased file rotation.
139
142
  test_files:
140
- - test/minitest_helper.rb
143
+ - test/from_ruby_repo/test_logdevice.rb
144
+ - test/from_ruby_repo/test_logger.rb
145
+ - test/from_ruby_repo/test_severity.rb
146
+ - test/helper.rb
141
147
  - test/test_chrono_logger.rb