liquid-logging 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 00283f12fce4c72f5d0a60aab70c3fa62dfbcd94
4
+ data.tar.gz: ec0ab79a50e4e326d08eac1fcd197183423c7c20
5
+ SHA512:
6
+ metadata.gz: 7290dc769e77b2f5b13586dc6a7777e767864fd46e98d9203b801119a0ba52205096d89bd4930307951e11aa03d7a9fda2c5d5e8eb560d7db963d954c0c8128d
7
+ data.tar.gz: 93ecc1de6af1cece32f77ac352d6f0dae9d7e68f2b521a2d2c91a1c838400669438d362b3de3f7999d746e5791c2826483e5235a28f2268d88cdd2149778a04d
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --color
3
+ --format Fuubar
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - jruby
4
+ - 2.0.0
5
+ - 1.9.3
@@ -0,0 +1 @@
1
+ --no-private
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'syslogger', :require => false
6
+ gem 'liquid-ext', path: '../ext'
7
+
8
+ group :development, :test do
9
+ gem 'liquid-development'
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2012 madvertise Mobile Advertising GmbH
2
+ Copyright (c) 2013 LiquidM, Inc.
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ # Liquid Logging
2
+
3
+ The liquid-logging gem is a collection of classes for logging related tasks.
4
+ The main class (`ImprovedLogger`) is an improved version of DaemonKits
5
+ `AbstractLogger` class including token support, buffer backend and more.
6
+
7
+ [![Build Status](https://secure.travis-ci.org/liquidm/logging.png)](http://travis-ci.org/liquidm/logging)
8
+ [![Code Climate](https://codeclimate.com/github/liquidm/logging.png)](https://codeclimate.com/github/liquidm/logging)
9
+ [![Dependency Status](https://gemnasium.com/liquidm/logging.png)](https://gemnasium.com/liquidm/logging)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'liquid-logging'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install liquid-logging
24
+
25
+ ## Usage
26
+
27
+ Please refer to the [API documentation](http://rubydoc.info/gems/liquid-logging/frames).
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it ( http://github.com/liquidm/logging/fork )
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
@@ -0,0 +1,3 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "liquid/tasks"
@@ -0,0 +1 @@
1
+ require 'liquid/logging'
@@ -0,0 +1,5 @@
1
+ require 'liquid/logging/improved_logger'
2
+ require 'liquid/logging/multi_logger'
3
+
4
+ ImprovedLogger = Liquid::Logging::ImprovedLogger
5
+ MultiLogger = Liquid::Logging::MultiLogger
@@ -0,0 +1,21 @@
1
+ require 'airbrake'
2
+
3
+ module Liquid
4
+ module Logging
5
+ class ImprovedLogger
6
+ # Log an exception with airbrake.
7
+ #
8
+ # @param [Exception] exc The exception to log.
9
+ # @param [String] message Additional reason to log.
10
+ def exception_with_airbrake(exc, message = nil, attribs = {})
11
+ Airbrake.notify_or_ignore(exc, {
12
+ :error_message => message,
13
+ :cgi_data => ENV.to_hash,
14
+ }.merge(attribs))
15
+ end
16
+
17
+ alias_method :exception_without_airbrake, :exception
18
+ alias_method :exception, :exception_with_airbrake
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ require 'logger'
2
+
3
+ module Liquid
4
+ module Logging
5
+
6
+ ##
7
+ # DocumentLogger is a Logger compliant class that keeps a structured
8
+ # document per log message in memory.
9
+ #
10
+ class DocumentLogger < ::Logger
11
+
12
+ attr_accessor :attrs
13
+ attr_accessor :messages
14
+
15
+ def initialize
16
+ super(nil)
17
+ @messages = []
18
+ @attrs = {}
19
+ end
20
+
21
+ def add(severity, message = nil, progname = nil, &block)
22
+ severity ||= UNKNOWN
23
+ if severity < @level
24
+ return true
25
+ end
26
+
27
+ progname ||= @progname
28
+
29
+ if message.nil?
30
+ if block_given?
31
+ message = yield
32
+ else
33
+ message = progname
34
+ progname = @progname
35
+ end
36
+ end
37
+
38
+ @messages << @attrs.merge({
39
+ severity: severity,
40
+ time: Time.now.to_f,
41
+ progname: progname,
42
+ message: message,
43
+ })
44
+
45
+ true
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,159 @@
1
+ module Liquid
2
+ module Logging
3
+
4
+ ##
5
+ # ImprovedIO is a subclass of IO with a bunch of methods reimplemented so
6
+ # that subclasses don't have to reimplement every IO method. Unfortunately
7
+ # this is necessary because Ruby does not provide a sane interface to IO
8
+ # like Enumerable for Arrays and Hashes.
9
+ #
10
+ class ImprovedIO < IO
11
+
12
+ def flush
13
+ self
14
+ end
15
+
16
+ def external_encoding
17
+ nil
18
+ end
19
+
20
+ def internal_encoding
21
+ nil
22
+ end
23
+
24
+ def set_encoding
25
+ self
26
+ end
27
+
28
+ def readbyte
29
+ getbyte.tap do |byte|
30
+ raise EOFError unless byte
31
+ end
32
+ end
33
+
34
+ def readchar
35
+ getc.tap do |char|
36
+ raise EOFError unless char
37
+ end
38
+ end
39
+
40
+ def readline
41
+ gets.tap do |string|
42
+ raise EOFError unless string
43
+ end
44
+ end
45
+
46
+ def tty?
47
+ false
48
+ end
49
+
50
+ def printf(format_string, *arguments)
51
+ write(sprintf(format_string, *arguments))
52
+ return nil
53
+ end
54
+
55
+ def print(*arguments)
56
+ args = if arguments.empty?
57
+ [$_]
58
+ else
59
+ arguments
60
+ end
61
+
62
+ write(args.join($,))
63
+ return nil
64
+ end
65
+
66
+ def putc
67
+ end
68
+
69
+ def puts(*arguments)
70
+ return nil if arguments.empty?
71
+
72
+ arguments.each do |arg|
73
+ if arg.is_a?(Array)
74
+ puts(*arg)
75
+ elsif arg.is_a?(String)
76
+ write(arg)
77
+ else
78
+ write(arg.to_s)
79
+ end
80
+ end
81
+
82
+ return nil
83
+ end
84
+
85
+ # provide sane aliases for IO compat
86
+ begin
87
+ alias_method :each_byte, :bytes
88
+ alias_method :each_char, :chars
89
+ alias_method :each_codepoint, :codepoints
90
+ alias_method :each_line, :lines
91
+ alias_method :each, :lines
92
+ alias_method :eof, :eof?
93
+ alias_method :isatty, :tty?
94
+ alias_method :sysread, :read
95
+ alias_method :syswrite, :write
96
+ rescue NameError
97
+ # do nothing, method may not exist in ruby 1.8
98
+ end
99
+
100
+ # skip these IO methods
101
+ [
102
+ :advise,
103
+ :autoclose=,
104
+ :autoclose?,
105
+ :binmode,
106
+ :binmode?,
107
+ :close_on_exec=,
108
+ :close_on_exec?,
109
+ :fcntl,
110
+ :fdatasync,
111
+ :fileno,
112
+ :fsync,
113
+ :ioctl,
114
+ :lineno,
115
+ :lineno=,
116
+ :pid,
117
+ :read_nonblock,
118
+ :stat,
119
+ :sysseek,
120
+ :tell,
121
+ :to_i,
122
+ :to_io,
123
+ :write_nonblock,
124
+ ].each do |meth|
125
+ begin
126
+ undef_method meth
127
+ rescue NameError
128
+ # do nothing, method may not exist in ruby 1.8
129
+ end
130
+ end
131
+
132
+ class << self
133
+ # skip these IO methods
134
+ [
135
+ :binread,
136
+ :binwrite,
137
+ :copy_stream,
138
+ :for_fd,
139
+ :foreach,
140
+ :open,
141
+ :pipe,
142
+ :popen,
143
+ :read,
144
+ :readlines,
145
+ :select,
146
+ :sysopen,
147
+ :try_convert,
148
+ :write,
149
+ ].each do |meth|
150
+ begin
151
+ undef_method meth
152
+ rescue NameError
153
+ # do nothing, method may not exist in ruby 1.8
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,448 @@
1
+ require 'logger'
2
+ require 'stringio'
3
+ require 'benchmark'
4
+
5
+ require 'liquid/logging/improved_io'
6
+ require 'liquid/logging/document_logger'
7
+
8
+ class String
9
+ def clean_quote
10
+ if index(/["\s]/)
11
+ %{"#{tr('"', "'")}"}
12
+ else
13
+ self
14
+ end
15
+ end
16
+ end
17
+
18
+ module Liquid
19
+ module Logging
20
+
21
+ ##
22
+ # ImprovedLogger is an enhanced version of DaemonKits AbstractLogger class
23
+ # with token support, buffer backend and more.
24
+ #
25
+ class ImprovedLogger < ImprovedIO
26
+
27
+ # Program name prefix. Used as ident for syslog backends.
28
+ attr_accessor :progname
29
+
30
+ # Arbitrary token to prefix log messages with.
31
+ attr_accessor :token
32
+
33
+ # Log the file/line where the message came from
34
+ attr_accessor :log_caller
35
+
36
+ # Log filename for file backend.
37
+ attr_reader :logfile
38
+
39
+ @severities = {
40
+ :debug => Logger::DEBUG,
41
+ :info => Logger::INFO,
42
+ :warn => Logger::WARN,
43
+ :error => Logger::ERROR,
44
+ :fatal => Logger::FATAL,
45
+ :unknown => Logger::UNKNOWN
46
+ }
47
+
48
+ @silencer = true
49
+
50
+ class << self
51
+ # Hash of Symbol/Fixnum pairs to map Logger levels.
52
+ attr_reader :severities
53
+
54
+ # Enable/disable the silencer on a global basis. Useful for debugging
55
+ # otherwise silenced code blocks.
56
+ attr_accessor :silencer
57
+ end
58
+
59
+ def initialize(backend = STDERR, progname = nil)
60
+ self.progname = progname || File.basename($0)
61
+ self.logger = backend
62
+ self.log_caller = false
63
+ end
64
+
65
+ # Get the backend logger.
66
+ #
67
+ # @return [Logger] The currently active backend logger object.
68
+ def logger
69
+ @logger ||= create_backend
70
+ end
71
+
72
+ # Set a different backend.
73
+ #
74
+ # @param [Symbol, String, IO, Logger] value The new logger backend. Either a
75
+ # Logger object, an IO object, a String containing the logfile path or a Symbol to
76
+ # create a default backend for :syslog or :buffer
77
+ # @return [Logger] The newly created backend logger object.
78
+ def logger=(value)
79
+ @backend = value
80
+ @logger = create_backend
81
+ define_level_methods
82
+ end
83
+
84
+ # Close any connections/descriptors that may have been opened by the
85
+ # current backend.
86
+ def close
87
+ @logger.close rescue nil
88
+ @logger = nil
89
+ end
90
+
91
+ # Retrieve the current buffer in case this instance is a buffered logger.
92
+ #
93
+ # @return [String] Contents of the buffer.
94
+ def buffer
95
+ @logfile.string if @backend == :buffer
96
+ end
97
+
98
+ # Retrieve collected messages in case this instance is a document logger.
99
+ #
100
+ # @return [Array] An array of logged messages.
101
+ def messages
102
+ logger.messages if @backend == :document
103
+ end
104
+
105
+ # Get the current logging level.
106
+ #
107
+ # @return [Symbol] Current logging level.
108
+ def level
109
+ logger.level
110
+ end
111
+
112
+ # Set the logging level.
113
+ #
114
+ # @param [Symbol, Fixnum] level New level as Symbol or Fixnum from Logger class.
115
+ # @return [Fixnum] New level converted to Fixnum from Logger class.
116
+ def level=(level)
117
+ logger.level = level.is_a?(Symbol) ? self.class.severities[level] : level
118
+ configure_log4j(logger)
119
+ define_level_methods
120
+ end
121
+
122
+ # @private
123
+ def define_level_methods
124
+ # We do this dynamically here, so we can implement a no-op for levels
125
+ # which are disabled.
126
+ self.class.severities.each do |severity, num|
127
+ if num >= level
128
+ instance_eval(<<-EOM, __FILE__, __LINE__)
129
+ def #{severity}(*args, &block)
130
+ if block_given?
131
+ add(:#{severity}, *yield)
132
+ else
133
+ add(:#{severity}, *args)
134
+ end
135
+ end
136
+
137
+ def #{severity}?
138
+ true
139
+ end
140
+ EOM
141
+ else
142
+ instance_eval("def #{severity}(*args); end", __FILE__, __LINE__)
143
+ instance_eval("def #{severity}?; false; end", __FILE__, __LINE__)
144
+ end
145
+ end
146
+ end
147
+
148
+ # Compatibility method
149
+ # @private
150
+ def <<(msg)
151
+ add(:info, msg)
152
+ end
153
+
154
+ alias write <<
155
+
156
+ # Log an exception with fatal level.
157
+ #
158
+ # @param [Exception] exc The exception to log.
159
+ # @param [String] message Additional reason to log.
160
+ def exception(exc, message = nil, attribs = {})
161
+ fatal("exception", {
162
+ class: exc.class,
163
+ reason: exc.message,
164
+ message: message,
165
+ backtrace: clean_trace(exc.backtrace)
166
+ }.merge(attribs).merge(called_from))
167
+ end
168
+
169
+ # Log a realtime benchmark
170
+ #
171
+ # @param [String] msg The log message
172
+ # @param [String,Symbol] key The realtime key
173
+ def realtime(severity, msg, attribs = {}, &block)
174
+ result = nil
175
+ rt = Benchmark.realtime { result = yield }
176
+ add(severity, msg, attribs.merge({rt: rt}))
177
+ return result
178
+ end
179
+
180
+ def add(severity, message, attribs = {})
181
+ severity = severity.is_a?(Symbol) ? severity : self.class.severities.key(severity)
182
+
183
+ attribs.merge!(called_from) if @log_caller
184
+ attribs.merge!(token: @token) if @token
185
+ attribs = attribs.map do |k,v|
186
+ "#{k}=#{v.to_s.clean_quote}"
187
+ end.join(' ')
188
+
189
+ message = "#{message} #{attribs}" if attribs.length > 0
190
+ logger.send(severity) { message }
191
+
192
+ return nil
193
+ end
194
+
195
+ # Save the current token and associate it with obj#object_id.
196
+ def save_token(obj)
197
+ if @token
198
+ @tokens ||= {}
199
+ @tokens[obj.object_id] = @token
200
+ end
201
+ end
202
+
203
+ # Restore the token that has been associated with obj#object_id.
204
+ def restore_token(obj)
205
+ @tokens ||= {}
206
+ @token = @tokens.delete(obj.object_id)
207
+ end
208
+
209
+ # Silence the logger for the duration of the block.
210
+ def silence(temporary_level = :error)
211
+ if self.class.silencer
212
+ begin
213
+ old_level, self.level = self.level, temporary_level
214
+ yield self
215
+ ensure
216
+ self.level = old_level
217
+ end
218
+ else
219
+ yield self
220
+ end
221
+ end
222
+
223
+ # Remove references to the liquid-logging gem from exception
224
+ # backtraces.
225
+ #
226
+ # @private
227
+ def clean_trace(trace)
228
+ return unless trace
229
+ trace.reject do |line|
230
+ line =~ /(gems|vendor)\/liquid-logging/
231
+ end
232
+ end
233
+
234
+ private
235
+
236
+ # Return the first callee outside the liquid-logging gem. Used in add
237
+ # to figure out where in the source code a message has been produced.
238
+ def called_from
239
+ location = caller.detect('unknown:0') do |line|
240
+ line.match(/(improved_logger|multi_logger)\.rb/).nil?
241
+ end
242
+
243
+ file, line, _ = location.split(':')
244
+ { :file => file, :line => line }
245
+ end
246
+
247
+ def create_backend
248
+ self.close
249
+
250
+ case @backend
251
+ when :log4j
252
+ create_log4j_logger
253
+ when :ruby
254
+ create_ruby_logger(STDOUT)
255
+ when :stdout
256
+ create_io_backend(STDOUT)
257
+ when :stderr
258
+ create_io_backend(STDERR)
259
+ when :syslog
260
+ create_syslog_backend
261
+ when :buffer
262
+ create_buffer_backend
263
+ when :document
264
+ create_document_backend
265
+ when String
266
+ create_file_backend
267
+ when IO
268
+ create_io_backend(@backend)
269
+ when Logger
270
+ @backend
271
+ else
272
+ raise "unknown backend: #{@backend.inspect}"
273
+ end
274
+ end
275
+
276
+ def create_syslog_backend
277
+ begin
278
+ require 'syslogger'
279
+ Syslogger.new(progname, Syslog::LOG_PID, Syslog::LOG_LOCAL1)
280
+ rescue LoadError
281
+ self.logger = $stderr
282
+ error("Couldn't load syslogger gem, reverting to STDERR for logging")
283
+ end
284
+ end
285
+
286
+ def create_buffer_backend
287
+ @logfile = StringIO.new
288
+ create_logger
289
+ end
290
+
291
+ def create_document_backend
292
+ DocumentLogger.new.tap do |logger|
293
+ logger.formatter = Formatter.new
294
+ logger.progname = progname
295
+ end
296
+ end
297
+
298
+ def create_io_backend(backend)
299
+ @logfile = backend
300
+ @logfile.sync = true
301
+ create_logger
302
+ end
303
+
304
+ def create_file_backend
305
+ @logfile = @backend
306
+
307
+ begin
308
+ FileUtils.mkdir_p(File.dirname(@logfile))
309
+ rescue
310
+ self.logger = $stderr
311
+ error("#{@logfile} not writable, using STDERR for logging")
312
+ else
313
+ create_logger
314
+ end
315
+ end
316
+
317
+ def create_logger
318
+ create_ruby_logger(@logfile)
319
+ end
320
+
321
+ def create_log4j_logger
322
+ Log4jruby::Logger.get($0).tap do |logger|
323
+ @backend = :log4j
324
+ configure_log4j(logger)
325
+ end
326
+ end
327
+
328
+ def configure_log4j(logger)
329
+ return unless RUBY_PLATFORM == 'java'
330
+
331
+ require 'log4j'
332
+ require 'log4jruby'
333
+
334
+ @console = org.apache.log4j.ConsoleAppender.new
335
+ @console.setLayout(org.apache.log4j.PatternLayout.new(Formatter.log4j_format))
336
+ @console.setThreshold(org.apache.log4j.Level.const_get(self.class.severities.key(logger.level).to_s.upcase.to_sym))
337
+ @console.activateOptions
338
+
339
+ org.apache.log4j.Logger.getRootLogger.tap do |root|
340
+ root.getLoggerRepository.resetConfiguration
341
+ root.addAppender(@console)
342
+ end
343
+ end
344
+
345
+ def create_ruby_logger(io)
346
+ Logger.new(io).tap do |logger|
347
+ logger.formatter = Formatter.new
348
+ logger.progname = progname
349
+ end
350
+ end
351
+
352
+ ##
353
+ # The Formatter class is responsible for formatting log messages. The
354
+ # default format is:
355
+ #
356
+ # YYYY:MM:DD HH:MM:SS.MS daemon_name(pid) level: message
357
+ #
358
+ class Formatter
359
+
360
+ @format = "%{time} %{progname}(%{pid}) [%{severity}] %{msg}\n"
361
+ @log4j_format = "%d %c(%t) [%p] %m%n"
362
+ @time_format = "%Y-%m-%d %H:%M:%S.%N"
363
+
364
+ class << self
365
+ # Format string for log messages.
366
+ attr_accessor :format
367
+ attr_accessor :log4j_format
368
+
369
+ # Format string for timestamps in log messages.
370
+ attr_accessor :time_format
371
+ end
372
+
373
+ RUBY2SYSLOG = {
374
+ :debug => 7,
375
+ :info => 6,
376
+ :warn => 4,
377
+ :error => 3,
378
+ :fatal => 2,
379
+ :unknown => 3,
380
+ }
381
+
382
+ # @private
383
+ def call(severity, time, progname, msg)
384
+ self.class.format % {
385
+ :time => time.strftime(self.class.time_format),
386
+ :progname => progname,
387
+ :pid => $$,
388
+ :severity => severity,
389
+ :syslog_severity => RUBY2SYSLOG[severity.downcase.to_sym],
390
+ :msg => msg.to_s,
391
+ }
392
+ end
393
+ end
394
+
395
+ # @private
396
+ module IOCompat
397
+ def close_read
398
+ nil
399
+ end
400
+
401
+ def close_write
402
+ close
403
+ end
404
+
405
+ def closed?
406
+ raise NotImplementedError
407
+ end
408
+
409
+ def sync
410
+ @backend != :buffer
411
+ end
412
+
413
+ def sync=(value)
414
+ raise NotImplementedError, "#{self} cannot change sync mode"
415
+ end
416
+
417
+ # ImprovedLogger is write-only
418
+ def _raise_write_only
419
+ raise IOError, "#{self} is a buffer-less, write-only, non-seekable stream."
420
+ end
421
+
422
+ [
423
+ :bytes,
424
+ :chars,
425
+ :codepoints,
426
+ :lines,
427
+ :eof?,
428
+ :getbyte,
429
+ :getc,
430
+ :gets,
431
+ :pos,
432
+ :pos=,
433
+ :read,
434
+ :readlines,
435
+ :readpartial,
436
+ :rewind,
437
+ :seek,
438
+ :ungetbyte,
439
+ :ungetc
440
+ ].each do |meth|
441
+ alias_method meth, :_raise_write_only
442
+ end
443
+ end
444
+
445
+ include IOCompat
446
+ end
447
+ end
448
+ end
@@ -0,0 +1,14 @@
1
+ ---
2
+ LongParameterList:
3
+ exclude:
4
+ - Formatter#call
5
+ FeatureEnvy:
6
+ exclude:
7
+ - ImprovedLogger#clean_trace
8
+ - ImprovedLogger#exception
9
+ UtilityFunction:
10
+ exclude:
11
+ - ImprovedLogger#clean_trace
12
+ LargeClass:
13
+ exclude:
14
+ - ImprovedLogger
@@ -0,0 +1,34 @@
1
+ module Liquid
2
+ module Logging
3
+
4
+ ##
5
+ # MultiLogger is a simple class for multiplexing ImprovedLogger objects. It
6
+ # support attach/detach to send messages to any number of loggers.
7
+
8
+ class MultiLogger
9
+ def initialize(*loggers)
10
+ @loggers = loggers
11
+ end
12
+
13
+ # Attach an ImprovedLogger object.
14
+ def attach(logger)
15
+ logger.token = @loggers.first.token rescue nil
16
+ @loggers << logger
17
+ end
18
+
19
+ # Detach an ImprovedLogger object.
20
+ def detach(logger)
21
+ @loggers.delete(logger)
22
+ end
23
+
24
+ # Delegate all method calls to all attached loggers.
25
+ #
26
+ # @private
27
+ def method_missing(name, *args, &block)
28
+ @loggers.map do |logger|
29
+ logger.send(name, *args, &block)
30
+ end.first
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "liquid-logging"
5
+ spec.version = "2.0.0"
6
+ spec.authors = ["LiquidM, Inc."]
7
+ spec.email = ["opensource@liquidm.com"]
8
+ spec.description = %q{Advanced logging classes with buffer backend, transactions, multi logger, etc}
9
+ spec.summary = %q{Advanced logging classes with buffer backend, transactions, multi logger, etc}
10
+ spec.homepage = "https://github.com/liquidm/logging"
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ["lib"]
16
+
17
+ if RUBY_PLATFORM == "java"
18
+ spec.platform = 'java'
19
+ spec.add_dependency "log4jruby", "~> 1.0.0.rc1"
20
+ spec.add_dependency "slyphon-log4j", "~> 1.2.15"
21
+ end
22
+ end
@@ -0,0 +1,322 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ImprovedLogger do
4
+
5
+ let(:logger) { ImprovedLogger.new(:document) }
6
+
7
+ before(:each) { logger.level = :debug }
8
+
9
+ subject { logger.messages }
10
+
11
+ ImprovedLogger.severities.keys.each do |level|
12
+ describe level do
13
+ before { logger.send(level, "testing #{level}") }
14
+ let(:prefix) { level == :unknown ? "ANY" : level.to_s.upcase }
15
+ it "logs #{level} messages" do
16
+ subject.last[:message].should == "testing #{level}"
17
+ end
18
+ end
19
+ end
20
+
21
+ it "logs info level messages with <<" do
22
+ logger << "Info test <<"
23
+ subject.last[:message].should == "Info test <<"
24
+ end
25
+
26
+ it "logs info level messages with write" do
27
+ logger.write("Info test write")
28
+ subject.last[:message].should == "Info test write"
29
+ end
30
+
31
+ it "supports additional attributes" do
32
+ logger.info("foo", key: "value", test: "with space")
33
+ subject.last[:message].should == 'foo key=value test="with space"'
34
+ end
35
+
36
+ it "supports lazy-evaluation via blocks" do
37
+ logger.debug { "debug message" }
38
+ subject.last[:message].should == "debug message"
39
+ end
40
+
41
+ it "supports lazy-evaluation with attributes" do
42
+ logger.debug { ["debug message", {key: "value"}] }
43
+ subject.last[:message].should == "debug message key=value"
44
+ end
45
+
46
+ it "accepts a different backend" do
47
+ l = Logger.new('/dev/null')
48
+ logger.logger = l
49
+ logger.logger.should == l
50
+ end
51
+
52
+ describe :log_caller do
53
+ it "logs the caller file and line number" do
54
+ f = __FILE__
55
+ l = __LINE__ + 3
56
+
57
+ logger.log_caller = true
58
+ logger.info("Caller test")
59
+ subject.last[:message].should == "Caller test file=#{f} line=#{l}"
60
+ end
61
+
62
+ it "does not log the caller file and line number" do
63
+ f = File.basename(__FILE__)
64
+ l = __LINE__ + 3
65
+
66
+ logger.log_caller = false
67
+ logger.info("Caller test")
68
+ subject.last[:message].should_not == "Caller test file=#{f} line=#{l}"
69
+ end
70
+ end
71
+
72
+ let(:fake_trace) do
73
+ [
74
+ "/home/jdoe/app/libexec/app.rb:1:in `foo'",
75
+ "/usr/lib/ruby/gems/1.8/gems/liquid-logging-0.1.0/lib/liquid/logging/improved_logger.rb:42: in `info'"
76
+ ]
77
+ end
78
+
79
+ describe :exceptions do
80
+ let(:exc) do
81
+ RuntimeError.new('Test error').tap do |exc|
82
+ exc.set_backtrace(fake_trace)
83
+ end
84
+ end
85
+
86
+ it "logs an exception object" do
87
+ logger.exception(exc)
88
+ subject.last[:message].should match(%r{exception class=RuntimeError reason=\"Test error\" message= backtrace=\"\['/home/jdoe/app/libexec/app\.rb:1:in `foo''\]\"})
89
+ end
90
+
91
+ it "logs an exception object and prefix" do
92
+ logger.exception(exc, "app failed to foo")
93
+ subject.last[:message].should match(%r{exception class=RuntimeError reason=\"Test error\" message=\"app failed to foo\" backtrace=\"\['/home/jdoe/app/libexec/app\.rb:1:in `foo''\]\"})
94
+ end
95
+ end
96
+
97
+ describe :clean_trace do
98
+ subject { logger.clean_trace(fake_trace) }
99
+ it { should include("/home/jdoe/app/libexec/app.rb:1:in `foo'") }
100
+ it { should_not include("/usr/lib/ruby/gems/1.8/gems/liquid-logging-0.1.0/lib/liquid/logging/improved_logger.rb:42: in `info'") }
101
+ end
102
+
103
+ it "should support silencing" do
104
+ logger.silence do |logger|
105
+ logger.info "This should never be logged"
106
+ end
107
+
108
+ subject.last.should be_nil
109
+ end
110
+
111
+ it "should not discard messages if silencer is disabled globally" do
112
+ ImprovedLogger.silencer = false
113
+
114
+ logger.silence do |logger|
115
+ logger.info "This should actually be logged"
116
+ end
117
+
118
+ subject.last[:message].should == "This should actually be logged"
119
+
120
+ ImprovedLogger.silencer = true
121
+ end
122
+
123
+ it "should support a token" do
124
+ token = "3d5e27f7-b97c-4adc-b1fd-adf1bd4314e0"
125
+
126
+ logger.token = token
127
+ logger.info "This should include a token"
128
+ subject.last[:message].should match(token)
129
+
130
+ logger.token = nil
131
+ logger.info "This should not include a token"
132
+ subject.last[:message].should_not match(token)
133
+ end
134
+
135
+ it "should support save/restore on tokens" do
136
+ token1 = "3d5e27f7-b97c-4adc-b1fd-adf1bd4314e0"
137
+ token2 = "1bdef605-34b9-4ec7-9a1c-cb58efc8a635"
138
+
139
+ obj = Object.new
140
+
141
+ logger.token = token1
142
+ logger.info "This should include token1"
143
+ subject.last[:message].should match(token1)
144
+
145
+ logger.save_token(obj)
146
+ logger.token = token2
147
+ logger.info "This should include token2"
148
+ subject.last[:message].should match(token2)
149
+
150
+ logger.restore_token(obj)
151
+ logger.info "This should include token1"
152
+ subject.last[:message].should match(token1)
153
+
154
+ logger.token = nil
155
+ logger.info "This should not include a token"
156
+ subject.last[:message].should_not match(token1)
157
+ subject.last[:message].should_not match(token2)
158
+ end
159
+
160
+ it "should fall back to stderr if logfile is not writable" do
161
+ $stderr.should_receive(:write).with(/not writable.*STDERR/)
162
+
163
+ @logfile = "/not/writable/spec.log"
164
+ logger = ImprovedLogger.new(@logfile)
165
+ logger.level = :debug
166
+
167
+ $stderr.should_receive(:write).with(/test/)
168
+ logger.info "test"
169
+ end
170
+
171
+ it "should fallback to standard logger if syslogger gem is missing" do
172
+ syslogger_paths = $:.select { |p| p.match(/gems\/.*syslogger-/) }
173
+ $:.replace($: - syslogger_paths)
174
+
175
+ $stderr.should_receive(:write).with(/reverting to STDERR/)
176
+ logger = ImprovedLogger.new(:syslog)
177
+ logger.logger.should be_instance_of(Logger)
178
+
179
+ $:.replace($: + syslogger_paths)
180
+ end
181
+
182
+ context "should behave like write-only IO and" do
183
+ subject { logger }
184
+
185
+ it { should be_a IO }
186
+ its(:logger) { should_not be_nil }
187
+ its(:flush) { should == logger }
188
+ its(:set_encoding) { should == logger }
189
+ its(:sync) { should == true }
190
+ its(:tty?) { should == false }
191
+
192
+ it "should close on close_write" do
193
+ logger.should_receive(:close)
194
+ logger.close_write
195
+ end
196
+
197
+ it "should not implement closed?" do
198
+ expect { logger.closed? }.to raise_error(NotImplementedError)
199
+ end
200
+
201
+ it "should not implement sync=" do
202
+ expect { logger.sync = false }.to raise_error(NotImplementedError)
203
+ end
204
+
205
+ it "should implement readbyte, readchar, readline" do
206
+ {
207
+ :readbyte => :getbyte,
208
+ :readchar => :getc,
209
+ :readline => :gets,
210
+ }.each do |m, should|
211
+ logger.should_receive(should)
212
+ expect { logger.send(m) }.to raise_error(IOError)
213
+ end
214
+ end
215
+
216
+ [
217
+ :bytes,
218
+ :chars,
219
+ :codepoints,
220
+ :lines,
221
+ :eof?,
222
+ :getbyte,
223
+ :getc,
224
+ :gets,
225
+ :pos,
226
+ :pos=,
227
+ :read,
228
+ :readlines,
229
+ :readpartial,
230
+ :rewind,
231
+ :seek,
232
+ :ungetbyte,
233
+ :ungetc
234
+ ].each do |m|
235
+ it "should raise IOError for method #{m}" do
236
+ expect { logger.send(m) }.to raise_error(IOError)
237
+ end
238
+ end
239
+
240
+ context "print functions" do
241
+ subject { logger.messages }
242
+
243
+ it "should support printf" do
244
+ logger.printf("%.2f %s", 1.12345, "foo")
245
+ subject.last[:message].should == "1.12 foo"
246
+ end
247
+
248
+ it "should support print" do
249
+ $,, old = ' ', $,
250
+ logger.print("foo", "bar", 123, ["baz", 345])
251
+ subject.last[:message].should == "foo bar 123 baz 345"
252
+ $, = old
253
+ end
254
+
255
+ it "should support puts" do
256
+ logger.puts("a", "b")
257
+ subject.last[:message].should == "b"
258
+ logger.puts(["c", "d"])
259
+ subject.last[:message].should == "d"
260
+ logger.puts(1, 2, 3)
261
+ subject.last[:message].should == "3"
262
+ end
263
+ end
264
+ end
265
+
266
+ context "buffer backend" do
267
+ let(:logger) { ImprovedLogger.new(:buffer) }
268
+ subject { logger }
269
+
270
+ its(:sync) { should == false }
271
+
272
+ it "should support a buffered logger" do
273
+ logger.info "test"
274
+ logger.buffer.should match(/test/)
275
+ end
276
+ end
277
+
278
+ context "document backend" do
279
+ let(:logger) { ImprovedLogger.new(:document) }
280
+
281
+ before do
282
+ @msg = "test"
283
+
284
+ @now = Time.now
285
+ Time.stub(:now).and_return(@now)
286
+
287
+ @expected = {
288
+ severity: Logger::INFO,
289
+ time: @now.to_f,
290
+ progname: "rspec",
291
+ message: @msg
292
+ }
293
+ end
294
+
295
+ it "should store all messages as documents" do
296
+ logger.info(@msg)
297
+ logger.messages.first.should == @expected
298
+ end
299
+
300
+ it "should add custom attributes" do
301
+ attrs = {txid: 1234}
302
+ logger.logger.attrs = attrs
303
+ logger.info(@msg)
304
+ logger.messages.first.should == attrs.merge(@expected)
305
+ end
306
+
307
+ end
308
+
309
+ context "syslog backend" do
310
+ let(:logger) { ImprovedLogger.new(:syslog) }
311
+ subject { logger }
312
+ its(:sync) { should == true }
313
+ its(:logger) { should be_instance_of(Syslogger) }
314
+ end
315
+
316
+ context "unknown backend" do
317
+ it "should raise for unknown backends " do
318
+ expect { ImprovedLogger.new(:unknown_logger) }.to raise_error(RuntimeError)
319
+ end
320
+ end
321
+
322
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ include Liquid::Logging
4
+
5
+ describe MultiLogger do
6
+
7
+ before(:each) do
8
+ @logger = ImprovedLogger.new
9
+ @logger.level = :debug
10
+ @ml = MultiLogger.new(@logger)
11
+ end
12
+
13
+ it "should support attach/detach of loggers" do
14
+ buflog = ImprovedLogger.new(:buffer)
15
+ @ml.attach(buflog)
16
+
17
+ $stderr.should_receive(:write).with(/test1/)
18
+ @ml.info("test1")
19
+ buflog.buffer.should match(/test1/)
20
+
21
+ @ml.detach(buflog)
22
+
23
+ $stderr.should_receive(:write).with(/test2/)
24
+ @ml.info("test2")
25
+ buflog.buffer.should_not match(/test2/)
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+
4
+ require 'simplecov'
5
+ SimpleCov.start
6
+
7
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
8
+ require 'liquid-logging'
9
+
10
+ RSpec.configure do |config|
11
+ # == Mock Framework
12
+ #
13
+ # RSpec uses it's own mocking framework by default. If you prefer to
14
+ # use mocha, flexmock or RR, uncomment the appropriate line:
15
+ #
16
+ # config.mock_with :mocha
17
+ # config.mock_with :flexmock
18
+ # config.mock_with :rr
19
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liquid-logging
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - LiquidM, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Advanced logging classes with buffer backend, transactions, multi logger,
14
+ etc
15
+ email:
16
+ - opensource@liquidm.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rspec
23
+ - .travis.yml
24
+ - .yardopts
25
+ - Gemfile
26
+ - LICENSE
27
+ - README.md
28
+ - Rakefile
29
+ - lib/liquid-logging.rb
30
+ - lib/liquid/logging.rb
31
+ - lib/liquid/logging/airbrake.rb
32
+ - lib/liquid/logging/document_logger.rb
33
+ - lib/liquid/logging/improved_io.rb
34
+ - lib/liquid/logging/improved_logger.rb
35
+ - lib/liquid/logging/mask.reek
36
+ - lib/liquid/logging/multi_logger.rb
37
+ - liquid-logging.gemspec
38
+ - spec/improved_logger_spec.rb
39
+ - spec/multi_logger_spec.rb
40
+ - spec/spec_helper.rb
41
+ homepage: https://github.com/liquidm/logging
42
+ licenses: []
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.0.6
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Advanced logging classes with buffer backend, transactions, multi logger,
64
+ etc
65
+ test_files:
66
+ - spec/improved_logger_spec.rb
67
+ - spec/multi_logger_spec.rb
68
+ - spec/spec_helper.rb
69
+ has_rdoc: