semantic_logger 4.3.1 → 4.4.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/semantic_logger.rb +7 -1
  3. data/lib/semantic_logger/appender.rb +3 -0
  4. data/lib/semantic_logger/appender/async.rb +29 -10
  5. data/lib/semantic_logger/appender/rabbitmq.rb +120 -0
  6. data/lib/semantic_logger/appenders.rb +89 -0
  7. data/lib/semantic_logger/base.rb +3 -3
  8. data/lib/semantic_logger/concerns/compatibility.rb +2 -2
  9. data/lib/semantic_logger/formatters.rb +1 -0
  10. data/lib/semantic_logger/formatters/base.rb +28 -6
  11. data/lib/semantic_logger/formatters/color.rb +4 -3
  12. data/lib/semantic_logger/formatters/fluentd.rb +37 -0
  13. data/lib/semantic_logger/formatters/json.rb +4 -2
  14. data/lib/semantic_logger/formatters/raw.rb +2 -2
  15. data/lib/semantic_logger/formatters/signalfx.rb +4 -3
  16. data/lib/semantic_logger/levels.rb +38 -0
  17. data/lib/semantic_logger/log.rb +11 -6
  18. data/lib/semantic_logger/loggable.rb +1 -1
  19. data/lib/semantic_logger/logger.rb +43 -1
  20. data/lib/semantic_logger/processor.rb +10 -130
  21. data/lib/semantic_logger/reporters/minitest.rb +49 -0
  22. data/lib/semantic_logger/semantic_logger.rb +40 -75
  23. data/lib/semantic_logger/version.rb +1 -1
  24. metadata +9 -81
  25. data/test/appender/async_batch_test.rb +0 -60
  26. data/test/appender/async_test.rb +0 -44
  27. data/test/appender/bugsnag_test.rb +0 -81
  28. data/test/appender/elasticsearch_http_test.rb +0 -74
  29. data/test/appender/elasticsearch_test.rb +0 -248
  30. data/test/appender/file_test.rb +0 -120
  31. data/test/appender/graylog_test.rb +0 -82
  32. data/test/appender/honeybadger_test.rb +0 -45
  33. data/test/appender/http_test.rb +0 -63
  34. data/test/appender/kafka_test.rb +0 -35
  35. data/test/appender/mongodb_test.rb +0 -104
  36. data/test/appender/new_relic_test.rb +0 -80
  37. data/test/appender/newrelic_rpm.rb +0 -14
  38. data/test/appender/sentry_test.rb +0 -47
  39. data/test/appender/splunk_http_test.rb +0 -79
  40. data/test/appender/splunk_test.rb +0 -83
  41. data/test/appender/syslog_test.rb +0 -61
  42. data/test/appender/tcp_test.rb +0 -66
  43. data/test/appender/udp_test.rb +0 -59
  44. data/test/appender/wrapper_test.rb +0 -95
  45. data/test/concerns/compatibility_test.rb +0 -117
  46. data/test/debug_as_trace_logger_test.rb +0 -81
  47. data/test/formatters/color_test.rb +0 -153
  48. data/test/formatters/default_test.rb +0 -175
  49. data/test/formatters/one_line_test.rb +0 -60
  50. data/test/formatters/signalfx_test.rb +0 -197
  51. data/test/formatters_test.rb +0 -36
  52. data/test/in_memory_appender.rb +0 -8
  53. data/test/in_memory_appender_helper.rb +0 -43
  54. data/test/in_memory_batch_appender.rb +0 -8
  55. data/test/in_memory_metrics_appender.rb +0 -13
  56. data/test/loggable_test.rb +0 -103
  57. data/test/logger_test.rb +0 -334
  58. data/test/measure_test.rb +0 -346
  59. data/test/metric/new_relic_test.rb +0 -35
  60. data/test/metric/signalfx_test.rb +0 -77
  61. data/test/semantic_logger_test.rb +0 -303
  62. data/test/test_helper.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 285869f2b402ea378eab2606d3d72f7b7be3db56102bea2c530a98411a077d6c
4
- data.tar.gz: cab30b7d7bcfda6083716a530aeedf20192b1fa1c3a83c7cb237d475516d0ca7
3
+ metadata.gz: 88a1f36f6ef4d857e13b696e283f6bbd643329a0ca7f3fd9ac4c9ef1e670aaae
4
+ data.tar.gz: 01a175a96d750f9692bb28512d914434e442336c68eadd9f7b9f409a4d2f4a2b
5
5
  SHA512:
6
- metadata.gz: e67d39bfbedb72f64b191f75d5786f521d311d78fb243bb97cbfa948333fcfa21f89ebcfa6a9ac34696a24c359bd8b67a9617228a25a1958bbf3092ed6324472
7
- data.tar.gz: c446a8410247e2b8393f401182f0bc1a266d329ce1190328a3f7253bf5b64273c87c5db05a4bb4f808caf9435b0e8a8d2a37a69c9ac245b43f3215f67f1f9710
6
+ metadata.gz: e06dd57a7ee95bfc5aea5d4b2948101a1fbdb00dd76ac1b9ac0b68a4a3f666670b227d559451d4137e510d1c63b9ec9bd8f99326bfe765f9a610294221c2259f
7
+ data.tar.gz: 0e2b66a5da5f5e2d21c185555f3502e7982d8d197ec9bade63092acae1750dcf1d8191de71dc8fbe0598d844b2c5f85931c407030a599260f41ad35470802a14
@@ -1,14 +1,15 @@
1
1
  require 'semantic_logger/core_ext/thread'
2
2
  require 'semantic_logger/version'
3
- require 'semantic_logger/semantic_logger'
4
3
 
5
4
  # @formatter:off
6
5
  module SemanticLogger
7
6
  autoload :AnsiColors, 'semantic_logger/ansi_colors'
8
7
  autoload :Appender, 'semantic_logger/appender'
8
+ autoload :Appenders, 'semantic_logger/appenders'
9
9
  autoload :Base, 'semantic_logger/base'
10
10
  autoload :DebugAsTraceLogger, 'semantic_logger/debug_as_trace_logger'
11
11
  autoload :Formatters, 'semantic_logger/formatters'
12
+ autoload :Levels, 'semantic_logger/levels'
12
13
  autoload :Log, 'semantic_logger/log'
13
14
  autoload :Logger, 'semantic_logger/logger'
14
15
  autoload :Loggable, 'semantic_logger/loggable'
@@ -26,12 +27,17 @@ module SemanticLogger
26
27
  autoload :Statsd, 'semantic_logger/metric/statsd'
27
28
  end
28
29
 
30
+ module Reporters
31
+ autoload :Minitest, 'semantic_logger/reporters/minitest'
32
+ end
33
+
29
34
  if defined?(JRuby)
30
35
  module JRuby
31
36
  autoload :GarbageCollectionLogger, 'semantic_logger/jruby/garbage_collection_logger'
32
37
  end
33
38
  end
34
39
  end
40
+ require 'semantic_logger/semantic_logger'
35
41
  # @formatter:on
36
42
 
37
43
  # Flush all appenders at exit, waiting for outstanding messages on the queue
@@ -14,6 +14,7 @@ module SemanticLogger
14
14
  autoload :Http, 'semantic_logger/appender/http'
15
15
  autoload :MongoDB, 'semantic_logger/appender/mongodb'
16
16
  autoload :NewRelic, 'semantic_logger/appender/new_relic'
17
+ autoload :Rabbitmq, 'semantic_logger/appender/rabbitmq'
17
18
  autoload :Splunk, 'semantic_logger/appender/splunk'
18
19
  autoload :SplunkHttp, 'semantic_logger/appender/splunk_http'
19
20
  autoload :Syslog, 'semantic_logger/appender/syslog'
@@ -56,6 +57,8 @@ module SemanticLogger
56
57
  elsif async == true
57
58
  proxy_options[:appender] = appender
58
59
  Appender::Async.new(proxy_options)
60
+
61
+
59
62
  else
60
63
  appender
61
64
  end
@@ -6,8 +6,8 @@ module SemanticLogger
6
6
  class Async
7
7
  extend Forwardable
8
8
 
9
- attr_accessor :logger, :lag_check_interval, :lag_threshold_s
10
- attr_reader :queue, :appender
9
+ attr_accessor :lag_check_interval, :lag_threshold_s
10
+ attr_reader :queue, :appender, :max_queue_size
11
11
 
12
12
  # Forward methods that can be called directly
13
13
  def_delegator :@appender, :name
@@ -18,6 +18,7 @@ module SemanticLogger
18
18
  def_delegator :@appender, :level
19
19
  def_delegator :@appender, :level=
20
20
  def_delegator :@appender, :logger
21
+ def_delegator :@appender, :logger=
21
22
 
22
23
  # Appender proxy to allow an existing appender to run asynchronously in a separate thread.
23
24
  #
@@ -43,17 +44,24 @@ module SemanticLogger
43
44
  @lag_check_interval = lag_check_interval
44
45
  @lag_threshold_s = lag_threshold_s
45
46
  @thread = nil
46
-
47
- if max_queue_size == -1
48
- @queue = Queue.new
49
- @capped = false
50
- else
51
- @queue = SizedQueue.new(max_queue_size)
52
- @capped = true
53
- end
47
+ @max_queue_size = max_queue_size
48
+ create_queue
54
49
  thread
55
50
  end
56
51
 
52
+ # Re-open appender after a fork
53
+ def reopen
54
+ # Workaround CRuby crash on fork by recreating queue on reopen
55
+ # https://github.com/rocketjob/semantic_logger/issues/103
56
+ @queue&.close
57
+ create_queue
58
+
59
+ appender.reopen if appender.respond_to?(:reopen)
60
+
61
+ @thread.kill if @thread&.alive?
62
+ @thread = Thread.new { process }
63
+ end
64
+
57
65
  # Returns [true|false] if the queue has a capped size.
58
66
  def capped?
59
67
  @capped
@@ -91,6 +99,16 @@ module SemanticLogger
91
99
 
92
100
  private
93
101
 
102
+ def create_queue
103
+ if max_queue_size == -1
104
+ @queue = Queue.new
105
+ @capped = false
106
+ else
107
+ @queue = SizedQueue.new(max_queue_size)
108
+ @capped = true
109
+ end
110
+ end
111
+
94
112
  # Separate thread for batching up log messages before writing.
95
113
  def process
96
114
  # This thread is designed to never go down unless the main thread terminates
@@ -140,6 +158,7 @@ module SemanticLogger
140
158
  break unless process_message(message)
141
159
  end
142
160
  end
161
+ logger.trace 'Async: Queue Closed'
143
162
  end
144
163
 
145
164
  # Returns false when message processing should be stopped
@@ -0,0 +1,120 @@
1
+ begin
2
+ require 'bunny'
3
+ rescue LoadError
4
+ raise 'Gem bunny is required for logging to RabbitMQ. Please add the gem "bunny" to your Gemfile.'
5
+ end
6
+
7
+ # Forward all log messages to RabbitMQ.
8
+ #
9
+ # Example:
10
+ #
11
+ # SemanticLogger.add_appender(
12
+ # appender: :rabbitmq,
13
+ #
14
+ # # Name of the queue in RabbitMQ where to publish the logs. This queue will be bound to "amqp.direct" exchange.
15
+ # queue: 'semantic_logger',
16
+ #
17
+ # # This host will be used for RabbitMQ connection.
18
+ # # NOTE this is different than :host option which is used by the logger directly.
19
+ # rabbitmq_host: '127.0.0.1',
20
+ #
21
+ # # RabbitMQ credentials
22
+ # username: 'my-username',
23
+ # password: 'my-secrect-pass',
24
+ #
25
+ # # All other options accepted by Bunny.new call
26
+ # vhost: 'production',
27
+ # )
28
+ module SemanticLogger
29
+ module Appender
30
+ class Rabbitmq < SemanticLogger::Subscriber
31
+ # Create RabbitMQ appender using Bunny gem
32
+ #
33
+ # Parameters:
34
+ #
35
+ # queue_name: [String]
36
+ # Name of RabbitMQ queue where to stream logs to.
37
+ # This will be a queue bound to AMQP Default exchange
38
+ # Default: semantic_logger
39
+ #
40
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
41
+ # Override the log level for this appender.
42
+ # Default: SemanticLogger.default_level
43
+ #
44
+ # formatter: [Object|Proc|Symbol|Hash]
45
+ # An instance of a class that implements #call, or a Proc to be used to format
46
+ # the output from this appender
47
+ # Default: :json (See: #call)
48
+ #
49
+ # filter: [Regexp|Proc]
50
+ # RegExp: Only include log messages where the class name matches the supplied.
51
+ # regular expression. All other messages will be ignored.
52
+ # Proc: Only include log messages where the supplied Proc returns true
53
+ # The Proc must return true or false.
54
+ #
55
+ # host: [String]
56
+ # Name of this host to appear in log messages.
57
+ # Default: SemanticLogger.host
58
+ #
59
+ # application: [String]
60
+ # Name of this application to appear in log messages.
61
+ # Default: SemanticLogger.application
62
+ #
63
+ # RabbitMQ Parameters:
64
+ #
65
+ # rabbitmq_host: [String]
66
+ # Host for AMQP connection. in Bunny this is called :host but here it has
67
+ # been remapped to avoid conflicting with SemanticLogger's :host param.
68
+ # Default: localhost
69
+ #
70
+ # username: [String]
71
+ # Username for AMQP connection
72
+ # Default: nil
73
+ #
74
+ # password: [String]
75
+ # Password for AMQP connection
76
+ # Default: nil
77
+ #
78
+ # more parameters supported by Bunny: http://rubybunny.info/articles/connecting.html
79
+ def initialize(queue_name: 'semantic_logger', rabbitmq_host: nil, metrics: false, **args, &block)
80
+ @queue_name = queue_name
81
+ @rabbitmq_args = args.dup
82
+ @rabbitmq_args[:host] = rabbitmq_host
83
+ @rabbitmq_args[:logger] = logger
84
+
85
+ super(level: level, formatter: formatter, filter: filter, application: application, host: host, metrics: metrics, &block)
86
+ reopen
87
+ end
88
+
89
+ def reopen
90
+ @connection = Bunny.new(@rabbitmq_args)
91
+ @connection.start
92
+ @channel = @connection.create_channel
93
+ end
94
+
95
+ def close
96
+ @channel&.close
97
+ @channel = nil
98
+ @connection&.close
99
+ @connection = nil
100
+ end
101
+
102
+ def log(log)
103
+ queue.publish(formatter.call(log, self))
104
+ end
105
+
106
+ def flush
107
+ # NOOP
108
+ end
109
+
110
+ # Use JSON Formatter by default.
111
+ def default_formatter
112
+ SemanticLogger::Formatters::Json.new
113
+ end
114
+
115
+ def queue
116
+ @queue ||= @channel.queue(@queue_name)
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,89 @@
1
+ module SemanticLogger
2
+ # Manage a collection of appenders.
3
+ class Appenders < Concurrent::Array
4
+ attr_accessor :logger
5
+
6
+ def initialize(logger = Processor.logger.dup)
7
+ @logger = logger
8
+ @logger.name = self.class.name
9
+ end
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)
14
+ self << appender
15
+ appender
16
+ end
17
+
18
+ def log(log)
19
+ each do |appender|
20
+ begin
21
+ appender.log(log) if appender.should_log?(log)
22
+ rescue Exception => exc
23
+ logger.error "Failed to log to appender: #{appender.inspect}", exc
24
+ end
25
+ end
26
+ end
27
+
28
+ def flush
29
+ each do |appender|
30
+ begin
31
+ logger.trace "Flushing appender: #{appender.name}"
32
+ appender.flush
33
+ rescue Exception => exc
34
+ logger.error "Failed to flush appender: #{appender.inspect}", exc
35
+ end
36
+ end
37
+ logger.trace 'All appenders flushed'
38
+ end
39
+
40
+ def close
41
+ each do |appender|
42
+ begin
43
+ logger.trace "Closing appender: #{appender.name}"
44
+ appender.flush
45
+ appender.close
46
+ appenders.delete(appender)
47
+ rescue Exception => exc
48
+ logger.error "Failed to close appender: #{appender.inspect}", exc
49
+ end
50
+ end
51
+ logger.trace 'All appenders closed and removed from appender list'
52
+ end
53
+
54
+ # After a fork the appender thread is not running, start it if it is not running.
55
+ def reopen
56
+ each do |appender|
57
+ begin
58
+ next unless appender.respond_to?(:reopen)
59
+
60
+ logger.trace "Reopening appender: #{appender.name}"
61
+ appender.reopen
62
+ rescue Exception => exc
63
+ logger.error "Failed to re-open appender: #{appender.inspect}", exc
64
+ end
65
+ 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
87
+ end
88
+ end
89
+ end
@@ -23,8 +23,8 @@ module SemanticLogger
23
23
  @level_index = nil
24
24
  @level = nil
25
25
  else
26
- @level_index = SemanticLogger.send(:level_to_index, level)
27
- @level = SemanticLogger.send(:index_to_level, @level_index)
26
+ @level_index = Levels.index(level)
27
+ @level = Levels.level(@level_index)
28
28
  end
29
29
  end
30
30
 
@@ -112,7 +112,7 @@ module SemanticLogger
112
112
 
113
113
  # Dynamically supply the log level with every measurement call
114
114
  def measure(level, message, params = {}, &block)
115
- index = SemanticLogger.level_to_index(level)
115
+ index = Levels.index(level)
116
116
  if level_index <= index
117
117
  measure_internal(level, index, message, params, &block)
118
118
  elsif block
@@ -38,9 +38,9 @@ module SemanticLogger
38
38
 
39
39
  # :nodoc:
40
40
  def add(severity, message = nil, progname = nil, &block)
41
- index = SemanticLogger.send(:level_to_index, severity)
41
+ index = Levels.index(severity)
42
42
  if level_index <= index
43
- level = SemanticLogger.send(:index_to_level, index)
43
+ level = Levels.level(index)
44
44
  log_internal(level, index, message, progname, &block)
45
45
  true
46
46
  else
@@ -9,6 +9,7 @@ module SemanticLogger
9
9
  autoload :OneLine, 'semantic_logger/formatters/one_line'
10
10
  autoload :Signalfx, 'semantic_logger/formatters/signalfx'
11
11
  autoload :Syslog, 'semantic_logger/formatters/syslog'
12
+ autoload :Fluentd, 'semantic_logger/formatters/fluentd'
12
13
  # @formatter:on
13
14
 
14
15
  # Return formatter that responds to call.
@@ -2,7 +2,7 @@ require 'time'
2
2
  module SemanticLogger
3
3
  module Formatters
4
4
  class Base
5
- attr_accessor :time_format, :log_host, :log_application
5
+ attr_accessor :time_format, :log_host, :log_application, :precision
6
6
 
7
7
  # Time precision varies by Ruby interpreter
8
8
  # JRuby 9.1.8.0 supports microseconds
@@ -17,7 +17,6 @@ module SemanticLogger
17
17
  else
18
18
  6
19
19
  end
20
- TIME_FORMAT = "%Y-%m-%d %H:%M:%S.%#{PRECISION}N".freeze
21
20
 
22
21
  # Parameters
23
22
  # time_format: [String|Symbol|nil]
@@ -25,11 +24,32 @@ module SemanticLogger
25
24
  # :iso_8601 Outputs an ISO8601 Formatted timestamp.
26
25
  # :ms Output in miliseconds since epoch.
27
26
  # nil: Returns Empty string for time ( no time is output ).
28
- # Default: '%Y-%m-%d %H:%M:%S.%6N'
29
- def initialize(time_format: TIME_FORMAT, log_host: true, log_application: true)
30
- @time_format = time_format
27
+ # Default: '%Y-%m-%d %H:%M:%S.%<precision>N'
28
+ # log_host: [Boolean]
29
+ # Whether or not to include hostname in logs
30
+ # Default: true
31
+ # log_application: [Boolean]
32
+ # Whether or not to include application name in logs
33
+ # Default: true
34
+ # precision: [Integer]
35
+ # How many fractional digits to log times with.
36
+ # Default: PRECISION (6, except on older JRuby, where 3)
37
+ def initialize(time_format: nil, log_host: true, log_application: true,
38
+ precision: PRECISION)
39
+ @time_format = time_format || self.class.build_time_format(precision)
31
40
  @log_host = log_host
32
41
  @log_application = log_application
42
+ @precision = precision
43
+ end
44
+
45
+ # Return default time format string
46
+ #
47
+ # Parameters
48
+ # precision: [Integer]
49
+ # How many fractional digits to log times with.
50
+ # Default: PRECISION (6, except on older JRuby, where 3)
51
+ def self.build_time_format(precision=PRECISION)
52
+ "%Y-%m-%d %H:%M:%S.%#{precision}N"
33
53
  end
34
54
 
35
55
  # Date & time
@@ -42,8 +62,10 @@ module SemanticLogger
42
62
  # Return the Time as a formatted string
43
63
  def format_time(time)
44
64
  case time_format
65
+ when :rfc_3339
66
+ time.utc.to_datetime.rfc3339
45
67
  when :iso_8601
46
- time.utc.iso8601(PRECISION)
68
+ time.utc.iso8601(precision)
47
69
  when :ms
48
70
  (time.to_f * 1_000).to_i
49
71
  when :none