semantic_logger 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|