semantic_logger 4.6.0.beta1 → 4.7.2

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 (52) 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/appender.rb +32 -33
  6. data/lib/semantic_logger/appender/async.rb +9 -8
  7. data/lib/semantic_logger/appender/async_batch.rb +4 -2
  8. data/lib/semantic_logger/appender/bugsnag.rb +7 -7
  9. data/lib/semantic_logger/appender/elasticsearch.rb +10 -10
  10. data/lib/semantic_logger/appender/elasticsearch_http.rb +4 -4
  11. data/lib/semantic_logger/appender/file.rb +2 -1
  12. data/lib/semantic_logger/appender/graylog.rb +15 -10
  13. data/lib/semantic_logger/appender/honeybadger.rb +3 -3
  14. data/lib/semantic_logger/appender/http.rb +20 -18
  15. data/lib/semantic_logger/appender/kafka.rb +5 -5
  16. data/lib/semantic_logger/appender/mongodb.rb +6 -6
  17. data/lib/semantic_logger/appender/new_relic.rb +2 -2
  18. data/lib/semantic_logger/appender/rabbitmq.rb +5 -5
  19. data/lib/semantic_logger/appender/sentry.rb +7 -7
  20. data/lib/semantic_logger/appender/splunk.rb +5 -5
  21. data/lib/semantic_logger/appender/splunk_http.rb +3 -4
  22. data/lib/semantic_logger/appender/syslog.rb +20 -14
  23. data/lib/semantic_logger/appender/tcp.rb +5 -5
  24. data/lib/semantic_logger/appender/udp.rb +2 -2
  25. data/lib/semantic_logger/appenders.rb +11 -11
  26. data/lib/semantic_logger/base.rb +42 -18
  27. data/lib/semantic_logger/formatters.rb +11 -11
  28. data/lib/semantic_logger/formatters/base.rb +8 -3
  29. data/lib/semantic_logger/formatters/color.rb +10 -6
  30. data/lib/semantic_logger/formatters/default.rb +18 -5
  31. data/lib/semantic_logger/formatters/fluentd.rb +3 -3
  32. data/lib/semantic_logger/formatters/json.rb +1 -1
  33. data/lib/semantic_logger/formatters/raw.rb +31 -7
  34. data/lib/semantic_logger/formatters/signalfx.rb +10 -9
  35. data/lib/semantic_logger/formatters/syslog.rb +7 -6
  36. data/lib/semantic_logger/formatters/syslog_cee.rb +7 -6
  37. data/lib/semantic_logger/jruby/garbage_collection_logger.rb +4 -2
  38. data/lib/semantic_logger/levels.rb +9 -7
  39. data/lib/semantic_logger/log.rb +52 -60
  40. data/lib/semantic_logger/logger.rb +6 -8
  41. data/lib/semantic_logger/metric/new_relic.rb +3 -3
  42. data/lib/semantic_logger/metric/signalfx.rb +3 -3
  43. data/lib/semantic_logger/metric/statsd.rb +7 -7
  44. data/lib/semantic_logger/processor.rb +7 -5
  45. data/lib/semantic_logger/reporters/minitest.rb +4 -4
  46. data/lib/semantic_logger/semantic_logger.rb +24 -11
  47. data/lib/semantic_logger/subscriber.rb +6 -5
  48. data/lib/semantic_logger/sync.rb +12 -0
  49. data/lib/semantic_logger/sync_processor.rb +43 -0
  50. data/lib/semantic_logger/utils.rb +6 -6
  51. data/lib/semantic_logger/version.rb +1 -1
  52. metadata +7 -5
@@ -1,13 +1,13 @@
1
1
  begin
2
- require 'syslog_protocol'
2
+ require "syslog_protocol"
3
3
  rescue LoadError
4
- raise 'Gem syslog_protocol is required for remote logging using the Syslog protocol. Please add the gem "syslog_protocol" to your Gemfile.'
4
+ raise LoadError, 'Gem syslog_protocol is required for remote logging using the Syslog protocol. Please add the gem "syslog_protocol" to your Gemfile.'
5
5
  end
6
6
 
7
7
  module SemanticLogger
8
8
  module Formatters
9
9
  class SyslogCee < Raw
10
- attr_accessor :level_map, :facility
10
+ attr_accessor :level_map, :facility, :max_size
11
11
 
12
12
  # CEE JSON Syslog format
13
13
  # Untested prototype code. Based on documentation only.
@@ -23,9 +23,10 @@ module SemanticLogger
23
23
  # Example:
24
24
  # # Log via udp to a remote syslog server on host: `server1` and port `8514`, using the CEE format.
25
25
  # SemanticLogger.add_appender(appender: :syslog, formatter: syslog_cee, url: 'udp://server1:8514')
26
- def initialize(facility: ::Syslog::LOG_USER, level_map: SemanticLogger::Formatters::Syslog::LevelMap.new)
26
+ def initialize(facility: ::Syslog::LOG_USER, level_map: SemanticLogger::Formatters::Syslog::LevelMap.new, max_size: Integer)
27
27
  @facility = facility
28
28
  @level_map = level_map.is_a?(SemanticLogger::Formatters::Syslog::LevelMap) ? level_map : SemanticLogger::Formatters::Syslog::LevelMap.new(level_map)
29
+ @max_size = max_size
29
30
  super()
30
31
  end
31
32
 
@@ -45,11 +46,11 @@ module SemanticLogger
45
46
  packet = SyslogProtocol::Packet.new
46
47
  packet.hostname = logger.host
47
48
  packet.facility = facility
48
- packet.tag = logger.application.delete(' ')
49
+ packet.tag = logger.application.delete(" ")
49
50
  packet.content = message
50
51
  packet.time = log.time
51
52
  packet.severity = level_map[log.level]
52
- packet.to_s
53
+ packet.assemble(@max_size)
53
54
  end
54
55
  end
55
56
  end
@@ -12,7 +12,9 @@ module SemanticLogger
12
12
  # Must leave the method name as-is so that it can be found by Java
13
13
  def handleNotification(notification, _)
14
14
  # Only care about GARBAGE_COLLECTION_NOTIFICATION notifications
15
- return unless notification.get_type == Java::ComSunManagement::GarbageCollectionNotificationInfo::GARBAGE_COLLECTION_NOTIFICATION
15
+ unless notification.get_type == Java::ComSunManagement::GarbageCollectionNotificationInfo::GARBAGE_COLLECTION_NOTIFICATION
16
+ return
17
+ end
16
18
 
17
19
  info = Java::ComSunManagement::GarbageCollectionNotificationInfo.from(notification.user_data)
18
20
  gc_info = info.gc_info
@@ -20,7 +22,7 @@ module SemanticLogger
20
22
 
21
23
  return unless duration >= @min_microseconds
22
24
 
23
- SemanticLogger['GarbageCollector'].measure_warn(
25
+ SemanticLogger["GarbageCollector"].measure_warn(
24
26
  "Garbage Collection completed: #{info.gc_name} ##{gc_info.id}",
25
27
  duration: duration.to_f / 1000
26
28
  )
@@ -16,17 +16,19 @@ module SemanticLogger
16
16
  LEVELS.index(level)
17
17
  elsif level.is_a?(Integer) && defined?(::Logger::Severity)
18
18
  # Mapping of Rails and Ruby Logger levels to SemanticLogger levels
19
- @map_levels ||= begin
20
- levels = []
21
- ::Logger::Severity.constants.each do |constant|
22
- levels[::Logger::Severity.const_get(constant)] =
23
- LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
19
+ @map_levels ||=
20
+ begin
21
+ levels = []
22
+ ::Logger::Severity.constants.each do |constant|
23
+ levels[::Logger::Severity.const_get(constant)] =
24
+ LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
25
+ end
26
+ levels
24
27
  end
25
- levels
26
- end
27
28
  @map_levels[level]
28
29
  end
29
30
  raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
31
+
30
32
  index
31
33
  end
32
34
 
@@ -47,6 +47,12 @@ module SemanticLogger
47
47
  # context [Hash]
48
48
  # Named contexts that were captured when the log entry was created.
49
49
  class Log
50
+ # Keys passed in without a payload that will be extracted and the remainder passed into the payload.
51
+ NON_PAYLOAD_KEYS = %i[message exception backtrace exception
52
+ duration min_duration
53
+ log_exception on_exception_level
54
+ metric metric_amount dimensions].freeze
55
+
50
56
  attr_accessor :level, :level_index, :name, :message, :time, :duration,
51
57
  :payload, :exception, :thread_name, :backtrace,
52
58
  :tags, :named_tags, :context,
@@ -79,20 +85,13 @@ module SemanticLogger
79
85
  log_exception: :full,
80
86
  on_exception_level: nil,
81
87
  dimensions: nil)
82
- # Elastic logging: Log when :duration exceeds :min_duration
83
- # Except if there is an exception when it will always be logged
84
- if duration
85
- self.duration = duration
86
- return false if (duration < min_duration) && exception.nil?
87
- end
88
88
 
89
- self.message = message
90
- if payload && payload.is_a?(Hash)
91
- self.payload = payload
92
- elsif payload
93
- self.message = message.nil? ? payload.to_s : "#{message} -- #{payload}"
94
- self.payload = nil
95
- end
89
+ self.message = message
90
+ self.payload = payload
91
+ self.duration = duration
92
+ self.metric = metric
93
+ self.metric_amount = metric_amount
94
+ self.dimensions = dimensions
96
95
 
97
96
  if exception
98
97
  case log_exception
@@ -113,57 +112,45 @@ module SemanticLogger
113
112
  end
114
113
  end
115
114
 
115
+ # Elastic logging: Log when :duration exceeds :min_duration
116
+ # Except if there is an exception when it will always be logged
117
+ if duration
118
+ return false if (duration < min_duration) && exception.nil?
119
+ end
120
+
116
121
  if backtrace
117
122
  self.backtrace = Utils.extract_backtrace(backtrace)
118
123
  elsif level_index >= SemanticLogger.backtrace_level_index
119
124
  self.backtrace = Utils.extract_backtrace
120
125
  end
121
126
 
122
- if metric
123
- self.metric = metric
124
- self.metric_amount = metric_amount
125
- self.dimensions = dimensions
126
- end
127
-
128
127
  true
129
128
  end
130
129
 
131
- # Assign positional arguments to this log entry, supplying defaults where applicable
132
- #
133
- # Returns [true|false] whether this log entry should be logged
134
- #
135
- # Example:
136
- # logger.info('value', :debug, 0, "hello world")
137
- def assign_positional(message = nil, payload = nil, exception = nil)
138
- # Exception being logged?
139
- # Under JRuby a java exception is not a Ruby Exception
140
- # Java::JavaLang::ClassCastException.new.is_a?(Exception) => false
141
- if exception.nil? && payload.nil? && message.respond_to?(:backtrace) && message.respond_to?(:message)
142
- exception = message
143
- message = nil
144
- elsif exception.nil? && payload && payload.respond_to?(:backtrace) && payload.respond_to?(:message)
145
- exception = payload
146
- payload = nil
147
- elsif payload && !payload.is_a?(Hash)
148
- message = message.nil? ? payload : "#{message} -- #{payload}"
149
- payload = nil
150
- end
151
-
152
- # Add result of block as message or payload if not nil
153
- if block_given? && (result = yield)
154
- if result.is_a?(String)
155
- message = message.nil? ? result : "#{message} -- #{result}"
156
- assign(message: message, payload: payload, exception: exception)
157
- elsif message.nil? && result.is_a?(Hash) && %i[message payload exception].any? { |k| result.key? k }
158
- assign(result)
159
- elsif payload&.respond_to?(:merge)
160
- assign(message: message, payload: payload.merge(result), exception: exception)
130
+ # Assign known keys to self, all other keys to the payload.
131
+ def assign_hash(hash)
132
+ self.payload ||= {}
133
+ hash.each_pair do |key, value|
134
+ if respond_to?("#{key}=".to_sym)
135
+ public_send("#{key}=".to_sym, value)
161
136
  else
162
- assign(message: message, payload: result, exception: exception)
137
+ payload[key] = value
163
138
  end
164
- else
165
- assign(message: message, payload: payload, exception: exception)
166
139
  end
140
+ self.payload = nil if payload.empty?
141
+ self
142
+ end
143
+
144
+ # Extract the arguments from a Hash Payload
145
+ def extract_arguments(payload)
146
+ raise(ArgumentError, "payload must be a Hash") unless payload.is_a?(Hash)
147
+
148
+ return payload if payload.key?(:payload)
149
+
150
+ args = {}
151
+ payload.each_key { |key| args[key] = payload.delete(key) if NON_PAYLOAD_KEYS.include?(key) }
152
+ args[:payload] = payload unless payload.empty?
153
+ args
167
154
  end
168
155
 
169
156
  MAX_EXCEPTIONS_TO_UNWRAP = 5
@@ -191,7 +178,7 @@ module SemanticLogger
191
178
 
192
179
  # Returns [String] the exception backtrace including all of the child / caused by exceptions
193
180
  def backtrace_to_s
194
- trace = ''
181
+ trace = ""
195
182
  each_exception do |exception, i|
196
183
  if i.zero?
197
184
  trace = (exception.backtrace || []).join("\n")
@@ -212,6 +199,7 @@ module SemanticLogger
212
199
  else
213
200
  def duration_to_s
214
201
  return unless duration
202
+
215
203
  duration < 10.0 ? "#{format('%.3f', duration)}ms" : "#{format('%.1f', duration)}ms"
216
204
  end
217
205
  end
@@ -219,13 +207,14 @@ module SemanticLogger
219
207
  # Returns [String] the duration in human readable form
220
208
  def duration_human
221
209
  return nil unless duration
210
+
222
211
  seconds = duration / 1000
223
212
  if seconds >= 86_400.0 # 1 day
224
213
  "#{(seconds / 86_400).to_i}d #{Time.at(seconds).strftime('%-Hh %-Mm')}"
225
214
  elsif seconds >= 3600.0 # 1 hour
226
- Time.at(seconds).strftime('%-Hh %-Mm')
215
+ Time.at(seconds).strftime("%-Hh %-Mm")
227
216
  elsif seconds >= 60.0 # 1 minute
228
- Time.at(seconds).strftime('%-Mm %-Ss')
217
+ Time.at(seconds).strftime("%-Mm %-Ss")
229
218
  elsif seconds >= 1.0 # 1 second
230
219
  "#{format('%.3f', seconds)}s"
231
220
  else
@@ -238,9 +227,7 @@ module SemanticLogger
238
227
  level.to_s[0..0].upcase
239
228
  end
240
229
 
241
- # Returns [String] the available process info
242
- # Example:
243
- # 18934:thread 23 test_logging.rb:51
230
+ # DEPRECATED
244
231
  def process_info(thread_name_length = 30)
245
232
  file, line = file_name_and_line(true)
246
233
  file_name = " #{file}:#{line}" if file
@@ -248,7 +235,7 @@ module SemanticLogger
248
235
  "#{$$}:#{format("%.#{thread_name_length}s", thread_name)}#{file_name}"
249
236
  end
250
237
 
251
- CALLER_REGEXP = /^(.*):(\d+).*/
238
+ CALLER_REGEXP = /^(.*):(\d+).*/.freeze
252
239
 
253
240
  # Extract the filename and line number from the last entry in the supplied backtrace
254
241
  def extract_file_and_line(stack, short_name = false)
@@ -265,7 +252,7 @@ module SemanticLogger
265
252
 
266
253
  # Strip the standard Rails colorizing from the logged message
267
254
  def cleansed_message
268
- message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, '').strip
255
+ message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, "").strip
269
256
  end
270
257
 
271
258
  # Return the payload in text form
@@ -279,6 +266,11 @@ module SemanticLogger
279
266
  !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
280
267
  end
281
268
 
269
+ def to_h(host = SemanticLogger.host, application = SemanticLogger.application, environment = SemanticLogger.environment)
270
+ logger = Struct.new(:host, :application, :environment).new(host, application, environment)
271
+ SemanticLogger::Formatters::Raw.new.call(self, logger)
272
+ end
273
+
282
274
  # Lazy initializes the context hash and assigns a key value pair.
283
275
  def set_context(key, value)
284
276
  (self.context ||= {})[key] = value
@@ -1,4 +1,4 @@
1
- require 'concurrent'
1
+ require "concurrent"
2
2
  module SemanticLogger
3
3
  # Logger stores the class name to be used for all log messages so that every
4
4
  # log message written by this instance will include the class name
@@ -9,7 +9,7 @@ module SemanticLogger
9
9
  subscriber = block || object
10
10
 
11
11
  unless subscriber.is_a?(Proc) || subscriber.respond_to?(:call)
12
- raise('When supplying an on_log subscriber, it must support the #call method')
12
+ raise("When supplying an on_log subscriber, it must support the #call method")
13
13
  end
14
14
 
15
15
  subscribers = (@subscribers ||= Concurrent::Array.new)
@@ -21,7 +21,7 @@ module SemanticLogger
21
21
  end
22
22
 
23
23
  def self.processor
24
- @processor
24
+ @processor ||= SemanticLogger.sync? ? SyncProcessor.new : Processor.new
25
25
  end
26
26
 
27
27
  # Returns a Logger instance
@@ -63,9 +63,7 @@ module SemanticLogger
63
63
  Logger.processor.log(log)
64
64
  end
65
65
 
66
- private
67
-
68
- @processor = Processor.new
66
+ @processor = nil
69
67
  @subscribers = nil
70
68
 
71
69
  def self.call_subscribers(log)
@@ -74,8 +72,8 @@ module SemanticLogger
74
72
  @subscribers.each do |subscriber|
75
73
  begin
76
74
  subscriber.call(log)
77
- rescue Exception => exc
78
- self.class.processor.logger.error('Exception calling :on_log subscriber', exc)
75
+ rescue Exception => e
76
+ processor.logger.error("Exception calling :on_log subscriber", e)
79
77
  end
80
78
  end
81
79
  end
@@ -1,7 +1,7 @@
1
1
  begin
2
- require 'newrelic_rpm'
2
+ require "newrelic_rpm"
3
3
  rescue LoadError
4
- raise 'Gem newrelic_rpm is required for logging to New Relic. Please add the gem "newrelic_rpm" to your Gemfile.'
4
+ raise LoadError, 'Gem newrelic_rpm is required for logging to New Relic. Please add the gem "newrelic_rpm" to your Gemfile.'
5
5
  end
6
6
 
7
7
  # Send Metrics to NewRelic
@@ -37,7 +37,7 @@ module SemanticLogger
37
37
  # regular expression. All other messages will be ignored.
38
38
  # Proc: Only include log messages where the supplied Proc returns true
39
39
  # The Proc must return true or false.
40
- def initialize(prefix: 'Custom', **args, &block)
40
+ def initialize(prefix: "Custom", **args, &block)
41
41
  @prefix = prefix
42
42
  super(**args, &block)
43
43
  end
@@ -10,7 +10,7 @@ module SemanticLogger
10
10
  class Signalfx < SemanticLogger::Appender::Http
11
11
  attr_reader :full_url
12
12
 
13
- END_POINT = 'v2/datapoint'.freeze
13
+ END_POINT = "v2/datapoint".freeze
14
14
 
15
15
  # Create SignalFx metrics appender.
16
16
  #
@@ -75,7 +75,7 @@ module SemanticLogger
75
75
  # end
76
76
  def initialize(token:,
77
77
  dimensions: nil,
78
- url: 'https://ingest.signalfx.com',
78
+ url: "https://ingest.signalfx.com",
79
79
  formatter: nil,
80
80
  **args,
81
81
  &block)
@@ -84,7 +84,7 @@ module SemanticLogger
84
84
 
85
85
  super(url: url, formatter: formatter, **args, &block)
86
86
 
87
- @header['X-SF-TOKEN'] = token
87
+ @header["X-SF-TOKEN"] = token
88
88
  @full_url = "#{url}/#{END_POINT}"
89
89
  end
90
90
 
@@ -1,8 +1,8 @@
1
- require 'uri'
1
+ require "uri"
2
2
  begin
3
- require 'statsd-ruby'
3
+ require "statsd-ruby"
4
4
  rescue LoadError
5
- raise 'Gem statsd-ruby is required for logging metrics. Please add the gem "statsd-ruby" to your Gemfile.'
5
+ raise LoadError, 'Gem statsd-ruby is required for logging metrics. Please add the gem "statsd-ruby" to your Gemfile.'
6
6
  end
7
7
 
8
8
  module SemanticLogger
@@ -26,17 +26,17 @@ module SemanticLogger
26
26
  # metric: :statsd,
27
27
  # url: 'localhost:8125'
28
28
  # )
29
- def initialize(url: 'udp://localhost:8125')
29
+ def initialize(url: "udp://localhost:8125")
30
30
  @url = url
31
31
  end
32
32
 
33
33
  def reopen
34
34
  uri = URI.parse(@url)
35
- raise('Statsd only supports udp. Example: "udp://localhost:8125"') if uri.scheme != 'udp'
35
+ raise('Statsd only supports udp. Example: "udp://localhost:8125"') if uri.scheme != "udp"
36
36
 
37
37
  @statsd = ::Statsd.new(uri.host, uri.port)
38
- path = uri.path.chomp('/')
39
- @statsd.namespace = path.sub('/', '') if path != ''
38
+ path = uri.path.chomp("/")
39
+ @statsd.namespace = path.sub("/", "") if path != ""
40
40
  end
41
41
 
42
42
  def log(log)
@@ -13,11 +13,12 @@ module SemanticLogger
13
13
  # For example when an appender is not working etc..
14
14
  # By default logs to STDERR
15
15
  def self.logger
16
- @logger ||= begin
17
- l = SemanticLogger::Appender::File.new(io: STDERR, level: :warn)
18
- l.name = name
19
- l
20
- end
16
+ @logger ||=
17
+ begin
18
+ l = SemanticLogger::Appender::File.new(io: STDERR, level: :warn)
19
+ l.name = name
20
+ l
21
+ end
21
22
  end
22
23
 
23
24
  attr_reader :appenders
@@ -30,6 +31,7 @@ module SemanticLogger
30
31
  # Start the appender thread
31
32
  def start
32
33
  return false if active?
34
+
33
35
  thread
34
36
  true
35
37
  end
@@ -23,7 +23,7 @@ module SemanticLogger
23
23
  class Minitest < ::Minitest::AbstractReporter
24
24
  include SemanticLogger::Loggable
25
25
 
26
- logger.name = 'Minitest'
26
+ logger.name = "Minitest"
27
27
 
28
28
  attr_accessor :io
29
29
 
@@ -33,11 +33,11 @@ module SemanticLogger
33
33
 
34
34
  def after_test(test)
35
35
  if test.error?
36
- logger.benchmark_error("FAIL #{test.class_name} #{test.name}", duration: test.time * 1_000, metric: 'minitest/fail')
36
+ logger.benchmark_error("FAIL #{test.class_name} #{test.name}", duration: test.time * 1_000, metric: "minitest/fail")
37
37
  elsif test.skipped?
38
- logger.benchmark_warn("SKIP #{test.class_name} #{test.name}", duration: test.time * 1_000, metric: 'minitest/skip')
38
+ logger.benchmark_warn("SKIP #{test.class_name} #{test.name}", duration: test.time * 1_000, metric: "minitest/skip")
39
39
  else
40
- logger.benchmark_info("PASS #{test.class_name} #{test.name}", duration: test.time * 1_000, metric: 'minitest/pass')
40
+ logger.benchmark_info("PASS #{test.class_name} #{test.name}", duration: test.time * 1_000, metric: "minitest/pass")
41
41
  end
42
42
  end
43
43
  end