semantic_logger 0.5.3 → 0.6.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.
- data/lib/semantic_logger.rb +6 -3
- data/lib/semantic_logger/appender/file.rb +26 -31
- data/lib/semantic_logger/appender/mongodb.rb +10 -7
- data/lib/semantic_logger/appender/{logger.rb → wrapper.rb} +10 -31
- data/lib/semantic_logger/base.rb +280 -0
- data/lib/semantic_logger/logger.rb +31 -221
- data/lib/semantic_logger/railtie.rb +13 -15
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender_file_test.rb +52 -0
- data/test/appender_mongodb_test.rb +1 -1
- data/test/{appender_logger_test.rb → appender_wrapper_test.rb} +8 -7
- data/test/logger_test.rb +8 -6
- metadata +8 -6
data/lib/semantic_logger.rb
CHANGED
@@ -2,14 +2,17 @@
|
|
2
2
|
require 'sync_attr'
|
3
3
|
|
4
4
|
module SemanticLogger
|
5
|
+
autoload :Base, 'semantic_logger/base'
|
5
6
|
autoload :Logger, 'semantic_logger/logger'
|
6
7
|
|
7
8
|
module Appender
|
8
|
-
autoload :File,
|
9
|
-
autoload :
|
10
|
-
# Only load the MongoDB appender if the Mongo Ruby Driver is loaded
|
9
|
+
autoload :File, 'semantic_logger/appender/file'
|
10
|
+
autoload :Wrapper, 'semantic_logger/appender/wrapper'
|
11
11
|
autoload :MongoDB, 'semantic_logger/appender/mongodb'
|
12
12
|
end
|
13
|
+
|
14
|
+
# Logging levels in order with most detailed logging first
|
15
|
+
LEVELS = [:trace, :debug, :info, :warn, :error, :fatal]
|
13
16
|
end
|
14
17
|
|
15
18
|
if defined?(Rails)
|
@@ -4,14 +4,16 @@
|
|
4
4
|
#
|
5
5
|
module SemanticLogger
|
6
6
|
module Appender
|
7
|
-
class File
|
8
|
-
attr_accessor :formatter
|
7
|
+
class File < SemanticLogger::Base
|
9
8
|
|
10
9
|
# Create a File Logger appender instance
|
11
10
|
#
|
12
11
|
# Example
|
13
12
|
# require 'semantic_logger'
|
14
13
|
#
|
14
|
+
# # Enable trace level logging
|
15
|
+
# SemanticLogger::Logger.level = :info
|
16
|
+
#
|
15
17
|
# # Log to screen
|
16
18
|
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT)
|
17
19
|
#
|
@@ -21,7 +23,23 @@ module SemanticLogger
|
|
21
23
|
# logger = SemanticLogger::Logger.new('test')
|
22
24
|
# logger.info 'Hello World'
|
23
25
|
#
|
24
|
-
|
26
|
+
# Example 2. To log all levels to file and only :info and above to screen:
|
27
|
+
#
|
28
|
+
# require 'semantic_logger'
|
29
|
+
#
|
30
|
+
# # Enable trace level logging
|
31
|
+
# SemanticLogger::Logger.level = :trace
|
32
|
+
#
|
33
|
+
# # Log to screen but only display :info and above
|
34
|
+
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT, :info)
|
35
|
+
#
|
36
|
+
# # And log to a file at the same time, including all :trace level data
|
37
|
+
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('application.log')
|
38
|
+
#
|
39
|
+
# logger = SemanticLogger::Logger.new('test')
|
40
|
+
# logger.info 'Hello World'
|
41
|
+
#
|
42
|
+
def initialize(filename, level=nil, &block)
|
25
43
|
raise "logger cannot be null when initializing the SemanticLogging::Appender::Logger" unless filename
|
26
44
|
@filename = filename
|
27
45
|
@log = if filename.respond_to?(:write) and filename.respond_to?(:close)
|
@@ -29,38 +47,14 @@ module SemanticLogger
|
|
29
47
|
else
|
30
48
|
@log = open(filename, (::File::WRONLY | ::File::APPEND | ::File::CREAT))
|
31
49
|
# Force all log entries to write immediately without buffering
|
50
|
+
# Allows multiple processes to write to the same log file simultaneously
|
32
51
|
@log.sync = true
|
33
52
|
@log.set_encoding(Encoding::BINARY) if @log.respond_to?(:set_encoding)
|
34
53
|
@log
|
35
54
|
end
|
36
55
|
|
37
|
-
# Set the formatter
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
# Default log formatter
|
42
|
-
# Replace this formatter by supplying a Block to the initializer
|
43
|
-
# Generates logs of the form:
|
44
|
-
# 2011-07-19 14:36:15.660 D [1149:ScriptThreadProcess] Rails -- Hello World\n
|
45
|
-
def default_formatter
|
46
|
-
Proc.new do |log|
|
47
|
-
message = log.message.to_s
|
48
|
-
tags = log.tags.collect { |tag| "[#{tag}]" }.join(" ") + " " if log.tags && (log.tags.size > 0)
|
49
|
-
|
50
|
-
if log.payload
|
51
|
-
if log.payload.is_a?(Exception)
|
52
|
-
exception = log.payload
|
53
|
-
message << " -- " << "#{exception.class}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
|
54
|
-
else
|
55
|
-
message << " -- " << log.payload.inspect
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
str = "#{log.time.strftime("%Y-%m-%d %H:%M:%S")}.#{"%03d" % (log.time.usec/1000)} #{log.level.to_s[0..0].upcase} [#{$$}:#{log.thread_name}] #{tags}#{log.name} -- #{message}"
|
60
|
-
str << " (#{'%.1f' % log.duration}ms)" if log.duration
|
61
|
-
str << "\n"
|
62
|
-
str
|
63
|
-
end
|
56
|
+
# Set the log level and formatter if supplied
|
57
|
+
super(level, &block)
|
64
58
|
end
|
65
59
|
|
66
60
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
@@ -69,7 +63,8 @@ module SemanticLogger
|
|
69
63
|
def log(log)
|
70
64
|
# Since only one appender thread will be writing to the file at a time
|
71
65
|
# it is not necessary to protect access to the file with a semaphore
|
72
|
-
|
66
|
+
# Allow this logger to filter out log levels lower than it's own
|
67
|
+
@log.write(@formatter.call(log) << "\n") if level_index <= (log.level_index || 0)
|
73
68
|
end
|
74
69
|
|
75
70
|
# Flush all pending logs to disk.
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'socket'
|
1
2
|
module SemanticLogger
|
2
3
|
module Appender
|
3
4
|
# The Mongo Appender for the SemanticLogger
|
@@ -41,9 +42,9 @@ module SemanticLogger
|
|
41
42
|
# params: Hash
|
42
43
|
#
|
43
44
|
# tracking_number: 'user defined tracking number'
|
44
|
-
class MongoDB
|
45
|
+
class MongoDB < SemanticLogger::Base
|
45
46
|
attr_reader :db, :collection_name
|
46
|
-
attr_accessor :
|
47
|
+
attr_accessor :host_name, :safe, :application
|
47
48
|
|
48
49
|
# Create a MongoDB Appender instance
|
49
50
|
#
|
@@ -84,6 +85,9 @@ module SemanticLogger
|
|
84
85
|
# :collection_max [Integer]
|
85
86
|
# Maximum number of log entries that the capped collection will hold
|
86
87
|
#
|
88
|
+
# :level [Symbol]
|
89
|
+
# Only allow log entries of this level or higher to be written to MongoDB
|
90
|
+
#
|
87
91
|
def initialize(params={}, &block)
|
88
92
|
@db = params[:db] || raise('Missing mandatory parameter :db')
|
89
93
|
@collection_name = params[:collection_name] || 'semantic_logger'
|
@@ -95,11 +99,11 @@ module SemanticLogger
|
|
95
99
|
@collection_size = params[:collection_size] || 1024**3
|
96
100
|
@collection_max = params[:collection_max]
|
97
101
|
|
98
|
-
# Set the formatter to the supplied block
|
99
|
-
@formatter = block || self.default_formatter
|
100
|
-
|
101
102
|
# Create the collection and necessary indexes
|
102
103
|
create_indexes
|
104
|
+
|
105
|
+
# Set the log level and formatter
|
106
|
+
super(params[:level], &block)
|
103
107
|
end
|
104
108
|
|
105
109
|
# Create the required capped collection
|
@@ -189,8 +193,7 @@ module SemanticLogger
|
|
189
193
|
# Insert log entry into Mongo
|
190
194
|
# Use safe=>false so that we do not wait for it to be written to disk, or
|
191
195
|
# for the response from the MongoDB server
|
192
|
-
|
193
|
-
collection.insert(document, :safe=>safe)
|
196
|
+
collection.insert(formatter.call(log), :safe=>safe) if level_index <= (log.level_index || 0)
|
194
197
|
end
|
195
198
|
|
196
199
|
end
|
@@ -1,14 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# Wrapper appender
|
2
2
|
#
|
3
|
-
#
|
3
|
+
# Wraps the Rails log, log4r, or Ruby Logger with the SemanticLogger API's
|
4
4
|
#
|
5
|
-
# The log level is controlled by the Logging implementation passed into
|
6
|
-
# this appender
|
7
5
|
module SemanticLogger
|
8
6
|
module Appender
|
9
|
-
class
|
7
|
+
class Wrapper < SemanticLogger::Base
|
10
8
|
attr_reader :logger
|
11
|
-
attr_accessor :formatter
|
12
9
|
|
13
10
|
# Create a Logger or Rails Logger appender instance
|
14
11
|
#
|
@@ -16,48 +13,30 @@ module SemanticLogger
|
|
16
13
|
# require 'logger'
|
17
14
|
# require 'semantic_logger'
|
18
15
|
# ruby_logger = Logger.new(STDOUT)
|
19
|
-
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::
|
16
|
+
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::Wrapper.new(ruby_logger)
|
20
17
|
# logger = SemanticLogger::Logger.new('test')
|
21
18
|
# logger.info('Hello World', :some => :payload)
|
22
19
|
#
|
23
20
|
# Enhance the Rails Logger
|
24
21
|
# # Add the Rails logger to the list of appenders
|
25
|
-
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::
|
22
|
+
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::Wrapper.new(Rails.logger)
|
26
23
|
# Rails.logger = SemanticLogger::Logger.new('Rails')
|
27
24
|
#
|
28
25
|
# # Make ActiveRecord logging include its class name in every log entry
|
29
26
|
# ActiveRecord::Base.logger = SemanticLogger::Logger.new('ActiveRecord')
|
27
|
+
#
|
28
|
+
# Note: Since the log level is controlled by setting the Ruby or Rails logger directly
|
29
|
+
# the level is ignored for this appender
|
30
30
|
def initialize(logger, &block)
|
31
31
|
raise "logger cannot be null when initiailizing the SemanticLogging::Appender::Logger" unless logger
|
32
32
|
@logger = logger
|
33
33
|
|
34
34
|
# Set the formatter to the supplied block
|
35
35
|
@formatter = block || self.default_formatter
|
36
|
+
super(:trace, &block)
|
36
37
|
end
|
37
38
|
|
38
|
-
#
|
39
|
-
# Replace this formatter by supplying a Block to the initializer
|
40
|
-
# Generates logs of the form:
|
41
|
-
# 2011-07-19 14:36:15.660 D [1149:ScriptThreadProcess] Rails -- Hello World
|
42
|
-
def default_formatter
|
43
|
-
Proc.new do |log|
|
44
|
-
message = log.message.to_s
|
45
|
-
tags = log.tags.collect { |tag| "[#{tag}]" }.join(" ") + " " if log.tags && (log.tags.size > 0)
|
46
|
-
|
47
|
-
if log.payload
|
48
|
-
if log.payload.is_a?(Exception)
|
49
|
-
exception = log.payload
|
50
|
-
message << " -- " << "#{exception.class}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
|
51
|
-
else
|
52
|
-
message << " -- " << log.payload.inspect
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
str = "#{log.time.strftime("%Y-%m-%d %H:%M:%S")}.#{"%03d" % (log.time.usec/1000)} #{log.level.to_s[0..0].upcase} [#{$$}:#{log.thread_name}] #{tags}#{log.name} -- #{message}"
|
57
|
-
str << " (#{'%.1f' % log.duration}ms)" if log.duration
|
58
|
-
str
|
59
|
-
end
|
60
|
-
end
|
39
|
+
# TODO Compatible calls to #level and #level= against the underlying Rails/Ruby logger
|
61
40
|
|
62
41
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
63
42
|
# trace entries are mapped to debug since :trace is not supported by the
|
@@ -0,0 +1,280 @@
|
|
1
|
+
# Base appender
|
2
|
+
#
|
3
|
+
# Abstract base class for appenders
|
4
|
+
#
|
5
|
+
# Implements common behavior such as log level, default text formatter etc
|
6
|
+
#
|
7
|
+
# Note: Do not create instances of this class directly
|
8
|
+
#
|
9
|
+
module SemanticLogger
|
10
|
+
class Base
|
11
|
+
attr_accessor :formatter
|
12
|
+
attr_reader :level
|
13
|
+
|
14
|
+
def initialize(level, &block)
|
15
|
+
# Set the formatter to the supplied block
|
16
|
+
@formatter = block || default_formatter
|
17
|
+
|
18
|
+
# Log everything that comes to the appender by default
|
19
|
+
self.level = level || :trace
|
20
|
+
end
|
21
|
+
|
22
|
+
# Default log formatter
|
23
|
+
# Replace this formatter by supplying a Block to the initializer
|
24
|
+
# Generates logs of the form:
|
25
|
+
# 2011-07-19 14:36:15.660 D [1149:ScriptThreadProcess] Rails -- Hello World
|
26
|
+
def default_formatter
|
27
|
+
Proc.new do |log|
|
28
|
+
message = log.message.to_s
|
29
|
+
tags = log.tags.collect { |tag| "[#{tag}]" }.join(" ") + " " if log.tags && (log.tags.size > 0)
|
30
|
+
|
31
|
+
if log.payload
|
32
|
+
if log.payload.is_a?(Exception)
|
33
|
+
exception = log.payload
|
34
|
+
message << " -- " << "#{exception.class}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
|
35
|
+
else
|
36
|
+
message << " -- " << log.payload.inspect
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
str = "#{log.time.strftime("%Y-%m-%d %H:%M:%S")}.#{"%03d" % (log.time.usec/1000)} #{log.level.to_s[0..0].upcase} [#{$$}:#{log.thread_name}] #{tags}#{log.name} -- #{message}"
|
41
|
+
str << " (#{'%.1f' % log.duration}ms)" if log.duration
|
42
|
+
str
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Write log data to underlying data storage
|
47
|
+
def log(log_)
|
48
|
+
raise "Logging Appender must implement #log(log)"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set the logging level for this appender
|
52
|
+
#
|
53
|
+
# Note: This level is only for this particular appender. It does not override
|
54
|
+
# the log level in any logging instance or the default log level
|
55
|
+
# SemanticLogger::Logger.level
|
56
|
+
#
|
57
|
+
# Must be one of the values in SemanticLogger::Logger::LEVELS
|
58
|
+
def level=(level)
|
59
|
+
@level_index = self.class.map_level_to_index(level)
|
60
|
+
@level = level
|
61
|
+
end
|
62
|
+
|
63
|
+
# Implement the log level calls
|
64
|
+
# logger.debug(message, hash|exception=nil, &block)
|
65
|
+
#
|
66
|
+
# Implement the log level query
|
67
|
+
# logger.debug?
|
68
|
+
#
|
69
|
+
# Example:
|
70
|
+
# require 'semantic_logger'
|
71
|
+
#
|
72
|
+
# # Enable trace level logging
|
73
|
+
# SemanticLogger::Logger.level = :info
|
74
|
+
#
|
75
|
+
# # Log to screen
|
76
|
+
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT)
|
77
|
+
#
|
78
|
+
# # And log to a file at the same time
|
79
|
+
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('application.log')
|
80
|
+
#
|
81
|
+
# logger = SemanticLogger::Logger.new('MyApplication')
|
82
|
+
# logger.debug("Only display this if log level is set to Debug or lower")
|
83
|
+
#
|
84
|
+
# # Log semantic information along with a text message
|
85
|
+
# logger.info("Request received", :user => "joe", :duration => 100)
|
86
|
+
#
|
87
|
+
# # Log an exception in a semantic way
|
88
|
+
# logger.info("Parsing received XML", exc)
|
89
|
+
#
|
90
|
+
SemanticLogger::LEVELS.each_with_index do |level, index|
|
91
|
+
class_eval <<-EOT, __FILE__, __LINE__
|
92
|
+
def #{level}(message = nil, payload = nil)
|
93
|
+
if @level_index <= #{index}
|
94
|
+
if block_given? && (result = yield)
|
95
|
+
if result.is_a?(String)
|
96
|
+
message = message.nil? ? result : "\#{message} -- \#{result.to_s}"
|
97
|
+
else
|
98
|
+
payload = payload.nil? ? result : payload.merge(result)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
# Add scoped payload
|
102
|
+
if self.payload
|
103
|
+
payload = payload.nil? ? self.payload : self.payload.merge(payload)
|
104
|
+
end
|
105
|
+
log Log.new(:#{level}, self.class.thread_name, name, message, payload, Time.now, nil, tags, #{index})
|
106
|
+
true
|
107
|
+
else
|
108
|
+
false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def #{level}?
|
113
|
+
@level_index <= #{index}
|
114
|
+
end
|
115
|
+
|
116
|
+
# Log the duration of the supplied block
|
117
|
+
# If an exception occurs in the block the exception is logged using the
|
118
|
+
# same log level. The exception will flow through to the caller unchanged
|
119
|
+
def benchmark_#{level}(message, payload = nil)
|
120
|
+
raise "Mandatory block missing" unless block_given?
|
121
|
+
if @level_index <= #{index}
|
122
|
+
start = Time.now
|
123
|
+
begin
|
124
|
+
result = yield
|
125
|
+
end_time = Time.now
|
126
|
+
# Add scoped payload
|
127
|
+
if self.payload
|
128
|
+
payload = payload.nil? ? self.payload : self.payload.merge(payload)
|
129
|
+
end
|
130
|
+
log Log.new(:#{level}, self.class.thread_name, name, message, payload, end_time, 1000.0 * (end_time - start), tags, #{index})
|
131
|
+
result
|
132
|
+
rescue Exception => exc
|
133
|
+
# TODO Need to be able to have both an exception and a Payload
|
134
|
+
log Log.new(:#{level}, self.class.thread_name, name, message, exc, Time.now, 1000.0 * (Time.now - start), tags, #{index})
|
135
|
+
raise exc
|
136
|
+
end
|
137
|
+
else
|
138
|
+
yield
|
139
|
+
end
|
140
|
+
end
|
141
|
+
EOT
|
142
|
+
end
|
143
|
+
|
144
|
+
# Add the supplied tags to the list of tags to log for this thread whilst
|
145
|
+
# the supplied block is active
|
146
|
+
# Returns nil if no tags are currently set
|
147
|
+
def with_tags(*tags)
|
148
|
+
current_tags = self.tags
|
149
|
+
# Check for nil tags
|
150
|
+
if tags
|
151
|
+
Thread.current[:semantic_logger_tags] = current_tags ? current_tags + tags : tags
|
152
|
+
end
|
153
|
+
yield
|
154
|
+
ensure
|
155
|
+
Thread.current[:semantic_logger_tags] = current_tags
|
156
|
+
end
|
157
|
+
|
158
|
+
# Add support for the ActiveSupport::TaggedLogging
|
159
|
+
alias_method :tagged, :with_tags
|
160
|
+
|
161
|
+
# Returns [Array] of [String] tags currently active for this thread
|
162
|
+
# Returns nil if no tags are set
|
163
|
+
def tags
|
164
|
+
Thread.current[:semantic_logger_tags]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Thread specific context information to be logged with every log entry
|
168
|
+
#
|
169
|
+
# Add a payload to all log calls on This Thread within the supplied block
|
170
|
+
#
|
171
|
+
# logger.with_payload(:tracking_number=>12345) do
|
172
|
+
# logger.debug('Hello World')
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# If a log call already includes a pyload, this payload will be merged with
|
176
|
+
# the supplied payload, with the supplied payload taking precedence
|
177
|
+
#
|
178
|
+
# logger.with_payload(:tracking_number=>12345) do
|
179
|
+
# logger.debug('Hello World', :result => 'blah')
|
180
|
+
# end
|
181
|
+
def with_payload(payload)
|
182
|
+
current_payload = self.payload
|
183
|
+
Thread.current[:semantic_logger_payload] = current_payload ? current_payload.merge(payload) : payload
|
184
|
+
yield
|
185
|
+
ensure
|
186
|
+
Thread.current[:semantic_logger_payload] = current_payload
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns [Hash] payload to be added to every log entry in the current scope
|
190
|
+
# on this thread.
|
191
|
+
# Returns nil if no payload is currently set
|
192
|
+
def payload
|
193
|
+
Thread.current[:semantic_logger_payload]
|
194
|
+
end
|
195
|
+
|
196
|
+
# Semantic Logging does not support :unknown level since these
|
197
|
+
# are not understood by the majority of the logging providers
|
198
|
+
# Map it to :error
|
199
|
+
alias :unknown :error
|
200
|
+
alias :unknown? :error?
|
201
|
+
|
202
|
+
# #TODO implement a thread safe #silence method
|
203
|
+
|
204
|
+
############################################################################
|
205
|
+
protected
|
206
|
+
|
207
|
+
# Return the level index for fast comparisons
|
208
|
+
attr_reader :level_index
|
209
|
+
|
210
|
+
# Struct Log
|
211
|
+
#
|
212
|
+
# level
|
213
|
+
# Log level of the supplied log call
|
214
|
+
# :trace, :debug, :info, :warn, :error, :fatal
|
215
|
+
#
|
216
|
+
# thread_name
|
217
|
+
# Name of the thread in which the logging call was called
|
218
|
+
#
|
219
|
+
# name
|
220
|
+
# Class name supplied to the logging instance
|
221
|
+
#
|
222
|
+
# message
|
223
|
+
# Text message to be logged
|
224
|
+
#
|
225
|
+
# payload
|
226
|
+
# Optional Hash or Ruby Exception object to be logged
|
227
|
+
#
|
228
|
+
# time
|
229
|
+
# The time at which the log entry was created
|
230
|
+
#
|
231
|
+
# duration
|
232
|
+
# The time taken to complete a benchmark call
|
233
|
+
#
|
234
|
+
# tags
|
235
|
+
# Any tags active on the thread when the log call was made
|
236
|
+
#
|
237
|
+
# level_index
|
238
|
+
# Internal index of the log level
|
239
|
+
Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index)
|
240
|
+
|
241
|
+
# For JRuby include the Thread name rather than its id
|
242
|
+
if defined? Java
|
243
|
+
def self.thread_name
|
244
|
+
Java::java.lang::Thread.current_thread.name
|
245
|
+
end
|
246
|
+
else
|
247
|
+
def self.thread_name
|
248
|
+
Thread.current.object_id
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Internal method to return the log level as an internal index
|
253
|
+
# Also supports mapping the ::Logger levels to SemanticLogger levels
|
254
|
+
def self.map_level_to_index(level)
|
255
|
+
index = if level.is_a?(Integer)
|
256
|
+
# Mapping of Rails and Ruby Logger levels to SemanticLogger levels
|
257
|
+
@@map_levels ||= begin
|
258
|
+
levels = []
|
259
|
+
::Logger::Severity.constants.each do |constant|
|
260
|
+
levels[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
|
261
|
+
end
|
262
|
+
end [level] if defined?(::Logger::Severity)
|
263
|
+
elsif level.is_a?(String)
|
264
|
+
level = level.downcase.to_sym
|
265
|
+
LEVELS.index(level)
|
266
|
+
else
|
267
|
+
LEVELS.index(level)
|
268
|
+
end
|
269
|
+
raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
|
270
|
+
index
|
271
|
+
end
|
272
|
+
|
273
|
+
# Appenders don't take a class name, so use this class name if an appender
|
274
|
+
# is logged to directly
|
275
|
+
def name
|
276
|
+
self.class.name
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
end
|
@@ -19,7 +19,7 @@
|
|
19
19
|
#
|
20
20
|
# # Now log to the Logger above as well as MongoDB at the same time
|
21
21
|
#
|
22
|
-
# db =
|
22
|
+
# db = Mongodb::Connection.new['production_logging']
|
23
23
|
#
|
24
24
|
# SemanticLogger::Logger.appenders << SemanticLogger::Appender::MongoDB.new(
|
25
25
|
# :db => db,
|
@@ -29,40 +29,31 @@
|
|
29
29
|
# # This will be logged to both the Ruby Logger and MongoDB
|
30
30
|
# logger.debug("Login time", :user => 'Mary', :duration => 230, :ip_address=>'192.168.0.1')
|
31
31
|
#
|
32
|
-
require 'logger'
|
33
32
|
module SemanticLogger
|
34
|
-
class Logger
|
33
|
+
class Logger < Base
|
35
34
|
include SyncAttr
|
36
35
|
|
37
|
-
# Logging levels in order of precedence
|
38
|
-
LEVELS = [:trace, :debug, :info, :warn, :error, :fatal]
|
39
|
-
|
40
|
-
# Mapping of Rails and Ruby Logger levels to SemanticLogger levels
|
41
|
-
MAP_LEVELS = []
|
42
|
-
::Logger::Severity.constants.each do |constant|
|
43
|
-
MAP_LEVELS[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
|
44
|
-
end
|
45
|
-
|
46
36
|
# Thread safe Class Attribute accessor for appenders array
|
47
37
|
sync_cattr_accessor :appenders do
|
48
38
|
[]
|
49
39
|
end
|
50
40
|
|
41
|
+
# Initial default Level for all new instances of SemanticLogger::Logger
|
42
|
+
@@default_level = :info
|
43
|
+
|
51
44
|
# Allow for setting the global default log level
|
52
45
|
# This change only applies to _new_ loggers, existing logger levels
|
53
46
|
# will not be changed in any way
|
54
|
-
def self.
|
55
|
-
@@
|
47
|
+
def self.default_level=(level)
|
48
|
+
@@default_level = level
|
56
49
|
end
|
57
50
|
|
58
51
|
# Returns the global default log level for new Logger instances
|
59
|
-
def self.
|
60
|
-
@@
|
52
|
+
def self.default_level
|
53
|
+
@@default_level
|
61
54
|
end
|
62
55
|
|
63
|
-
attr_reader :name
|
64
|
-
|
65
|
-
@@level = :info
|
56
|
+
attr_reader :name
|
66
57
|
|
67
58
|
# Returns a Logger instance
|
68
59
|
#
|
@@ -76,144 +67,11 @@ module SemanticLogger
|
|
76
67
|
# to be used in the logger
|
77
68
|
# options:
|
78
69
|
# :level The initial log level to start with for this logger instance
|
79
|
-
def initialize(klass,
|
70
|
+
def initialize(klass, level=self.class.default_level)
|
80
71
|
@name = klass.is_a?(String) ? klass : klass.name
|
81
|
-
|
72
|
+
self.level = level
|
82
73
|
end
|
83
74
|
|
84
|
-
# Set the logging level
|
85
|
-
# Must be one of the values in #LEVELS
|
86
|
-
def level=(level)
|
87
|
-
set_level(level)
|
88
|
-
end
|
89
|
-
|
90
|
-
# Implement the log level calls
|
91
|
-
# logger.debug(message|hash|exception, hash|exception=nil, &block)
|
92
|
-
#
|
93
|
-
# Implement the log level query
|
94
|
-
# logger.debug?
|
95
|
-
#
|
96
|
-
# Example:
|
97
|
-
# logger = SemanticLogging::Logger.new(self)
|
98
|
-
# logger.debug("Only display this if log level is set to Debug or lower")
|
99
|
-
#
|
100
|
-
# # Log semantic information along with a text message
|
101
|
-
# logger.info("Request received", :user => "joe", :duration => 100)
|
102
|
-
#
|
103
|
-
# # Log an exception in a semantic way
|
104
|
-
# logger.info("Parsing received XML", exc)
|
105
|
-
#
|
106
|
-
LEVELS.each_with_index do |level, index|
|
107
|
-
class_eval <<-EOT, __FILE__, __LINE__
|
108
|
-
def #{level}(message = nil, payload = nil)
|
109
|
-
if @level_index <= #{index}
|
110
|
-
if block_given? && (result = yield)
|
111
|
-
if result.is_a?(String)
|
112
|
-
message = message.nil? ? result : "\#{message} -- \#{result.to_s}"
|
113
|
-
else
|
114
|
-
payload = payload.nil? ? result : payload.merge(result)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
# Add scoped payload
|
118
|
-
if self.payload
|
119
|
-
payload = payload.nil? ? self.payload : self.payload.merge(payload)
|
120
|
-
end
|
121
|
-
self.class.queue << Log.new(:#{level}, self.class.thread_name, name, message, payload, Time.now, nil, tags)
|
122
|
-
true
|
123
|
-
else
|
124
|
-
false
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def #{level}?
|
129
|
-
@level_index <= #{index}
|
130
|
-
end
|
131
|
-
|
132
|
-
# Log the duration of the supplied block
|
133
|
-
# If an exception occurs in the block the exception is logged using the
|
134
|
-
# same log level. The exception will flow through to the caller unchanged
|
135
|
-
def benchmark_#{level}(message, payload = nil)
|
136
|
-
raise "Mandatory block missing" unless block_given?
|
137
|
-
if @level_index <= #{index}
|
138
|
-
start = Time.now
|
139
|
-
begin
|
140
|
-
result = yield
|
141
|
-
end_time = Time.now
|
142
|
-
# Add scoped payload
|
143
|
-
if self.payload
|
144
|
-
payload = payload.nil? ? self.payload : self.payload.merge(payload)
|
145
|
-
end
|
146
|
-
self.class.queue << Log.new(:#{level}, self.class.thread_name, name, message, payload, end_time, 1000.0 * (end_time - start), tags)
|
147
|
-
result
|
148
|
-
rescue Exception => exc
|
149
|
-
# TODO Need to be able to have both an exception and a Payload
|
150
|
-
self.class.queue << Log.new(:#{level}, self.class.thread_name, name, message, exc, Time.now, 1000.0 * (Time.now - start), tags)
|
151
|
-
raise exc
|
152
|
-
end
|
153
|
-
else
|
154
|
-
yield
|
155
|
-
end
|
156
|
-
end
|
157
|
-
EOT
|
158
|
-
end
|
159
|
-
|
160
|
-
# Add the supplied tags to the list of tags to log for this thread whilst
|
161
|
-
# the supplied block is active
|
162
|
-
# Returns nil if no tags are currently set
|
163
|
-
def with_tags(*tags)
|
164
|
-
current_tags = self.tags
|
165
|
-
# Check for nil tags
|
166
|
-
if tags
|
167
|
-
Thread.current[:semantic_logger_tags] = current_tags ? current_tags + tags : tags
|
168
|
-
end
|
169
|
-
yield
|
170
|
-
ensure
|
171
|
-
Thread.current[:semantic_logger_tags] = current_tags
|
172
|
-
end
|
173
|
-
|
174
|
-
# Returns [Array] of [String] tags currently active for this thread
|
175
|
-
# Returns nil if no tags are set
|
176
|
-
def tags
|
177
|
-
Thread.current[:semantic_logger_tags]
|
178
|
-
end
|
179
|
-
|
180
|
-
# Thread specific context information to be logged with every log entry
|
181
|
-
#
|
182
|
-
# Add a payload to all log calls on This Thread within the supplied block
|
183
|
-
#
|
184
|
-
# logger.with_payload(:tracking_number=>12345) do
|
185
|
-
# logger.debug('Hello World')
|
186
|
-
# end
|
187
|
-
#
|
188
|
-
# If a log call already includes a pyload, this payload will be merged with
|
189
|
-
# the supplied payload, with the supplied payload taking precedence
|
190
|
-
#
|
191
|
-
# logger.with_payload(:tracking_number=>12345) do
|
192
|
-
# logger.debug('Hello World', :result => 'blah')
|
193
|
-
# end
|
194
|
-
def with_payload(payload)
|
195
|
-
current_payload = self.payload
|
196
|
-
Thread.current[:semantic_logger_payload] = current_payload ? current_payload.merge(payload) : payload
|
197
|
-
yield
|
198
|
-
ensure
|
199
|
-
Thread.current[:semantic_logger_payload] = current_payload
|
200
|
-
end
|
201
|
-
|
202
|
-
# Returns [Hash] payload to be added to every log entry in the current scope
|
203
|
-
# on this thread.
|
204
|
-
# Returns nil if no payload is currently set
|
205
|
-
def payload
|
206
|
-
Thread.current[:semantic_logger_payload]
|
207
|
-
end
|
208
|
-
|
209
|
-
# Semantic Logging does not support :unknown level since these
|
210
|
-
# are not understood by the majority of the logging providers
|
211
|
-
# Map it to :error
|
212
|
-
alias :unknown :error
|
213
|
-
alias :unknown? :error?
|
214
|
-
|
215
|
-
# #TODO implement a thread safe #silence method
|
216
|
-
|
217
75
|
# Returns [Integer] the number of log entries that have not been written
|
218
76
|
# to the appenders
|
219
77
|
#
|
@@ -235,22 +93,27 @@ module SemanticLogger
|
|
235
93
|
reply_queue.pop
|
236
94
|
end
|
237
95
|
|
96
|
+
############################################################################
|
97
|
+
protected
|
98
|
+
|
99
|
+
# Place log request on the queue for the Appender thread to write to each
|
100
|
+
# appender in the order that they were registered
|
101
|
+
def log(log)
|
102
|
+
self.class.queue << log
|
103
|
+
end
|
104
|
+
|
238
105
|
# Internal logger for SemanticLogger
|
239
106
|
# For example when an appender is not working etc..
|
240
|
-
# By default logs to STDERR
|
241
|
-
# logger, but never to
|
107
|
+
# By default logs to STDERR
|
108
|
+
# Can be replaced with another Ruby logger or Rails logger, but never to
|
109
|
+
# SemanticLogger::Logger itself
|
242
110
|
#
|
243
111
|
# Warning: Do not use this logger directly it is intended for internal logging
|
244
112
|
# within Semantic Logger itself
|
245
113
|
sync_cattr_accessor :logger do
|
246
|
-
|
247
|
-
l.level = ::Logger::INFO
|
248
|
-
l
|
114
|
+
SemanticLogger::Appender::File.new(STDERR, :warn)
|
249
115
|
end
|
250
116
|
|
251
|
-
############################################################################
|
252
|
-
protected
|
253
|
-
|
254
117
|
# Log to queue
|
255
118
|
# Starts the appender thread the first time a logging call is made
|
256
119
|
sync_cattr_reader :queue do
|
@@ -259,70 +122,11 @@ module SemanticLogger
|
|
259
122
|
Queue.new
|
260
123
|
end
|
261
124
|
|
262
|
-
# Struct Log
|
263
|
-
#
|
264
|
-
# level
|
265
|
-
# Log level of the supplied log call
|
266
|
-
# :trace, :debug, :info, :warn, :error, :fatal
|
267
|
-
#
|
268
|
-
# thread_name
|
269
|
-
# Name of the thread in which the logging call was called
|
270
|
-
#
|
271
|
-
# name
|
272
|
-
# Class name supplied to the logging instance
|
273
|
-
#
|
274
|
-
# message
|
275
|
-
# Text message to be logged
|
276
|
-
#
|
277
|
-
# payload
|
278
|
-
# Optional Hash or Ruby Exception object to be logged
|
279
|
-
#
|
280
|
-
# time
|
281
|
-
# The time at which the log entry was created
|
282
|
-
#
|
283
|
-
# duration
|
284
|
-
# The time taken to complete a benchmark call
|
285
|
-
#
|
286
|
-
# tags
|
287
|
-
# Any tags active on the thread when the log call was made
|
288
|
-
#
|
289
|
-
Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags)
|
290
|
-
|
291
|
-
# For JRuby include the Thread name rather than its id
|
292
|
-
if defined? Java
|
293
|
-
def self.thread_name
|
294
|
-
Java::java.lang::Thread.current_thread.name
|
295
|
-
end
|
296
|
-
else
|
297
|
-
def self.thread_name
|
298
|
-
Thread.current.object_id
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
# Verify and set the level
|
303
|
-
def set_level(level)
|
304
|
-
index = if level.is_a?(Integer)
|
305
|
-
MAP_LEVELS[level]
|
306
|
-
elsif level.is_a?(String)
|
307
|
-
level = level.downcase.to_sym
|
308
|
-
LEVELS.index(level)
|
309
|
-
else
|
310
|
-
LEVELS.index(level)
|
311
|
-
end
|
312
|
-
|
313
|
-
raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
|
314
|
-
@level_index = index
|
315
|
-
@level = level
|
316
|
-
end
|
317
|
-
|
318
125
|
# Start a separate appender thread responsible for reading log messages and
|
319
126
|
# calling the appenders in it's thread
|
320
127
|
def self.startup
|
321
128
|
@@appender_thread = Thread.new do
|
322
129
|
begin
|
323
|
-
# #TODO Logger needs it's own "reliable" appender ;)
|
324
|
-
# For example if an appender constantly fails
|
325
|
-
# ( bad filename or path, invalid server )
|
326
130
|
logger.debug "SemanticLogger::Logger Appender thread started"
|
327
131
|
while message=queue.pop
|
328
132
|
if message.is_a? Log
|
@@ -365,5 +169,11 @@ module SemanticLogger
|
|
365
169
|
result
|
366
170
|
end
|
367
171
|
|
172
|
+
# Formatting does not occur within this thread, it is done by each appender
|
173
|
+
# in the appender thread
|
174
|
+
def default_formatter
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
|
368
178
|
end
|
369
179
|
end
|
@@ -27,7 +27,7 @@ module SemanticLogger #:nodoc:
|
|
27
27
|
config = Rails.application.config
|
28
28
|
|
29
29
|
# Set the default log level based on the Rails config
|
30
|
-
SemanticLogger::Logger.
|
30
|
+
SemanticLogger::Logger.default_level = config.log_level
|
31
31
|
|
32
32
|
# Existing loggers are ignored because servers like trinidad supply their
|
33
33
|
# own file loggers which would result in duplicate logging to the same log file
|
@@ -38,26 +38,24 @@ module SemanticLogger #:nodoc:
|
|
38
38
|
FileUtils.mkdir_p File.dirname path
|
39
39
|
end
|
40
40
|
|
41
|
-
# First set the internal logger in case something goes wrong
|
42
|
-
# with an appender
|
43
|
-
SemanticLogger::Logger.logger = begin
|
44
|
-
l = ::Logger.new(path)
|
45
|
-
l.level = ::Logger.const_get(config.log_level.to_s.upcase)
|
46
|
-
l
|
47
|
-
end
|
48
|
-
|
49
41
|
# Add the log file to the list of appenders
|
50
|
-
|
51
|
-
|
52
|
-
#logger = ActiveSupport::TaggedLogging.new(logger) if defined?(ActiveSupport::TaggedLogging)
|
42
|
+
file_appender = SemanticLogger::Appender::File.new(path)
|
53
43
|
|
44
|
+
# Set internal logger to log to file only, in case another appender
|
45
|
+
# experiences logging problems
|
46
|
+
SemanticLogger::Logger.logger = file_appender
|
47
|
+
SemanticLogger::Logger.appenders << file_appender
|
48
|
+
|
54
49
|
SemanticLogger::Logger.new(Rails)
|
55
50
|
rescue StandardError
|
56
|
-
|
51
|
+
# If not able to log to file, log to standard error with warning level only
|
52
|
+
SemanticLogger::Logger.default_level = :warn
|
53
|
+
|
54
|
+
file_appender = SemanticLogger::Appender::File.new(STDERR)
|
55
|
+
SemanticLogger::Logger.logger = file_appender
|
56
|
+
SemanticLogger::Logger.appenders << file_appender
|
57
57
|
|
58
58
|
logger = SemanticLogger::Logger.new(Rails)
|
59
|
-
logger.level = :warn
|
60
|
-
#logger = ActiveSupport::TaggedLogging.new(logger) if defined?(ActiveSupport::TaggedLogging)
|
61
59
|
logger.warn(
|
62
60
|
"Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " +
|
63
61
|
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Allow test to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'shoulda'
|
7
|
+
require 'logger'
|
8
|
+
require 'semantic_logger'
|
9
|
+
require 'test/mock_logger'
|
10
|
+
|
11
|
+
# Unit Test for SemanticLogger::Appender::File
|
12
|
+
#
|
13
|
+
class AppenderFileTest < Test::Unit::TestCase
|
14
|
+
context SemanticLogger::Appender::File do
|
15
|
+
setup do
|
16
|
+
@time = Time.parse("2012-08-02 09:48:32.482")
|
17
|
+
@io = StringIO.new
|
18
|
+
@appender = SemanticLogger::Appender::File.new(@io)
|
19
|
+
@hash = { :session_id => 'HSSKLEU@JDK767', :tracking_number => 12345 }
|
20
|
+
@hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
|
21
|
+
@thread_name = SemanticLogger::Base.thread_name
|
22
|
+
end
|
23
|
+
|
24
|
+
context "format logs into text form" do
|
25
|
+
should "handle no message or payload" do
|
26
|
+
@appender.debug
|
27
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:#{@thread_name}\] SemanticLogger::Appender::File -- \n/, @io.string
|
28
|
+
end
|
29
|
+
|
30
|
+
should "handle message" do
|
31
|
+
@appender.debug 'hello world'
|
32
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:#{@thread_name}\] SemanticLogger::Appender::File -- hello world\n/, @io.string
|
33
|
+
end
|
34
|
+
|
35
|
+
should "handle message and payload" do
|
36
|
+
@appender.debug 'hello world', @hash
|
37
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:#{@thread_name}\] SemanticLogger::Appender::File -- hello world -- #{@hash_str}\n/, @io.string
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "for each log level" do
|
42
|
+
# Ensure that any log level can be logged
|
43
|
+
SemanticLogger::LEVELS.each do |level|
|
44
|
+
should "log #{level} information" do
|
45
|
+
@appender.send(level, 'hello world', @hash)
|
46
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:#{@thread_name}\] SemanticLogger::Appender::File -- hello world -- #{@hash_str}\n/, @io.string
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -98,7 +98,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
|
|
98
98
|
|
99
99
|
context "for each log level" do
|
100
100
|
# Ensure that any log level can be logged
|
101
|
-
SemanticLogger::
|
101
|
+
SemanticLogger::LEVELS.each do |level|
|
102
102
|
should "log #{level} information" do
|
103
103
|
@appender.log SemanticLogger::Logger::Log.new(level, 'thread', 'my_class', 'hello world -- Calculations', @hash, @time)
|
104
104
|
document = @appender.collection.find_one
|
@@ -8,15 +8,16 @@ require 'logger'
|
|
8
8
|
require 'semantic_logger'
|
9
9
|
require 'test/mock_logger'
|
10
10
|
|
11
|
-
# Unit Test for SemanticLogger::Appender::
|
11
|
+
# Unit Test for SemanticLogger::Appender::Wrapper
|
12
12
|
#
|
13
|
-
class
|
14
|
-
context SemanticLogger::Appender::
|
13
|
+
class AppenderWrapperTest < Test::Unit::TestCase
|
14
|
+
context SemanticLogger::Appender::Wrapper do
|
15
15
|
setup do
|
16
16
|
@time = Time.parse("2012-08-02 09:48:32.482")
|
17
17
|
@mock_logger = MockLogger.new
|
18
|
-
@appender = SemanticLogger::Appender::
|
18
|
+
@appender = SemanticLogger::Appender::Wrapper.new(@mock_logger)
|
19
19
|
@hash = { :session_id => 'HSSKLEU@JDK767', :tracking_number => 12345 }
|
20
|
+
@hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
|
20
21
|
end
|
21
22
|
|
22
23
|
context "format logs into text form" do
|
@@ -55,7 +56,7 @@ class AppenderLoggerTest < Test::Unit::TestCase
|
|
55
56
|
log.message = 'hello world'
|
56
57
|
log.payload = @hash
|
57
58
|
@appender.log(log)
|
58
|
-
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:\] class -- hello world --
|
59
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:\] class -- hello world -- #{@hash_str}/, @mock_logger.message
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
@@ -63,8 +64,8 @@ class AppenderLoggerTest < Test::Unit::TestCase
|
|
63
64
|
# Ensure that any log level can be logged
|
64
65
|
Logger::Severity.constants.each do |level|
|
65
66
|
should "log #{level.downcase.to_sym} info" do
|
66
|
-
@appender.log SemanticLogger::Logger::Log.new(level.downcase.to_sym, 'thread', 'class', 'hello world
|
67
|
-
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:thread\] class -- hello world --
|
67
|
+
@appender.log SemanticLogger::Logger::Log.new(level.downcase.to_sym, 'thread', 'class', 'hello world', @hash, Time.now)
|
68
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:thread\] class -- hello world -- #{@hash_str}/, @mock_logger.message
|
68
69
|
end
|
69
70
|
end
|
70
71
|
end
|
data/test/logger_test.rb
CHANGED
@@ -16,13 +16,14 @@ class LoggerTest < Test::Unit::TestCase
|
|
16
16
|
setup do
|
17
17
|
# Use a mock logger that just keeps the last logged entry in an instance variable
|
18
18
|
@mock_logger = MockLogger.new
|
19
|
-
@appender = SemanticLogger::Appender::
|
19
|
+
@appender = SemanticLogger::Appender::Wrapper.new(@mock_logger)
|
20
20
|
SemanticLogger::Logger.appenders << @appender
|
21
21
|
|
22
22
|
# Use this test's class name as the application name in the log output
|
23
|
-
@logger = SemanticLogger::Logger.new('LoggerTest', :
|
23
|
+
@logger = SemanticLogger::Logger.new('LoggerTest', :trace)
|
24
24
|
|
25
25
|
@hash = { :session_id => 'HSSKLEU@JDK767', :tracking_number => 12345 }
|
26
|
+
@hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
|
26
27
|
end
|
27
28
|
|
28
29
|
teardown do
|
@@ -30,11 +31,11 @@ class LoggerTest < Test::Unit::TestCase
|
|
30
31
|
end
|
31
32
|
|
32
33
|
# Ensure that any log level can be logged
|
33
|
-
SemanticLogger::
|
34
|
+
SemanticLogger::LEVELS.each do |level|
|
34
35
|
should "log #{level} info" do
|
35
36
|
@logger.send(level, 'hello world', @hash) { "Calculations" }
|
36
37
|
SemanticLogger::Logger.flush
|
37
|
-
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- hello world -- Calculations --
|
38
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- hello world -- Calculations -- #{@hash_str}/, @mock_logger.message
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -58,12 +59,13 @@ class LoggerTest < Test::Unit::TestCase
|
|
58
59
|
end
|
59
60
|
|
60
61
|
should "add payload to log entries" do
|
62
|
+
hash = {:tracking_number=>"123456", :even=>2, :more=>"data"}
|
63
|
+
hash_str = hash.inspect.sub("{", "\\{").sub("}", "\\}")
|
61
64
|
@logger.with_payload(:tracking_number => '123456') do
|
62
65
|
@logger.with_payload(:even => 2, :more => 'data') do
|
63
66
|
@logger.info('Hello world')
|
64
67
|
SemanticLogger::Logger.flush
|
65
|
-
|
66
|
-
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- Hello world -- \{:even=>2, :more=>\"data\", :tracking_number=>\"123456\"\}/, @mock_logger.message
|
68
|
+
assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message
|
67
69
|
end
|
68
70
|
end
|
69
71
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 6
|
8
|
+
- 0
|
9
|
+
version: 0.6.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Reid Morrison
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2012-09-
|
17
|
+
date: 2012-09-18 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -54,8 +54,9 @@ files:
|
|
54
54
|
- FUTURE.rb
|
55
55
|
- init.txt
|
56
56
|
- lib/semantic_logger/appender/file.rb
|
57
|
-
- lib/semantic_logger/appender/logger.rb
|
58
57
|
- lib/semantic_logger/appender/mongodb.rb
|
58
|
+
- lib/semantic_logger/appender/wrapper.rb
|
59
|
+
- lib/semantic_logger/base.rb
|
59
60
|
- lib/semantic_logger/logger.rb
|
60
61
|
- lib/semantic_logger/railtie.rb
|
61
62
|
- lib/semantic_logger/version.rb
|
@@ -63,8 +64,9 @@ files:
|
|
63
64
|
- LICENSE.txt
|
64
65
|
- Rakefile
|
65
66
|
- README.md
|
66
|
-
- test/
|
67
|
+
- test/appender_file_test.rb
|
67
68
|
- test/appender_mongodb_test.rb
|
69
|
+
- test/appender_wrapper_test.rb
|
68
70
|
- test/logger_test.rb
|
69
71
|
- test/mock_logger.rb
|
70
72
|
has_rdoc: true
|