semantic_logger 4.5.0 → 4.7.1

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 (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,12 +1,10 @@
1
- require 'json'
1
+ require "json"
2
2
  module SemanticLogger
3
3
  module Formatters
4
4
  class Json < Raw
5
5
  # Default JSON time format is ISO8601
6
- def initialize(time_format: :iso_8601, log_host: true, log_application: true, time_key: :timestamp,
7
- precision: PRECISION)
8
- super(time_format: time_format, log_host: log_host, log_application: log_application, time_key: time_key,
9
- precision: precision)
6
+ def initialize(time_format: :iso_8601, time_key: :timestamp, **args)
7
+ super(time_format: time_format, time_key: time_key, **args)
10
8
  end
11
9
 
12
10
  # Returns log messages in JSON format
@@ -1,14 +1,14 @@
1
- require 'json'
1
+ require "json"
2
2
  module SemanticLogger
3
3
  module Formatters
4
4
  class Raw < Base
5
5
  # Fields are added by populating this hash.
6
- attr_accessor :hash, :log, :logger, :time_key
6
+ attr_accessor :hash, :time_key
7
7
 
8
8
  # By default Raw formatter does not reformat the time
9
- def initialize(time_format: :none, log_host: true, log_application: true, time_key: :time, precision: PRECISION)
9
+ def initialize(time_format: :none, time_key: :time, **args)
10
10
  @time_key = time_key
11
- super(time_format: time_format, log_host: log_host, log_application: log_application, precision: precision)
11
+ super(time_format: time_format, **args)
12
12
  end
13
13
 
14
14
  # Host name
@@ -18,7 +18,12 @@ module SemanticLogger
18
18
 
19
19
  # Application name
20
20
  def application
21
- hash[:application] = logger.application if log_application && logger.application
21
+ hash[:application] = logger.application if log_application && logger && logger.application
22
+ end
23
+
24
+ # Environment
25
+ def environment
26
+ hash[:environment] = logger.environment if log_environment && logger && logger.environment
22
27
  end
23
28
 
24
29
  # Date & time
@@ -32,11 +37,18 @@ module SemanticLogger
32
37
  hash[:level_index] = log.level_index
33
38
  end
34
39
 
35
- # Process info
36
- def process_info
37
- hash[:pid] = $$
40
+ # Process ID
41
+ def pid
42
+ hash[:pid] = super
43
+ end
44
+
45
+ # Name of the thread that logged the message.
46
+ def thread_name
38
47
  hash[:thread] = log.thread_name
48
+ end
39
49
 
50
+ # Ruby file name and line number that logged the message.
51
+ def file_name_and_line
40
52
  file, line = log.file_name_and_line
41
53
  return unless file
42
54
 
@@ -80,6 +92,7 @@ module SemanticLogger
80
92
  # Exception
81
93
  def exception
82
94
  return unless log.exception
95
+
83
96
  root = hash
84
97
  log.each_exception do |exception, i|
85
98
  name = i.zero? ? :exception : :cause
@@ -88,7 +101,7 @@ module SemanticLogger
88
101
  message: exception.message,
89
102
  stack_trace: exception.backtrace
90
103
  }
91
- root = root[name]
104
+ root = root[name]
92
105
  end
93
106
  end
94
107
 
@@ -104,7 +117,23 @@ module SemanticLogger
104
117
  self.log = log
105
118
  self.logger = logger
106
119
 
107
- host; application; time; level; process_info; duration; tags; named_tags; name; message; payload; exception; metric
120
+ host
121
+ application
122
+ environment
123
+ time
124
+ level
125
+ pid
126
+ thread_name
127
+ file_name_and_line
128
+ duration
129
+ tags
130
+ named_tags
131
+ name
132
+ message
133
+ payload
134
+ exception
135
+ metric
136
+
108
137
  hash
109
138
  end
110
139
  end
@@ -1,49 +1,41 @@
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, :environment
5
+ attr_accessor :token, :dimensions, :hash, :gauge_name, :counter_name
6
6
 
7
7
  def initialize(token:,
8
8
  dimensions: nil,
9
- log_host: true,
10
- log_application: true,
11
- gauge_name: 'Application.average',
12
- counter_name: 'Application.counter',
13
- environment: true,
14
- precision: PRECISION)
9
+ gauge_name: "Application.average",
10
+ counter_name: "Application.counter",
11
+ time_format: :ms,
12
+ **args)
15
13
 
16
14
  @token = token
17
15
  @dimensions = dimensions.map(&:to_sym) if dimensions
18
16
  @gauge_name = gauge_name
19
17
  @counter_name = counter_name
20
18
 
21
- if environment == true
22
- @environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
23
- elsif environment
24
- @environment = environment
25
- end
26
-
27
- super(time_format: :ms, log_host: log_host, log_application: log_application, precision: precision)
19
+ super(time_format: time_format, **args)
28
20
  end
29
21
 
30
22
  # Create SignalFx friendly metric.
31
23
  # Strip leading '/'
32
24
  # Convert remaining '/' to '.'
33
25
  def metric
34
- name = log.metric.to_s.sub(/\A\/+/, '')
26
+ name = log.metric.to_s.sub(%r{\A/+}, "")
35
27
  if log.dimensions
36
- name.tr!('/', '.')
28
+ name.tr!("/", ".")
37
29
  hash[:metric] = name
38
30
  else
39
31
  # Extract class and action from metric name
40
- names = name.split('/')
32
+ names = name.split("/")
41
33
  h = (hash[:dimensions] ||= {})
42
34
  if names.size > 1
43
35
  h[:action] = names.pop
44
- h[:class] = names.join('::')
36
+ h[:class] = names.join("::")
45
37
  else
46
- h[:class] = 'Unknown'
38
+ h[:class] = "Unknown"
47
39
  h[:action] = names.first || log.metric
48
40
  end
49
41
 
@@ -75,12 +67,13 @@ module SemanticLogger
75
67
  name = name.to_sym
76
68
  value = value.to_s
77
69
  next if value.empty?
70
+
78
71
  h[name] = value if dimensions&.include?(name)
79
72
  end
80
73
  end
81
74
  h[:host] = logger.host if log_host && logger.host
82
75
  h[:application] = logger.application if log_application && logger.application
83
- h[:environment] = environment if environment
76
+ h[:environment] = logger.environment if log_environment && logger.environment
84
77
  end
85
78
 
86
79
  # Returns [Hash] log message in Signalfx format.
@@ -1,7 +1,7 @@
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
@@ -73,7 +73,7 @@ module SemanticLogger
73
73
  packet = SyslogProtocol::Packet.new
74
74
  packet.hostname = logger.host
75
75
  packet.facility = facility
76
- packet.tag = logger.application.delete(' ')
76
+ packet.tag = logger.application.delete(" ")
77
77
  packet.content = message
78
78
  packet.time = log.time
79
79
  packet.severity = level_map[log.level]
@@ -1,7 +1,7 @@
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
@@ -45,7 +45,7 @@ module SemanticLogger
45
45
  packet = SyslogProtocol::Packet.new
46
46
  packet.hostname = logger.host
47
47
  packet.facility = facility
48
- packet.tag = logger.application.delete(' ')
48
+ packet.tag = logger.application.delete(" ")
49
49
  packet.content = message
50
50
  packet.time = log.time
51
51
  packet.severity = level_map[log.level]
@@ -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,19 +266,8 @@ module SemanticLogger
279
266
  !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
280
267
  end
281
268
 
282
- # DEPRECATED
283
- alias has_payload? payload?
284
-
285
- # DEPRECATED
286
- def formatted_time
287
- time.strftime(Formatters::Base.build_time_format)
288
- end
289
-
290
- DeprecatedLogger = Struct.new(:host, :application)
291
-
292
- # DEPRECATED: Use SemanticLogger::Formatters::Raw
293
- def to_h(host = SemanticLogger.host, application = SemanticLogger.application)
294
- logger = DeprecatedLogger.new(host, application)
269
+ def to_h(host = SemanticLogger.host, application = SemanticLogger.application, environment = SemanticLogger.environment)
270
+ logger = Struct.new(:host, :application, :environment).new(host, application, environment)
295
271
  SemanticLogger::Formatters::Raw.new.call(self, logger)
296
272
  end
297
273