tlogger 0.8.0 → 0.21.0

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