semantic_logger 0.0.2
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.
- 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
|