semantic_logger 4.5.0 → 4.12.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -21
  3. data/Rakefile +7 -7
  4. data/lib/semantic_logger/ansi_colors.rb +0 -10
  5. data/lib/semantic_logger/appender/async.rb +12 -10
  6. data/lib/semantic_logger/appender/async_batch.rb +7 -3
  7. data/lib/semantic_logger/appender/bugsnag.rb +43 -30
  8. data/lib/semantic_logger/appender/elasticsearch.rb +34 -15
  9. data/lib/semantic_logger/appender/elasticsearch_http.rb +4 -4
  10. data/lib/semantic_logger/appender/file.rb +249 -67
  11. data/lib/semantic_logger/appender/graylog.rb +15 -10
  12. data/lib/semantic_logger/appender/honeybadger.rb +3 -3
  13. data/lib/semantic_logger/appender/http.rb +41 -20
  14. data/lib/semantic_logger/appender/io.rb +68 -0
  15. data/lib/semantic_logger/appender/kafka.rb +46 -31
  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/sentry_ruby.rb +138 -0
  21. data/lib/semantic_logger/appender/splunk.rb +7 -5
  22. data/lib/semantic_logger/appender/splunk_http.rb +6 -5
  23. data/lib/semantic_logger/appender/syslog.rb +23 -15
  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/appender/wrapper.rb +3 -2
  27. data/lib/semantic_logger/appender.rb +62 -65
  28. data/lib/semantic_logger/appenders.rb +36 -53
  29. data/lib/semantic_logger/base.rb +61 -39
  30. data/lib/semantic_logger/formatters/base.rb +16 -6
  31. data/lib/semantic_logger/formatters/color.rb +14 -15
  32. data/lib/semantic_logger/formatters/default.rb +18 -5
  33. data/lib/semantic_logger/formatters/fluentd.rb +7 -18
  34. data/lib/semantic_logger/formatters/json.rb +3 -5
  35. data/lib/semantic_logger/formatters/logfmt.rb +77 -0
  36. data/lib/semantic_logger/formatters/raw.rb +39 -10
  37. data/lib/semantic_logger/formatters/signalfx.rb +14 -21
  38. data/lib/semantic_logger/formatters/syslog.rb +8 -6
  39. data/lib/semantic_logger/formatters/syslog_cee.rb +9 -7
  40. data/lib/semantic_logger/formatters.rb +13 -13
  41. data/lib/semantic_logger/jruby/garbage_collection_logger.rb +4 -2
  42. data/lib/semantic_logger/levels.rb +9 -7
  43. data/lib/semantic_logger/log.rb +58 -73
  44. data/lib/semantic_logger/loggable.rb +8 -1
  45. data/lib/semantic_logger/logger.rb +19 -11
  46. data/lib/semantic_logger/metric/new_relic.rb +3 -3
  47. data/lib/semantic_logger/metric/signalfx.rb +3 -3
  48. data/lib/semantic_logger/metric/statsd.rb +7 -7
  49. data/lib/semantic_logger/processor.rb +9 -7
  50. data/lib/semantic_logger/reporters/minitest.rb +4 -4
  51. data/lib/semantic_logger/semantic_logger.rb +57 -23
  52. data/lib/semantic_logger/subscriber.rb +24 -7
  53. data/lib/semantic_logger/sync.rb +12 -0
  54. data/lib/semantic_logger/sync_processor.rb +58 -0
  55. data/lib/semantic_logger/test/capture_log_events.rb +34 -0
  56. data/lib/semantic_logger/utils.rb +32 -13
  57. data/lib/semantic_logger/version.rb +1 -1
  58. data/lib/semantic_logger.rb +27 -22
  59. metadata +15 -10
@@ -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,54 @@ 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
+ return false if duration && ((duration < min_duration) && exception.nil?)
118
+
116
119
  if backtrace
117
120
  self.backtrace = Utils.extract_backtrace(backtrace)
118
121
  elsif level_index >= SemanticLogger.backtrace_level_index
119
- self.backtrace = Utils.extract_backtrace
120
- end
121
-
122
- if metric
123
- self.metric = metric
124
- self.metric_amount = metric_amount
125
- self.dimensions = dimensions
122
+ self.backtrace = Utils.extract_backtrace(caller)
126
123
  end
127
124
 
128
125
  true
129
126
  end
130
127
 
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
128
+ # Assign known keys to self, all other keys to the payload.
129
+ def assign_hash(hash)
130
+ self.payload ||= {}
131
+ hash.each_pair do |key, value|
132
+ if respond_to?("#{key}=".to_sym)
133
+ public_send("#{key}=".to_sym, value)
134
+ else
135
+ payload[key] = value
136
+ end
150
137
  end
138
+ self.payload = nil if payload.empty?
139
+ self
140
+ end
151
141
 
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)
161
- else
162
- assign(message: message, payload: result, exception: exception)
142
+ # Extract the arguments from a Hash Payload
143
+ def extract_arguments(payload, message = nil)
144
+ raise(ArgumentError, "payload must be a Hash") unless payload.is_a?(Hash)
145
+
146
+ message = nil if message == ""
147
+ return payload if payload.key?(:payload)
148
+
149
+ new_payload = {}
150
+ args = {}
151
+ payload.each_pair do |key, value|
152
+ # Supplied message takes precedence
153
+ if (key == :message) && !message.nil?
154
+ new_payload[key] = value
155
+ next
163
156
  end
164
- else
165
- assign(message: message, payload: payload, exception: exception)
157
+
158
+ NON_PAYLOAD_KEYS.include?(key) ? args[key] = value : new_payload[key] = value
166
159
  end
160
+ args[:payload] = new_payload unless new_payload.empty?
161
+ args[:message] = message if message
162
+ args
167
163
  end
168
164
 
169
165
  MAX_EXCEPTIONS_TO_UNWRAP = 5
@@ -191,7 +187,7 @@ module SemanticLogger
191
187
 
192
188
  # Returns [String] the exception backtrace including all of the child / caused by exceptions
193
189
  def backtrace_to_s
194
- trace = ''
190
+ trace = ""
195
191
  each_exception do |exception, i|
196
192
  if i.zero?
197
193
  trace = (exception.backtrace || []).join("\n")
@@ -212,6 +208,7 @@ module SemanticLogger
212
208
  else
213
209
  def duration_to_s
214
210
  return unless duration
211
+
215
212
  duration < 10.0 ? "#{format('%.3f', duration)}ms" : "#{format('%.1f', duration)}ms"
216
213
  end
217
214
  end
@@ -219,13 +216,14 @@ module SemanticLogger
219
216
  # Returns [String] the duration in human readable form
220
217
  def duration_human
221
218
  return nil unless duration
219
+
222
220
  seconds = duration / 1000
223
221
  if seconds >= 86_400.0 # 1 day
224
222
  "#{(seconds / 86_400).to_i}d #{Time.at(seconds).strftime('%-Hh %-Mm')}"
225
223
  elsif seconds >= 3600.0 # 1 hour
226
- Time.at(seconds).strftime('%-Hh %-Mm')
224
+ Time.at(seconds).strftime("%-Hh %-Mm")
227
225
  elsif seconds >= 60.0 # 1 minute
228
- Time.at(seconds).strftime('%-Mm %-Ss')
226
+ Time.at(seconds).strftime("%-Mm %-Ss")
229
227
  elsif seconds >= 1.0 # 1 second
230
228
  "#{format('%.3f', seconds)}s"
231
229
  else
@@ -238,9 +236,7 @@ module SemanticLogger
238
236
  level.to_s[0..0].upcase
239
237
  end
240
238
 
241
- # Returns [String] the available process info
242
- # Example:
243
- # 18934:thread 23 test_logging.rb:51
239
+ # DEPRECATED
244
240
  def process_info(thread_name_length = 30)
245
241
  file, line = file_name_and_line(true)
246
242
  file_name = " #{file}:#{line}" if file
@@ -248,7 +244,7 @@ module SemanticLogger
248
244
  "#{$$}:#{format("%.#{thread_name_length}s", thread_name)}#{file_name}"
249
245
  end
250
246
 
251
- CALLER_REGEXP = /^(.*):(\d+).*/
247
+ CALLER_REGEXP = /^(.*):(\d+).*/.freeze
252
248
 
253
249
  # Extract the filename and line number from the last entry in the supplied backtrace
254
250
  def extract_file_and_line(stack, short_name = false)
@@ -265,7 +261,7 @@ module SemanticLogger
265
261
 
266
262
  # Strip the standard Rails colorizing from the logged message
267
263
  def cleansed_message
268
- message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, '').strip
264
+ message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, "").strip
269
265
  end
270
266
 
271
267
  # Return the payload in text form
@@ -279,19 +275,8 @@ module SemanticLogger
279
275
  !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
280
276
  end
281
277
 
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)
278
+ def to_h(host = SemanticLogger.host, application = SemanticLogger.application, environment = SemanticLogger.environment)
279
+ logger = Struct.new(:host, :application, :environment).new(host, application, environment)
295
280
  SemanticLogger::Formatters::Raw.new.call(self, logger)
296
281
  end
297
282
 
@@ -8,7 +8,7 @@
8
8
  # Example:
9
9
  # require 'semantic_logger'
10
10
  # SemanticLogger.default_level = :debug
11
- # SemanticLogger.add_appender(io: STDOUT, formatter: :color)
11
+ # SemanticLogger.add_appender(io: $stdout, formatter: :color)
12
12
  #
13
13
  # class ExternalSupplier
14
14
  # # Create class and instance logger methods
@@ -32,7 +32,14 @@ module SemanticLogger
32
32
  module Loggable
33
33
  def self.included(base)
34
34
  base.extend ClassMethods
35
+ base.singleton_class.class_eval do
36
+ undef_method :logger if method_defined?(:logger)
37
+ undef_method :logger= if method_defined?(:logger=)
38
+ end
35
39
  base.class_eval do
40
+ undef_method :logger if method_defined?(:logger)
41
+ undef_method :logger= if method_defined?(:logger=)
42
+
36
43
  # Returns [SemanticLogger::Logger] class level logger
37
44
  def self.logger
38
45
  @semantic_logger ||= SemanticLogger[self]
@@ -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,19 @@ module SemanticLogger
21
21
  end
22
22
 
23
23
  def self.processor
24
- @processor
24
+ @processor ||= Processor.new
25
+ end
26
+
27
+ # Switch to the synchronous processor
28
+ def self.sync!
29
+ return if @processor.is_a?(SyncProcessor)
30
+
31
+ @processor = SyncProcessor.new(@processor&.appenders)
32
+ end
33
+
34
+ # Running without the background logging thread?
35
+ def self.sync?
36
+ processor.is_a?(SyncProcessor)
25
37
  end
26
38
 
27
39
  # Returns a Logger instance
@@ -63,20 +75,16 @@ module SemanticLogger
63
75
  Logger.processor.log(log)
64
76
  end
65
77
 
66
- private
67
-
68
- @processor = Processor.new
78
+ @processor = nil
69
79
  @subscribers = nil
70
80
 
71
81
  def self.call_subscribers(log)
72
82
  return unless @subscribers
73
83
 
74
84
  @subscribers.each do |subscriber|
75
- begin
76
- subscriber.call(log)
77
- rescue Exception => exc
78
- self.class.processor.logger.error('Exception calling :on_log subscriber', exc)
79
- end
85
+ subscriber.call(log)
86
+ rescue Exception => e
87
+ processor.logger.error("Exception calling :on_log subscriber", e)
80
88
  end
81
89
  end
82
90
  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)
@@ -1,7 +1,7 @@
1
1
  module SemanticLogger
2
2
  # Thread that submits and processes log requests
3
3
  class Processor < Appender::Async
4
- # Allow the internal logger to be overridden from its default of STDERR
4
+ # Allow the internal logger to be overridden from its default of $stderr
5
5
  # Can be replaced with another Ruby logger or Rails logger, but never to
6
6
  # SemanticLogger::Logger itself since it is for reporting problems
7
7
  # while trying to log to the various appenders
@@ -11,13 +11,14 @@ module SemanticLogger
11
11
 
12
12
  # Internal logger for SemanticLogger
13
13
  # For example when an appender is not working etc..
14
- # By default logs to STDERR
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::IO.new($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
@@ -1,5 +1,5 @@
1
- require 'concurrent'
2
- require 'socket'
1
+ require "concurrent"
2
+ require "socket"
3
3
 
4
4
  module SemanticLogger
5
5
  # Logging levels in order of most detailed to most severe
@@ -52,7 +52,7 @@ module SemanticLogger
52
52
  # Returns [String] name of this host for logging purposes
53
53
  # Note: Not all appenders use `host`
54
54
  def self.host
55
- @host ||= Socket.gethostname.force_encoding('UTF-8')
55
+ @host ||= Socket.gethostname.force_encoding("UTF-8")
56
56
  end
57
57
 
58
58
  # Override the default host name
@@ -71,7 +71,19 @@ module SemanticLogger
71
71
  @application = application
72
72
  end
73
73
 
74
- @application = 'Semantic Logger'
74
+ # Returns [String] name of this environment for logging purposes
75
+ # Note: Not all appenders use `environment`
76
+ def self.environment
77
+ @environment
78
+ end
79
+
80
+ # Override the default environment
81
+ def self.environment=(environment)
82
+ @environment = environment
83
+ end
84
+
85
+ @application = ENV["SEMANTIC_LOGGER_APP"] || "Semantic Logger"
86
+ @environment = ENV["SEMANTIC_LOGGER_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"]
75
87
 
76
88
  # Add a new logging appender as a new destination for all log messages
77
89
  # emitted from Semantic Logger
@@ -89,7 +101,7 @@ module SemanticLogger
89
101
  # Or,
90
102
  # io: [IO]
91
103
  # An IO Stream to log to.
92
- # For example STDOUT, STDERR, etc.
104
+ # For example $stdout, $stderr, etc.
93
105
  #
94
106
  # Or,
95
107
  # appender: [Symbol|SemanticLogger::Subscriber]
@@ -110,7 +122,7 @@ module SemanticLogger
110
122
  # Default: SemanticLogger.default_level
111
123
  #
112
124
  # formatter: [Symbol|Object|Proc]
113
- # Any of the following symbol values: :default, :color, :json
125
+ # Any of the following symbol values: :default, :color, :json, :logfmt, etc...
114
126
  # Or,
115
127
  # An instance of a class that implements #call
116
128
  # Or,
@@ -126,14 +138,14 @@ module SemanticLogger
126
138
  # Examples:
127
139
  #
128
140
  # # Send all logging output to Standard Out (Screen)
129
- # SemanticLogger.add_appender(io: STDOUT)
141
+ # SemanticLogger.add_appender(io: $stdout)
130
142
  #
131
143
  # # Send all logging output to a file
132
144
  # SemanticLogger.add_appender(file_name: 'logfile.log')
133
145
  #
134
146
  # # Send all logging output to a file and only :info and above to standard output
135
147
  # SemanticLogger.add_appender(file_name: 'logfile.log')
136
- # SemanticLogger.add_appender(io: STDOUT, level: :info)
148
+ # SemanticLogger.add_appender(io: $stdout, level: :info)
137
149
  #
138
150
  # Log to log4r, Logger, etc.:
139
151
  #
@@ -142,7 +154,7 @@ module SemanticLogger
142
154
  # require 'semantic_logger'
143
155
  #
144
156
  # # Built-in Ruby logger
145
- # log = Logger.new(STDOUT)
157
+ # log = Logger.new($stdout)
146
158
  # log.level = Logger::DEBUG
147
159
  #
148
160
  # SemanticLogger.default_level = :debug
@@ -151,8 +163,8 @@ module SemanticLogger
151
163
  # logger = SemanticLogger['Example']
152
164
  # logger.info "Hello World"
153
165
  # logger.debug("Login time", user: 'Joe', duration: 100, ip_address: '127.0.0.1')
154
- def self.add_appender(options, deprecated_level = nil, &block)
155
- appender = Logger.processor.appenders.add(options, deprecated_level, &block)
166
+ def self.add_appender(**args, &block)
167
+ appender = appenders.add(**args, &block)
156
168
  # Start appender thread if it is not already running
157
169
  Logger.processor.start
158
170
  appender
@@ -161,7 +173,15 @@ module SemanticLogger
161
173
  # Remove an existing appender
162
174
  # Currently only supports appender instances
163
175
  def self.remove_appender(appender)
164
- Logger.processor.appenders.delete(appender)
176
+ return unless appender
177
+
178
+ appenders.delete(appender)
179
+ appender.close
180
+ end
181
+
182
+ # Clear out all previously registered appenders
183
+ def self.clear_appenders!
184
+ Logger.processor.close
165
185
  end
166
186
 
167
187
  # Returns [SemanticLogger::Subscriber] a copy of the list of active
@@ -169,7 +189,7 @@ module SemanticLogger
169
189
  # Use SemanticLogger.add_appender and SemanticLogger.remove_appender
170
190
  # to manipulate the active appenders list
171
191
  def self.appenders
172
- Logger.processor.appenders.to_a
192
+ Logger.processor.appenders
173
193
  end
174
194
 
175
195
  # Flush all queued log entries disk, database, etc.
@@ -233,9 +253,9 @@ module SemanticLogger
233
253
  # When the log_level_signal is raised on this process, the global default log level
234
254
  # rotates through the following log levels in the following order, starting
235
255
  # from the current global default level:
236
- # :warn, :info, :debug, :trace
256
+ # :fatal, :error, :warn, :info, :debug, :trace
237
257
  #
238
- # If the current level is :trace it wraps around back to :warn
258
+ # If the current level is :trace it wraps around back to :fatal
239
259
  #
240
260
  # 2. Logging a Ruby thread dump
241
261
  #
@@ -256,22 +276,24 @@ module SemanticLogger
256
276
  # Note:
257
277
  # To only register one of the signal handlers, set the other to nil
258
278
  # Set gc_log_microseconds to nil to not enable JRuby Garbage collections
259
- def self.add_signal_handler(log_level_signal = 'USR2', thread_dump_signal = 'TTIN', gc_log_microseconds = 100_000)
279
+ def self.add_signal_handler(log_level_signal = "USR2", thread_dump_signal = "TTIN", gc_log_microseconds = 100_000)
260
280
  if log_level_signal
261
281
  Signal.trap(log_level_signal) do
262
- index = default_level == :trace ? LEVELS.find_index(:error) : LEVELS.find_index(default_level)
263
- new_level = LEVELS[index - 1]
264
- self['SemanticLogger'].warn "Changed global default log level to #{new_level.inspect}"
282
+ current_level_index = LEVELS.find_index(default_level)
283
+ new_level_index = current_level_index == 0 ? LEVELS.size - 1 : current_level_index - 1
284
+ new_level = LEVELS[new_level_index]
265
285
  self.default_level = new_level
286
+ self["SemanticLogger"].warn "Changed global default log level to #{new_level.inspect}"
266
287
  end
267
288
  end
268
289
 
269
290
  if thread_dump_signal
270
291
  Signal.trap(thread_dump_signal) do
271
- logger = SemanticLogger['Thread Dump']
292
+ logger = SemanticLogger["Thread Dump"]
272
293
  Thread.list.each do |thread|
273
294
  # MRI re-uses the main thread for signals, JRuby uses `SIGTTIN handler` thread.
274
295
  next if defined?(JRuby) && (thread == Thread.current)
296
+
275
297
  logger.backtrace(thread: thread)
276
298
  end
277
299
  end
@@ -290,7 +312,7 @@ module SemanticLogger
290
312
  # If the tag being supplied is definitely a string then this fast
291
313
  # tag api can be used for short lived tags
292
314
  def self.fast_tag(tag)
293
- return yield if tag.nil? || tag == ''
315
+ return yield if tag.nil? || tag == ""
294
316
 
295
317
  t = Thread.current[:semantic_logger_tags] ||= []
296
318
  begin
@@ -367,7 +389,7 @@ module SemanticLogger
367
389
  # :nodoc
368
390
  def self.named_tagged(hash)
369
391
  return yield if hash.nil? || hash.empty?
370
- raise(ArgumentError, '#named_tagged only accepts named parameters (Hash)') unless hash.is_a?(Hash)
392
+ raise(ArgumentError, "#named_tagged only accepts named parameters (Hash)") unless hash.is_a?(Hash)
371
393
 
372
394
  begin
373
395
  push_named_tags(hash)
@@ -476,11 +498,23 @@ module SemanticLogger
476
498
  Thread.current[:semantic_logger_silence] || @default_level_index
477
499
  end
478
500
 
479
- private
501
+ # Run Semantic Logger in Synchronous mode.
502
+ #
503
+ # I.e. Instead of logging messages in a separate thread for better performance,
504
+ # log them using the current thread.
505
+ def self.sync!
506
+ Logger.sync!
507
+ end
508
+
509
+ # Running in synchronous mode?
510
+ def self.sync?
511
+ Logger.sync?
512
+ end
480
513
 
481
514
  # Initial default Level for all new instances of SemanticLogger::Logger
482
515
  @default_level = :info
483
516
  @default_level_index = Levels.index(@default_level)
484
517
  @backtrace_level = :error
485
518
  @backtrace_level_index = Levels.index(@backtrace_level)
519
+ @sync = false
486
520
  end