lumberjack 1.0.13 → 1.2.8
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +129 -0
- data/{MIT_LICENSE → MIT_LICENSE.txt} +0 -0
- data/README.md +142 -23
- data/VERSION +1 -1
- data/lib/lumberjack.rb +70 -22
- data/lib/lumberjack/context.rb +35 -0
- data/lib/lumberjack/device.rb +22 -8
- data/lib/lumberjack/device/date_rolling_log_file.rb +9 -9
- data/lib/lumberjack/device/log_file.rb +14 -3
- data/lib/lumberjack/device/multi.rb +46 -0
- data/lib/lumberjack/device/null.rb +1 -3
- data/lib/lumberjack/device/rolling_log_file.rb +45 -21
- data/lib/lumberjack/device/size_rolling_log_file.rb +10 -10
- data/lib/lumberjack/device/writer.rb +92 -61
- data/lib/lumberjack/formatter.rb +97 -28
- data/lib/lumberjack/formatter/date_time_formatter.rb +25 -0
- data/lib/lumberjack/formatter/exception_formatter.rb +25 -2
- data/lib/lumberjack/formatter/id_formatter.rb +23 -0
- data/lib/lumberjack/formatter/object_formatter.rb +12 -0
- data/lib/lumberjack/formatter/pretty_print_formatter.rb +4 -4
- data/lib/lumberjack/formatter/string_formatter.rb +1 -1
- data/lib/lumberjack/formatter/strip_formatter.rb +12 -0
- data/lib/lumberjack/formatter/structured_formatter.rb +63 -0
- data/lib/lumberjack/log_entry.rb +44 -16
- data/lib/lumberjack/logger.rb +275 -69
- data/lib/lumberjack/rack.rb +3 -2
- data/lib/lumberjack/rack/context.rb +18 -0
- data/lib/lumberjack/rack/request_id.rb +4 -4
- data/lib/lumberjack/rack/unit_of_work.rb +1 -1
- data/lib/lumberjack/severity.rb +11 -10
- data/lib/lumberjack/tag_formatter.rb +96 -0
- data/lib/lumberjack/tagged_logger_support.rb +66 -0
- data/lib/lumberjack/tagged_logging.rb +29 -0
- data/lib/lumberjack/tags.rb +42 -0
- data/lib/lumberjack/template.rb +81 -33
- data/lumberjack.gemspec +31 -0
- metadata +26 -53
- data/Rakefile +0 -40
- data/spec/device/date_rolling_log_file_spec.rb +0 -73
- data/spec/device/log_file_spec.rb +0 -48
- data/spec/device/null_spec.rb +0 -12
- data/spec/device/rolling_log_file_spec.rb +0 -151
- data/spec/device/size_rolling_log_file_spec.rb +0 -58
- data/spec/device/writer_spec.rb +0 -118
- data/spec/formatter/exception_formatter_spec.rb +0 -20
- data/spec/formatter/inspect_formatter_spec.rb +0 -13
- data/spec/formatter/pretty_print_formatter_spec.rb +0 -14
- data/spec/formatter/string_formatter_spec.rb +0 -12
- data/spec/formatter_spec.rb +0 -45
- data/spec/log_entry_spec.rb +0 -69
- data/spec/logger_spec.rb +0 -411
- data/spec/lumberjack_spec.rb +0 -29
- data/spec/rack/request_id_spec.rb +0 -48
- data/spec/rack/unit_of_work_spec.rb +0 -26
- data/spec/severity_spec.rb +0 -23
- data/spec/spec_helper.rb +0 -32
- data/spec/template_spec.rb +0 -34
data/lib/lumberjack/formatter.rb
CHANGED
@@ -1,33 +1,57 @@
|
|
1
1
|
# frozen_string_literals: true
|
2
2
|
|
3
3
|
module Lumberjack
|
4
|
-
# This class controls the conversion of log entry messages into
|
5
|
-
# to log any object you want and have the logging system
|
4
|
+
# This class controls the conversion of log entry messages into a loggable format. This allows you
|
5
|
+
# to log any object you want and have the logging system deal with converting it into a string.
|
6
6
|
#
|
7
7
|
# Formats are added to a Formatter by associating them with a class using the +add+ method. Formats
|
8
8
|
# are any object that responds to the +call+ method.
|
9
9
|
#
|
10
10
|
# By default, all object will be converted to strings using their inspect method except for Strings
|
11
11
|
# and Exceptions. Strings are not converted and Exceptions are converted using the ExceptionFormatter.
|
12
|
+
#
|
13
|
+
# Enumerable objects (including Hash and Array) will call the formatter recursively for each element.
|
12
14
|
class Formatter
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
require_relative "formatter/date_time_formatter"
|
16
|
+
require_relative "formatter/exception_formatter"
|
17
|
+
require_relative "formatter/id_formatter"
|
18
|
+
require_relative "formatter/inspect_formatter"
|
19
|
+
require_relative "formatter/object_formatter"
|
20
|
+
require_relative "formatter/pretty_print_formatter"
|
21
|
+
require_relative "formatter/string_formatter"
|
22
|
+
require_relative "formatter/strip_formatter"
|
23
|
+
require_relative "formatter/structured_formatter"
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Returns a new empty formatter with no mapping. For historical reasons, a formatter
|
27
|
+
# is initialized with mappings to help output objects as strings. This will return one
|
28
|
+
# without the default mappings.
|
29
|
+
def empty
|
30
|
+
new.clear
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
18
34
|
def initialize
|
19
35
|
@class_formatters = {}
|
20
|
-
@
|
21
|
-
|
22
|
-
add(String, :
|
36
|
+
@module_formatters = {}
|
37
|
+
structured_formatter = StructuredFormatter.new(self)
|
38
|
+
add([String, Numeric, TrueClass, FalseClass], :object)
|
39
|
+
add(Object, InspectFormatter.new)
|
23
40
|
add(Exception, :exception)
|
41
|
+
add(Enumerable, structured_formatter)
|
24
42
|
end
|
25
|
-
|
43
|
+
|
26
44
|
# Add a formatter for a class. The formatter can be specified as either an object
|
27
45
|
# that responds to the +call+ method or as a symbol representing one of the predefined
|
28
46
|
# formatters, or as a block to the method call.
|
29
47
|
#
|
30
|
-
# The predefined formatters are:
|
48
|
+
# The predefined formatters are: :inspect, :string, :exception, and :pretty_print.
|
49
|
+
#
|
50
|
+
# You can add multiple classes at once by passing an array of classes.
|
51
|
+
#
|
52
|
+
# You can also pass class names as strings instead of the classes themselves. This can
|
53
|
+
# help avoid loading dependency issues. This applies only to classes; modules cannot be
|
54
|
+
# passed in as strings.
|
31
55
|
#
|
32
56
|
# === Examples
|
33
57
|
#
|
@@ -44,40 +68,85 @@ module Lumberjack
|
|
44
68
|
# formatter.add(MyClass, :pretty_print).add(YourClass){|obj| obj.humanize}
|
45
69
|
def add(klass, formatter = nil, &block)
|
46
70
|
formatter ||= block
|
47
|
-
if formatter.
|
48
|
-
|
49
|
-
|
71
|
+
if formatter.nil?
|
72
|
+
remove(klass)
|
73
|
+
else
|
74
|
+
if formatter.is_a?(Symbol)
|
75
|
+
formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/) { |m| $~[2].upcase }}Formatter"
|
76
|
+
formatter = Formatter.const_get(formatter_class_name).new
|
77
|
+
end
|
78
|
+
|
79
|
+
Array(klass).each do |k|
|
80
|
+
if k.class == Module
|
81
|
+
@module_formatters[k] = formatter
|
82
|
+
else
|
83
|
+
k = k.name if k.is_a?(Class)
|
84
|
+
@class_formatters[k] = formatter
|
85
|
+
end
|
86
|
+
end
|
50
87
|
end
|
51
|
-
@class_formatters[klass] = formatter
|
52
88
|
self
|
53
89
|
end
|
54
|
-
|
90
|
+
|
55
91
|
# Remove the formatter associated with a class. Remove statements can be chained together.
|
92
|
+
#
|
93
|
+
# You can remove multiple classes at once by passing an array of classes.
|
94
|
+
#
|
95
|
+
# You can also pass class names as strings instead of the classes themselves. This can
|
96
|
+
# help avoid loading dependency issues. This applies only to classes; modules cannot be
|
97
|
+
# passed in as strings.
|
56
98
|
def remove(klass)
|
57
|
-
|
99
|
+
Array(klass).each do |k|
|
100
|
+
if k.class == Module
|
101
|
+
@module_formatters.delete(k)
|
102
|
+
else
|
103
|
+
k = k.name if k.is_a?(Class)
|
104
|
+
@class_formatters.delete(k)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
# Remove all formatters including the default formatter. Can be chained to add method calls.
|
111
|
+
def clear
|
112
|
+
@class_formatters.clear
|
113
|
+
@module_formatters.clear
|
58
114
|
self
|
59
115
|
end
|
60
|
-
|
116
|
+
|
61
117
|
# Format a message object as a string.
|
62
118
|
def format(message)
|
63
|
-
formatter_for(message.class)
|
119
|
+
formatter = formatter_for(message.class)
|
120
|
+
if formatter&.respond_to?(:call)
|
121
|
+
formatter.call(message)
|
122
|
+
else
|
123
|
+
message
|
124
|
+
end
|
64
125
|
end
|
65
|
-
|
66
|
-
#
|
126
|
+
|
127
|
+
# Compatibility with the Logger::Formatter signature. This method will just convert the message
|
128
|
+
# object to a string and ignores the other parameters.
|
67
129
|
def call(severity, timestamp, progname, msg)
|
68
|
-
"#{format(msg)}
|
69
|
-
end
|
130
|
+
"#{format(msg)}#{Lumberjack::LINE_SEPARATOR}"
|
131
|
+
end
|
70
132
|
|
71
133
|
private
|
72
|
-
|
134
|
+
|
73
135
|
# Find the formatter for a class by looking it up using the class hierarchy.
|
74
136
|
def formatter_for(klass) #:nodoc:
|
75
|
-
|
76
|
-
|
137
|
+
check_modules = true
|
138
|
+
until klass.nil?
|
139
|
+
formatter = @class_formatters[klass.name]
|
77
140
|
return formatter if formatter
|
141
|
+
|
142
|
+
if check_modules
|
143
|
+
_, formatter = @module_formatters.detect { |mod, f| klass.include?(mod) }
|
144
|
+
check_modules = false
|
145
|
+
return formatter if formatter
|
146
|
+
end
|
147
|
+
|
78
148
|
klass = klass.superclass
|
79
149
|
end
|
80
|
-
@_default_formatter
|
81
150
|
end
|
82
151
|
end
|
83
152
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literals: true
|
2
|
+
|
3
|
+
module Lumberjack
|
4
|
+
class Formatter
|
5
|
+
# Format a Date, Time, or DateTime object. If you don't specify a format in the constructor,
|
6
|
+
# it will use the ISO-8601 format.
|
7
|
+
class DateTimeFormatter
|
8
|
+
attr_reader :format
|
9
|
+
|
10
|
+
def initialize(format = nil)
|
11
|
+
@format = format.dup.to_s.freeze unless format.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(obj)
|
15
|
+
if @format && obj.respond_to?(:strftime)
|
16
|
+
obj.strftime(@format)
|
17
|
+
elsif obj.respond_to?(:iso8601)
|
18
|
+
obj.iso8601
|
19
|
+
else
|
20
|
+
obj.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,13 +2,36 @@
|
|
2
2
|
|
3
3
|
module Lumberjack
|
4
4
|
class Formatter
|
5
|
-
# Format an exception including the backtrace.
|
5
|
+
# Format an exception including the backtrace. You can specify an object that
|
6
|
+
# responds to `call` as a backtrace cleaner. The exception backtrace will be
|
7
|
+
# passed to this object and the returned array is what will be logged. You can
|
8
|
+
# use this to clean out superfluous lines.
|
6
9
|
class ExceptionFormatter
|
10
|
+
attr_accessor :backtrace_cleaner
|
11
|
+
|
12
|
+
def initialize(backtrace_cleaner = nil)
|
13
|
+
self.backtrace_cleaner = backtrace_cleaner
|
14
|
+
end
|
15
|
+
|
7
16
|
def call(exception)
|
8
17
|
message = "#{exception.class.name}: #{exception.message}"
|
9
|
-
|
18
|
+
trace = exception.backtrace
|
19
|
+
if trace
|
20
|
+
trace = clean_backtrace(trace)
|
21
|
+
message << "#{Lumberjack::LINE_SEPARATOR} #{trace.join("#{Lumberjack::LINE_SEPARATOR} ")}"
|
22
|
+
end
|
10
23
|
message
|
11
24
|
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def clean_backtrace(trace)
|
29
|
+
if trace && backtrace_cleaner
|
30
|
+
backtrace_cleaner.call(trace)
|
31
|
+
else
|
32
|
+
trace
|
33
|
+
end
|
34
|
+
end
|
12
35
|
end
|
13
36
|
end
|
14
37
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literals: true
|
2
|
+
|
3
|
+
module Lumberjack
|
4
|
+
class Formatter
|
5
|
+
# Format an object that has an id as a hash with keys for class and id. This formatter is useful
|
6
|
+
# as a default formatter for objects pulled from a data store. By default it will use :id as the
|
7
|
+
# id attribute.
|
8
|
+
class IdFormatter
|
9
|
+
def initialize(id_attribute = :id)
|
10
|
+
@id_attribute = id_attribute
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(obj)
|
14
|
+
if obj.respond_to?(@id_attribute)
|
15
|
+
id = obj.send(@id_attribute)
|
16
|
+
{"class" => obj.class.name, "id" => id}
|
17
|
+
else
|
18
|
+
obj.to_s
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
# frozen_string_literals: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "pp"
|
4
|
+
require "stringio"
|
5
5
|
|
6
6
|
module Lumberjack
|
7
7
|
class Formatter
|
8
8
|
# Format an object with it's pretty print method.
|
9
9
|
class PrettyPrintFormatter
|
10
10
|
attr_accessor :width
|
11
|
-
|
11
|
+
|
12
12
|
# Create a new formatter. The maximum width of the message can be specified with the width
|
13
13
|
# parameter (defaults to 79 characters).
|
14
14
|
def initialize(width = 79)
|
15
15
|
@width = width
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def call(obj)
|
19
19
|
s = StringIO.new
|
20
20
|
PP.pp(obj, s)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literals: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module Lumberjack
|
6
|
+
class Formatter
|
7
|
+
# Dereference arrays and hashes and recursively call formatters on each element.
|
8
|
+
class StructuredFormatter
|
9
|
+
class RecusiveReferenceError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(formatter = nil)
|
13
|
+
@formatter = formatter
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(obj)
|
17
|
+
call_with_references(obj, Set.new)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def call_with_references(obj, references)
|
23
|
+
if obj.is_a?(Hash)
|
24
|
+
with_object_reference(obj, references) do
|
25
|
+
hash = {}
|
26
|
+
obj.each do |name, value|
|
27
|
+
value = call_with_references(value, references)
|
28
|
+
hash[name.to_s] = value unless value.is_a?(RecusiveReferenceError)
|
29
|
+
end
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
elsif obj.is_a?(Enumerable) && obj.respond_to?(:size) && obj.size != Float::INFINITY
|
33
|
+
with_object_reference(obj, references) do
|
34
|
+
array = []
|
35
|
+
obj.each do |value|
|
36
|
+
value = call_with_references(value, references)
|
37
|
+
array << value unless value.is_a?(RecusiveReferenceError)
|
38
|
+
end
|
39
|
+
array
|
40
|
+
end
|
41
|
+
elsif @formatter
|
42
|
+
@formatter.format(obj)
|
43
|
+
else
|
44
|
+
obj
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_object_reference(obj, references)
|
49
|
+
if obj.is_a?(Enumerable)
|
50
|
+
return RecusiveReferenceError.new if references.include?(obj.object_id)
|
51
|
+
references << obj.object_id
|
52
|
+
begin
|
53
|
+
yield
|
54
|
+
ensure
|
55
|
+
references.delete(obj.object_id)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/lumberjack/log_entry.rb
CHANGED
@@ -4,35 +4,63 @@ module Lumberjack
|
|
4
4
|
# An entry in a log is a data structure that captures the log message as well as
|
5
5
|
# information about the system that logged the message.
|
6
6
|
class LogEntry
|
7
|
-
attr_accessor :time, :message, :severity, :progname, :pid, :
|
8
|
-
|
9
|
-
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
10
|
-
|
11
|
-
|
7
|
+
attr_accessor :time, :message, :severity, :progname, :pid, :tags
|
8
|
+
|
9
|
+
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
10
|
+
|
11
|
+
UNIT_OF_WORK_ID = "unit_of_work_id"
|
12
|
+
|
13
|
+
def initialize(time, severity, message, progname, pid, tags)
|
12
14
|
@time = time
|
13
15
|
@severity = (severity.is_a?(Integer) ? severity : Severity.label_to_level(severity))
|
14
16
|
@message = message
|
15
17
|
@progname = progname
|
16
18
|
@pid = pid
|
17
|
-
|
19
|
+
# backward compatibility with 1.0 API where the last argument was the unit of work id
|
20
|
+
@tags = if tags.nil? || tags.is_a?(Hash)
|
21
|
+
tags
|
22
|
+
else
|
23
|
+
{UNIT_OF_WORK_ID => tags}
|
24
|
+
end
|
18
25
|
end
|
19
|
-
|
26
|
+
|
20
27
|
def severity_label
|
21
28
|
Severity.level_to_label(severity)
|
22
29
|
end
|
23
|
-
|
30
|
+
|
24
31
|
def to_s
|
25
|
-
|
26
|
-
if unit_of_work_id
|
27
|
-
buf << " #"
|
28
|
-
buf << unit_of_work_id
|
29
|
-
end
|
30
|
-
buf << "] "
|
31
|
-
buf << message
|
32
|
+
"[#{time.strftime(TIME_FORMAT)}.#{(time.usec / 1000.0).round.to_s.rjust(3, "0")} #{severity_label} #{progname}(#{pid})#{tags_to_s}] #{message}"
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
def inspect
|
35
36
|
to_s
|
36
37
|
end
|
38
|
+
|
39
|
+
# Deprecated - backward compatibility with 1.0 API
|
40
|
+
def unit_of_work_id
|
41
|
+
tags[UNIT_OF_WORK_ID] if tags
|
42
|
+
end
|
43
|
+
|
44
|
+
# Deprecated - backward compatibility with 1.0 API
|
45
|
+
def unit_of_work_id=(value)
|
46
|
+
if tags
|
47
|
+
tags[UNIT_OF_WORK_ID] = value
|
48
|
+
else
|
49
|
+
@tags = {UNIT_OF_WORK_ID => value}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return the tag with the specified name.
|
54
|
+
def tag(name)
|
55
|
+
tags[name.to_s] if tags
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def tags_to_s
|
61
|
+
tags_string = ""
|
62
|
+
tags&.each { |name, value| tags_string << " #{name}:#{value.inspect}" }
|
63
|
+
tags_string
|
64
|
+
end
|
37
65
|
end
|
38
66
|
end
|