simple_rotate 1.0.0 → 1.1.0

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.
@@ -0,0 +1,13 @@
1
+ #--------------------
2
+ # access definitions
3
+ #--------------------
4
+ class SimpleRotate
5
+ attr_accessor :threshold,
6
+ :date_format,
7
+ :logging_format,
8
+ :rename_format,
9
+ :allow_overwrite,
10
+ :sleep_time
11
+
12
+ attr_reader :limit
13
+ end
@@ -0,0 +1,7 @@
1
+ class SimpleRotate
2
+ LIBS_NAME = "simple_rotate"
3
+ VERSION = "1.1.0"
4
+ SUMMARY = "Simple and safety logger."
5
+ HOMEPAGE = "http://khotta.github.io/"
6
+ DESCRIPTION = "#{SUMMARY} #{HOMEPAGE}"
7
+ end
@@ -0,0 +1,306 @@
1
+ # coding: utf-8
2
+
3
+ # load standard attachments
4
+ require "singleton"
5
+ require "monitor"
6
+
7
+ # load internal modules
8
+ require_relative "./internal/log_level"
9
+ require_relative "./internal/rotate_term"
10
+ require_relative "./internal/validator"
11
+ require_relative "./internal/error"
12
+
13
+ # load internal classes
14
+ require_relative "./internal/error"
15
+ require_relative "./internal/process_sync"
16
+
17
+ # extends myself
18
+ require_relative "./const"
19
+ require_relative "./accesser"
20
+ require_relative "./private"
21
+
22
+ class SimpleRotate
23
+ # mix-in
24
+ include Singleton
25
+ include MonitorMixin
26
+ include LogLevel
27
+ include RotateTerm
28
+ include Validator
29
+
30
+ # return method missing error
31
+ def method_missing(name, *argv)
32
+ SimpleRotate::Error.missing(name)
33
+ end
34
+
35
+ # @param string|symbol $file_name
36
+ # @param string|int $limit
37
+ # @param int $rotation
38
+ # @return self
39
+ def init(file_name=File.absolute_path($0+".log"), limit="100M", rotation=0)
40
+ @file_name = file_name
41
+ @limit = limit
42
+ @rotation = rotation
43
+
44
+ # load defaults
45
+ include_defaults
46
+
47
+ # validation
48
+ valid_file_name
49
+ valid_int("rotation", @rotation)
50
+
51
+ # stdout only
52
+ return self if @only_stdout
53
+
54
+ if rotate_by_term?
55
+ # term rotation
56
+ set_days_cnt_of_term
57
+ @limit_term = @limit
58
+ @rotate_by_term = true
59
+
60
+ else
61
+ # file_size rotation
62
+ @limit_size = trim_byte(@limit)
63
+ if @limit_size <= 0
64
+ SimpleRotate::Error.argv("limit", @limit)
65
+ end
66
+ @rotate_by_term = false
67
+ end
68
+
69
+ # for process sync
70
+ @psync = ProcessSync.new(self)
71
+
72
+ # open or generate the log file
73
+ synchronize do
74
+ @psync.lock
75
+
76
+ prepare_logf
77
+
78
+ @psync.unlock
79
+ end
80
+
81
+ # if block given, colse IO
82
+ if defined? yield
83
+ yield self
84
+ e
85
+ end
86
+
87
+ return self
88
+ end
89
+
90
+ # log message out to STDOUT when use SimpleRotate#w method
91
+ def with_stdout
92
+ @with_stdout = true
93
+ return nil
94
+ end
95
+
96
+ # enable compress
97
+ def compress
98
+ @compress = true
99
+ use_zlib
100
+ return nil
101
+ end
102
+
103
+ # define the compression level
104
+ # this method load 'zlib'
105
+ # this method enable compress flag
106
+ # @param int level - 0-9
107
+ # default is 6
108
+ def compress_level(level)
109
+ @compress_level = level
110
+ valid_int("compress_level", @compress_level)
111
+ compress
112
+ return nil
113
+ end
114
+
115
+ # @param string $log message write to log file
116
+ # @return string
117
+ def w(log)
118
+ if @file_name == nil
119
+ msg = "file_name is Nil Class! Please call #init method"
120
+ SimpleRotate::Error.throw_error(msg)
121
+ end
122
+
123
+ # don't out log message if Doesn't reach threshold
124
+ return nil if (!over_threshold?)
125
+
126
+ content = get_trimmed_log(log)
127
+
128
+ # return and end func, if only_stdout enable
129
+ if @only_stdout
130
+ puts content
131
+ return log
132
+ end
133
+
134
+ # write message to file
135
+ synchronize do
136
+ @psync.lock
137
+
138
+ sync_inode if !@no_sync_inode
139
+ @logf.puts(content)
140
+ @logf.flush if @enable_wflush
141
+ @logf.fsync if @enable_wflush
142
+
143
+ @psync.unlock
144
+ end
145
+
146
+ # dump log message STDOUT, if with_stdout enable
147
+ puts content if @with_stdout
148
+
149
+ # rotate if necessary
150
+ rotate_if if !@no_wcheck
151
+
152
+ return log
153
+ end
154
+
155
+ # disable call File#flush after #w method
156
+ def enable_wflush
157
+ @enable_wflush = true
158
+ return nil
159
+ end
160
+
161
+ # enable call File#flush after #w method
162
+ def disable_wflush
163
+ @enable_wflush = false
164
+ return nil
165
+ end
166
+
167
+ # close file
168
+ def e
169
+ return nil if logf_not_usable
170
+
171
+ synchronize do
172
+ @psync.lock
173
+
174
+ @logf.close
175
+
176
+ @psync.unlock
177
+ end
178
+
179
+ return true
180
+ end
181
+
182
+ # file reopen
183
+ def reopen
184
+ return nil if logf_not_usable
185
+
186
+ if !file_closed?
187
+ SimpleRotate::Error.warning("File is already open!")
188
+ return nil
189
+ end
190
+
191
+ openadd
192
+ return @logf
193
+ end
194
+
195
+ # force rotation
196
+ def flush
197
+ return nil if logf_not_usable
198
+ return nil if @rotate_by_term
199
+ rotation(:FLUSH)
200
+ return true
201
+ end
202
+
203
+ # don't check can to rotate at #w method
204
+ def no_wcheck
205
+ @no_wcheck = true
206
+ return nil;
207
+ end
208
+
209
+ # is log file open?
210
+ # @return nil|bool
211
+ def file_closed?
212
+ return nil if logf_not_usable
213
+ return @logf.closed?
214
+ end
215
+
216
+ # skip warning message
217
+ def silence
218
+ SimpleRotate::Error.silence
219
+ return nil;
220
+ end
221
+
222
+ # set log level FATAL
223
+ # @return self
224
+ def fatal
225
+ @log_level = 5
226
+ return self
227
+ end
228
+
229
+ # set log level ERROR
230
+ # @return self
231
+ def error
232
+ @log_level = 4
233
+ return self
234
+ end
235
+
236
+ # set log level WORN
237
+ # @return self
238
+ def warn
239
+ @log_level = 3
240
+ return self
241
+ end
242
+
243
+ # set log level INFO
244
+ # @return self
245
+ def info
246
+ @log_level = 2
247
+ return self
248
+ end
249
+
250
+ # set log level DEBUG
251
+ # @return self
252
+ def debug
253
+ @log_level = 1
254
+ return self
255
+ end
256
+
257
+ # synchronize processes
258
+ def psync(sleep_time=0.1)
259
+ @is_psync = true
260
+ @enable_wflush = true
261
+ @sleep_time = sleep_time
262
+
263
+ @psync = ProcessSync.new(self)
264
+ return nil
265
+ end
266
+
267
+ # reopen file necessary
268
+ # @return bool|nil
269
+ def sync_inode
270
+ return nil if logf_not_usable
271
+
272
+ cnt = 0
273
+ begin
274
+ # check i-node number
275
+ open_inode = @logf.stat.ino
276
+ logf_inode = File.stat(@file_name).ino
277
+ raise if open_inode != logf_inode
278
+
279
+ rescue
280
+ cnt += 1
281
+ sleep(0.1)
282
+ e
283
+ openadd
284
+
285
+ if cnt <= @sync_inode_limit
286
+ retry
287
+ else
288
+ SimpleRotate::Error.warning(%{inode number didn't not match, tried #{cnt} times!})
289
+ return false
290
+ end
291
+ end
292
+
293
+ return true
294
+ end
295
+
296
+ # disable #sync_inode
297
+ def no_sync_inode
298
+ @no_sync_inode = true
299
+ return nil
300
+ end
301
+
302
+ #--------------------
303
+ # method alias
304
+ #--------------------
305
+ alias_method :<<, :w
306
+ end
@@ -0,0 +1,56 @@
1
+ class SimpleRotate
2
+ class Error < RuntimeError
3
+ msg = "Aborted the log files rotation,"
4
+ msg += " an unexpected error has occured."
5
+ ROTATION_FAILED = msg
6
+
7
+ @@silence = false
8
+
9
+ # skip warning message
10
+ def self.silence
11
+ @@silence = true
12
+ end
13
+
14
+ # argument error
15
+ def self.argv(param, argv)
16
+ msg = "'#{param}'='#{argv}' is invalid argument value!"
17
+ self.throw_error(msg)
18
+ end
19
+
20
+ # method missing
21
+ def self.missing(name)
22
+ msg = "undifined method 'SimpleRotate##{name}'"
23
+ self.throw_error(msg)
24
+ end
25
+
26
+ # file open error
27
+ def self.open(name)
28
+ msg = "Couldn't open a '#{name}'"
29
+ self.throw_error(msg)
30
+ end
31
+
32
+ # load error
33
+ def self.load(name)
34
+ msg = "Couldn't load a '#{name}'"
35
+ self.throw_error(msg)
36
+ end
37
+
38
+ # exist error
39
+ def self.exist(name, type)
40
+ msg = "Already exists this #{type} => '#{name}'"
41
+ self.throw_error(msg)
42
+ end
43
+
44
+ # warning - don't throw error
45
+ def self.warning(msg)
46
+ warn "[WARNING] #{msg} - (SimpleRotate::Error)" if !@@silence
47
+ end
48
+
49
+ # @param msg string
50
+ def self.throw_error(msg)
51
+ exeption = self.new(msg)
52
+ warn exeption.message if !@@silence
53
+ raise SimpleRotate::Error
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,14 @@
1
+ class SimpleRotate
2
+ module LogLevel
3
+ LOG_LEVEL_5 = "FATAL"
4
+ LOG_LEVEL_4 = "ERROR"
5
+ LOG_LEVEL_3 = "WARN"
6
+ LOG_LEVEL_2 = "INFO"
7
+ LOG_LEVEL_1 = "DEBUG"
8
+ LEVEL_ID_FATAL = 5
9
+ LEVEL_ID_ERROR = 4
10
+ LEVEL_ID_WARN = 3
11
+ LEVEL_ID_INFO = 2
12
+ LEVEL_ID_DEBUG = 1
13
+ end
14
+ end
@@ -0,0 +1,115 @@
1
+ require_relative "./process_sync_mixin"
2
+
3
+ class SimpleRotate
4
+ class ProcessSync
5
+ include ProcessSyncMixin
6
+
7
+ ProcessSyncMixin.instance_methods.each do |method_name|
8
+ method = instance_method(method_name)
9
+ define_method(method_name) do |*args|
10
+ # common execution
11
+ return nil if !@enable
12
+ # -------------------
13
+
14
+ method.bind(self).call(*args)
15
+ end
16
+ end
17
+
18
+ def initialize(sr)
19
+ @sr = sr
20
+ @enable = sr.instance_variable_get(:@is_psync)
21
+ @file_name = sr.instance_variable_get(:@file_name)
22
+
23
+ # #init not called
24
+ return self if @file_name == nil
25
+
26
+ @logf = sr.instance_variable_get(:@logf)
27
+ @try_limit = 3
28
+ @@tempf_name = File.dirname(@file_name) + File::SEPARATOR + ".SimpleRotate_tempfile_#{File.basename($0)}"
29
+
30
+ create_tempfile if @enable && !@@scheduled_del_lockfile
31
+ end
32
+
33
+ # Create the temp file for locking
34
+ private
35
+ def create_tempfile
36
+ if File.exist?(@@tempf_name)
37
+ open_temp_file
38
+ return nil
39
+ end
40
+
41
+ begin
42
+ @@tempf = File.open(@@tempf_name, File::RDWR|File::CREAT|File::EXCL)
43
+
44
+ rescue
45
+ SimpleRotate::Error.warning("Couldn't create temp file => #{@@tempf_name}")
46
+
47
+ ensure
48
+ set_delete_tempfile
49
+ end
50
+ end
51
+
52
+ private
53
+ def tempf_exists?
54
+ return File.exist?(@@tempf_name)
55
+ end
56
+
57
+ # Delete the lock file at the end of the script
58
+ private
59
+ def set_delete_tempfile
60
+ return true if @@scheduled_del_lockfile
61
+
62
+ if File.exists?(@@tempf_name)
63
+ # is it empty?
64
+ if File.size(@@tempf_name) == 0
65
+ delete_at_end
66
+ else
67
+ # it is not empty
68
+ msg = "File is not empty => #{@@tempf_name}#{$-0}"
69
+ msg += "Skip to delete temp file!"
70
+ SimpleRotate::Error.warning(msg)
71
+ end
72
+ end
73
+ @@scheduled_del_lockfile = true
74
+ end
75
+
76
+ private
77
+ def delete_at_end
78
+ at_exit do
79
+ begin
80
+ File.delete(@@tempf_name)
81
+ rescue
82
+ #SimpleRotate::Error.warning("Couldn't delete temp file => #{@@tempf_name}")
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+ def reopen_temp_file
89
+ close_temp_file
90
+ open_temp_file
91
+ end
92
+
93
+ private
94
+ def open_temp_file
95
+ if @@tempf.is_a?(IO) && @@tempf.closed? || !@@tempf.is_a?(IO)
96
+ begin
97
+ @@tempf = File.open(@@tempf_name, File::RDWR|File::CREAT|File::APPEND)
98
+ rescue
99
+ SimpleRotate::Error.warning("Couldn't open temp file => #{@@tempf_name}")
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+ def close_temp_file
106
+ if @@tempf.is_a?(IO) && !@@tempf.closed?
107
+ begin
108
+ @@tempf.close
109
+ rescue
110
+ SimpleRotate::Error.warning("Couldn't close temp file => #{@@tempf_name}")
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end