semantic_logger 4.6.1 → 4.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +7 -7
  3. data/lib/semantic_logger.rb +23 -22
  4. data/lib/semantic_logger/appender.rb +32 -33
  5. data/lib/semantic_logger/appender/async.rb +9 -8
  6. data/lib/semantic_logger/appender/async_batch.rb +4 -2
  7. data/lib/semantic_logger/appender/bugsnag.rb +43 -30
  8. data/lib/semantic_logger/appender/elasticsearch.rb +10 -10
  9. data/lib/semantic_logger/appender/elasticsearch_http.rb +4 -4
  10. data/lib/semantic_logger/appender/file.rb +2 -1
  11. data/lib/semantic_logger/appender/graylog.rb +12 -10
  12. data/lib/semantic_logger/appender/honeybadger.rb +3 -3
  13. data/lib/semantic_logger/appender/http.rb +20 -18
  14. data/lib/semantic_logger/appender/kafka.rb +5 -5
  15. data/lib/semantic_logger/appender/mongodb.rb +6 -6
  16. data/lib/semantic_logger/appender/new_relic.rb +2 -2
  17. data/lib/semantic_logger/appender/rabbitmq.rb +5 -5
  18. data/lib/semantic_logger/appender/sentry.rb +7 -7
  19. data/lib/semantic_logger/appender/splunk.rb +5 -5
  20. data/lib/semantic_logger/appender/splunk_http.rb +4 -4
  21. data/lib/semantic_logger/appender/syslog.rb +20 -14
  22. data/lib/semantic_logger/appender/tcp.rb +5 -5
  23. data/lib/semantic_logger/appender/udp.rb +2 -2
  24. data/lib/semantic_logger/appenders.rb +11 -11
  25. data/lib/semantic_logger/base.rb +52 -23
  26. data/lib/semantic_logger/formatters.rb +11 -11
  27. data/lib/semantic_logger/formatters/base.rb +8 -3
  28. data/lib/semantic_logger/formatters/color.rb +10 -6
  29. data/lib/semantic_logger/formatters/default.rb +18 -5
  30. data/lib/semantic_logger/formatters/fluentd.rb +3 -3
  31. data/lib/semantic_logger/formatters/json.rb +1 -1
  32. data/lib/semantic_logger/formatters/raw.rb +31 -7
  33. data/lib/semantic_logger/formatters/signalfx.rb +10 -9
  34. data/lib/semantic_logger/formatters/syslog.rb +7 -6
  35. data/lib/semantic_logger/formatters/syslog_cee.rb +7 -6
  36. data/lib/semantic_logger/jruby/garbage_collection_logger.rb +4 -2
  37. data/lib/semantic_logger/levels.rb +9 -7
  38. data/lib/semantic_logger/log.rb +52 -60
  39. data/lib/semantic_logger/logger.rb +6 -8
  40. data/lib/semantic_logger/metric/new_relic.rb +3 -3
  41. data/lib/semantic_logger/metric/signalfx.rb +3 -3
  42. data/lib/semantic_logger/metric/statsd.rb +7 -7
  43. data/lib/semantic_logger/processor.rb +7 -5
  44. data/lib/semantic_logger/reporters/minitest.rb +4 -4
  45. data/lib/semantic_logger/semantic_logger.rb +24 -11
  46. data/lib/semantic_logger/subscriber.rb +6 -5
  47. data/lib/semantic_logger/sync.rb +12 -0
  48. data/lib/semantic_logger/sync_processor.rb +43 -0
  49. data/lib/semantic_logger/utils.rb +6 -6
  50. data/lib/semantic_logger/version.rb +1 -1
  51. metadata +9 -7
@@ -1,13 +1,13 @@
1
- require 'json'
1
+ require "json"
2
2
  module SemanticLogger
3
3
  module Formatters
4
4
  class Signalfx < Base
5
- attr_accessor :token, :dimensions, :hash, :log, :logger, :gauge_name, :counter_name
5
+ attr_accessor :token, :dimensions, :hash, :gauge_name, :counter_name
6
6
 
7
7
  def initialize(token:,
8
8
  dimensions: nil,
9
- gauge_name: 'Application.average',
10
- counter_name: 'Application.counter',
9
+ gauge_name: "Application.average",
10
+ counter_name: "Application.counter",
11
11
  time_format: :ms,
12
12
  **args)
13
13
 
@@ -23,19 +23,19 @@ module SemanticLogger
23
23
  # Strip leading '/'
24
24
  # Convert remaining '/' to '.'
25
25
  def metric
26
- name = log.metric.to_s.sub(/\A\/+/, '')
26
+ name = log.metric.to_s.sub(%r{\A/+}, "")
27
27
  if log.dimensions
28
- name.tr!('/', '.')
28
+ name.tr!("/", ".")
29
29
  hash[:metric] = name
30
30
  else
31
31
  # Extract class and action from metric name
32
- names = name.split('/')
32
+ names = name.split("/")
33
33
  h = (hash[:dimensions] ||= {})
34
34
  if names.size > 1
35
35
  h[:action] = names.pop
36
- h[:class] = names.join('::')
36
+ h[:class] = names.join("::")
37
37
  else
38
- h[:class] = 'Unknown'
38
+ h[:class] = "Unknown"
39
39
  h[:action] = names.first || log.metric
40
40
  end
41
41
 
@@ -67,6 +67,7 @@ module SemanticLogger
67
67
  name = name.to_sym
68
68
  value = value.to_s
69
69
  next if value.empty?
70
+
70
71
  h[name] = value if dimensions&.include?(name)
71
72
  end
72
73
  end
@@ -1,13 +1,13 @@
1
1
  begin
2
- require 'syslog_protocol'
2
+ require "syslog_protocol"
3
3
  rescue LoadError
4
- raise LoadError.new('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 Syslog < Default
10
- attr_accessor :level_map, :facility
10
+ attr_accessor :level_map, :facility, :max_size
11
11
 
12
12
  # Default level map for every log level
13
13
  #
@@ -50,9 +50,10 @@ module SemanticLogger
50
50
  # Example:
51
51
  # # Change the warn level to LOG_NOTICE level instead of a the default of LOG_WARNING.
52
52
  # SemanticLogger.add_appender(appender: :syslog, level_map: {warn: ::Syslog::LOG_NOTICE})
53
- def initialize(facility: ::Syslog::LOG_USER, level_map: LevelMap.new)
53
+ def initialize(facility: ::Syslog::LOG_USER, level_map: LevelMap.new, max_size: Integer)
54
54
  @facility = facility
55
55
  @level_map = level_map.is_a?(LevelMap) ? level_map : LevelMap.new(level_map)
56
+ @max_size = max_size
56
57
  super()
57
58
  end
58
59
 
@@ -73,11 +74,11 @@ module SemanticLogger
73
74
  packet = SyslogProtocol::Packet.new
74
75
  packet.hostname = logger.host
75
76
  packet.facility = facility
76
- packet.tag = logger.application.delete(' ')
77
+ packet.tag = logger.application.delete(" ")
77
78
  packet.content = message
78
79
  packet.time = log.time
79
80
  packet.severity = level_map[log.level]
80
- packet.to_s
81
+ packet.assemble(@max_size)
81
82
  end
82
83
  end
83
84
  end
@@ -1,13 +1,13 @@
1
1
  begin
2
- require 'syslog_protocol'
2
+ require "syslog_protocol"
3
3
  rescue LoadError
4
- raise LoadError.new('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 LoadError.new('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