tlogger 0.6.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,105 @@
1
+
2
+
3
+ module Tlogger
4
+
5
+ # LoggerGroup is yet at wrapper around the Tlogger
6
+ # This class can be used as Tlogger replacement as it is
7
+ # delegated to the Tlogger upon method_missing method triggered.
8
+ #
9
+ # However this allow configuration of multiple loggers into
10
+ # single class.
11
+ #
12
+ # When operation such as 'debug' is called on this class
13
+ # all the registered logger shall be called the 'debug' method each therefore
14
+ # it will be logged to all registered loggers
15
+ class LoggerGroup
16
+
17
+ def initialize
18
+ @loggers = { }
19
+ end
20
+
21
+ ##
22
+ # Create and add the logger into the group and registerd it with the given +key+
23
+ #
24
+ # *params shall be passed to underlying Tlogger new method
25
+ #
26
+ # Returns created Tlogger object
27
+ def create_logger(key, *params)
28
+ @loggers[key] = Tlogger.new(*params)
29
+ @loggers[key]
30
+ end # #create_logger
31
+
32
+ # Delete this logger from the group.
33
+ #
34
+ # delete_logger different from detach_logger as delete_logger shall close and set the logger to nil.
35
+ #
36
+ # detach_logger however just remove the logger from the group and it is up to the applicaation to close it.
37
+ def delete_logger(key)
38
+ logger = @loggers[key]
39
+ if not logger.nil?
40
+ logger.close
41
+ logger = nil
42
+ end
43
+ @loggers.delete(key)
44
+ end # #delete logger
45
+
46
+ ##
47
+ # Detach the logger from the group, but not close the logger
48
+ #
49
+ # Detach the logger return the object to the caller and remove it from the internal group
50
+ #
51
+ # The effect is after detach operation, any logging done to this group would not include that particular logger and
52
+ # application is free to use the logger to log messages
53
+ #
54
+ def detach_logger(key)
55
+ @loggers.delete(key)
56
+ end # # detach_logger
57
+
58
+ ##
59
+ # Add an existing logger instance to this group
60
+ #
61
+ # Noted that this logger object not necessary to be Tlogger object. It can be any object as long as it has the method that
62
+ # response to the usage.
63
+ #
64
+ # This is due to this class just a thin wrapper around the 'logger' object that any method call unknown to local shall be
65
+ # redirected to the 'logger' class.
66
+ #
67
+ # In another way, it is perfectly ok to call say_hello() on LoggerGroup if the 'logger' given response to method say_hello() or else
68
+ # NoMethodException shall be thrown. It is that simple.
69
+ def add_logger(key, logger)
70
+ @loggers[key] = logger
71
+ end
72
+
73
+ # Returns the logger that registered to the +key+
74
+ def get_log(key)
75
+ @loggers[key]
76
+ end
77
+
78
+ # Close the logger group, effectively close all registered loggers
79
+ def close
80
+ @loggers.each do |k,v|
81
+ v.close
82
+ v = nil
83
+ end
84
+ @loggers.clear
85
+ end # # close
86
+
87
+ # Delegate unknown method to the underlying logger
88
+ def method_missing(mtd,*args,&block)
89
+
90
+ #hit = false
91
+ @loggers.each do |k,v|
92
+ begin
93
+ v.send(mtd,*args,&block)
94
+ #hit = true
95
+ rescue Exception => ex
96
+ STDERR.puts ex.message
97
+ end
98
+ end
99
+
100
+ #super if not hit
101
+
102
+ end # # method_missing
103
+
104
+ end
105
+ end
@@ -0,0 +1,380 @@
1
+
2
+ require 'logger'
3
+ require 'openssl'
4
+
5
+ module Tlogger
6
+
7
+ ##
8
+ # Tlogger class is meant to be a thin wrapper around the Ruby Logger class (by default) to provide contextual logging capabilities to the logging system.
9
+ #
10
+ # Contextual logging allow developers:
11
+ # * Structure the log message not only by the severity (debug, info, error, warning) but also another key that is useful for the application
12
+ # during troublehshooting purposes. This can provide more info to the log message being logged. For example can automatically tag the log
13
+ # message with number of 1789 loops in the program
14
+ # * Subsequent from this context info, developer now can selectively turn on / off certain logging message to get the clarity needed. Relying
15
+ # on the above example, if we are only interested in loop 1232, we can disabled all other log message EXCEPT the tag show loop_1232.
16
+ # This will drastically reduce the effort required in reading all the 1231 lines of log.
17
+ #
18
+ # I found it rather helpful especially in managing the print out of log AFTER it has been logging from everywhere in my programs.
19
+ # I also found that just filtering by log level (debug, info, error and warn) is not sufficient sometime to lessen the effort of log reading
20
+ # for a specific issue especially the only way to debug is turned on the debug level and ALL debug messages now sprang to life!
21
+ # It is really not an easy task to look for specific info especially when it was an old project.
22
+ #
23
+ # Now you can just disable all and only turn on the specific tag around the issue area to investigate the issue. You will now have better focus
24
+ # in the issue on hand instead of hunting the line that you need from long list of debug output.
25
+ class Tlogger
26
+
27
+ # +tag+ is the tag that is being set for this logger session.
28
+ #
29
+ # One session shall only have one specific tag value, which is the default tag for this logger session.
30
+ #
31
+ # If multiple tags are required, use the method tdebug, terror, twarn, tinfo or #with_tag block to create a new tag
32
+ #
33
+ # Note that the tag can be in symbol or string, however shall convert to symbol when processing
34
+ attr_accessor :tag
35
+ # +include_caller+ (true/false) tell the logger to print the caller together with the tag
36
+ attr_accessor :include_caller
37
+ # +logger+ it is the actual logger instance of this Tlogger
38
+ attr_reader :logger
39
+
40
+ def initialize(*args , &block)
41
+ # default to console
42
+ if args.length == 0
43
+ args << STDOUT
44
+ end
45
+
46
+ @opts = {}
47
+ if args[-1].is_a?(Hash)
48
+ @opts = opts
49
+ @opts = { } if @opts.nil?
50
+ args = args[0..-2]
51
+ end
52
+
53
+ @logger = @opts[:logger_instance] || Logger.new(*args,&block)
54
+ @disabled = []
55
+ @dHistory = {}
56
+ @include_caller = false
57
+ @tag = nil
58
+
59
+ @genable = true
60
+ @exception = []
61
+ end # initialize
62
+
63
+ #
64
+ # :method: with_tag
65
+ #
66
+ # Tag all log inside the block with the given tag value
67
+ #
68
+ # Useful to tag multiple lines of log under single tag
69
+ #
70
+ def with_tag(tag,&block)
71
+ if block and not tag.nil?
72
+ log = self.clone
73
+ log.tag = tag
74
+ block.call(log)
75
+ end
76
+ end # with_tag
77
+
78
+ #
79
+ # :method: off_tag
80
+ #
81
+ # Turn off a tag. After turning off, the log message that tie to this tag shall not be printed out
82
+ #
83
+ # Do note that all tags by default are turned on.
84
+ #
85
+ def off_tag(*tags)
86
+ tags.each do |tag|
87
+ if not (tag.nil? or tag.empty? or @disabled.include?(tag))
88
+ @disabled << tag.to_sym
89
+ end
90
+ end
91
+ end # off_tag
92
+ alias_method :tag_off, :off_tag
93
+
94
+ #
95
+ # :method: on_tag
96
+ #
97
+ # Turn on a tag.
98
+ #
99
+ # Note that by default all tags are turned on. This only affect whatever tags that has been turned off via
100
+ # the method #off_tag. It doesn't work as adding a tag. Adding a tag message should use tdebug, terror, tinfo,
101
+ # twarn or #with_tag
102
+ def on_tag(*tags)
103
+ tags.each do |tag|
104
+ @disabled.delete(tag.to_sym) if not (tag.nil? or tag.empty?)
105
+ end
106
+ end # on_tag
107
+ alias_method :tag_on, :on_tag
108
+
109
+ #
110
+ # :method: off_all_tags
111
+ #
112
+ # All log messages with tag out of your face!
113
+ #
114
+ def off_all_tags
115
+ @genable = false
116
+ clear_exceptions
117
+ end
118
+ alias_method :all_tags_off, :off_all_tags
119
+ alias_method :tags_all_off, :off_all_tags
120
+
121
+ #
122
+ # :method: on_all_tags
123
+ #
124
+ # All log messages now come down on you! RUN!
125
+ #
126
+ # No wait, you need to read that before you run away...
127
+ #
128
+ def on_all_tags
129
+ @genable = true
130
+ clear_exceptions
131
+ end
132
+ alias_method :all_tags_on, :on_all_tags
133
+ alias_method :tags_all_on, :on_all_tags
134
+
135
+ #
136
+ # :method: off_all_tags_except
137
+ #
138
+ # Turn off all tags EXCEPT the tags given.
139
+ #
140
+ # Note the parameters can be a list (multiple tags with ',' separator)
141
+ #
142
+ def off_all_tags_except(*tags)
143
+ off_all_tags
144
+ clear_exceptions
145
+ @exception.concat tags.map(&:to_sym)
146
+ end
147
+ alias_method :off_all_except, :off_all_tags_except
148
+ alias_method :all_off_except, :off_all_tags_except
149
+
150
+ #
151
+ # :method: on_all_tags_except
152
+ #
153
+ # Turn on all tags EXCEPT the tags given
154
+ #
155
+ # Note the parameters can be a list (multiple tags with ',' separator)
156
+ #
157
+ def on_all_tags_except(*tags)
158
+ on_all_tags
159
+ clear_exceptions
160
+ @exception.concat tags.map(&:to_sym)
161
+ end
162
+ alias_method :on_all_except, :on_all_tags_except
163
+ alias_method :all_on_except, :on_all_tags_except
164
+
165
+ #
166
+ # :method: clear_exceptions
167
+ #
168
+ # Clear the exception list. All exampted tags given either by #off_all_tags_except or #on_all_tags_except
169
+ # shall be reset
170
+ #
171
+ def clear_exceptions
172
+ @exception.clear
173
+ end
174
+
175
+ #
176
+ # :method: remove_from_exception
177
+ #
178
+ # Remote a set of tags from the exception list
179
+ #
180
+ def remove_from_exception(*tags)
181
+ @exception.delete_if { |e| tags.include?(e) }
182
+ end
183
+
184
+ #
185
+ # :method: method_missing
186
+ #
187
+ # This is where the delegation to the Logger object happen or no_method_exception shall be thrown
188
+ #
189
+ def method_missing(mtd, *args, &block)
190
+ if [:debug, :error, :info, :warn].include?(mtd)
191
+
192
+ if args.length > 0 and args[0].is_a?(Symbol)
193
+ tag = args[0]
194
+ args = args[1..-1]
195
+ else
196
+ tag = @tag
197
+ end
198
+
199
+ if is_genabled?(tag) and not tag_disabled?(tag)
200
+
201
+ if block
202
+ if not (tag.nil? or tag.empty?) and args.length == 0
203
+ args = [ format_message(tag) ]
204
+ end
205
+
206
+ out = block
207
+ else
208
+ if not (tag.nil? or tag.empty?)
209
+ str = args[0]
210
+ args = [ format_message(tag) ]
211
+ out = Proc.new { str }
212
+ else
213
+ out = block
214
+ end
215
+ end
216
+
217
+ @logger.send(mtd, *args, &out)
218
+
219
+ end # if not disabled
220
+
221
+
222
+ elsif [:tdebug, :terror, :tinfo, :twarn].include?(mtd)
223
+
224
+ key = args[0]
225
+
226
+ if is_genabled?(key) and not tag_disabled?(key.to_sym)
227
+ if block
228
+ out = Proc.new { block.call }
229
+ args = [ format_message(key) ]
230
+ else
231
+ str = args[1]
232
+ out = Proc.new { str }
233
+ args = [ format_message(key) ]
234
+ end
235
+
236
+ mtd = mtd.to_s[1..-1].to_sym
237
+ @logger.send(mtd, *args, &out)
238
+ end
239
+
240
+ elsif [:odebug, :oerror, :oinfo, :owarn].include?(mtd)
241
+
242
+ key = args[0]
243
+
244
+ if is_genabled?(key) and not tag_disabled?(key)
245
+
246
+ if block
247
+ out = Proc.new { block.call }
248
+ args = [ format_message(key) ]
249
+ else
250
+ str = args[1]
251
+ out = Proc.new { str }
252
+ args = [ format_message(key) ]
253
+ end
254
+
255
+ msg = out.call
256
+ if not (msg.nil? or msg.empty?)
257
+ if not already_shown_or_add(key,msg)
258
+ mtd = mtd.to_s[1..-1].to_sym
259
+ @logger.send(mtd, *args, &out)
260
+ end
261
+ end
262
+
263
+ end
264
+
265
+ elsif [:ifdebug, :iferror, :ifinfo, :ifwarn].include?(mtd)
266
+
267
+ cond = args[0]
268
+ key = args[1]
269
+
270
+ if cond.is_a?(Proc)
271
+ cond = cond.call
272
+ end
273
+
274
+ if is_genabled?(key) and not tag_disabled?(key) and cond
275
+
276
+ if block
277
+ out = Proc.new { block.call }
278
+ args = [ format_message(key) ]
279
+ else
280
+ str = args[2]
281
+ out = Proc.new { str }
282
+ args = [ format_message(key) ]
283
+ end
284
+
285
+ msg = out.call
286
+ if not (msg.nil? or msg.empty?)
287
+ mtd = mtd.to_s[2..-1].to_sym
288
+ @logger.send(mtd, *args, &out)
289
+ end
290
+
291
+ end
292
+
293
+ elsif @logger.respond_to?(mtd)
294
+ @logger.send(mtd, *args, &block)
295
+ else
296
+ super
297
+ end
298
+ end # method_missing
299
+
300
+ #
301
+ # :method: tag_disabled?
302
+ #
303
+ # Check if the tag is disabled
304
+ #
305
+ def tag_disabled?(tag)
306
+ if tag.nil? or tag.empty?
307
+ false
308
+ else
309
+ @disabled.include?(tag.to_sym)
310
+ end
311
+ end
312
+
313
+ #
314
+ # :method: show_source
315
+ # Helper setting the flag include_caller
316
+ #
317
+ def show_source
318
+ @include_caller = true
319
+ end
320
+
321
+ private
322
+ def format_message(key)
323
+ # returning args array
324
+ if @include_caller
325
+ "[#{key}] #{find_caller} "
326
+ else
327
+ "[#{key}] "
328
+ end
329
+ end
330
+
331
+ def is_genabled?(key)
332
+ if key.nil?
333
+ true
334
+ else
335
+ (@genable and not @exception.include?(key.to_sym)) or (not @genable and @exception.include?(key.to_sym))
336
+ end
337
+ end
338
+
339
+ def already_shown_or_add(key,msg)
340
+ smsg = Digest::SHA256.hexdigest(msg)
341
+ if @dHistory[key.to_sym].nil?
342
+ add_to_history(key,smsg)
343
+ false
344
+ else
345
+ res = @dHistory[key.to_sym].include?(smsg)
346
+ add_to_history(key,smsg) if not res
347
+ res
348
+ end
349
+ end # already_shown_or_add
350
+
351
+ def add_to_history(key,dgt)
352
+ @dHistory[key.to_sym] = [] if @dHistory[key.to_sym].nil?
353
+ @dHistory[key.to_sym] << dgt if not @dHistory[key.to_sym].include?(dgt)
354
+ end # add_to_history
355
+
356
+ def find_caller
357
+ caller.each do |c|
358
+ next if c =~ /tlogger.rb/
359
+ @cal = c
360
+ break
361
+ end
362
+
363
+ if @cal.nil? or @cal.empty?
364
+ @cal = caller[0]
365
+ end
366
+
367
+ # reduce it down to last two folder?
368
+ sp = @cal.split(File::SEPARATOR)
369
+ if sp.length > 1
370
+ msg = "/#{sp[-2]}/#{sp[-1]}"
371
+ else
372
+ msg = sp[-1]
373
+ end
374
+
375
+ msg
376
+
377
+ end # find_caller
378
+
379
+ end
380
+ end