madvertise-logging 1.2.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - ruby-head
6
+ - jruby-head
@@ -0,0 +1 @@
1
+ --no-private
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'syslogger', :require => false
6
+
7
+ group :development, :test do
8
+ gem 'rake'
9
+ gem 'reek'
10
+ gem 'rspec'
11
+ gem 'simplecov'
12
+ gem 'yard'
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 madvertise Mobile Advertising GmbH
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ # Madvertise::Logging
2
+
3
+ The madvertise-logging gem is a collection of classes for logging related
4
+ tasks. 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/madvertise/logging.png)](http://travis-ci.org/madvertise/logging)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'madvertise-logging'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install madvertise-logging
22
+
23
+ ## Usage
24
+
25
+ Please refer to the [API documentation](http://rubydoc.info/gems/madvertise-logging/frames).
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create new Pull Request
@@ -0,0 +1,3 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ Dir['tasks/**/*.rake'].each { |t| load t }
@@ -0,0 +1 @@
1
+ require 'madvertise/logging'
@@ -0,0 +1,5 @@
1
+ require 'madvertise/logging/improved_logger'
2
+ require 'madvertise/logging/multi_logger'
3
+
4
+ ImprovedLogger = Madvertise::Logging::ImprovedLogger
5
+ MultiLogger = Madvertise::Logging::MultiLogger
@@ -0,0 +1,21 @@
1
+ require 'airbrake'
2
+
3
+ module Madvertise
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 Madvertise
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 Madvertise
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,457 @@
1
+ require 'logger'
2
+ require 'stringio'
3
+ require 'benchmark'
4
+
5
+ require 'madvertise/logging/improved_io'
6
+ require 'madvertise/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 Madvertise
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 madvertise-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)\/madvertise-logging/
231
+ end
232
+ end
233
+
234
+ private
235
+
236
+ # Return the first callee outside the madvertise-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_backend
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
+ case RUBY_PLATFORM
319
+ when 'java'
320
+ create_log4j_logger
321
+ else
322
+ create_ruby_logger(@logfile)
323
+ end
324
+ end
325
+
326
+ def create_log4j_logger
327
+ begin
328
+ require 'log4j'
329
+ require 'log4jruby'
330
+ Log4jruby::Logger.get($0).tap do |logger|
331
+ @backend = :log4j
332
+ configure_log4j(logger)
333
+ end
334
+ rescue LoadError
335
+ self.logger = :ruby
336
+ error("Couldn't load log4jruby gem, falling back to pure ruby Logger")
337
+ end
338
+ end
339
+
340
+ def configure_log4j(logger)
341
+ return unless @backend == :log4j
342
+
343
+ @console = org.apache.log4j.ConsoleAppender.new
344
+ @console.setLayout(org.apache.log4j.PatternLayout.new(Formatter.log4j_format))
345
+ @console.setThreshold(org.apache.log4j.Level.const_get(self.class.severities.key(logger.level).to_s.upcase.to_sym))
346
+ @console.activateOptions
347
+
348
+ org.apache.log4j.Logger.getRootLogger.tap do |root|
349
+ root.getLoggerRepository.resetConfiguration
350
+ root.addAppender(@console)
351
+ end
352
+ end
353
+
354
+ def create_ruby_logger(io)
355
+ Logger.new(io).tap do |logger|
356
+ logger.formatter = Formatter.new
357
+ logger.progname = progname
358
+ end
359
+ end
360
+
361
+ ##
362
+ # The Formatter class is responsible for formatting log messages. The
363
+ # default format is:
364
+ #
365
+ # YYYY:MM:DD HH:MM:SS.MS daemon_name(pid) level: message
366
+ #
367
+ class Formatter
368
+
369
+ @format = "%{time} %{progname}(%{pid}) [%{severity}] %{msg}\n"
370
+ @log4j_format = "%d %c(%t) [%p] %m%n"
371
+ @time_format = "%Y-%m-%d %H:%M:%S.%N"
372
+
373
+ class << self
374
+ # Format string for log messages.
375
+ attr_accessor :format
376
+ attr_accessor :log4j_format
377
+
378
+ # Format string for timestamps in log messages.
379
+ attr_accessor :time_format
380
+ end
381
+
382
+ RUBY2SYSLOG = {
383
+ :debug => 7,
384
+ :info => 6,
385
+ :warn => 4,
386
+ :error => 3,
387
+ :fatal => 2,
388
+ :unknown => 3,
389
+ }
390
+
391
+ # @private
392
+ def call(severity, time, progname, msg)
393
+ self.class.format % {
394
+ :time => time.strftime(self.class.time_format),
395
+ :progname => progname,
396
+ :pid => $$,
397
+ :severity => severity,
398
+ :syslog_severity => RUBY2SYSLOG[severity.downcase.to_sym],
399
+ :msg => msg.to_s,
400
+ }
401
+ end
402
+ end
403
+
404
+ # @private
405
+ module IOCompat
406
+ def close_read
407
+ nil
408
+ end
409
+
410
+ def close_write
411
+ close
412
+ end
413
+
414
+ def closed?
415
+ raise NotImplementedError
416
+ end
417
+
418
+ def sync
419
+ @backend != :buffer
420
+ end
421
+
422
+ def sync=(value)
423
+ raise NotImplementedError, "#{self} cannot change sync mode"
424
+ end
425
+
426
+ # ImprovedLogger is write-only
427
+ def _raise_write_only
428
+ raise IOError, "#{self} is a buffer-less, write-only, non-seekable stream."
429
+ end
430
+
431
+ [
432
+ :bytes,
433
+ :chars,
434
+ :codepoints,
435
+ :lines,
436
+ :eof?,
437
+ :getbyte,
438
+ :getc,
439
+ :gets,
440
+ :pos,
441
+ :pos=,
442
+ :read,
443
+ :readlines,
444
+ :readpartial,
445
+ :rewind,
446
+ :seek,
447
+ :ungetbyte,
448
+ :ungetc
449
+ ].each do |meth|
450
+ alias_method meth, :_raise_write_only
451
+ end
452
+ end
453
+
454
+ include IOCompat
455
+ end
456
+ end
457
+ end