simple_rotate 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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