lumberjack 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT_LICENSE +20 -0
- data/README.rdoc +86 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/lib/lumberjack.rb +39 -0
- data/lib/lumberjack/device.rb +26 -0
- data/lib/lumberjack/device/date_rolling_log_file.rb +58 -0
- data/lib/lumberjack/device/log_file.rb +18 -0
- data/lib/lumberjack/device/null.rb +15 -0
- data/lib/lumberjack/device/rolling_log_file.rb +109 -0
- data/lib/lumberjack/device/size_rolling_log_file.rb +58 -0
- data/lib/lumberjack/device/writer.rb +119 -0
- data/lib/lumberjack/formatter.rb +76 -0
- data/lib/lumberjack/formatter/exception_formatter.rb +12 -0
- data/lib/lumberjack/formatter/inspect_formatter.rb +10 -0
- data/lib/lumberjack/formatter/pretty_print_formatter.rb +23 -0
- data/lib/lumberjack/formatter/string_formatter.rb +10 -0
- data/lib/lumberjack/log_entry.rb +36 -0
- data/lib/lumberjack/logger.rb +302 -0
- data/lib/lumberjack/rack.rb +5 -0
- data/lib/lumberjack/rack/unit_of_work.rb +15 -0
- data/lib/lumberjack/severity.rb +23 -0
- data/lib/lumberjack/template.rb +71 -0
- data/spec/device/date_rolling_log_file_spec.rb +66 -0
- data/spec/device/date_rolling_log_file_spec.rbc +2118 -0
- data/spec/device/log_file_spec.rb +26 -0
- data/spec/device/log_file_spec.rbc +727 -0
- data/spec/device/null_spec.rb +12 -0
- data/spec/device/null_spec.rbc +362 -0
- data/spec/device/rolling_log_file_spec.rb +117 -0
- data/spec/device/rolling_log_file_spec.rbc +2894 -0
- data/spec/device/size_rolling_log_file_spec.rb +54 -0
- data/spec/device/size_rolling_log_file_spec.rbc +1961 -0
- data/spec/device/stream_spec.rbc +3310 -0
- data/spec/device/writer_spec.rb +118 -0
- data/spec/entry_spec.rbc +2333 -0
- data/spec/formatter/exception_formatter_spec.rb +20 -0
- data/spec/formatter/exception_formatter_spec.rbc +620 -0
- data/spec/formatter/inspect_formatter_spec.rb +13 -0
- data/spec/formatter/inspect_formatter_spec.rbc +360 -0
- data/spec/formatter/pretty_print_formatter_spec.rb +14 -0
- data/spec/formatter/pretty_print_formatter_spec.rbc +380 -0
- data/spec/formatter/string_formatter_spec.rb +12 -0
- data/spec/formatter/string_formatter_spec.rbc +314 -0
- data/spec/formatter_spec.rb +45 -0
- data/spec/formatter_spec.rbc +1431 -0
- data/spec/log_entry_spec.rb +69 -0
- data/spec/logger_spec.rb +390 -0
- data/spec/logger_spec.rbc +10043 -0
- data/spec/lumberjack_spec.rb +22 -0
- data/spec/lumberjack_spec.rbc +523 -0
- data/spec/rack/unit_of_work_spec.rb +26 -0
- data/spec/rack/unit_of_work_spec.rbc +697 -0
- data/spec/severity_spec.rb +23 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/spec_helper.rbc +391 -0
- data/spec/template_spec.rb +34 -0
- data/spec/template_spec.rbc +1563 -0
- data/spec/unique_identifier_spec.rbc +329 -0
- metadata +128 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
module Lumberjack
|
2
|
+
class Device
|
3
|
+
# This logging device writes log entries as strings to an IO stream.
|
4
|
+
class Writer < Device
|
5
|
+
DEFAULT_FIRST_LINE_TEMPLATE = "[:time :severity :progname(:pid) #:unit_of_work_id] :message".freeze
|
6
|
+
DEFAULT_ADDITIONAL_LINES_TEMPLATE = "#{Lumberjack::LINE_SEPARATOR}> [#:unit_of_work_id] :message".freeze
|
7
|
+
|
8
|
+
# The size of the internal buffer. Defaults to 32K.
|
9
|
+
attr_accessor :buffer_size
|
10
|
+
|
11
|
+
# Internal buffer to batch writes to the stream.
|
12
|
+
class Buffer # :nodoc:
|
13
|
+
attr_reader :size
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@values = []
|
17
|
+
@size = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(string)
|
21
|
+
@values << string
|
22
|
+
@size += string.size
|
23
|
+
end
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
@values.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def join(delimiter)
|
30
|
+
@values.join(delimiter)
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear
|
34
|
+
@values = []
|
35
|
+
@size = 0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a new device to write log entries to a stream. Entries are converted to strings
|
40
|
+
# using a Template. The template can be specified using the <tt>:template</tt> option. This can
|
41
|
+
# either be a Proc or a string that will compile into a Template object.
|
42
|
+
#
|
43
|
+
# If the template is a Proc, it should accept an LogEntry as its only argument and output a string.
|
44
|
+
#
|
45
|
+
# If the template is a template string, it will be used to create a Template. The
|
46
|
+
# <tt>:additional_lines</tt> and <tt>:time_format</tt> options will be passed through to the
|
47
|
+
# Template constuctor.
|
48
|
+
#
|
49
|
+
# The default template is <tt>"[:time :severity :progname(:pid) #:unit_of_work_id] :message"</tt>
|
50
|
+
# with additional lines formatted as <tt>"\n [#:unit_of_work_id] :message"</tt>. The unit of
|
51
|
+
# work id will only appear if it is present.
|
52
|
+
def initialize(stream, options = {})
|
53
|
+
@lock = Mutex.new
|
54
|
+
@stream = stream
|
55
|
+
@stream.sync = true if @stream.respond_to?(:sync=)
|
56
|
+
@buffer = Buffer.new
|
57
|
+
@buffer_size = (options[:buffer_size] || 32 * 1024)
|
58
|
+
template = (options[:template] || DEFAULT_FIRST_LINE_TEMPLATE)
|
59
|
+
if template.respond_to?(:call)
|
60
|
+
@template = template
|
61
|
+
else
|
62
|
+
additional_lines = (options[:additional_lines] || DEFAULT_ADDITIONAL_LINES_TEMPLATE)
|
63
|
+
@template = Template.new(template, :additional_lines => additional_lines, :time_format => options[:time_format])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Write an entry to the stream. The entry will be converted into a string using the defined template.
|
68
|
+
def write(entry)
|
69
|
+
string = @template.call(entry)
|
70
|
+
@lock.synchronize do
|
71
|
+
@buffer << string
|
72
|
+
end
|
73
|
+
flush if @buffer.size >= buffer_size
|
74
|
+
end
|
75
|
+
|
76
|
+
# Close the underlying stream.
|
77
|
+
def close
|
78
|
+
flush
|
79
|
+
stream.close
|
80
|
+
end
|
81
|
+
|
82
|
+
# Flush the underlying stream.
|
83
|
+
def flush
|
84
|
+
@lock.synchronize do
|
85
|
+
before_flush
|
86
|
+
unless @buffer.empty?
|
87
|
+
out = @buffer.join(Lumberjack::LINE_SEPARATOR) << Lumberjack::LINE_SEPARATOR
|
88
|
+
begin
|
89
|
+
stream.write(out)
|
90
|
+
stream.flush
|
91
|
+
rescue => e
|
92
|
+
$stderr.write("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
|
93
|
+
$stderr.write(out)
|
94
|
+
$stderr.flush
|
95
|
+
end
|
96
|
+
@buffer.clear
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
# Callback method that will be executed before data is written to the stream. Subclasses
|
104
|
+
# can override this method if needed.
|
105
|
+
def before_flush
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the underlying stream.
|
109
|
+
def stream=(stream)
|
110
|
+
@stream = stream
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get the underlying stream.
|
114
|
+
def stream
|
115
|
+
@stream
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Lumberjack
|
2
|
+
# This class controls the conversion of log entry messages into strings. This allows you
|
3
|
+
# to log any object you want and have the logging system worry about converting it into a string.
|
4
|
+
#
|
5
|
+
# Formats are added to a Formatter by associating them with a class using the +add+ method. Formats
|
6
|
+
# are any object that responds to the +call+ method.
|
7
|
+
#
|
8
|
+
# By default, all object will be converted to strings using their inspect method except for Strings
|
9
|
+
# and Exceptions. Strings are not converted and Exceptions are converted using the ExceptionFormatter.
|
10
|
+
class Formatter
|
11
|
+
autoload :ExceptionFormatter, File.expand_path("../formatter/exception_formatter.rb", __FILE__)
|
12
|
+
autoload :InspectFormatter, File.expand_path("../formatter/inspect_formatter.rb", __FILE__)
|
13
|
+
autoload :PrettyPrintFormatter, File.expand_path("../formatter/pretty_print_formatter.rb", __FILE__)
|
14
|
+
autoload :StringFormatter, File.expand_path("../formatter/string_formatter.rb", __FILE__)
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@class_formatters = {}
|
18
|
+
@default_formatter = InspectFormatter.new
|
19
|
+
add(Object, @default_formatter)
|
20
|
+
add(String, :string)
|
21
|
+
add(Exception, :exception)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a formatter for a class. The formatter can be specified as either an object
|
25
|
+
# that responds to the +call+ method or as a symbol representing one of the predefined
|
26
|
+
# formatters, or as a block to the method call.
|
27
|
+
#
|
28
|
+
# The predefined formatters are: <tt>:inspect</tt>, <tt>:string</tt>, <tt>:exception</tt>, and <tt>:pretty_print</tt>.
|
29
|
+
#
|
30
|
+
# === Examples
|
31
|
+
#
|
32
|
+
# # Use a predefined formatter
|
33
|
+
# formatter.add(MyClass, :pretty_print)
|
34
|
+
#
|
35
|
+
# # Pass in a formatter object
|
36
|
+
# formatter.add(MyClass, Lumberjack::Formatter::PrettyPrintFormatter.new)
|
37
|
+
#
|
38
|
+
# # Use a block
|
39
|
+
# formatter.add(MyClass){|obj| obj.humanize}
|
40
|
+
#
|
41
|
+
# # Add statements can be chained together
|
42
|
+
# formatter.add(MyClass, :pretty_print).add(YourClass){|obj| obj.humanize}
|
43
|
+
def add(klass, formatter = nil, &block)
|
44
|
+
formatter ||= block
|
45
|
+
if formatter.is_a?(Symbol)
|
46
|
+
formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/){|m| $~[2].upcase}}Formatter"
|
47
|
+
formatter = Formatter.const_get(formatter_class_name).new
|
48
|
+
end
|
49
|
+
@class_formatters[klass] = formatter
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Remove the formatter associated with a class. Remove statements can be chained together.
|
54
|
+
def remove(klass)
|
55
|
+
@class_formatters.delete(klass)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Format a message object as a string.
|
60
|
+
def format(message)
|
61
|
+
formatter_for(message.class).call(message)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Find the formatter for a class by looking it up using the class hierarchy.
|
67
|
+
def formatter_for(klass) #:nodoc:
|
68
|
+
while klass != nil do
|
69
|
+
formatter = @class_formatters[klass]
|
70
|
+
return formatter if formatter
|
71
|
+
klass = klass.superclass
|
72
|
+
end
|
73
|
+
@default_formatter
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Lumberjack
|
2
|
+
class Formatter
|
3
|
+
# Format an exception including the backtrace.
|
4
|
+
class ExceptionFormatter
|
5
|
+
def call(exception)
|
6
|
+
message = "#{exception.class.name}: #{exception.message}"
|
7
|
+
message << "#{Lumberjack::LINE_SEPARATOR} #{exception.backtrace.join("#{Lumberjack::LINE_SEPARATOR} ")}" if exception.backtrace
|
8
|
+
message
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Lumberjack
|
5
|
+
class Formatter
|
6
|
+
# Format an object with it's pretty print method.
|
7
|
+
class PrettyPrintFormatter
|
8
|
+
attr_accessor :width
|
9
|
+
|
10
|
+
# Create a new formatter. The maximum width of the message can be specified with the width
|
11
|
+
# parameter (defaults to 79 characters).
|
12
|
+
def initialize(width = 79)
|
13
|
+
@width = width
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(obj)
|
17
|
+
s = StringIO.new
|
18
|
+
PP.pp(obj, s)
|
19
|
+
s.string.chomp
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Lumberjack
|
2
|
+
# An entry in a log is a data structure that captures the log message as well as
|
3
|
+
# information about the system that logged the message.
|
4
|
+
class LogEntry
|
5
|
+
attr_accessor :time, :message, :severity, :progname, :pid, :unit_of_work_id
|
6
|
+
|
7
|
+
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S".freeze
|
8
|
+
|
9
|
+
def initialize(time, severity, message, progname, pid, unit_of_work_id)
|
10
|
+
@time = time
|
11
|
+
@severity = (severity.is_a?(Fixnum) ? severity : Severity.label_to_level(severity))
|
12
|
+
@message = message
|
13
|
+
@progname = progname
|
14
|
+
@pid = pid
|
15
|
+
@unit_of_work_id = unit_of_work_id
|
16
|
+
end
|
17
|
+
|
18
|
+
def severity_label
|
19
|
+
Severity.level_to_label(severity)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
buf = "[#{time.strftime(TIME_FORMAT)}.#{(time.usec / 1000.0).round.to_s.rjust(3, '0')} #{severity_label} #{progname}(#{pid})"
|
24
|
+
if unit_of_work_id
|
25
|
+
buf << " #"
|
26
|
+
buf << unit_of_work_id
|
27
|
+
end
|
28
|
+
buf << "] "
|
29
|
+
buf << message
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
module Lumberjack
|
2
|
+
# Logger is a thread safe logging object. It has a compatible API with the Ruby
|
3
|
+
# standard library Logger class, the Log4r gem, and ActiveSupport::BufferedLogger.
|
4
|
+
#
|
5
|
+
# === Example
|
6
|
+
#
|
7
|
+
# logger = Lumberjack::Logger.new
|
8
|
+
# logger.info("Starting processing")
|
9
|
+
# logger.debug("Processing options #{options.inspect}")
|
10
|
+
# logger.fatal("OMG the application is on fire!")
|
11
|
+
#
|
12
|
+
# Log entries are written to a logging Device if their severity meets or exceeds the log level.
|
13
|
+
#
|
14
|
+
# Devices may use buffers internally and the log entries are not guaranteed to be written until you call
|
15
|
+
# the +flush+ method. Sometimes this can result in problems when trying to track down extraordinarily
|
16
|
+
# long running sections of code since it is likely that none of the messages logged before the long
|
17
|
+
# running code will appear in the log until the entire process finishes. You can set the +:flush_seconds+
|
18
|
+
# option on the constructor to force the device to be flushed periodically. This will create a new
|
19
|
+
# monitoring thread, but its use is highly recommended.
|
20
|
+
#
|
21
|
+
# Each log entry records the log message and severity along with the time it was logged, the
|
22
|
+
# program name, process id, and unit of work id. The message will be converted to a string, but
|
23
|
+
# otherwise, it is up to the device how these values are recorded. Messages are converted to strings
|
24
|
+
# using a Formatter associated with the logger.
|
25
|
+
class Logger
|
26
|
+
include Severity
|
27
|
+
|
28
|
+
# The Formatter object used to convert messages into strings.
|
29
|
+
attr_reader :formatter
|
30
|
+
|
31
|
+
# The time that the device was last flushed.
|
32
|
+
attr_reader :last_flushed_at
|
33
|
+
|
34
|
+
# The name of the program associated with log messages.
|
35
|
+
attr_writer :progname
|
36
|
+
|
37
|
+
# The device being written to.
|
38
|
+
attr_reader :device
|
39
|
+
|
40
|
+
# Set +silencer+ to false to disable silencing the log.
|
41
|
+
attr_accessor :silencer
|
42
|
+
|
43
|
+
# Create a new logger to log to a Device.
|
44
|
+
#
|
45
|
+
# The +device+ argument can be in any one of several formats.
|
46
|
+
#
|
47
|
+
# If it is a Device object, that object will be used.
|
48
|
+
# If it has a +write+ method, it will be wrapped in a Device::Writer class.
|
49
|
+
# If it is <tt>:null</tt>, it will be a Null device that won't record any output.
|
50
|
+
# Otherwise, it will be assumed to be file path and wrapped in a Device::LogFile class.
|
51
|
+
#
|
52
|
+
# This method can take the following options:
|
53
|
+
#
|
54
|
+
# * <tt>:level</tt> - The logging level below which messages will be ignored.
|
55
|
+
# * <tt>:progname</tt> - The name of the program that will be recorded with each log entry.
|
56
|
+
# * <tt>:flush_seconds</tt> - The maximum number of seconds between flush calls.
|
57
|
+
# * <tt>:roll</tt> - If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.
|
58
|
+
# * <tt>:max_size</tt> - If the log device is a file path, it will be a Device::SizeRollingLogFile if this is set.
|
59
|
+
#
|
60
|
+
# All other options are passed to the device constuctor.
|
61
|
+
def initialize(device = STDOUT, options = {})
|
62
|
+
@thread_settings = {}
|
63
|
+
|
64
|
+
options = options.dup
|
65
|
+
self.level = options.delete(:level) || INFO
|
66
|
+
self.progname = options.delete(:progname)
|
67
|
+
max_flush_seconds = options.delete(:flush_seconds).to_f
|
68
|
+
|
69
|
+
@device = open_device(device, options)
|
70
|
+
@formatter = Formatter.new
|
71
|
+
@lock = Mutex.new
|
72
|
+
@last_flushed_at = Time.now
|
73
|
+
@silencer = true
|
74
|
+
|
75
|
+
create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get the level of severity of entries that are logged. Entries with a lower
|
79
|
+
# severity level will be ignored.
|
80
|
+
def level
|
81
|
+
thread_local_value(:lumberjack_logger_level) || @level
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add a message to the log with a given severity. The message can be either
|
85
|
+
# passed in the +message+ argument or supplied with a block. This method
|
86
|
+
# is not normally called. Instead call one of the helper functions
|
87
|
+
# +fatal+, +error+, +warn+, +info+, or +debug+.
|
88
|
+
#
|
89
|
+
# The severity can be passed in either as one of the Severity constants,
|
90
|
+
# or as a Severity label.
|
91
|
+
#
|
92
|
+
# === Example
|
93
|
+
#
|
94
|
+
# logger.add(Lumberjack::Severity::ERROR, exception)
|
95
|
+
# logger.add(Lumberjack::Severity::INFO, "Request completed")
|
96
|
+
# logger.add(:warn, "Request took a long time")
|
97
|
+
# logger.add(Lumberjack::Severity::DEBUG){"Start processing with options #{options.inspect}"}
|
98
|
+
def add(severity, message = nil, progname = nil)
|
99
|
+
severity = Severity.label_to_level(severity) if severity.is_a?(String) || severity.is_a?(Symbol)
|
100
|
+
if severity && severity >= level
|
101
|
+
time = Time.now
|
102
|
+
message = yield if message.nil? && block_given?
|
103
|
+
message = @formatter.format(message)
|
104
|
+
entry = LogEntry.new(time, severity, message, progname || self.progname, $$, Lumberjack.unit_of_work_id)
|
105
|
+
begin
|
106
|
+
device.write(entry)
|
107
|
+
rescue => e
|
108
|
+
$stderr.puts("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
|
109
|
+
$stderr.puts(entry.to_s)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
alias_method :log, :add
|
116
|
+
|
117
|
+
# Flush the logging device.
|
118
|
+
def flush
|
119
|
+
device.flush
|
120
|
+
@last_flushed_at = Time.now
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Close the logging device.
|
125
|
+
def close
|
126
|
+
flush
|
127
|
+
@device.close if @device.respond_to?(:close)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Log a +FATAL+ message. The message can be passed in either the +message+ argument or in a block.
|
131
|
+
def fatal(message = nil, progname = nil, &block)
|
132
|
+
add(FATAL, message, progname, &block)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return +true+ if +FATAL+ messages are being logged.
|
136
|
+
def fatal?
|
137
|
+
level <= FATAL
|
138
|
+
end
|
139
|
+
|
140
|
+
# Log an +ERROR+ message. The message can be passed in either the +message+ argument or in a block.
|
141
|
+
def error(message = nil, progname = nil, &block)
|
142
|
+
add(ERROR, message, progname, &block)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Return +true+ if +ERROR+ messages are being logged.
|
146
|
+
def error?
|
147
|
+
level <= ERROR
|
148
|
+
end
|
149
|
+
|
150
|
+
# Log a +WARN+ message. The message can be passed in either the +message+ argument or in a block.
|
151
|
+
def warn(message = nil, progname = nil, &block)
|
152
|
+
add(WARN, message, progname, &block)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Return +true+ if +WARN+ messages are being logged.
|
156
|
+
def warn?
|
157
|
+
level <= WARN
|
158
|
+
end
|
159
|
+
|
160
|
+
# Log an +INFO+ message. The message can be passed in either the +message+ argument or in a block.
|
161
|
+
def info(message = nil, progname = nil, &block)
|
162
|
+
add(INFO, message, progname, &block)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Return +true+ if +INFO+ messages are being logged.
|
166
|
+
def info?
|
167
|
+
level <= INFO
|
168
|
+
end
|
169
|
+
|
170
|
+
# Log a +DEBUG+ message. The message can be passed in either the +message+ argument or in a block.
|
171
|
+
def debug(message = nil, progname = nil, &block)
|
172
|
+
add(DEBUG, message, progname, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Return +true+ if +DEBUG+ messages are being logged.
|
176
|
+
def debug?
|
177
|
+
level <= DEBUG
|
178
|
+
end
|
179
|
+
|
180
|
+
# Log a message when the severity is not known. Unknown messages will always appear in the log.
|
181
|
+
# The message can be passed in either the +message+ argument or in a block.
|
182
|
+
def unknown(message = nil, progname = nil, &block)
|
183
|
+
add(UNKNOWN, message, progname, &block)
|
184
|
+
end
|
185
|
+
|
186
|
+
alias_method :<<, :unknown
|
187
|
+
|
188
|
+
# Set the minimum level of severity of messages to log.
|
189
|
+
def level=(severity)
|
190
|
+
if severity.is_a?(Fixnum)
|
191
|
+
@level = severity
|
192
|
+
else
|
193
|
+
@level = Severity.label_to_level(severity)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Silence the logger by setting a new log level inside a block. By default, only +ERROR+ or +FATAL+
|
198
|
+
# messages will be logged.
|
199
|
+
#
|
200
|
+
# === Example
|
201
|
+
#
|
202
|
+
# logger.level = Lumberjack::Severity::INFO
|
203
|
+
# logger.silence do
|
204
|
+
# do_something # Log level inside the block is +ERROR+
|
205
|
+
# end
|
206
|
+
def silence(temporary_level = ERROR, &block)
|
207
|
+
if silencer
|
208
|
+
push_thread_local_value(:lumberjack_logger_level, temporary_level, &block)
|
209
|
+
else
|
210
|
+
yield
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Set the program name that is associated with log messages. If a block
|
215
|
+
# is given, the program name will be valid only within the block.
|
216
|
+
def set_progname(value, &block)
|
217
|
+
if block
|
218
|
+
push_thread_local_value(:lumberjack_logger_progname, value, &block)
|
219
|
+
else
|
220
|
+
self.progname = value
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Get the program name associated with log messages.
|
225
|
+
def progname
|
226
|
+
thread_local_value(:lumberjack_logger_progname) || @progname
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
# Set a local value for a thread tied to this object.
|
232
|
+
def set_thread_local_value(name, value) #:nodoc:
|
233
|
+
values = Thread.current[name]
|
234
|
+
unless values
|
235
|
+
values = {}
|
236
|
+
Thread.current[name] = values
|
237
|
+
end
|
238
|
+
if value.nil?
|
239
|
+
values.delete(self)
|
240
|
+
Thread.current[name] = nil if values.empty?
|
241
|
+
else
|
242
|
+
values[self] = value
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Get a local value for a thread tied to this object.
|
247
|
+
def thread_local_value(name) #:nodoc:
|
248
|
+
values = Thread.current[name]
|
249
|
+
values[self] if values
|
250
|
+
end
|
251
|
+
|
252
|
+
# Set a local value for a thread tied to this object within a block.
|
253
|
+
def push_thread_local_value(name, value) #:nodoc:
|
254
|
+
save_val = thread_local_value(name)
|
255
|
+
set_thread_local_value(name, value)
|
256
|
+
begin
|
257
|
+
yield
|
258
|
+
ensure
|
259
|
+
set_thread_local_value(name, save_val)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Open a logging device.
|
264
|
+
def open_device(device, options) #:nodoc:
|
265
|
+
if device.is_a?(Device)
|
266
|
+
device
|
267
|
+
elsif device.respond_to?(:write)
|
268
|
+
Device::Writer.new(device, options)
|
269
|
+
elsif device == :null
|
270
|
+
Device::Null.new
|
271
|
+
else
|
272
|
+
device = device.to_s
|
273
|
+
if options[:roll]
|
274
|
+
Device::DateRollingLogFile.new(device, options)
|
275
|
+
elsif options[:max_size]
|
276
|
+
Device::SizeRollingLogFile.new(device, options)
|
277
|
+
else
|
278
|
+
Device::LogFile.new(device, options)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Create a thread that will periodically call flush.
|
284
|
+
def create_flusher_thread(flush_seconds) #:nodoc:
|
285
|
+
if flush_seconds > 0
|
286
|
+
begin
|
287
|
+
logger = self
|
288
|
+
Thread.new do
|
289
|
+
loop do
|
290
|
+
begin
|
291
|
+
sleep(flush_seconds)
|
292
|
+
logger.flush if Time.now - logger.last_flushed_at >= flush_seconds
|
293
|
+
rescue => e
|
294
|
+
STDERR.puts("Error flushing log: #{e.inspect}")
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|