semantic_logger 3.0.1 → 3.1.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/semantic_logger.rb +29 -13
  4. data/lib/semantic_logger/ansi_colors.rb +27 -0
  5. data/lib/semantic_logger/appender/base.rb +54 -128
  6. data/lib/semantic_logger/appender/bugsnag.rb +29 -19
  7. data/lib/semantic_logger/appender/elasticsearch.rb +9 -9
  8. data/lib/semantic_logger/appender/file.rb +40 -18
  9. data/lib/semantic_logger/appender/graylog.rb +30 -26
  10. data/lib/semantic_logger/appender/http.rb +14 -19
  11. data/lib/semantic_logger/appender/mongodb.rb +20 -20
  12. data/lib/semantic_logger/appender/new_relic.rb +15 -15
  13. data/lib/semantic_logger/appender/splunk.rb +1 -1
  14. data/lib/semantic_logger/appender/splunk_http.rb +28 -25
  15. data/lib/semantic_logger/appender/syslog.rb +41 -42
  16. data/lib/semantic_logger/appender/wrapper.rb +19 -17
  17. data/lib/semantic_logger/base.rb +57 -32
  18. data/lib/semantic_logger/concerns/compatibility.rb +51 -0
  19. data/lib/semantic_logger/debug_as_trace_logger.rb +6 -2
  20. data/lib/semantic_logger/formatters/color.rb +66 -0
  21. data/lib/semantic_logger/formatters/default.rb +39 -0
  22. data/lib/semantic_logger/formatters/json.rb +16 -0
  23. data/lib/semantic_logger/jruby/garbage_collection_logger.rb +1 -1
  24. data/lib/semantic_logger/log.rb +13 -8
  25. data/lib/semantic_logger/loggable.rb +2 -2
  26. data/lib/semantic_logger/logger.rb +18 -13
  27. data/lib/semantic_logger/metrics/new_relic.rb +18 -0
  28. data/lib/semantic_logger/metrics/statsd.rb +48 -0
  29. data/lib/semantic_logger/semantic_logger.rb +122 -55
  30. data/lib/semantic_logger/version.rb +1 -1
  31. data/test/appender/bugsnag_test.rb +12 -3
  32. data/test/appender/mongodb_test.rb +6 -5
  33. data/test/appender/new_relic_test.rb +1 -1
  34. data/test/appender/splunk_http_test.rb +1 -0
  35. data/test/concerns/compatibility_test.rb +106 -0
  36. data/test/debug_as_trace_logger_test.rb +1 -1
  37. data/test/loggable_test.rb +1 -1
  38. data/test/logger_test.rb +183 -24
  39. metadata +12 -3
@@ -11,8 +11,12 @@ module SemanticLogger
11
11
  trace?
12
12
  end
13
13
 
14
+ def measure_debug(*args, &block)
15
+ measure_trace(*args, &block)
16
+ end
17
+
14
18
  def benchmark_debug(*args, &block)
15
- benchmark_trace(*args, &block)
19
+ measure_trace(*args, &block)
16
20
  end
17
21
  end
18
- end
22
+ end
@@ -0,0 +1,66 @@
1
+ # Load AwesomePrint if available
2
+ begin
3
+ require 'awesome_print'
4
+ rescue LoadError
5
+ end
6
+
7
+ module SemanticLogger
8
+ module Formatters
9
+ class Color
10
+ # Parameters:
11
+ # Any valid AwesomePrint option for rendering data.
12
+ # These options can also be changed be creating a `~/.aprc` file.
13
+ # See: https://github.com/michaeldv/awesome_print
14
+ #
15
+ # Note: The option :multiline is set to false if not supplied.
16
+ # Note: Has no effect if Awesome Print is not installed.
17
+ def initialize(options={})
18
+ @ai_options = options.dup
19
+ @ai_options[:multiline] = false unless @ai_options.has_key?(:multiline)
20
+ end
21
+
22
+ # Adds color to the default log formatter
23
+ # Example:
24
+ # SemanticLogger.add_appender(io: $stdout, formatter: :color)
25
+ def call(log, logger)
26
+ colors = SemanticLogger::AnsiColors
27
+ level_color = colors::LEVEL_MAP[log.level]
28
+
29
+ # Header with date, time, log level and process info
30
+ message = "#{log.formatted_time} #{level_color}#{log.level_to_s}#{colors::CLEAR} [#{log.process_info}]"
31
+
32
+ # Tags
33
+ message << ' ' << log.tags.collect { |tag| "[#{level_color}#{tag}#{colors::CLEAR}]" }.join(' ') if log.tags && (log.tags.size > 0)
34
+
35
+ # Duration
36
+ message << " (#{colors::BOLD}#{log.duration_human}#{colors::CLEAR})" if log.duration
37
+
38
+ # Class / app name
39
+ message << " #{level_color}#{log.name}#{colors::CLEAR}"
40
+
41
+ # Log message
42
+ message << " -- #{log.message}" if log.message
43
+
44
+ # Payload: Colorize the payload if the AwesomePrint gem is loaded
45
+ if log.has_payload?
46
+ payload = log.payload
47
+ message << ' -- ' <<
48
+ if !defined?(AwesomePrint) || !payload.respond_to?(:ai)
49
+ payload.inspect
50
+ else
51
+ payload.ai(@ai_options) rescue payload.inspect
52
+ end
53
+ end
54
+
55
+ # Exceptions
56
+ if log.exception
57
+ message << " -- Exception: #{colors::BOLD}#{log.exception.class}: #{log.exception.message}#{colors::CLEAR}\n"
58
+ message << log.backtrace_to_s
59
+ end
60
+ message
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+
@@ -0,0 +1,39 @@
1
+ module SemanticLogger
2
+ module Formatters
3
+ class Default
4
+ # Default log formatter
5
+ # Generates logs of the form:
6
+ # 2011-07-19 14:36:15.660 D [1149:ScriptThreadProcess] Rails -- Hello World
7
+ def call(log, logger)
8
+ # Header with date, time, log level and process info
9
+ message = "#{log.formatted_time} #{log.level_to_s} [#{log.process_info}]"
10
+
11
+ # Tags
12
+ message << ' ' << log.tags.collect { |tag| "[#{tag}]" }.join(' ') if log.tags && (log.tags.size > 0)
13
+
14
+ # Duration
15
+ message << " (#{log.duration_human})" if log.duration
16
+
17
+ # Class / app name
18
+ message << " #{log.name}"
19
+
20
+ # Log message
21
+ message << " -- #{log.message}" if log.message
22
+
23
+ # Payload
24
+ if payload = log.payload_to_s
25
+ message << ' -- ' << payload
26
+ end
27
+
28
+ # Exceptions
29
+ if log.exception
30
+ message << " -- Exception: #{log.exception.class}: #{log.exception.message}\n"
31
+ message << log.backtrace_to_s
32
+ end
33
+ message
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+ module SemanticLogger
3
+ module Formatters
4
+ class Json
5
+ # Returns log messages in JSON format
6
+ def call(log, logger)
7
+ h = log.to_h
8
+ h.delete(:time)
9
+ h[:timestamp] = log.time.utc.iso8601(defined?(JRuby) ? 3 : 6)
10
+ h.to_json
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+
@@ -18,7 +18,7 @@ module SemanticLogger
18
18
  gc_info = info.gc_info
19
19
  duration = gc_info.duration
20
20
  if duration >= @min_microseconds
21
- SemanticLogger['GarbageCollector'].benchmark_warn "Garbage Collection completed: #{info.gc_name} ##{gc_info.id}", duration: duration.to_f / 1000
21
+ SemanticLogger['GarbageCollector'].measure_warn "Garbage Collection completed: #{info.gc_name} ##{gc_info.id}", duration: duration.to_f / 1000
22
22
  end
23
23
  end
24
24
  end
@@ -23,7 +23,7 @@ module SemanticLogger
23
23
  # The time at which the log entry was created
24
24
  #
25
25
  # duration
26
- # The time taken to complete a benchmark call
26
+ # The time taken to complete a measure call
27
27
  #
28
28
  # tags
29
29
  # Any tags active on the thread when the log call was made
@@ -35,11 +35,15 @@ module SemanticLogger
35
35
  # Ruby Exception object to log
36
36
  #
37
37
  # metric [Object]
38
- # Object supplied when benchmark_x was called
38
+ # Object supplied when measure_x was called
39
39
  #
40
40
  # backtrace [Array<String>]
41
41
  # The backtrace captured at source when the log level >= SemanticLogger.backtrace_level
42
- Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace) do
42
+ #
43
+ # metric_amount [Numeric]
44
+ # Used for numeric or counter metrics.
45
+ # For example, the number of inquiries or, the amount purchased etc.
46
+ Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace, :metric_amount) do
43
47
 
44
48
  MAX_EXCEPTIONS_TO_UNWRAP = 5
45
49
  # Call the block for exception and any nested exception
@@ -147,12 +151,13 @@ module SemanticLogger
147
151
 
148
152
  # Return the payload in text form
149
153
  # Returns nil if payload is missing or empty
150
- def payload_to_s(colorized = false)
151
- return if payload.nil? || (payload.respond_to?(:empty?) && payload.empty?)
152
- return payload.inspect if !colorized || !defined?(AwesomePrint) || !payload.respond_to?(:ai)
154
+ def payload_to_s
155
+ payload.inspect if has_payload?
156
+ end
153
157
 
154
- # Colorize the payload if the AwesomePrint gem is loaded
155
- payload.ai(multiline: false) rescue payload.inspect
158
+ # Returns [true|false] whether the log entry has a payload
159
+ def has_payload?
160
+ !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
156
161
  end
157
162
 
158
163
  if defined? JRuby
@@ -9,7 +9,7 @@
9
9
  #
10
10
  # require 'semantic_logger'
11
11
  # SemanticLogger.default_level = :debug
12
- # SemanticLogger.add_appender(STDOUT)
12
+ # SemanticLogger.add_appender(io: STDOUT, formatter: :color)
13
13
  #
14
14
  # class ExternalSupplier
15
15
  # # Create class and instance logger methods
@@ -19,7 +19,7 @@
19
19
  # logger.debug "Calculating with amount", { amount: amount, name: name }
20
20
  #
21
21
  # # Measure and log on completion how long the call took to the external supplier
22
- # logger.benchmark_info "Calling external interface" do
22
+ # logger.measure_info "Calling external interface" do
23
23
  # # Code to call the external supplier ...
24
24
  # end
25
25
  # end
@@ -3,6 +3,8 @@ 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
5
5
  class Logger < Base
6
+ include SemanticLogger::Concerns::Compatibility
7
+
6
8
  # Returns a Logger instance
7
9
  #
8
10
  # Return the logger for a specific class, supports class specific log levels
@@ -95,18 +97,27 @@ module SemanticLogger
95
97
  queue_size
96
98
  end
97
99
 
98
- # Supply a block to be called whenever a metric is seen during benchmark logging
100
+ # Supply a block to be called whenever a metric is seen during measure logging
99
101
  #
100
102
  # Parameters
101
103
  # block
102
104
  # The block to be called
103
105
  #
104
106
  # Example:
105
- # SemanticLogger.on_metric do |log_struct|
106
- # puts "#{log_struct.metric} was received. Log Struct: #{log_struct.inspect}"
107
+ # SemanticLogger.on_metric do |log|
108
+ # puts "#{log.metric} was received. Log Struct: #{log.inspect}"
107
109
  # end
108
- def self.on_metric(&block)
109
- (@@metric_subscribers ||= Concurrent::Array.new) << block
110
+ def self.on_metric(object = nil, &block)
111
+ raise('When supplying an object, it must support the #call method') if object && !object.respond_to?(:call)
112
+ (@@metric_subscribers ||= Concurrent::Array.new) << (object || block)
113
+ end
114
+
115
+ # Place log request on the queue for the Appender thread to write to each
116
+ # appender in the order that they were registered
117
+ def log(log, message = nil, progname = nil, &block)
118
+ # Compatibility with ::Logger
119
+ return add(log, message, progname, &block) unless log.is_a?(SemanticLogger::Log)
120
+ self.class.queue << log if @@appender_thread
110
121
  end
111
122
 
112
123
  private
@@ -120,12 +131,6 @@ module SemanticLogger
120
131
  @@queue
121
132
  end
122
133
 
123
- # Place log request on the queue for the Appender thread to write to each
124
- # appender in the order that they were registered
125
- def log(log)
126
- self.class.queue << log if @@appender_thread
127
- end
128
-
129
134
  # Internal logger for SemanticLogger
130
135
  # For example when an appender is not working etc..
131
136
  # By default logs to STDERR
@@ -219,13 +224,13 @@ module SemanticLogger
219
224
  end
220
225
 
221
226
  # Call Metric subscribers
222
- def self.call_metric_subscribers(log_struct)
227
+ def self.call_metric_subscribers(log)
223
228
  # If no subscribers registered, then return immediately
224
229
  return unless @@metric_subscribers
225
230
 
226
231
  @@metric_subscribers.each do |subscriber|
227
232
  begin
228
- subscriber.call(log_struct)
233
+ subscriber.call(log)
229
234
  rescue Exception => exc
230
235
  logger.error 'Exception calling metrics subscriber', exc
231
236
  end
@@ -0,0 +1,18 @@
1
+ module SemanticLogger
2
+ module Metrics
3
+ class NewRelic
4
+ def call(log)
5
+ metric = log.metric
6
+ # Add 'Custom/' prefix for NewRelic
7
+ metric = "Custom/#{metric}" unless metric.start_with?('Custom')
8
+
9
+ if duration = log.duration
10
+ # Convert duration to seconds
11
+ ::NewRelic::Agent.record_metric(metric, duration / 1000.0)
12
+ else
13
+ ::NewRelic::Agent.increment_metric(metric, log.metric_amount || 1)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,48 @@
1
+ require 'uri'
2
+ begin
3
+ require 'statsd-ruby'
4
+ rescue LoadError
5
+ raise 'Gem statsd-ruby is required for logging metrics. Please add the gem "statsd-ruby" to your Gemfile.'
6
+ end
7
+
8
+ module SemanticLogger
9
+ module Metrics
10
+ class Statsd
11
+ # Create Statsd metrics subscriber
12
+ #
13
+ # Parameters:
14
+ # url: [String]
15
+ # Valid URL to post to.
16
+ # Example:
17
+ # udp://localhost:8125
18
+ # Example, send all metrics to a particular namespace:
19
+ # udp://localhost:8125/namespace
20
+ # Default: udp://localhost:8125
21
+ def initialize(options = {})
22
+ options = options.dup
23
+ @url = options.delete(:url) || 'udp://localhost:8125'
24
+ uri = URI.parse(@url)
25
+ raise('Statsd only supports udp. Example: "udp://localhost:8125"') if uri.scheme != 'udp'
26
+
27
+ @statsd = ::Statsd.new(uri.host, uri.port)
28
+ path = uri.path.chomp('/')
29
+ @statsd.namespace = path.sub('/', '') if path != ''
30
+ end
31
+
32
+ def call(log)
33
+ metric = log.metric
34
+ if duration = log.duration
35
+ $statsd.timing(metric, duration)
36
+ else
37
+ amount = (log.metric_amount || 1).round
38
+ if amount < 0
39
+ amount.times { $statsd.decrement(metric) }
40
+ else
41
+ amount.times { $statsd.increment(metric) }
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -81,40 +81,59 @@ module SemanticLogger
81
81
  # more information on custom formatters
82
82
  #
83
83
  # Parameters
84
- # appender [String|IO|SemanticLogger::Appender::Base|::Logger]
85
- # Filename to write log messages to
86
- # Or,
87
- # STDOUT, STDERR, or any IO stream to write log messages to
88
- # Or,
89
- # Any SemanticLogger::Appender instance such as
90
- # SemanticLogger::Appender::File
91
- # SemanticLogger::Appender::Wrapper
92
- # SemanticLogger::Appender::Mongodb
93
- # Or,
94
- # A custom appender derived from SemanticLogger::Appender::Base
95
- # Or,
96
- # Ruby built-in Logger, or any logger that implements the following methods:
97
- # :debug, :info, :warn, :error, :fatal
98
- #
99
- # level [Symbol]
100
- # Optional
101
- # By setting the level higher than the SemanticLogger::default_level
102
- # this appender can exclude lower level log messages
103
- # Any one of SemanticLogger::LEVELS. For example: :trace, :debug, :info, :warn, :error, :fatal
84
+ # file_name: [String]
85
+ # File name to write log messages to.
86
+ #
87
+ # Or,
88
+ # io: [IO]
89
+ # An IO Stream to log to.
90
+ # For example STDOUT, STDERR, etc.
91
+ #
92
+ # Or,
93
+ # appender: [Symbol|SemanticLogger::Appender::Base]
94
+ # A symbol identifying the appender to create.
95
+ # For example:
96
+ # :bugsnag, :elasticsearch, :graylog, :http, :mongodb, :new_relic, :splunk_http, :syslog, :wrapper
97
+ # Or,
98
+ # An instance of an appender derived from SemanticLogger::Appender::Base
99
+ # For example:
100
+ # SemanticLogger::Appender::Http.new(url: 'http://localhost:8088/path')
101
+ #
102
+ # Or,
103
+ # logger: [Logger|Log4r]
104
+ # An instance of a Logger or a Log4r logger.
105
+ #
106
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
107
+ # Override the log level for this appender.
108
+ # Default: SemanticLogger.default_level
109
+ #
110
+ # formatter: [Symbol|Object|Proc]
111
+ # Any of the following symbol values: :default, :color, :json
112
+ # Or,
113
+ # An instance of a class that implements #call
114
+ # Or,
115
+ # A Proc to be used to format the output from this appender
116
+ # Default: :default
117
+ #
118
+ # filter: [Regexp|Proc]
119
+ # RegExp: Only include log messages where the class name matches the supplied.
120
+ # regular expression. All other messages will be ignored.
121
+ # Proc: Only include log messages where the supplied Proc returns true
122
+ # The Proc must return true or false.
104
123
  #
105
124
  # Examples:
106
125
  #
107
126
  # # Send all logging output to Standard Out (Screen)
108
- # SemanticLogger.add_appender(STDOUT)
127
+ # SemanticLogger.add_appender(io: STDOUT)
109
128
  #
110
129
  # # Send all logging output to a file
111
- # SemanticLogger.add_appender('logfile.log')
130
+ # SemanticLogger.add_appender(file_name: 'logfile.log')
112
131
  #
113
132
  # # Send all logging output to a file and only :info and above to standard output
114
- # SemanticLogger.add_appender('logfile.log')
115
- # SemanticLogger.add_appender(STDOUT, :info)
133
+ # SemanticLogger.add_appender(file_name: 'logfile.log')
134
+ # SemanticLogger.add_appender(io: STDOUT, level: :info)
116
135
  #
117
- # Log to an existing logger:
136
+ # Log to log4r, Logger, etc.:
118
137
  #
119
138
  # # Send Semantic logging output to an existing logger
120
139
  # require 'logger'
@@ -125,37 +144,19 @@ module SemanticLogger
125
144
  # log.level = Logger::DEBUG
126
145
  #
127
146
  # SemanticLogger.default_level = :debug
128
- # SemanticLogger.add_appender(log)
147
+ # SemanticLogger.add_appender(logger: log)
129
148
  #
130
149
  # logger = SemanticLogger['Example']
131
150
  # logger.info "Hello World"
132
151
  # logger.debug("Login time", user: 'Joe', duration: 100, ip_address: '127.0.0.1')
133
- #
134
- def self.add_appender(appender, level=nil, &block)
135
- appender_instance =
136
- if appender.is_a?(String) || appender.is_a?(IO)
137
- # $stderr, STDOUT, other IO, or a filename
138
- SemanticLogger::Appender::File.new(appender, level, &block)
139
- elsif appender.is_a? Appender::Base
140
- # Already an instance of an appender
141
- appender.level = level if level
142
- appender.formatter = block if block
143
- appender
144
- else
145
- # Check if the custom appender responds to all the log levels. For example Ruby ::Logger
146
- if does_not_implement = LEVELS[1..-1].find { |i| !appender.respond_to?(i) }
147
- raise "Supplied appender does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}"
148
- end
149
-
150
- raise "Change the log level to #{level}, update the log level directly against the supplied appender" if level
151
- SemanticLogger::Appender::Wrapper.new(appender, &block)
152
- end
153
- @@appenders << appender_instance
152
+ def self.add_appender(options, deprecated_level = nil, &block)
153
+ options = options.is_a?(Hash) ? options.dup : convert_old_appender_args(options, deprecated_level)
154
+ appender = appender_from_options(options, &block)
155
+ @@appenders << appender
154
156
 
155
157
  # Start appender thread if it is not already running
156
158
  SemanticLogger::Logger.start_appender_thread
157
-
158
- appender_instance
159
+ appender
159
160
  end
160
161
 
161
162
  # Remove an existing appender
@@ -188,18 +189,18 @@ module SemanticLogger
188
189
  SemanticLogger::Logger.start_appender_thread
189
190
  end
190
191
 
191
- # Supply a block to be called whenever a metric is seen during benchmark logging
192
+ # Supply a block to be called whenever a metric is seen during measure logging
192
193
  #
193
194
  # Parameters
194
195
  # block
195
196
  # The block to be called
196
197
  #
197
198
  # Example:
198
- # SemanticLogger.on_metric do |log_struct|
199
- # puts "#{log_struct.metric} was received. Log Struct: #{log_struct.inspect}"
199
+ # SemanticLogger.on_metric do |log|
200
+ # puts "#{log.metric} was received. Log Struct: #{log.inspect}"
200
201
  # end
201
- def self.on_metric(&block)
202
- SemanticLogger::Logger.on_metric(&block)
202
+ def self.on_metric(object = nil, &block)
203
+ SemanticLogger::Logger.on_metric(object, &block)
203
204
  end
204
205
 
205
206
  # Add signal handlers for Semantic Logger
@@ -279,7 +280,7 @@ module SemanticLogger
279
280
  end
280
281
 
281
282
  # Returns the symbolic level for the supplied level index
282
- def index_to_level(level_index)
283
+ def self.index_to_level(level_index)
283
284
  LEVELS[level_index]
284
285
  end
285
286
 
@@ -309,6 +310,72 @@ module SemanticLogger
309
310
  index
310
311
  end
311
312
 
313
+ # Backward compatibility
314
+ def self.convert_old_appender_args(appender, level)
315
+ options = {}
316
+ options[:level] = level if level
317
+
318
+ if appender.is_a?(String)
319
+ options[:file_name] = appender
320
+ elsif appender.is_a?(IO)
321
+ options[:io] = appender
322
+ elsif appender.is_a?(Symbol) || appender.is_a?(Appender::Base)
323
+ options[:appender] = appender
324
+ else
325
+ options[:logger] = appender
326
+ end
327
+ warn "[DEPRECATED] SemanticLogger.add_appender parameters have changed. Please use: #{options.inspect}"
328
+ options
329
+ end
330
+
331
+ # Returns [SemanticLogger::Appender::Base] appender for the supplied options
332
+ def self.appender_from_options(options, &block)
333
+ if options[:io] || options[:file_name]
334
+ SemanticLogger::Appender::File.new(options, &block)
335
+ elsif appender = options.delete(:appender)
336
+ if appender.is_a?(Symbol)
337
+ named_appender(appender).new(options)
338
+ elsif appender.is_a?(Appender::Base)
339
+ appender
340
+ else
341
+ raise(ArgumentError, "Parameter :appender must be either a Symbol or an object derived from SemanticLogger::Appender::Base, not: #{appender.inspect}")
342
+ end
343
+ elsif options[:logger]
344
+ SemanticLogger::Appender::Wrapper.new(options, &block)
345
+ end
346
+ end
347
+
348
+ def self.named_appender(appender)
349
+ appender = appender.to_s
350
+ klass = appender.respond_to?(:camelize) ? appender.camelize : camelize(appender)
351
+ klass = "SemanticLogger::Appender::#{klass}"
352
+ begin
353
+ appender.respond_to?(:constantize) ? klass.constantize : eval(klass)
354
+ rescue NameError
355
+ raise(ArgumentError, "Could not find appender class: #{klass} for #{appender}")
356
+ end
357
+ end
358
+
359
+ def self.named_formatter(formatter)
360
+ formatter = formatter.to_s
361
+ klass = formatter.respond_to?(:camelize) ? formatter.camelize : camelize(formatter)
362
+ klass = "SemanticLogger::Formatters::#{klass}"
363
+ begin
364
+ formatter.respond_to?(:constantize) ? klass.constantize : eval(klass)
365
+ rescue NameError => exc
366
+ raise(ArgumentError, "Could not find formatter class: #{klass} for #{appender}")
367
+ end
368
+ end
369
+
370
+ # Borrow from Rails, when not running Rails
371
+ def self.camelize(term)
372
+ string = term.to_s
373
+ string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
374
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
375
+ string.gsub!('/'.freeze, '::'.freeze)
376
+ string
377
+ end
378
+
312
379
  # Initial default Level for all new instances of SemanticLogger::Logger
313
380
  @@default_level = :info
314
381
  @@default_level_index = level_to_index(@@default_level)