tlogger 0.8.0 → 0.21.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,352 @@
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(args[0]) ]
230
+ else
231
+ str = args[1]
232
+ out = Proc.new { str }
233
+ args = [ format_message(args[0]) ]
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(args[0]) ]
249
+ else
250
+ str = args[1]
251
+ out = Proc.new { str }
252
+ args = [ format_message(args[0]) ]
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 @logger.respond_to?(mtd)
266
+ @logger.send(mtd, *args, &block)
267
+ else
268
+ super
269
+ end
270
+ end # method_missing
271
+
272
+ #
273
+ # :method: tag_disabled?
274
+ #
275
+ # Check if the tag is disabled
276
+ #
277
+ def tag_disabled?(tag)
278
+ if tag.nil? or tag.empty?
279
+ false
280
+ else
281
+ @disabled.include?(tag.to_sym)
282
+ end
283
+ end
284
+
285
+ #
286
+ # :method: show_source
287
+ # Helper setting the flag include_caller
288
+ #
289
+ def show_source
290
+ @include_caller = true
291
+ end
292
+
293
+ private
294
+ def format_message(key)
295
+ # returning args array
296
+ if @include_caller
297
+ "[#{key}] #{find_caller} "
298
+ else
299
+ "[#{key}] "
300
+ end
301
+ end
302
+
303
+ def is_genabled?(key)
304
+ if key.nil?
305
+ true
306
+ else
307
+ (@genable and not @exception.include?(key.to_sym)) or (not @genable and @exception.include?(key.to_sym))
308
+ end
309
+ end
310
+
311
+ def already_shown_or_add(key,msg)
312
+ smsg = Digest::SHA256.hexdigest(msg)
313
+ if @dHistory[key.to_sym].nil?
314
+ add_to_history(key,smsg)
315
+ false
316
+ else
317
+ res = @dHistory[key.to_sym].include?(smsg)
318
+ add_to_history(key,smsg) if not res
319
+ res
320
+ end
321
+ end # already_shown_or_add
322
+
323
+ def add_to_history(key,dgt)
324
+ @dHistory[key.to_sym] = [] if @dHistory[key.to_sym].nil?
325
+ @dHistory[key.to_sym] << dgt if not @dHistory[key.to_sym].include?(dgt)
326
+ end # add_to_history
327
+
328
+ def find_caller
329
+ caller.each do |c|
330
+ next if c =~ /tlogger.rb/
331
+ @cal = c
332
+ break
333
+ end
334
+
335
+ if @cal.nil? or @cal.empty?
336
+ @cal = caller[0]
337
+ end
338
+
339
+ # reduce it down to last two folder?
340
+ sp = @cal.split(File::SEPARATOR)
341
+ if sp.length > 1
342
+ msg = "/#{sp[-2]}/#{sp[-1]}"
343
+ else
344
+ msg = sp[-1]
345
+ end
346
+
347
+ msg
348
+
349
+ end # find_caller
350
+
351
+ end
352
+ end
@@ -1,3 +1,3 @@
1
1
  module Tlogger
2
- VERSION = "0.8.0"
2
+ VERSION = "0.21.0"
3
3
  end
data/lib/tlogger.rb CHANGED
@@ -1,330 +1,11 @@
1
-
2
-
3
- require 'logger'
4
- require 'singleton'
5
-
6
1
  require "tlogger/version"
7
2
 
3
+ require_relative "tlogger/tlogger"
4
+ require_relative "tlogger/logger_group"
5
+
6
+ #
7
+ # :nodoc:
8
+ #
8
9
  module Tlogger
9
10
  class Error < StandardError; end
10
- # Your code goes here...
11
-
12
- #
13
- # Singleton object to facilitate tag management
14
- #
15
- class TloggerConf
16
- include Singleton
17
- attr_reader :active_tags, :scoped_tag, :blacklisted_tags, :show_source
18
- #GLOBAL_TAG = :global
19
- GLOBAL_TAG = ""
20
- INT_TAG = :tlogger
21
-
22
- def initialize
23
- @active_tags = []
24
- # tag added to black listed will not be printed out even the tag is added later in the code path
25
- @blacklisted_tags = []
26
- @disable_all_tags = false
27
- @auto_tag = false
28
- # todo
29
- # allow to redirect certain tag to specific output. tag: [<config>]
30
- @output_map = {}
31
- # if output_map is there, then there should be pre created log file with specific output defined in the map. [<config>] => log_instance
32
- @output_log = {}
33
- # show where is the log being printed out. Like auto_tag output
34
- @show_source = false
35
- # end todo
36
- end
37
-
38
- def show_source
39
- @show_source = true
40
- end
41
- alias_method :auto_tag_on, :show_source
42
-
43
- def hide_source
44
- @show_source = false
45
- end
46
- alias_method :auto_tag_off, :hide_source
47
-
48
- def is_show_source?
49
- @show_source
50
- end
51
- alias_method :is_auto_tag_on?, :is_show_source?
52
-
53
- #def auto_tag_on
54
- # @auto_tag = true
55
- #end
56
-
57
- #def auto_tag_off
58
- # @auto_tag = false
59
- #end
60
-
61
- #def is_auto_tag_on?
62
- # @auto_tag
63
- #end
64
-
65
- def activate_tag(tag)
66
- @active_tags << tag
67
- end
68
-
69
- def deactivate_tag(tag)
70
- @active_tags.delete(tag)
71
- end
72
-
73
- def blacklist_tag(tag)
74
- if tag.is_a?(Array)
75
- @blacklisted_tags += tag
76
- else
77
- @blacklisted_tags << tag
78
- end
79
- end
80
-
81
- def all_tags_off
82
- @disable_all_tags = true
83
- end
84
-
85
- def all_tags_on
86
- @disable_all_tags = false
87
- end
88
-
89
- def whitelist_tag(tag)
90
- if tag.is_a?(Array)
91
- tag.each do |t|
92
- @blacklisted_tags.delete(t)
93
- end
94
- else
95
- @blacklisted_tags.delete(tag)
96
- end
97
- end
98
-
99
- def whitelist_all_tags
100
- @disabled_tags.clear
101
- end
102
-
103
- def is_tag_active?(tag)
104
- if @disable_all_tags
105
- false
106
- else
107
- @active_tags.include?(tag) #and not @disabled_tags.include?(tag)
108
- end
109
- end
110
-
111
- def remove_all_active_tags
112
- @active_tags.clear
113
- end
114
-
115
- def set_scoped_tag(tag)
116
- @scoped_tag = tag
117
- end
118
-
119
- def clear_scoped_tag
120
- @scoped_tag = nil
121
- end
122
-
123
- def has_scoped_tag?
124
- if @disable_all_tags
125
- false
126
- else
127
- not @scoped_tag.nil? and not @scoped_tag.empty?
128
- end
129
- end
130
-
131
- def is_scoped_tag_active?
132
- #@active_tags.include?(@scoped_tag)
133
- not @blacklisted_tags.include?(@scoped_tag)
134
- end
135
-
136
- def self.method_missing(mtd,*args,&block)
137
- if TloggerConf.instance.respond_to?(mtd)
138
- TloggerConf.instance.send(mtd,*args,&block)
139
- else
140
- super
141
- end
142
- end
143
- end
144
- #
145
- # end TloggerConf singleton
146
- #
147
-
148
- #
149
- # add object like methods to make module a class
150
- #
151
- class << self
152
- attr_accessor :tag
153
- include Tlogger
154
-
155
- PROXY_MTD = [:debug, :info, :error, :warn]
156
- def new(*args)
157
- if args.length == 0
158
- args << STDOUT
159
- end
160
- @tlogger = Logger.new(*args)
161
- @tag = TloggerConf::GLOBAL_TAG
162
- # disable by default
163
- # If there is issue then enable it back in application level
164
- self
165
- end
166
-
167
- def tag=(val)
168
- @tag = val
169
- TloggerConf.activate_tag(@tag)
170
- self
171
- end
172
-
173
- def tdebug(tag,*args)
174
- with_tag(tag) do
175
- self.debug(*args)
176
- end
177
- self
178
- end
179
-
180
- def terror(tag, *args)
181
- with_tag(tag) do
182
- self.error(*args)
183
- end
184
- self
185
- end
186
-
187
- def tinfo(tag, *args)
188
- with_tag(tag) do
189
- self.info(*args)
190
- end
191
- self
192
- end
193
-
194
- def twarn(tag, *args)
195
- with_tag(tag) do
196
- self.warn(*args)
197
- end
198
- self
199
- end
200
-
201
- def intDebug(msg)
202
- #puts TloggerConf.active_tags
203
- if TloggerConf.instance.is_tag_active?(TloggerConf::INT_TAG)
204
- msg2 = "[#{TloggerConf::INT_TAG}] #{msg}"
205
- #puts "intDebug"
206
- @tlogger.debug(msg2)
207
- end
208
- end
209
-
210
- def method_missing(mtd,*args,&block)
211
- #@tlogger.debug "[tlogger] method_missing: Method #{mtd}"
212
- intDebug("method_missing: #{mtd}")
213
- if @tlogger.respond_to?(mtd)
214
-
215
- if PROXY_MTD.include?(mtd)
216
-
217
- if @tag.nil? or @tag.empty?
218
- # no tag. Output like normal log
219
- @tlogger.send(mtd, *args, &block)
220
-
221
- else
222
-
223
- if TloggerConf.has_scoped_tag?
224
- if TloggerConf.is_scoped_tag_active?
225
- intDebug("Scoped tag detected")
226
- tag = []
227
- tag << TloggerConf.scoped_tag
228
- if TloggerConf.instance.is_show_source?
229
- tag << " - "
230
- tag << find_caller
231
- end
232
- args[0] = "[#{tag.join}] #{args[0]}"
233
- @tlogger.send(mtd,*args,&block)
234
- end
235
- elsif TloggerConf.is_tag_active?(@tag)
236
- intDebug("Tagged output...")
237
- tag = []
238
- tag << @tag
239
- if TloggerConf.instance.is_show_source?
240
- tag << " - "
241
- tag << find_caller
242
- end
243
- args[0] = "[#{tag.join}] #{args[0]}"
244
- @tlogger.send(mtd,*args,&block)
245
-
246
- elsif TloggerConf.is_auto_tag_on?
247
- intDebug("auto_tag is on...")
248
- args = tag_class(*args)
249
- @tlogger.send(mtd,*args,&block)
250
-
251
- end
252
-
253
- end
254
-
255
- else
256
- intDebug("Not proxy method for logger. Pass to logger to handle. (#{mtd})")
257
- ## not the debug, info, warn and error method, no need change message
258
- @tlogger.send(mtd, *args, &block)
259
- end
260
-
261
-
262
- elsif TloggerConf.instance.respond_to?(mtd)
263
- # redirect the config method to make it consistent API
264
- intDebug("Redirect to TloggerConf for consistancy (#{mtd})")
265
- TloggerConf.send(mtd, *args, &block)
266
- else
267
- intDebug("Call method_missing parent to handle method '#{mtd}'")
268
- super
269
- end
270
- end
271
- # end method_missing
272
- #
273
-
274
- private
275
- def tag_class(*args)
276
- args[0] = "[#{find_caller}] #{args[0]}"
277
- args
278
- end
279
- # end tag_class()
280
- #
281
-
282
- def find_caller
283
- caller.each do |c|
284
- next if c =~ /tlogger.rb/
285
- @cal = c
286
- break
287
- end
288
-
289
- if @cal.nil? or @cal.empty?
290
- @cal = caller[0]
291
- end
292
-
293
- # reduce it down to last two folder?
294
- sp = @cal.split(File::SEPARATOR)
295
- if sp.length > 1
296
- msg = "/#{sp[-2]}/#{sp[-1]}"
297
- else
298
- msg = sp[-1]
299
- end
300
-
301
- msg
302
-
303
- #wd = Dir.getwd
304
- #indx = @cal =~ /#{wd}/
305
- #if indx != nil
306
- # @scal = []
307
- # @scal << @cal[0..indx]
308
- # @scal << @cal[indx+wd.length..-1]
309
- # @scal.join
310
- #else
311
- # @cal
312
- #end
313
- end
314
- # end find_caller
315
- #
316
- end
317
- #
318
- # end class definition
319
- #
320
-
321
-
322
- def with_tag(tag,&block)
323
- if block
324
- TloggerConf.instance.set_scoped_tag(tag) #if not TloggerConf.instance.disabled_tags.include?(tag)
325
- block.call
326
- TloggerConf.instance.clear_scoped_tag
327
- end
328
- end
329
-
330
11
  end