semantic_logger 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/FUTURE.rb +211 -0
- data/LICENSE.txt +201 -0
- data/README.md +131 -0
- data/Rakefile +37 -0
- data/init.txt +59 -0
- data/lib/semantic_logger/appender/logger.rb +102 -0
- data/lib/semantic_logger/appender/mongodb.rb +219 -0
- data/lib/semantic_logger/logger.rb +136 -0
- data/lib/semantic_logger/rails.rb +48 -0
- data/lib/semantic_logger/railtie.rb +43 -0
- data/lib/semantic_logger/version.rb +3 -0
- data/lib/semantic_logger.rb +18 -0
- data/test/appender_logger_test.rb +78 -0
- data/test/appender_mongodb_test.rb +117 -0
- data/test/logger_test.rb +44 -0
- data/test/mock_logger.rb +25 -0
- metadata +89 -0
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/testtask'
|
7
|
+
require 'date'
|
8
|
+
require 'semantic_logger/version'
|
9
|
+
|
10
|
+
desc "Build gem"
|
11
|
+
task :gem do |t|
|
12
|
+
gemspec = Gem::Specification.new do |spec|
|
13
|
+
spec.name = 'semantic_logger'
|
14
|
+
spec.version = SemanticLogger::VERSION
|
15
|
+
spec.platform = Gem::Platform::RUBY
|
16
|
+
spec.authors = ['Reid Morrison']
|
17
|
+
spec.email = ['reidmo@gmail.com']
|
18
|
+
spec.homepage = 'https://github.com/ClarityServices/semantic_logger'
|
19
|
+
spec.date = Date.today.to_s
|
20
|
+
spec.summary = "Semantic Logger for Ruby, and Ruby on Rails"
|
21
|
+
spec.description = "Logging with additional machine readable and parseable log data"
|
22
|
+
spec.files = FileList["./**/*"].exclude('*.gem', 'nbproject').map{|f| f.sub(/^\.\//, '')}
|
23
|
+
spec.has_rdoc = true
|
24
|
+
spec.add_development_dependency 'shoulda'
|
25
|
+
end
|
26
|
+
Gem::Builder.new(gemspec).build
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Run Test Suite"
|
30
|
+
task :test do
|
31
|
+
Rake::TestTask.new(:functional) do |t|
|
32
|
+
t.test_files = FileList['test/*_test.rb']
|
33
|
+
t.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
Rake::Task['functional'].invoke
|
37
|
+
end
|
data/init.txt
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
-- if (($0 == 'irb' || $0 == 'jirb') && ENV['RAILS_CONSOLE_STDOUT']) || ENV['RAILS_LOGGER_STDOUT']
|
2
|
+
-- config.logger = Logger.new(STDOUT)
|
3
|
+
-- end
|
4
|
+
++ # Setup Semantic Logging
|
5
|
+
++# require 'lib/semantic_logger'
|
6
|
+
++# if (($0 == 'irb' || $0 == 'jirb') && ENV['RAILS_CONSOLE_STDOUT']) || ENV['RAILS_LOGGER_STDOUT']
|
7
|
+
++# config.logger = SemanticLogger::LoggerAppender.new(Logger.new(STDOUT))
|
8
|
+
++# else
|
9
|
+
++# exc = nil
|
10
|
+
++# begin
|
11
|
+
++# # Don't use Mongo Log Appender in dev and test
|
12
|
+
++# unless false #Rails.env.test? #or Rails.env.development?
|
13
|
+
++# require 'mongo'
|
14
|
+
++# require 'lib/auto_failover_repl_set_connection'
|
15
|
+
++# # Log only to Mongo
|
16
|
+
++# cfg = YAML.load_file(File.join(Rails.root, "config", "mongo.yml"))[Rails.env]
|
17
|
+
++# conn = Mongo::AutoFailoverReplSetConnection.from_uri(cfg['uri'],cfg['options'] || {})
|
18
|
+
++# SemanticLogger::MongoAppender.db = conn.db("logger_#{Rails.env}")
|
19
|
+
++# SemanticLogger::MongoAppender.create_indexes
|
20
|
+
++# config.logger = SemanticLogger::MongoAppender.new('Rails', config.log_level)
|
21
|
+
++# end
|
22
|
+
++# rescue StandardError => exc
|
23
|
+
++# end
|
24
|
+
++#
|
25
|
+
++# begin
|
26
|
+
++# unless config.logger
|
27
|
+
++# # Wrap the BufferedLogger with the Semantic Logger
|
28
|
+
++# logger = ActiveSupport::BufferedLogger.new(config.log_path)
|
29
|
+
++# logger.level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase)
|
30
|
+
++# logger.auto_flushing = false if config.environment == "production"
|
31
|
+
++# config.logger = SemanticLogger::LoggerAppender.new(logger)
|
32
|
+
++# config.logger.error("Failed to start Mongo Logger, falling back to File Logging", exc) if exc
|
33
|
+
++# end
|
34
|
+
++# rescue StandardError => exc
|
35
|
+
++# end
|
36
|
+
++#
|
37
|
+
++# unless config.logger
|
38
|
+
++# config.logger = ActiveSupport::BufferedLogger.new(STDERR)
|
39
|
+
++# config.logger.level = ActiveSupport::BufferedLogger::WARN
|
40
|
+
++# config.logger.error("Failed to start SemanticLogger::LoggerAppender")
|
41
|
+
++# config.logger.error(exc) if exc
|
42
|
+
++# config.logger.warn(
|
43
|
+
++# "Rails Error: Unable to access log file. Please ensure that #{config.log_path} exists and is chmod 0666. " +
|
44
|
+
++# "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
|
45
|
+
++# )
|
46
|
+
++# end
|
47
|
+
++
|
48
|
+
++ # Rails.logger =
|
49
|
+
++# def initialize_framework_logging
|
50
|
+
++# for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks)
|
51
|
+
++# framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger
|
52
|
+
++# end
|
53
|
+
++#
|
54
|
+
++# ActiveSupport::Dependencies.logger ||= Rails.logger
|
55
|
+
++# Rails.cache.logger ||= Rails.logger
|
56
|
+
++# end
|
57
|
+
++
|
58
|
+
++
|
59
|
+
++# end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Logger appender
|
2
|
+
#
|
3
|
+
# Maps the SemanticLogger API's to the Rails log, log4j, or Ruby Logger
|
4
|
+
#
|
5
|
+
# Installation:
|
6
|
+
# Rails.logger = SemanticLogger::Appender::Logger.new(Rails.logger)
|
7
|
+
#
|
8
|
+
# Also works with the Ruby Logger
|
9
|
+
# require 'logger'
|
10
|
+
# require 'semantic_logger'
|
11
|
+
# logger = Logger.new(STDOUT)
|
12
|
+
# Rails.log = SemanticLogger::Appender::Logger.new(logger)
|
13
|
+
#
|
14
|
+
# ActiveResource::BufferedLogger
|
15
|
+
# ...
|
16
|
+
#
|
17
|
+
# The log level is controlled by the Logging implementation passed into
|
18
|
+
# this appender
|
19
|
+
module SemanticLogger
|
20
|
+
module Appender
|
21
|
+
class Logger
|
22
|
+
attr_reader :logger
|
23
|
+
|
24
|
+
def initialize(logger)
|
25
|
+
raise "logger cannot be null when initiailizing the SemanticLogging::Appender::Logger" unless logger
|
26
|
+
@logger = logger
|
27
|
+
end
|
28
|
+
|
29
|
+
# The default log formatter
|
30
|
+
# Generates logs of the form:
|
31
|
+
# 2011-07-19 14:36:15.660 D [1149:ScriptThreadProcess] Rails -- Hello World
|
32
|
+
@@formatter = Proc.new do |level, name, message, time, duration|
|
33
|
+
str = "#{time.strftime("%Y-%m-%d %H:%M:%S")}.#{"%03d" % (time.usec/1000)} #{level.to_s[0..0].upcase} [#{$$}:#{thread_name}] #{name} -- #{message}\n"
|
34
|
+
str << " (#{duration}ms)" if duration
|
35
|
+
str
|
36
|
+
end
|
37
|
+
|
38
|
+
# For JRuby include the Thread name rather than its id
|
39
|
+
if defined? Java
|
40
|
+
def self.thread_name
|
41
|
+
Java::java.lang::Thread.current_thread.name
|
42
|
+
end
|
43
|
+
else
|
44
|
+
def self.thread_name
|
45
|
+
Thread.object_id
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Allow the global formatter to be replaced
|
50
|
+
def self.formatter=(formatter)
|
51
|
+
@@formatter = formatter
|
52
|
+
end
|
53
|
+
|
54
|
+
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
55
|
+
# trace entries are mapped to debug since :trace is not supported by the
|
56
|
+
# Ruby or Rails Loggers
|
57
|
+
def log(level, name, message, hash, &block)
|
58
|
+
@logger.send(level == :trace ? :debug : level) { self.class.format_message(level, name, message, hash, &block) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Convert a semantic log entry into plain text
|
62
|
+
def self.format_message(level, name, message, hash=nil, &block)
|
63
|
+
# TODO need to define :try if not already defined. E.g. Outside Rails
|
64
|
+
msg = time = duration = nil
|
65
|
+
if hash
|
66
|
+
msg = hash.delete(:message)
|
67
|
+
time = hash.delete(:time)
|
68
|
+
duration = hash.delete(:duration)
|
69
|
+
end
|
70
|
+
msg ||= message.to_s
|
71
|
+
time ||= Time.now
|
72
|
+
|
73
|
+
msg << " -- " << self.msg2str(hash) if hash
|
74
|
+
msg << " -- " << self.msg2str(block.call) if block
|
75
|
+
@@formatter.call(level, name, msg, time, duration)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convert Objects to Strings for text based logging
|
79
|
+
def self.msg2str(message)
|
80
|
+
case message
|
81
|
+
when ::String
|
82
|
+
message
|
83
|
+
when ::Exception
|
84
|
+
"#{message.class}: #{message.message}\n#{(message.backtrace || []).join("\n")}"
|
85
|
+
when ::Hash
|
86
|
+
# With a hash, the message can be an element of the hash itself
|
87
|
+
if msg = message[:message]
|
88
|
+
# Cannot change supplied hash
|
89
|
+
hash = message.clone
|
90
|
+
hash.delete(:message)
|
91
|
+
"#{msg} #{hash.inspect}"
|
92
|
+
else
|
93
|
+
message.inspect
|
94
|
+
end
|
95
|
+
else
|
96
|
+
message.inspect
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module SemanticLogger
|
2
|
+
module Appender
|
3
|
+
# The Mongo Appender for the SemanticLogger
|
4
|
+
# clarity_log Schema:
|
5
|
+
# _id : ObjectId("4d9cbcbf7abb3abdaf9679cd"),
|
6
|
+
# time : ISODate("2011-04-06T19:19:27.006Z"),
|
7
|
+
# host_name : "Name of the host on which this log entry originated",
|
8
|
+
# application: 'Name of application or service logging the data - clarity_base, nginx, tomcat',
|
9
|
+
# pid : process id
|
10
|
+
# thread : "name of thread",
|
11
|
+
# name : "ch.qos.logback.classic.db.mongo.MongoDBAppenderTest",
|
12
|
+
# level : 'trace|debug|warn|info|error',
|
13
|
+
# message : "blah blah",
|
14
|
+
# duration : ms, # Set by Logger#benchmark
|
15
|
+
# tracking_number : "Some tracking id"
|
16
|
+
# metadata : {
|
17
|
+
# Optional. Any user supplied data, including any thread specific context variables
|
18
|
+
# values supplied on a per log entry will override any thread context values
|
19
|
+
# }
|
20
|
+
# # When an exception is supplied as the first or second parameter
|
21
|
+
# # If supplied as the first parameter, message='exception name'
|
22
|
+
# exception: {
|
23
|
+
# name: 'MyException',
|
24
|
+
# description: 'blah',
|
25
|
+
# stack_trace: []
|
26
|
+
# }
|
27
|
+
#
|
28
|
+
# # For trace and debug level logging, the following can also be logged
|
29
|
+
# # for all levels. Not on for higher levels due to performance impact
|
30
|
+
# source: {
|
31
|
+
# filename:
|
32
|
+
# method:
|
33
|
+
# line_number:
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
# # Future, the Rails around filter can log the following additional data
|
37
|
+
# controller:
|
38
|
+
# action:
|
39
|
+
# duration: 'ms'
|
40
|
+
# http_verb: 'get|post|..'
|
41
|
+
# params: Hash
|
42
|
+
#
|
43
|
+
# tracking_number: 'user defined tracking number'
|
44
|
+
class MongoDB
|
45
|
+
attr_reader :db, :collection_name
|
46
|
+
attr_accessor :formatter, :host_name, :safe, :application
|
47
|
+
|
48
|
+
# Create a MongoDB Appender instance
|
49
|
+
# SemanticLogger::Appender::MongoDB.new(
|
50
|
+
# :db => Cache::Work.db
|
51
|
+
# )
|
52
|
+
#
|
53
|
+
# Parameters:
|
54
|
+
# :db [Mongo::Database]
|
55
|
+
# The MongoDB database connection to use, not the database name
|
56
|
+
#
|
57
|
+
# :collection_name [String]
|
58
|
+
# Name of the collection to store log data in
|
59
|
+
# Default: semantic_logger
|
60
|
+
#
|
61
|
+
# :host_name [String]
|
62
|
+
# host_name to include in the document logged to Mongo
|
63
|
+
# Default: first part of host name extracted from Socket
|
64
|
+
#
|
65
|
+
# :safe [Boolean]
|
66
|
+
# Whether to use safe write for logging
|
67
|
+
# Not recommended to change this value except to diagnose connection
|
68
|
+
# issues or when log entries are not being written to Mongo
|
69
|
+
# Default: false
|
70
|
+
#
|
71
|
+
# :application [String]
|
72
|
+
# Name of the application to include in the document written to mongo
|
73
|
+
# Default: nil (None)
|
74
|
+
#
|
75
|
+
# :collection_size [Integer]
|
76
|
+
# The size of the MongoDB capped collection to create in bytes
|
77
|
+
# Default: 1 GB
|
78
|
+
#
|
79
|
+
# Some examples
|
80
|
+
# Prod: 25GB (.5GB per day across 4 servers over 10 days)
|
81
|
+
# Dev: .5GB
|
82
|
+
# Test: File
|
83
|
+
# Release: 4GB
|
84
|
+
#
|
85
|
+
# :collection_max [Integer]
|
86
|
+
# Maximum number of log entries that the capped collection will hold
|
87
|
+
#
|
88
|
+
def initialize(params={}, &block)
|
89
|
+
@db = params[:db] || raise('Missing mandatory parameter :db')
|
90
|
+
@collection_name = params[:collection_name] || 'semantic_logger'
|
91
|
+
@host_name = params[:host_name] || Socket.gethostname.split('.').first
|
92
|
+
@safe = params[:safe] || false
|
93
|
+
@application = params[:application]
|
94
|
+
|
95
|
+
# Create a collection that will hold the lesser of 1GB space or 10K documents
|
96
|
+
@collection_size = params[:collection_size] || 1024**3
|
97
|
+
@collection_max = params[:collection_max]
|
98
|
+
|
99
|
+
# Set the formatter to the supplied block
|
100
|
+
@formatter = block || self.default_formatter
|
101
|
+
|
102
|
+
# Create the collection and necessary indexes
|
103
|
+
create_indexes
|
104
|
+
end
|
105
|
+
|
106
|
+
# Create the required capped collection
|
107
|
+
# Features of capped collection:
|
108
|
+
# * No indexes by default (not even on _id)
|
109
|
+
# * Documents cannot be deleted,
|
110
|
+
# * Document updates cannot make them any larger
|
111
|
+
# * Documents are always stored in insertion order
|
112
|
+
# * A find will always return the documents in their insertion order
|
113
|
+
def create_indexes
|
114
|
+
db.create_collection(collection_name, {:capped => true, :size => @collection_size, :max => @collection_max})
|
115
|
+
end
|
116
|
+
|
117
|
+
# Purge all data from the capped collection by dropping the collection
|
118
|
+
# and recreating it.
|
119
|
+
# Also useful when the size of the capped collection needs to be changed
|
120
|
+
def purge_all
|
121
|
+
collection.drop
|
122
|
+
@collection = nil
|
123
|
+
create_indexes
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return the collection being used to write the log document to
|
127
|
+
def collection
|
128
|
+
@collection ||= db[collection_name]
|
129
|
+
end
|
130
|
+
|
131
|
+
# For JRuby include the Thread name rather than its id
|
132
|
+
if defined? Java
|
133
|
+
def self.thread_name
|
134
|
+
Java::java.lang::Thread.current_thread.name
|
135
|
+
end
|
136
|
+
else
|
137
|
+
def self.thread_name
|
138
|
+
Thread.object_id
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Default log formatter
|
143
|
+
# Replace this formatter by supplying a Block to the initializer
|
144
|
+
def default_formatter
|
145
|
+
Proc.new do |level, name, message, hash, block|
|
146
|
+
document = {
|
147
|
+
:time => Time.now,
|
148
|
+
:host_name => host_name,
|
149
|
+
:pid => $PID,
|
150
|
+
:thread => SemanticLogger::Appender::MongoDB.thread_name,
|
151
|
+
:name => name,
|
152
|
+
:level => level,
|
153
|
+
}
|
154
|
+
document[:application] = application if application
|
155
|
+
document[:message] = self.class.strip_colorizing(message) if message
|
156
|
+
|
157
|
+
SemanticLogger::Appender::MongoDB.populate(document, hash) if hash
|
158
|
+
SemanticLogger::Appender::MongoDB.populate(document, block.call) if block
|
159
|
+
document
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Strip the standard Rails colorizing from the logged message
|
164
|
+
def self.strip_colorizing(message)
|
165
|
+
message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, '').strip
|
166
|
+
end
|
167
|
+
|
168
|
+
# Log the message
|
169
|
+
def log(level, name, message, hash, &block)
|
170
|
+
# Insert log entry into Mongo
|
171
|
+
# Use safe=>false so that we do not wait for it to be written to disk, or
|
172
|
+
# for the response from the MongoDB server
|
173
|
+
document = formatter.call(level, name, message, hash, &block)
|
174
|
+
collection.insert(document, :safe=>safe)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Populate Log Hash
|
178
|
+
def self.populate(document, message)
|
179
|
+
case message
|
180
|
+
when ::String
|
181
|
+
if document[:message]
|
182
|
+
document[:message] << " " << strip_colorizing(message)
|
183
|
+
else
|
184
|
+
document[:message] = strip_colorizing(message)
|
185
|
+
end
|
186
|
+
when ::Exception
|
187
|
+
document[:exception] = {
|
188
|
+
:class => message.class.name,
|
189
|
+
:message => message.message,
|
190
|
+
:backtrace => message.backtrace
|
191
|
+
}
|
192
|
+
when ::Hash
|
193
|
+
# With a hash, the message can be an element of the hash itself
|
194
|
+
if msg = message[:message]
|
195
|
+
# Cannot change supplied hash
|
196
|
+
hash = message.clone
|
197
|
+
hash.delete(:message)
|
198
|
+
if document[:message]
|
199
|
+
document[:message] << " " << strip_colorizing(msg)
|
200
|
+
else
|
201
|
+
document[:message] = strip_colorizing(msg)
|
202
|
+
end
|
203
|
+
document[:metadata] = hash
|
204
|
+
else
|
205
|
+
document[:metadata] = message
|
206
|
+
end
|
207
|
+
else
|
208
|
+
if document[:message]
|
209
|
+
document[:message] << " " << msg.inspect
|
210
|
+
else
|
211
|
+
document[:message] = msg.inspect
|
212
|
+
end
|
213
|
+
end
|
214
|
+
document
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Logger is the interface used by
|
2
|
+
#
|
3
|
+
# Logger maintains the logging name to be used for all log entries generated
|
4
|
+
# by the invoking classes or modules
|
5
|
+
#
|
6
|
+
# It is recommended to create an instance of the class for every class or
|
7
|
+
# module so that it can be uniquely identified and searched on
|
8
|
+
#
|
9
|
+
# Example, log to Logger:
|
10
|
+
# require 'logger'
|
11
|
+
# require 'semantic_logger'
|
12
|
+
# log = Logger.new(STDOUT)
|
13
|
+
# log.level = Logger::DEBUG
|
14
|
+
#
|
15
|
+
# SemanticLogger::Manager.register_appender(SemanticLogger::Appender::Logger.new(log))
|
16
|
+
#
|
17
|
+
# logger = SemanticLogger::Logger.new("my.app.class")
|
18
|
+
# logger.debug("Login time", :user => 'Joe', :duration => 100, :ip_address=>'127.0.0.1')
|
19
|
+
#
|
20
|
+
# # Now log to the Logger above as well as Mongo at the same time
|
21
|
+
#
|
22
|
+
# SemanticLogger::Manager.register_appender(SemanticLogger::Appender::Mongo.new(cfg))
|
23
|
+
# ...
|
24
|
+
# logger.debug("Login time", :user => 'Mary', :duration => 230, :ip_address=>'192.168.0.1')
|
25
|
+
module SemanticLogger
|
26
|
+
class Logger
|
27
|
+
include SyncAttr
|
28
|
+
|
29
|
+
# Logging levels in order of precendence
|
30
|
+
LEVELS = [:trace, :debug, :info, :warn, :error]
|
31
|
+
|
32
|
+
# Mapping of Rails and Ruby Logger levels to SemanticLogger levels
|
33
|
+
MAP_LEVELS = []
|
34
|
+
::Logger::Severity.constants.each do |constant|
|
35
|
+
MAP_LEVELS[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Thread safe Class Attribute accessor for appenders array
|
39
|
+
sync_cattr_accessor :appenders do
|
40
|
+
[]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Allow for setting the default log level
|
44
|
+
def self.default_level=(default_level)
|
45
|
+
@@default_level = default_level
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.default_level
|
49
|
+
@@default_level
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :application, :level
|
53
|
+
|
54
|
+
@@default_level = :info
|
55
|
+
|
56
|
+
# Create a Logger instance
|
57
|
+
# Parameters:
|
58
|
+
# application: A class, module or a string with the application/class name
|
59
|
+
# to be used in the logger
|
60
|
+
# options:
|
61
|
+
# :level The initial log level to start with for this logger instance
|
62
|
+
def initialize(application, options={})
|
63
|
+
@application = application.is_a?(String) ? application : application.name
|
64
|
+
set_level(options[:level] || self.class.default_level)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Set the logging level
|
68
|
+
# Must be one of the values in #LEVELS
|
69
|
+
def level=(level)
|
70
|
+
set_level(level)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Implement the log level calls
|
74
|
+
# logger.debug(message|hash|exception, hash|exception=nil, &block)
|
75
|
+
#
|
76
|
+
# Implement the log level query
|
77
|
+
# logger.debug?
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
# logger = SemanticLogging::Logger.new('MyApplication')
|
81
|
+
# logger.debug("Only display this if log level is set to Debug or lower")
|
82
|
+
#
|
83
|
+
# # Log semantic information along with a text message
|
84
|
+
# logger.info("Request received", :user => "joe", :duration => 100)
|
85
|
+
#
|
86
|
+
# # Log an exception in a semantic way
|
87
|
+
# logger.info("Parsing received XML", exc)
|
88
|
+
#
|
89
|
+
LEVELS.each_with_index do |level, index|
|
90
|
+
class_eval <<-EOT, __FILE__, __LINE__
|
91
|
+
def #{level}(message = nil, data = nil, &block) # def trace(message = nil, data = nil, &block)
|
92
|
+
if @level_index <= #{index} # if @level_index <= 0
|
93
|
+
self.class.appenders.each {|appender| appender.log(:#{level}, application, message, data, &block) } # self.class.appenders.each {|appender| appender.log(:trace, application, message, data, &block) }
|
94
|
+
true # true
|
95
|
+
else # else
|
96
|
+
false # false
|
97
|
+
end # end
|
98
|
+
end # end
|
99
|
+
|
100
|
+
def #{level}? # def trace?
|
101
|
+
@level_index <= #{index} # @level_index <= 0
|
102
|
+
end # end
|
103
|
+
EOT
|
104
|
+
end
|
105
|
+
|
106
|
+
# Semantic Logging does not support :fatal or :unknown levels since these
|
107
|
+
# are not understood by the majority of the logging providers
|
108
|
+
# Map them to :error
|
109
|
+
alias :fatal :error
|
110
|
+
alias :fatal? :error?
|
111
|
+
alias :unknown :error
|
112
|
+
alias :unknown? :error?
|
113
|
+
|
114
|
+
# forward other calls to ActiveResource::BufferedLogger
|
115
|
+
# #silence is not implemented since it is not thread safe prior to Rails 3.2
|
116
|
+
# #TODO implement a thread safe silence method
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
# Verify and set the level
|
121
|
+
def set_level(level)
|
122
|
+
index = if level.is_a?(Integer)
|
123
|
+
MAP_LEVELS[level]
|
124
|
+
elsif level.is_a?(String)
|
125
|
+
level = level.downcase.to_sym
|
126
|
+
LEVELS.index(level)
|
127
|
+
else
|
128
|
+
LEVELS.index(level)
|
129
|
+
end
|
130
|
+
|
131
|
+
raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
|
132
|
+
@level_index = index
|
133
|
+
@level = level
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|