semantic_logger 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)