semantic_logger 3.0.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/semantic_logger.rb +29 -13
- data/lib/semantic_logger/ansi_colors.rb +27 -0
- data/lib/semantic_logger/appender/base.rb +54 -128
- data/lib/semantic_logger/appender/bugsnag.rb +29 -19
- data/lib/semantic_logger/appender/elasticsearch.rb +9 -9
- data/lib/semantic_logger/appender/file.rb +40 -18
- data/lib/semantic_logger/appender/graylog.rb +30 -26
- data/lib/semantic_logger/appender/http.rb +14 -19
- data/lib/semantic_logger/appender/mongodb.rb +20 -20
- data/lib/semantic_logger/appender/new_relic.rb +15 -15
- data/lib/semantic_logger/appender/splunk.rb +1 -1
- data/lib/semantic_logger/appender/splunk_http.rb +28 -25
- data/lib/semantic_logger/appender/syslog.rb +41 -42
- data/lib/semantic_logger/appender/wrapper.rb +19 -17
- data/lib/semantic_logger/base.rb +57 -32
- data/lib/semantic_logger/concerns/compatibility.rb +51 -0
- data/lib/semantic_logger/debug_as_trace_logger.rb +6 -2
- data/lib/semantic_logger/formatters/color.rb +66 -0
- data/lib/semantic_logger/formatters/default.rb +39 -0
- data/lib/semantic_logger/formatters/json.rb +16 -0
- data/lib/semantic_logger/jruby/garbage_collection_logger.rb +1 -1
- data/lib/semantic_logger/log.rb +13 -8
- data/lib/semantic_logger/loggable.rb +2 -2
- data/lib/semantic_logger/logger.rb +18 -13
- data/lib/semantic_logger/metrics/new_relic.rb +18 -0
- data/lib/semantic_logger/metrics/statsd.rb +48 -0
- data/lib/semantic_logger/semantic_logger.rb +122 -55
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender/bugsnag_test.rb +12 -3
- data/test/appender/mongodb_test.rb +6 -5
- data/test/appender/new_relic_test.rb +1 -1
- data/test/appender/splunk_http_test.rb +1 -0
- data/test/concerns/compatibility_test.rb +106 -0
- data/test/debug_as_trace_logger_test.rb +1 -1
- data/test/loggable_test.rb +1 -1
- data/test/logger_test.rb +183 -24
- metadata +12 -3
@@ -5,27 +5,19 @@ require 'socket'
|
|
5
5
|
# Send log messages to local syslog, or remote syslog servers over TCP or UDP.
|
6
6
|
#
|
7
7
|
# Example: Log to a local Syslog daemon
|
8
|
-
# SemanticLogger.add_appender(
|
8
|
+
# SemanticLogger.add_appender(appender: :syslog)
|
9
9
|
#
|
10
10
|
# Example: Log to a remote Syslog server using TCP:
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# SemanticLogger.add_appender(
|
12
|
+
# appender: :syslog,
|
13
|
+
# url: 'tcp://myloghost:514'
|
13
14
|
# )
|
14
15
|
#
|
15
|
-
# # Optional: Add filter to exclude health_check, or other log entries
|
16
|
-
# appender.filter = Proc.new { |log| log.message !~ /(health_check|Not logged in)/ }
|
17
|
-
#
|
18
|
-
# SemanticLogger.add_appender(appender)
|
19
|
-
#
|
20
16
|
# Example: Log to a remote Syslog server using UDP:
|
21
|
-
#
|
22
|
-
#
|
17
|
+
# SemanticLogger.add_appender(
|
18
|
+
# appender: :syslog,
|
19
|
+
# url: 'udp://myloghost:514'
|
23
20
|
# )
|
24
|
-
#
|
25
|
-
# # Optional: Add filter to exclude health_check, or other log entries
|
26
|
-
# appender.filter = Proc.new { |log| log.message !~ /(health_check|Not logged in)/ }
|
27
|
-
#
|
28
|
-
# SemanticLogger.add_appender(appender)
|
29
21
|
module SemanticLogger
|
30
22
|
module Appender
|
31
23
|
class Syslog < SemanticLogger::Appender::Base
|
@@ -81,6 +73,11 @@ module SemanticLogger
|
|
81
73
|
# Override the log level for this appender.
|
82
74
|
# Default: SemanticLogger.default_level
|
83
75
|
#
|
76
|
+
# formatter: [Object|Proc]
|
77
|
+
# An instance of a class that implements #call, or a Proc to be used to format
|
78
|
+
# the output from this appender
|
79
|
+
# Default: Use the built-in formatter (See: #call)
|
80
|
+
#
|
84
81
|
# filter: [Regexp|Proc]
|
85
82
|
# RegExp: Only include log messages where the class name matches the supplied.
|
86
83
|
# regular expression. All other messages will be ignored.
|
@@ -143,10 +140,15 @@ module SemanticLogger
|
|
143
140
|
# debug: ::Syslog::LOG_INFO,
|
144
141
|
# trace: ::Syslog::LOG_DEBUG
|
145
142
|
# }
|
143
|
+
#
|
144
|
+
# format: [Symbol]
|
145
|
+
# Format for the Syslog message
|
146
|
+
# :syslog uses the default syslog format
|
147
|
+
# :json uses the CEE JSON Syslog format
|
148
|
+
# Example: "@cee: #{JSON.dump(data)}"
|
149
|
+
# Default: :syslog
|
146
150
|
def initialize(options = {}, &block)
|
147
151
|
options = options.dup
|
148
|
-
level = options.delete(:level)
|
149
|
-
filter = options.delete(:filter)
|
150
152
|
@application = options.delete(:application) || options.delete(:ident) || 'ruby'
|
151
153
|
@options = options.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
|
152
154
|
@facility = options.delete(:facility) || ::Syslog::LOG_USER
|
@@ -161,7 +163,6 @@ module SemanticLogger
|
|
161
163
|
@tcp_client_options = options.delete(:tcp_client)
|
162
164
|
|
163
165
|
raise "Unknown protocol #{@protocol}!" unless [:syslog, :tcp, :udp].include?(@protocol)
|
164
|
-
raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
|
165
166
|
|
166
167
|
@level_map = DEFAULT_LEVEL_MAP.dup
|
167
168
|
@level_map.update(level_map) if level_map
|
@@ -188,7 +189,7 @@ module SemanticLogger
|
|
188
189
|
|
189
190
|
reopen
|
190
191
|
|
191
|
-
super(
|
192
|
+
super(options, &block)
|
192
193
|
end
|
193
194
|
|
194
195
|
# After forking an active process call #reopen to re-open
|
@@ -235,35 +236,33 @@ module SemanticLogger
|
|
235
236
|
|
236
237
|
# Custom log formatter for syslog.
|
237
238
|
# Only difference is the removal of the timestamp string since it is in the syslog packet.
|
238
|
-
def
|
239
|
-
|
240
|
-
|
241
|
-
entry = "#{log.level_to_s} [#{log.process_info}]"
|
239
|
+
def call(log, logger)
|
240
|
+
# Header with date, time, log level and process info
|
241
|
+
message = "#{log.level_to_s} [#{log.process_info}]"
|
242
242
|
|
243
|
-
|
244
|
-
|
243
|
+
# Tags
|
244
|
+
message << ' ' << log.tags.collect { |tag| "[#{tag}]" }.join(' ') if log.tags && (log.tags.size > 0)
|
245
245
|
|
246
|
-
|
247
|
-
|
246
|
+
# Duration
|
247
|
+
message << " (#{log.duration_human})" if log.duration
|
248
248
|
|
249
|
-
|
250
|
-
|
249
|
+
# Class / app name
|
250
|
+
message << " #{log.name}"
|
251
251
|
|
252
|
-
|
253
|
-
|
252
|
+
# Log message
|
253
|
+
message << " -- #{log.message}" if log.message
|
254
254
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
255
|
+
# Payload
|
256
|
+
if payload = log.payload_to_s
|
257
|
+
message << ' -- ' << payload
|
258
|
+
end
|
259
259
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
end
|
265
|
-
entry
|
260
|
+
# Exceptions
|
261
|
+
if log.exception
|
262
|
+
message << " -- Exception: #{log.exception.class}: #{log.exception.message}\n"
|
263
|
+
message << log.backtrace_to_s
|
266
264
|
end
|
265
|
+
message
|
267
266
|
end
|
268
267
|
|
269
268
|
# Format the syslog packet so it can be sent over TCP or UDP
|
@@ -273,7 +272,7 @@ module SemanticLogger
|
|
273
272
|
packet.facility = @facility
|
274
273
|
packet.severity = @level_map[log.level]
|
275
274
|
packet.tag = @application
|
276
|
-
packet.content =
|
275
|
+
packet.content = formatter.call(log, self)
|
277
276
|
packet.time = log.time
|
278
277
|
packet.to_s
|
279
278
|
end
|
@@ -17,6 +17,11 @@ module SemanticLogger
|
|
17
17
|
# Override the log level for this appender.
|
18
18
|
# Default: SemanticLogger.default_level
|
19
19
|
#
|
20
|
+
# formatter: [Object|Proc]
|
21
|
+
# An instance of a class that implements #call, or a Proc to be used to format
|
22
|
+
# the output from this appender
|
23
|
+
# Default: Use the built-in formatter (See: #call)
|
24
|
+
#
|
20
25
|
# filter: [Regexp|Proc]
|
21
26
|
# RegExp: Only include log messages where the class name matches the supplied.
|
22
27
|
# regular expression. All other messages will be ignored.
|
@@ -28,27 +33,25 @@ module SemanticLogger
|
|
28
33
|
# require 'semantic_logger'
|
29
34
|
#
|
30
35
|
# ruby_logger = Logger.new(STDOUT)
|
31
|
-
# SemanticLogger.add_appender(ruby_logger)
|
36
|
+
# SemanticLogger.add_appender(logger: ruby_logger)
|
32
37
|
#
|
33
38
|
# logger = SemanticLogger['test']
|
34
39
|
# logger.info('Hello World', some: :payload)
|
35
40
|
#
|
36
|
-
# Enhance the Rails Logger
|
37
|
-
# # Add the Rails logger to the list of appenders
|
38
|
-
# SemanticLogger.add_appender(Rails.logger)
|
39
|
-
# Rails.logger = SemanticLogger['Rails']
|
40
|
-
#
|
41
|
-
# # Make ActiveRecord logging include its class name in every log entry
|
42
|
-
# ActiveRecord::Base.logger = SemanticLogger['ActiveRecord']
|
43
|
-
#
|
44
41
|
# Install the `rails_semantic_logger` gem to replace the Rails logger with Semantic Logger.
|
45
|
-
def initialize(
|
46
|
-
|
47
|
-
|
42
|
+
def initialize(options, &block)
|
43
|
+
# Backward compatibility
|
44
|
+
options = {logger: options} unless options.is_a?(Hash)
|
45
|
+
options = options.dup
|
46
|
+
@logger = options.delete(:logger)
|
47
|
+
|
48
|
+
# Check if the custom appender responds to all the log levels. For example Ruby ::Logger
|
49
|
+
if does_not_implement = LEVELS[1..-1].find { |i| !@logger.respond_to?(i) }
|
50
|
+
raise(ArgumentError, "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}")
|
51
|
+
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
super(level, filter, &block)
|
53
|
+
raise 'SemanticLogging::Appender::Wrapper missing mandatory parameter :logger' unless @logger
|
54
|
+
super(options, &block)
|
52
55
|
end
|
53
56
|
|
54
57
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
@@ -58,8 +61,7 @@ module SemanticLogger
|
|
58
61
|
# Ensure minimum log level is met, and check filter
|
59
62
|
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
60
63
|
|
61
|
-
|
62
|
-
@logger.send(log.level == :trace ? :debug : log.level, @formatter.call(log, self))
|
64
|
+
@logger.send(log.level == :trace ? :debug : log.level, formatter.call(log, self))
|
63
65
|
true
|
64
66
|
end
|
65
67
|
|
data/lib/semantic_logger/base.rb
CHANGED
@@ -21,7 +21,7 @@ module SemanticLogger
|
|
21
21
|
# nil if this logger instance should use the global default level
|
22
22
|
def level=(level)
|
23
23
|
@level_index = SemanticLogger.level_to_index(level)
|
24
|
-
@level =
|
24
|
+
@level = SemanticLogger.send(:index_to_level, @level_index)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Returns the current log level if set, otherwise it returns the global
|
@@ -59,10 +59,10 @@ module SemanticLogger
|
|
59
59
|
# SemanticLogger.default_level = :info
|
60
60
|
#
|
61
61
|
# # Log to screen
|
62
|
-
# SemanticLogger.add_appender(STDOUT)
|
62
|
+
# SemanticLogger.add_appender(io: STDOUT, formatter: :color)
|
63
63
|
#
|
64
64
|
# # And log to a file at the same time
|
65
|
-
# SemanticLogger.add_appender('application.log')
|
65
|
+
# SemanticLogger.add_appender(file: 'application.log', formatter: :color)
|
66
66
|
#
|
67
67
|
# logger = SemanticLogger['MyApplication']
|
68
68
|
# logger.debug("Only display this if log level is set to Debug or lower")
|
@@ -88,9 +88,17 @@ module SemanticLogger
|
|
88
88
|
level_index <= #{index}
|
89
89
|
end
|
90
90
|
|
91
|
+
def measure_#{level}(message, params = {}, &block)
|
92
|
+
if level_index <= #{index}
|
93
|
+
measure_internal(:#{level}, #{index}, message, params, &block)
|
94
|
+
else
|
95
|
+
block.call(params) if block
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
91
99
|
def benchmark_#{level}(message, params = {}, &block)
|
92
100
|
if level_index <= #{index}
|
93
|
-
|
101
|
+
measure_internal(:#{level}, #{index}, message, params, &block)
|
94
102
|
else
|
95
103
|
block.call(params) if block
|
96
104
|
end
|
@@ -98,16 +106,18 @@ module SemanticLogger
|
|
98
106
|
EOT
|
99
107
|
end
|
100
108
|
|
101
|
-
# Dynamically supply the log level with every
|
102
|
-
def
|
109
|
+
# Dynamically supply the log level with every measurement call
|
110
|
+
def measure(level, message, params = {}, &block)
|
103
111
|
index = SemanticLogger.level_to_index(level)
|
104
112
|
if level_index <= index
|
105
|
-
|
113
|
+
measure_internal(level, index, message, params, &block)
|
106
114
|
else
|
107
115
|
block.call(params) if block
|
108
116
|
end
|
109
117
|
end
|
110
118
|
|
119
|
+
alias_method :benchmark, :measure
|
120
|
+
|
111
121
|
# If the tag being supplied is definitely a string then this fast
|
112
122
|
# tag api can be used for short lived tags
|
113
123
|
def fast_tag(tag)
|
@@ -184,12 +194,6 @@ module SemanticLogger
|
|
184
194
|
Thread.current[:semantic_logger_payload]
|
185
195
|
end
|
186
196
|
|
187
|
-
# Semantic Logging does not support :unknown level since these
|
188
|
-
# are not understood by the majority of the logging providers
|
189
|
-
# Map it to :error
|
190
|
-
alias :unknown :error
|
191
|
-
alias :unknown? :error?
|
192
|
-
|
193
197
|
# Silence noisy log levels by changing the default_level within the block
|
194
198
|
#
|
195
199
|
# This setting is thread-safe and only applies to the current thread
|
@@ -246,6 +250,13 @@ module SemanticLogger
|
|
246
250
|
SemanticLogger.default_level
|
247
251
|
end
|
248
252
|
|
253
|
+
protected
|
254
|
+
|
255
|
+
# Write log data to underlying data storage
|
256
|
+
def log(log_)
|
257
|
+
raise NotImplementedError.new('Logging Appender must implement #log(log)')
|
258
|
+
end
|
259
|
+
|
249
260
|
private
|
250
261
|
|
251
262
|
# Initializer for Abstract Class SemanticLogger::Base
|
@@ -275,11 +286,6 @@ module SemanticLogger
|
|
275
286
|
self.level = level unless level.nil?
|
276
287
|
end
|
277
288
|
|
278
|
-
# Write log data to underlying data storage
|
279
|
-
def log(log_)
|
280
|
-
raise NotImplementedError.new('Logging Appender must implement #log(log)')
|
281
|
-
end
|
282
|
-
|
283
289
|
# Return the level index for fast comparisons
|
284
290
|
# Returns the global default level index if the level has not been explicitly
|
285
291
|
# set for this instance
|
@@ -288,25 +294,25 @@ module SemanticLogger
|
|
288
294
|
end
|
289
295
|
|
290
296
|
# Whether to log the supplied message based on the current filter if any
|
291
|
-
def include_message?(
|
297
|
+
def include_message?(log)
|
292
298
|
return true if @filter.nil?
|
293
299
|
|
294
300
|
if @filter.is_a?(Regexp)
|
295
|
-
(@filter =~
|
301
|
+
(@filter =~ log.name) != nil
|
296
302
|
elsif @filter.is_a?(Proc)
|
297
|
-
@filter.call(
|
303
|
+
@filter.call(log) == true
|
298
304
|
end
|
299
305
|
end
|
300
306
|
|
301
307
|
# Log message at the specified level
|
302
308
|
def log_internal(level, index, message=nil, payload=nil, exception=nil)
|
303
|
-
#
|
304
|
-
|
309
|
+
# Exception being logged?
|
310
|
+
# Under JRuby a java exception is not a Ruby Exception
|
311
|
+
# Java::JavaLang::ClassCastException.new.is_a?(Exception) => false
|
312
|
+
if exception.nil? && payload.nil? && message.respond_to?(:backtrace) && message.respond_to?(:message)
|
305
313
|
exception = message
|
306
314
|
message = nil
|
307
315
|
elsif exception.nil? && payload && payload.respond_to?(:backtrace) && payload.respond_to?(:message)
|
308
|
-
# Under JRuby a java exception is not a Ruby Exception
|
309
|
-
# Java::JavaLang::ClassCastException.new.is_a?(Exception) => false
|
310
316
|
exception = payload
|
311
317
|
payload = nil
|
312
318
|
end
|
@@ -315,6 +321,8 @@ module SemanticLogger
|
|
315
321
|
if block_given? && (result = yield)
|
316
322
|
if result.is_a?(String)
|
317
323
|
message = message.nil? ? result : "#{message} -- #{result}"
|
324
|
+
elsif message.nil? && result.is_a?(Hash)
|
325
|
+
message = result
|
318
326
|
elsif payload && payload.respond_to?(:merge)
|
319
327
|
payload.merge(result)
|
320
328
|
else
|
@@ -330,8 +338,25 @@ module SemanticLogger
|
|
330
338
|
# Add caller stack trace
|
331
339
|
backtrace = extract_backtrace if index >= SemanticLogger.backtrace_level_index
|
332
340
|
|
333
|
-
|
334
|
-
|
341
|
+
log = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception, nil, backtrace)
|
342
|
+
|
343
|
+
# Logging Hash only?
|
344
|
+
# logger.info(name: 'value')
|
345
|
+
if payload.nil? && exception.nil? && message.is_a?(Hash)
|
346
|
+
payload = message.dup
|
347
|
+
min_duration = payload.delete(:min_duration) || 0.0
|
348
|
+
log.exception = payload.delete(:exception)
|
349
|
+
log.message = payload.delete(:message)
|
350
|
+
log.metric = payload.delete(:metric)
|
351
|
+
log.metric_amount = payload.delete(:metric_amount) || 1
|
352
|
+
if duration = payload.delete(:duration)
|
353
|
+
return false if duration <= min_duration
|
354
|
+
log.duration = duration
|
355
|
+
end
|
356
|
+
log.payload = payload if payload.size > 0
|
357
|
+
end
|
358
|
+
|
359
|
+
self.log(log) if include_message?(log)
|
335
360
|
end
|
336
361
|
|
337
362
|
SELF_PATTERN = File.join('lib', 'semantic_logger')
|
@@ -346,7 +371,7 @@ module SemanticLogger
|
|
346
371
|
end
|
347
372
|
|
348
373
|
# Measure the supplied block and log the message
|
349
|
-
def
|
374
|
+
def measure_internal(level, index, message, params)
|
350
375
|
start = Time.now
|
351
376
|
exception = nil
|
352
377
|
begin
|
@@ -407,16 +432,16 @@ module SemanticLogger
|
|
407
432
|
logged_exception = nil
|
408
433
|
backtrace = exception.backtrace
|
409
434
|
end
|
410
|
-
|
411
|
-
log(
|
435
|
+
log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, logged_exception, metric, backtrace)
|
436
|
+
self.log(log) if include_message?(log)
|
412
437
|
raise exception
|
413
438
|
elsif duration >= min_duration
|
414
439
|
# Only log if the block took longer than 'min_duration' to complete
|
415
440
|
# Add caller stack trace
|
416
441
|
backtrace = extract_backtrace if index >= SemanticLogger.backtrace_level_index
|
417
442
|
|
418
|
-
|
419
|
-
log(
|
443
|
+
log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric, backtrace)
|
444
|
+
self.log(log) if include_message?(log)
|
420
445
|
end
|
421
446
|
end
|
422
447
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# :nodoc:
|
2
|
+
module SemanticLogger
|
3
|
+
# :nodoc:
|
4
|
+
module Concerns
|
5
|
+
# :nodoc:
|
6
|
+
module Compatibility
|
7
|
+
#
|
8
|
+
# For compatibility with Ruby Logger only.
|
9
|
+
#
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
# Map :unknown to :error
|
13
|
+
alias_method :unknown, :error # :nodoc:
|
14
|
+
alias_method :unknown?, :error? # :nodoc:
|
15
|
+
|
16
|
+
alias_method :<<, :info # :nodoc:
|
17
|
+
|
18
|
+
alias_method :progname, :name # :nodoc:
|
19
|
+
alias_method :progname=, :name= # :nodoc:
|
20
|
+
|
21
|
+
alias_method :sev_threshold, :level # :nodoc:
|
22
|
+
alias_method :sev_threshold=, :level= # :nodoc:
|
23
|
+
|
24
|
+
attr_accessor :formatter # :nodoc:
|
25
|
+
attr_accessor :datetime_format # :nodoc:
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nodoc:
|
30
|
+
def close
|
31
|
+
end
|
32
|
+
|
33
|
+
# :nodoc:
|
34
|
+
def reopen(logdev = nil)
|
35
|
+
end
|
36
|
+
|
37
|
+
# :nodoc:
|
38
|
+
def add(severity, message = nil, progname = nil, &block)
|
39
|
+
index = SemanticLogger.send(:level_to_index, severity)
|
40
|
+
if level_index <= index
|
41
|
+
level = SemanticLogger.send(:index_to_level, index)
|
42
|
+
log_internal(level, index, message, progname, &block)
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|