semantic_logger 4.5.0 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +7 -7
  4. data/lib/semantic_logger.rb +23 -22
  5. data/lib/semantic_logger/ansi_colors.rb +0 -10
  6. data/lib/semantic_logger/appender.rb +54 -64
  7. data/lib/semantic_logger/appender/async.rb +10 -8
  8. data/lib/semantic_logger/appender/async_batch.rb +4 -2
  9. data/lib/semantic_logger/appender/bugsnag.rb +7 -7
  10. data/lib/semantic_logger/appender/elasticsearch.rb +12 -11
  11. data/lib/semantic_logger/appender/elasticsearch_http.rb +4 -4
  12. data/lib/semantic_logger/appender/file.rb +2 -1
  13. data/lib/semantic_logger/appender/graylog.rb +15 -10
  14. data/lib/semantic_logger/appender/honeybadger.rb +3 -3
  15. data/lib/semantic_logger/appender/http.rb +20 -18
  16. data/lib/semantic_logger/appender/kafka.rb +5 -5
  17. data/lib/semantic_logger/appender/mongodb.rb +6 -6
  18. data/lib/semantic_logger/appender/new_relic.rb +2 -2
  19. data/lib/semantic_logger/appender/rabbitmq.rb +5 -5
  20. data/lib/semantic_logger/appender/sentry.rb +7 -7
  21. data/lib/semantic_logger/appender/splunk.rb +6 -5
  22. data/lib/semantic_logger/appender/splunk_http.rb +3 -3
  23. data/lib/semantic_logger/appender/syslog.rb +12 -12
  24. data/lib/semantic_logger/appender/tcp.rb +9 -9
  25. data/lib/semantic_logger/appender/udp.rb +2 -2
  26. data/lib/semantic_logger/appenders.rb +13 -34
  27. data/lib/semantic_logger/base.rb +43 -31
  28. data/lib/semantic_logger/formatters.rb +11 -11
  29. data/lib/semantic_logger/formatters/base.rb +15 -6
  30. data/lib/semantic_logger/formatters/color.rb +12 -13
  31. data/lib/semantic_logger/formatters/default.rb +18 -5
  32. data/lib/semantic_logger/formatters/fluentd.rb +7 -18
  33. data/lib/semantic_logger/formatters/json.rb +3 -5
  34. data/lib/semantic_logger/formatters/raw.rb +39 -10
  35. data/lib/semantic_logger/formatters/signalfx.rb +14 -21
  36. data/lib/semantic_logger/formatters/syslog.rb +3 -3
  37. data/lib/semantic_logger/formatters/syslog_cee.rb +3 -3
  38. data/lib/semantic_logger/jruby/garbage_collection_logger.rb +4 -2
  39. data/lib/semantic_logger/levels.rb +9 -7
  40. data/lib/semantic_logger/log.rb +49 -73
  41. data/lib/semantic_logger/logger.rb +6 -8
  42. data/lib/semantic_logger/metric/new_relic.rb +3 -3
  43. data/lib/semantic_logger/metric/signalfx.rb +3 -3
  44. data/lib/semantic_logger/metric/statsd.rb +7 -7
  45. data/lib/semantic_logger/processor.rb +7 -5
  46. data/lib/semantic_logger/reporters/minitest.rb +4 -4
  47. data/lib/semantic_logger/semantic_logger.rb +37 -12
  48. data/lib/semantic_logger/subscriber.rb +14 -7
  49. data/lib/semantic_logger/sync.rb +12 -0
  50. data/lib/semantic_logger/sync_processor.rb +43 -0
  51. data/lib/semantic_logger/utils.rb +6 -6
  52. data/lib/semantic_logger/version.rb +1 -1
  53. metadata +5 -3
@@ -1,4 +1,4 @@
1
- require 'socket'
1
+ require "socket"
2
2
  module SemanticLogger
3
3
  module Appender
4
4
  # UDP log appender.
@@ -74,7 +74,7 @@ module SemanticLogger
74
74
  def reopen
75
75
  close
76
76
  @socket = UDPSocket.new
77
- host, port = server.split(':')
77
+ host, port = server.split(":")
78
78
  @socket.connect(host, port.to_i)
79
79
  end
80
80
 
@@ -8,9 +8,8 @@ module SemanticLogger
8
8
  @logger.name = self.class.name
9
9
  end
10
10
 
11
- def add(options, deprecated_level = nil, &block)
12
- options = options.is_a?(Hash) ? options.dup : convert_old_appender_args(options, deprecated_level)
13
- appender = SemanticLogger::Appender.factory(options, &block)
11
+ def add(**args, &block)
12
+ appender = SemanticLogger::Appender.factory(**args, &block)
14
13
  self << appender
15
14
  appender
16
15
  end
@@ -19,8 +18,8 @@ module SemanticLogger
19
18
  each do |appender|
20
19
  begin
21
20
  appender.log(log) if appender.should_log?(log)
22
- rescue Exception => exc
23
- logger.error "Failed to log to appender: #{appender.inspect}", exc
21
+ rescue Exception => e
22
+ logger.error "Failed to log to appender: #{appender.name}", e
24
23
  end
25
24
  end
26
25
  end
@@ -30,11 +29,11 @@ module SemanticLogger
30
29
  begin
31
30
  logger.trace "Flushing appender: #{appender.name}"
32
31
  appender.flush
33
- rescue Exception => exc
34
- logger.error "Failed to flush appender: #{appender.inspect}", exc
32
+ rescue Exception => e
33
+ logger.error "Failed to flush appender: #{appender.name}", e
35
34
  end
36
35
  end
37
- logger.trace 'All appenders flushed'
36
+ logger.trace "All appenders flushed"
38
37
  end
39
38
 
40
39
  def close
@@ -44,11 +43,11 @@ module SemanticLogger
44
43
  appender.flush
45
44
  appender.close
46
45
  delete(appender)
47
- rescue Exception => exc
48
- logger.error "Failed to close appender: #{appender.inspect}", exc
46
+ rescue Exception => e
47
+ logger.error "Failed to close appender: #{appender.name}", e
49
48
  end
50
49
  end
51
- logger.trace 'All appenders closed and removed from appender list'
50
+ logger.trace "All appenders closed and removed from appender list"
52
51
  end
53
52
 
54
53
  # After a fork the appender thread is not running, start it if it is not running.
@@ -59,31 +58,11 @@ module SemanticLogger
59
58
 
60
59
  logger.trace "Reopening appender: #{appender.name}"
61
60
  appender.reopen
62
- rescue Exception => exc
63
- logger.error "Failed to re-open appender: #{appender.inspect}", exc
61
+ rescue Exception => e
62
+ logger.error "Failed to re-open appender: #{appender.name}", e
64
63
  end
65
64
  end
66
- logger.trace 'All appenders re-opened'
67
- end
68
-
69
- private
70
-
71
- # Backward compatibility
72
- def convert_old_appender_args(appender, level)
73
- options = {}
74
- options[:level] = level if level
75
-
76
- if appender.is_a?(String)
77
- options[:file_name] = appender
78
- elsif appender.is_a?(IO)
79
- options[:io] = appender
80
- elsif appender.is_a?(Symbol) || appender.is_a?(Subscriber)
81
- options[:appender] = appender
82
- else
83
- options[:logger] = appender
84
- end
85
- warn "[DEPRECATED] SemanticLogger.add_appender parameters have changed. Please use: #{options.inspect}"
86
- options
65
+ logger.trace "All appenders re-opened"
87
66
  end
88
67
  end
89
68
  end
@@ -126,7 +126,7 @@ module SemanticLogger
126
126
  # Log a thread backtrace
127
127
  def backtrace(thread: Thread.current,
128
128
  level: :warn,
129
- message: 'Backtrace:',
129
+ message: "Backtrace:",
130
130
  payload: nil,
131
131
  metric: nil,
132
132
  metric_amount: nil)
@@ -190,7 +190,8 @@ module SemanticLogger
190
190
  # Allow named tags to be passed into the logger
191
191
  if tags.size == 1
192
192
  tag = tags[0]
193
- return yield if tag.nil? || tag == ''
193
+ return yield if tag.nil? || tag == ""
194
+
194
195
  return tag.is_a?(Hash) ? SemanticLogger.named_tagged(tag, &block) : SemanticLogger.fast_tag(tag.to_s, &block)
195
196
  end
196
197
 
@@ -233,26 +234,14 @@ module SemanticLogger
233
234
  SemanticLogger.silence(new_level, &block)
234
235
  end
235
236
 
236
- # Deprecated. Use `SemanticLogger.tagged`
237
+ # :nodoc:
237
238
  def fast_tag(tag, &block)
238
239
  SemanticLogger.fast_tag(tag, &block)
239
240
  end
240
241
 
241
- # :nodoc:
242
- def with_payload(payload, &block)
243
- warn '#with_payload is deprecated, use SemanticLogger.named_tagged'
244
- SemanticLogger.named_tagged(payload, &block)
245
- end
246
-
247
- # :nodoc:
248
- def payload
249
- warn '#payload is deprecated, use SemanticLogger.named_tags'
250
- SemanticLogger.named_tags
251
- end
252
-
253
242
  # Write log data to underlying data storage
254
243
  def log(_log_)
255
- raise NotImplementedError, 'Logging Appender must implement #log(log)'
244
+ raise NotImplementedError, "Logging Appender must implement #log(log)"
256
245
  end
257
246
 
258
247
  # Whether this log entry meets the criteria to be logged by this appender.
@@ -281,7 +270,7 @@ module SemanticLogger
281
270
  # The Proc must return true or false
282
271
  def initialize(klass, level = nil, filter = nil)
283
272
  # Support filtering all messages to this logger using a Regular Expression or Proc
284
- raise ':filter must be a Regexp or Proc' unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
273
+ raise ":filter must be a Regexp or Proc" unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
285
274
 
286
275
  @filter = filter.is_a?(Regexp) ? filter.freeze : filter
287
276
  @name = klass.is_a?(String) ? klass : klass.name
@@ -314,20 +303,42 @@ module SemanticLogger
314
303
  end
315
304
 
316
305
  # Log message at the specified level
317
- def log_internal(level, index, message = nil, payload = nil, exception = nil, &block)
318
- log = Log.new(name, level, index)
306
+ def log_internal(level, index, message = nil, payload = nil, exception = nil)
307
+ # Handle variable number of arguments by detecting exception object and payload hash.
308
+ if exception.nil? && payload.nil? && message.respond_to?(:backtrace) && message.respond_to?(:message)
309
+ exception = message
310
+ message = nil
311
+ elsif exception.nil? && payload && payload.respond_to?(:backtrace) && payload.respond_to?(:message)
312
+ exception = payload
313
+ payload = nil
314
+ elsif payload && !payload.is_a?(Hash)
315
+ message = message.nil? ? payload : "#{message} -- #{payload}"
316
+ payload = nil
317
+ end
318
+
319
+ log = Log.new(name, level, index)
319
320
  should_log =
320
321
  if payload.nil? && exception.nil? && message.is_a?(Hash)
321
- # Check if someone just logged a hash payload instead of meaning to call semantic logger
322
- if message.key?(:message) || message.key?(:payload) || message.key?(:exception) || message.key?(:metric)
323
- log.assign(message)
324
- else
325
- log.assign_positional(nil, message, nil, &block)
326
- end
322
+ # Everything as keyword arguments.
323
+ log.assign(**log.extract_arguments(message))
324
+ elsif exception.nil? && message && payload && payload.is_a?(Hash)
325
+ # Message with keyword arguments as the rest.
326
+ log.assign(message: message, **log.extract_arguments(payload))
327
327
  else
328
- log.assign_positional(message, payload, exception, &block)
328
+ # No keyword arguments.
329
+ log.assign(message: message, payload: payload, exception: exception)
329
330
  end
330
331
 
332
+ # Add result of block to message or payload if not nil
333
+ if block_given?
334
+ result = yield(log)
335
+ if result.is_a?(String)
336
+ log.message = log.message.nil? ? result : "#{log.message} -- #{result}"
337
+ elsif result.is_a?(Hash)
338
+ log.assign_hash(result)
339
+ end
340
+ end
341
+
331
342
  # Log level may change during assign due to :on_exception_level
332
343
  self.log(log) if should_log && should_log?(log)
333
344
  end
@@ -353,8 +364,8 @@ module SemanticLogger
353
364
  yield(params)
354
365
  end
355
366
  end
356
- rescue Exception => exc
357
- exception = exc
367
+ rescue Exception => e
368
+ exception = e
358
369
  ensure
359
370
  # Must use ensure block otherwise a `return` in the yield above will skip the log entry
360
371
  log = Log.new(name, level, index)
@@ -364,7 +375,7 @@ module SemanticLogger
364
375
  if block_given?
365
376
  1_000.0 * (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
366
377
  else
367
- params[:duration] || raise('Mandatory block missing when :duration option is not supplied')
378
+ params[:duration] || raise("Mandatory block missing when :duration option is not supplied")
368
379
  end
369
380
 
370
381
  # Extract options after block completes so that block can modify any of the options
@@ -386,6 +397,7 @@ module SemanticLogger
386
397
  # Log level may change during assign due to :on_exception_level
387
398
  self.log(log) if should_log && should_log?(log)
388
399
  raise exception if exception
400
+
389
401
  result
390
402
  end
391
403
  end
@@ -404,8 +416,8 @@ module SemanticLogger
404
416
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
405
417
  begin
406
418
  yield
407
- rescue Exception => exc
408
- exception = exc
419
+ rescue Exception => e
420
+ exception = e
409
421
  ensure
410
422
  log = Log.new(name, level, index)
411
423
  # May return false due to elastic logging
@@ -1,15 +1,15 @@
1
1
  module SemanticLogger
2
2
  module Formatters
3
3
  # @formatter:off
4
- autoload :Base, 'semantic_logger/formatters/base'
5
- autoload :Color, 'semantic_logger/formatters/color'
6
- autoload :Default, 'semantic_logger/formatters/default'
7
- autoload :Json, 'semantic_logger/formatters/json'
8
- autoload :Raw, 'semantic_logger/formatters/raw'
9
- autoload :OneLine, 'semantic_logger/formatters/one_line'
10
- autoload :Signalfx, 'semantic_logger/formatters/signalfx'
11
- autoload :Syslog, 'semantic_logger/formatters/syslog'
12
- autoload :Fluentd, 'semantic_logger/formatters/fluentd'
4
+ autoload :Base, "semantic_logger/formatters/base"
5
+ autoload :Color, "semantic_logger/formatters/color"
6
+ autoload :Default, "semantic_logger/formatters/default"
7
+ autoload :Json, "semantic_logger/formatters/json"
8
+ autoload :Raw, "semantic_logger/formatters/raw"
9
+ autoload :OneLine, "semantic_logger/formatters/one_line"
10
+ autoload :Signalfx, "semantic_logger/formatters/signalfx"
11
+ autoload :Syslog, "semantic_logger/formatters/syslog"
12
+ autoload :Fluentd, "semantic_logger/formatters/fluentd"
13
13
  # @formatter:on
14
14
 
15
15
  # Return formatter that responds to call.
@@ -22,10 +22,10 @@ module SemanticLogger
22
22
  # - Any object that responds to :call
23
23
  def self.factory(formatter)
24
24
  if formatter.is_a?(Symbol)
25
- SemanticLogger::Utils.constantize_symbol(formatter, 'SemanticLogger::Formatters').new
25
+ SemanticLogger::Utils.constantize_symbol(formatter, "SemanticLogger::Formatters").new
26
26
  elsif formatter.is_a?(Hash) && formatter.size.positive?
27
27
  fmt, options = formatter.first
28
- SemanticLogger::Utils.constantize_symbol(fmt.to_sym, 'SemanticLogger::Formatters').new(options)
28
+ SemanticLogger::Utils.constantize_symbol(fmt.to_sym, "SemanticLogger::Formatters").new(**options)
29
29
  elsif formatter.respond_to?(:call)
30
30
  formatter
31
31
  else
@@ -1,12 +1,12 @@
1
- require 'time'
1
+ require "time"
2
2
  module SemanticLogger
3
3
  module Formatters
4
4
  class Base
5
- attr_accessor :time_format, :log_host, :log_application, :precision
5
+ attr_accessor :log, :logger, :time_format, :log_host, :log_application, :log_environment, :precision
6
6
 
7
7
  # Time precision varies by Ruby interpreter
8
8
  # JRuby 9.1.8.0 supports microseconds
9
- PRECISION =
9
+ PRECISION =
10
10
  if defined?(JRuby)
11
11
  if JRUBY_VERSION.to_f >= 9.1
12
12
  maint = JRUBY_VERSION.match(/\A\d+\.\d+\.(\d+)\./)[1].to_i
@@ -34,11 +34,15 @@ module SemanticLogger
34
34
  # precision: [Integer]
35
35
  # How many fractional digits to log times with.
36
36
  # Default: PRECISION (6, except on older JRuby, where 3)
37
- def initialize(time_format: nil, log_host: true, log_application: true,
37
+ def initialize(time_format: nil,
38
+ log_host: true,
39
+ log_application: true,
40
+ log_environment: true,
38
41
  precision: PRECISION)
39
42
  @time_format = time_format || self.class.build_time_format(precision)
40
43
  @log_host = log_host
41
44
  @log_application = log_application
45
+ @log_environment = log_environment
42
46
  @precision = precision
43
47
  end
44
48
 
@@ -48,7 +52,7 @@ module SemanticLogger
48
52
  # precision: [Integer]
49
53
  # How many fractional digits to log times with.
50
54
  # Default: PRECISION (6, except on older JRuby, where 3)
51
- def self.build_time_format(precision=PRECISION)
55
+ def self.build_time_format(precision = PRECISION)
52
56
  "%Y-%m-%d %H:%M:%S.%#{precision}N"
53
57
  end
54
58
 
@@ -57,6 +61,11 @@ module SemanticLogger
57
61
  format_time(log.time) if time_format
58
62
  end
59
63
 
64
+ # Process ID
65
+ def pid
66
+ $$
67
+ end
68
+
60
69
  private
61
70
 
62
71
  # Return the Time as a formatted string
@@ -73,7 +82,7 @@ module SemanticLogger
73
82
  when :seconds
74
83
  time.to_f
75
84
  when nil
76
- ''
85
+ ""
77
86
  else
78
87
  time.strftime(time_format)
79
88
  end
@@ -1,8 +1,12 @@
1
- # Load AwesomePrint if available
1
+ # Load Amazing Print, or Awesome Print if available
2
2
  begin
3
- require 'awesome_print'
3
+ require "amazing_print"
4
4
  rescue LoadError
5
- nil
5
+ begin
6
+ require "awesome_print"
7
+ rescue LoadError
8
+ nil
9
+ end
6
10
  end
7
11
 
8
12
  module SemanticLogger
@@ -61,24 +65,19 @@ module SemanticLogger
61
65
  #
62
66
  # Parameters:
63
67
  # ap: [Hash]
64
- # Any valid AwesomePrint option for rendering data.
68
+ # Any valid Amazing Print option for rendering data.
65
69
  # These options can also be changed be creating a `~/.aprc` file.
66
- # See: https://github.com/michaeldv/awesome_print
70
+ # See: https://github.com/amazing-print/amazing_print
67
71
  #
68
72
  # Note: The option :multiline is set to false if not supplied.
69
73
  # Note: Has no effect if Awesome Print is not installed.
70
74
  #
71
75
  # color_map: [Hash | SemanticLogger::Formatters::Color::ColorMap]
72
76
  # ColorMaps each of the log levels to a color
73
- def initialize(ap: {multiline: false},
74
- color_map: ColorMap.new,
75
- time_format: nil,
76
- log_host: false,
77
- log_application: false,
78
- precision: PRECISION)
77
+ def initialize(ap: {multiline: false}, color_map: ColorMap.new, **args)
79
78
  @ai_options = ap
80
79
  @color_map = color_map.is_a?(ColorMap) ? color_map : ColorMap.new(color_map)
81
- super(time_format: time_format, log_host: log_host, log_application: log_application, precision: precision)
80
+ super(**args)
82
81
  end
83
82
 
84
83
  def level
@@ -110,7 +109,7 @@ module SemanticLogger
110
109
  def payload
111
110
  return unless log.payload?
112
111
 
113
- if !defined?(AwesomePrint) || !log.payload.respond_to?(:ai)
112
+ if !log.payload.respond_to?(:ai)
114
113
  super
115
114
  else
116
115
  begin
@@ -2,8 +2,6 @@ module SemanticLogger
2
2
  module Formatters
3
3
  # Default non-colored text log output
4
4
  class Default < Base
5
- attr_accessor :log, :logger
6
-
7
5
  # Formatting methods, must return nil, or a string
8
6
  # Nil values are ignored
9
7
 
@@ -12,9 +10,24 @@ module SemanticLogger
12
10
  log.level_to_s
13
11
  end
14
12
 
15
- # Process info
13
+ # Name of the thread that logged the message.
14
+ def thread_name
15
+ format("%.30s", log.thread_name)
16
+ end
17
+
18
+ # Ruby file name and line number that logged the message.
19
+ def file_name_and_line
20
+ file, line = log.file_name_and_line(true)
21
+ "#{file}:#{line}" if file
22
+ end
23
+
24
+ # Returns [String] the available process info
25
+ # Example:
26
+ # [18934:thread_name test_logging.rb:51]
16
27
  def process_info
17
- "[#{log.process_info}]"
28
+ process_id = "#{pid}:" if pid
29
+ fname = file_name_and_line
30
+ fname ? "[#{process_id}#{thread_name} #{fname}]" : "[#{process_id}#{thread_name}]"
18
31
  end
19
32
 
20
33
  # Tags
@@ -67,7 +80,7 @@ module SemanticLogger
67
80
  self.log = log
68
81
  self.logger = logger
69
82
 
70
- [time, level, process_info, tags, named_tags, duration, name, message, payload, exception].compact.join(' ')
83
+ [time, level, process_info, tags, named_tags, duration, name, message, payload, exception].compact.join(" ")
71
84
  end
72
85
  end
73
86
  end
@@ -1,37 +1,26 @@
1
- require 'json'
1
+ require "json"
2
2
 
3
3
  module SemanticLogger
4
4
  module Formatters
5
- # Fluentd is similar to SemanticLogger::Formatters::Json but with log level that are recongnized
5
+ # Fluentd is similar to SemanticLogger::Formatters::Json but with log levels that are recognized
6
6
  # by kubernetes fluentd.
7
7
  class Fluentd < Json
8
8
  attr_reader :need_process_info
9
9
 
10
- def initialize(log_host: true, log_application: true, need_process_info: false)
10
+ def initialize(time_format: :rfc_3339, time_key: :time, need_process_info: false, **args)
11
11
  @need_process_info = need_process_info
12
- super(log_host: log_host, log_application: log_application, time_key: 'time', time_format: :rfc_3339)
12
+ super(time_format: time_format, time_key: time_key, **args)
13
13
  end
14
14
 
15
- def severity
16
- hash['severity'] = log.level
17
- hash['severity_index'] = log.level_index
15
+ def level
16
+ hash["severity"] = log.level
17
+ hash["severity_index"] = log.level_index
18
18
  end
19
19
 
20
20
  def process_info
21
21
  # Ignore fields: pid, thread, file and line by default
22
22
  super() if need_process_info
23
23
  end
24
-
25
- def call(log, logger)
26
- self.hash = {}
27
- self.log = log
28
- self.logger = logger
29
-
30
- host; application; time; severity; process_info; duration; tags; named_tags; name; message; payload; exception; metric
31
- hash
32
-
33
- hash.to_json
34
- end
35
24
  end
36
25
  end
37
26
  end