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,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