Class: RMTools::RMLogger

Inherits:
Object show all
Defined in:
lib/rmtools/dev/logging.rb

Overview

Lazy logger with timer, coloring and caller hints Usage: > $log <= “Starting process…” 13:43:01.632 DEBUG [(irb):1 :irb_binding]: Starting process… > $log << [“Got response:”, 200, body: “Hello”] 13:43:20.524 INFO [(irb):2 :irb_binding]: [“Got response:”, :body=>“Hello”] $log < “Oops, something went wrong!” 13:43:32.030 WARN [(irb):3 :irb_binding]: Oops, something went wrong!

which is aliases of #debug, #info and #warn, consequently

If you want to wrap logger call into another method: > class Exception > def warn! > $log.warn “#selfself.class – #message”, caller: 2 > end > end but still see in log string a reference to that method calling Exeption#warn! just pass stack frames quantity as :caller param

If you want to log an info that need a calculations (remember, #inspect is a calculations as well) to be logged, but don’t want a production server to calculate this, you may pass that calculations in a block: > $log.debug a_large_object and it won’t run if #debug should not run at this moment

Log level might be set for one server or console session using ENV variables: LOGLEVEL=| INFO | WARN | ERROR or DEBUG=1 | WARN=1 | SILENT=1 Default log level is INFO

Constant Summary collapse

Modes =
[:debug, :log, :info, :warn, :error]
NOPRINT =
8
NOLOG =
4
PREV_CALLER =
2
INLINE =
1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(format = {}) ⇒ RMLogger

Returns a new instance of RMLogger.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rmtools/dev/logging.rb', line 53

def initialize format={}
  @c = Painter
  @highlight = {
      :error => @c.red_bold("ERROR"),
      :warn => @c.red_bold("WARN"),
      :log => @c.cyan("INFO"),
      :info => @c.cyan_bold("INFO"),
      :debug => @c.gray_bold("DEBUG")
  }
  @file_formats = Hash.new(@default_format = {})
  set_format :global, format
  
  if ENV['LOGLEVEL']
    self.log_level = ENV['LOGLEVEL']
  elsif ENV['DEBUG'] || ENV['VERBOSE']
    self.log_level = 'DEBUG'
  elsif ENV['WARN'] || ENV['QUIET']
    self.log_level = 'WARN'
  elsif ENV['SILENT']
    self.log_level = 'ERROR'
  else
    self.log_level = 'INFO'
  end
end

Instance Attribute Details

#default_formatObject (readonly)

Returns the value of attribute default_format.



45
46
47
# File 'lib/rmtools/dev/logging.rb', line 45

def default_format
  @default_format
end

#log_levelObject

Returns the value of attribute log_level.



45
46
47
# File 'lib/rmtools/dev/logging.rb', line 45

def log_level
  @log_level
end

#mute_debugObject

Returns the value of attribute mute_debug.



44
45
46
# File 'lib/rmtools/dev/logging.rb', line 44

def mute_debug
  @mute_debug
end

#mute_errorObject

Returns the value of attribute mute_error.



44
45
46
# File 'lib/rmtools/dev/logging.rb', line 44

def mute_error
  @mute_error
end

#mute_infoObject

Returns the value of attribute mute_info.



44
45
46
# File 'lib/rmtools/dev/logging.rb', line 44

def mute_info
  @mute_info
end

#mute_logObject

Returns the value of attribute mute_log.



44
45
46
# File 'lib/rmtools/dev/logging.rb', line 44

def mute_log
  @mute_log
end

#mute_warnObject

Returns the value of attribute mute_warn.



44
45
46
# File 'lib/rmtools/dev/logging.rb', line 44

def mute_warn
  @mute_warn
end

Instance Method Details

#_print(mode, text, opts, caler, bind, cfg) ⇒ Object

TODO: добавить фильтров, например, для обработки текста, который будет логирован



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/rmtools/dev/logging.rb', line 147

def _print mode, text, opts, caler, bind, cfg
  log_ = opts&NOLOG==0
  print_ = opts&NOPRINT==0
  str = cfg.fmt.dup
  str.gsub! "%mode", @highlight[mode]
  if bind
    text = bind.report text
  elsif !text.is String
    text = text.inspect
  elsif cfg.detect_comments and text =~ /\A[ \t]*#[ \t]+\S/
    text = "\n" + @c.green(text.gsub(/^([ \t]*#[ \t])?/, cfg.precede_comments).chop)
  end
  out = cfg.out
  if cfg._time or cfg.path_format
    now = Time.now
    if cfg._time
      time = now.strftime cfg.tf[0]
      time << ".#{cfg.tf[1]%[now.usec/1000]}" if cfg.tf[1]
      str.gsub! "%time", time
    end
    out = now.strftime cfg.out if cfg.path_format
  end
  if caler
    caler.sub!(/block (?:\((\d+) levels\) )?in/) {"{#{$1||1}}"}
    str.gsub! "%caller", caler.sub(String::SIMPLE_CALLER_RE, cfg.cf)
  end
  str.gsub! "%text", text
  str << "\n" if opts&INLINE==0
  log_str = cfg.color_out ? str : @c.clean(str)
  RMTools.write out, log_str if log_
  Kernel::print str if print_
end

#_set_format(file, format) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rmtools/dev/logging.rb', line 78

def _set_format file, format
  file.print = !format.q
  file.out = format.out || format.log_file
  file.color_out = format.color_out || format.color_log
  file.detect_comments = !!format.detect_comments
  file.precede_comments = format.precede_comments || "# "
            
  file.path_format = '%'.in file.out if file.out
  file.tf   = format.time.to_a
  file.cf0 = format.caller
  file.cf = file.cf0.sub('%p') {'\1'}.sub('%f') {'\2'}.sub('%l') {'\3'}.sub('%m') {'\4'}
  file.fmt = format.format
  file._time, file._caller = '%time'.in(file.fmt), '%caller'.in(file.fmt)
end

#debug(*args) ⇒ Object Also known as: <=



239
240
241
242
243
244
245
246
247
248
# File 'lib/rmtools/dev/logging.rb', line 239

def debug *args
  cfg = get_config!
  if (cfg.print or cfg.out && cfg.out_all) && !@mute_debug
    text, bind, opts = args.get_opts [!block_given? && args[0].kinda(Hash) ? args[0] : "\b\b ", nil], :mute => 0
    opts[:mute] |= NOLOG if !(cfg.out && cfg.out_all)
    opts[:mute] |= NOPRINT if !cfg.print
    return if block_given? && (text = yield).nil?
    _print(:debug, text, opts[:mute], cfg._caller && (@current_caller || caller)[(opts[:caller] || opts[:caller_offset]).to_i], bind, cfg)
  end 
end

#defaultsObject Also known as: usage



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/rmtools/dev/logging.rb', line 93

def defaults
  Kernel::puts %{    # #{@c.y 'common options:'}
:q => false,   # not print
:out => false, # output to file, may contain strftime's %H%M%Y etc for filename
:time => ["%H:%M:%S", "%03d"], # strftime, [msecs]
:type => :console, # or :html
# #{@c.y 'console values:'}
:caller => "#{@c.gray('%f:%l')} #{@c.red_bold(':%m')}", # "file:line :method", %p is for fullpath
:format => "%time %mode [%caller]: %text" # format of entire log string, %mode is {#{%w(debug log info warn).map {|i| @highlight[i.to_sym]}*', '}}
:color_out => false, # do not clean control characters that make output to file colorful; set to true makes more readable output of `tail' but hardly readable by gui file output
:detect_comments => false, # highlight and strip comments blocks
:precede_comments => "# ",
# :detect_comments with default :precede_comments allows comment blocks looking like:
$log<<<<-'#'
# ... comment string one ...
# ... comment string two ...
#
be logged like #{@c.green "\n# ... comment string one ...\n# ... comment string two ..."}
# #{@c.y 'html options:'}
:caller => "<a class='l'>%f:%l</a> <a class='m'>:%m</a>",
:format => "<div class='line'><a class='t'>%time</a> <a class='%mode'>%mode</m> [%caller]: <p>%text</p>%att</div>", # %att is for array of objects that should be formatted by the next option
:att =>"<div class='att'><div class='hide'>+</div><pre>%s</pre></div>", # .hide should be scripted to work like a spoiler
:serializer => RMTools::RMLogger::HTML # should respond to :render(obj); nil value means each object will be just #inspect'ed}
end

#error(*args) ⇒ Object Also known as: fatal

controls:

  • @mute_warn, @mute_info, @mute_log, @mute_debug:

    do not print this messages regardless of any globals
    
  • @out_all: write to file info and debug messages

  • @out: write to file

  • @print: write to stdout



195
196
197
198
199
200
201
202
203
204
# File 'lib/rmtools/dev/logging.rb', line 195

def error *args
  cfg = get_config!
  if (cfg.out or cfg.print) && !@mute_error
    text, bind, opts = args.get_opts [!block_given? && args[0].kinda(Hash) ? args[0] : "\b\b ", nil], :mute => 0
    opts[:mute] |= NOLOG if !cfg.out
    opts[:mute] |= NOPRINT if !cfg.print
    return if block_given? && (text = yield).nil?
    _print(:error, text, opts[:mute], cfg._caller && (@current_caller || caller)[(opts[:caller] || opts[:caller_offset]).to_i], bind, cfg)
  end  
end

#get_config(file = nil) ⇒ Object



184
185
186
# File 'lib/rmtools/dev/logging.rb', line 184

def get_config(file=nil)
  @file_formats[file && File.expand_path(file)]
end

#get_config!Object



180
181
182
# File 'lib/rmtools/dev/logging.rb', line 180

def get_config!
  @file_formats.empty? ? @default_format : @file_formats[File.expand_path((@current_caller = caller)[1].till ':')]
end

#get_format(file = nil) ⇒ Object



139
140
141
142
143
# File 'lib/rmtools/dev/logging.rb', line 139

def get_format file=nil
  cfg = @file_formats[file && File.expand_path(file)]
  modes = Modes.reject {|m| send :"mute_#{m}"}
  %{<Logger #{cfg.fmt.sub('%time', "%time(#{cfg.tf*'.'})").sub('%caller', "%caller(#{cfg.cf0})")}#{' -> '+cfg.out if cfg.out} #{modes.b ? modes.inspect : 'muted'}>}
end

#info(*args) ⇒ Object Also known as: <<, puts



228
229
230
231
232
233
234
235
236
237
# File 'lib/rmtools/dev/logging.rb', line 228

def info *args
  cfg = get_config!
  if (cfg.print or cfg.out && cfg.out_all) && !@mute_info
    text, bind, opts = args.get_opts [!block_given? && args[0].kinda(Hash) ? args[0] : "\b\b ", nil], :mute => 0
    opts[:mute] |= NOLOG if !(cfg.out && cfg.out_all)
    opts[:mute] |= NOPRINT if !cfg.print
    return if block_given? && (text = yield).nil?
    _print(:info, text, opts[:mute], cfg._caller && (@current_caller || caller)[(opts[:caller] || opts[:caller_offset]).to_i], bind, cfg)
  end 
end

#inspectObject



281
# File 'lib/rmtools/dev/logging.rb', line 281

def inspect() get_format end

#log(*args) ⇒ Object



217
218
219
220
221
222
223
224
225
226
# File 'lib/rmtools/dev/logging.rb', line 217

def log *args
  cfg = get_config!
  if (cfg.out or cfg.print) && !@mute_log
    text, bind, opts = args.get_opts [!block_given? && args[0].kinda(Hash) ? args[0] : "\b\b ", nil], :mute => 0
    opts[:mute] |= NOLOG if !cfg.out
    opts[:mute] |= NOPRINT if !(cfg.print && !@mute_debug)
    return if block_given? && (text = yield).nil?
    _print(:log, text, opts[:mute], cfg._caller && (@current_caller || caller)[(opts[:caller] || opts[:caller_offset]).to_i], bind, cfg)
  end
end


256
257
258
# File 'lib/rmtools/dev/logging.rb', line 256

def print text
  info text, caller: 1, mute: INLINE 
end

#set_format(*args) ⇒ Object

set any needed params, the rest will be set by default



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rmtools/dev/logging.rb', line 120

def set_format *args
  global, format = args.fetch_opts [nil], :type => :console, :time => ["%H:%M:%S", "%03d"]                  
  format = if format[:type] == :html; {
                :caller => "<a class='l'>%f:%l</a> <a class='m'>:%m</a>", 
                :format => "<div class='line'><a class='t'>%time</a> <a class='%mode'>%mode</m> [%caller]: <p>%text</p>%att</div>",
                :att =>"<div class='att'><div class='hide'>+</div><pre>%s</pre></div>",
                :serializer => RMTools::RMLogger::HTML
              }; else {
                :caller => "#{@c.gray('%f:%l')} #{@c.red_bold(':%m')}", 
                :format => "%time %mode [%caller]: %text"
              } end.merge format
  if global
    _set_format @default_format, format
  else
    _set_format(file_format={}, format)
    @file_formats[File.expand_path(caller[0].till ':')] = file_format
  end
end

#warn(*args) ⇒ Object Also known as: <



206
207
208
209
210
211
212
213
214
215
# File 'lib/rmtools/dev/logging.rb', line 206

def warn *args
  cfg = get_config!
  if (cfg.out or cfg.print) && !@mute_warn
    text, bind, opts = args.get_opts [!block_given? && args[0].kinda(Hash) ? args[0] : "\b\b ", nil], :mute => 0
    opts[:mute] |= NOLOG if !cfg.out
    opts[:mute] |= NOPRINT if !cfg.print
    return if block_given? && (text = yield).nil?
    _print(:warn, text, opts[:mute], cfg._caller && (@current_caller || caller)[(opts[:caller] || opts[:caller_offset]).to_i], bind, cfg)
  end  
end