tlogger 0.6.0 → 0.22.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,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