lumberjack 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +47 -13
- data/VERSION +1 -1
- data/lib/lumberjack.rb +4 -1
- data/lib/lumberjack/device/multi.rb +1 -1
- data/lib/lumberjack/formatter.rb +53 -20
- data/lib/lumberjack/formatter/exception_formatter.rb +27 -2
- 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 +2 -2
- data/lib/lumberjack/log_entry.rb +5 -0
- data/lib/lumberjack/logger.rb +44 -11
- data/lib/lumberjack/tag_formatter.rb +98 -0
- data/lib/lumberjack/tagged_logger_support.rb +67 -0
- data/lib/lumberjack/template.rb +11 -5
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10f2db93994b2941a8684c4fd933f07fc2d0067afdcfa3798f4ba3b5cc34b354
|
4
|
+
data.tar.gz: e6413b5b475b1c05ffcc17662e4f553af8e6d47d79e961a5802d0d875a413f9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 809fc20f5cb8cc7d704f5a5e6878f321285953c8c79139fbd9892fa6c954b645b6bdcc0ce0e0265e18b29974fd864a96fdd735a424f57bd1224653759b139fd8
|
7
|
+
data.tar.gz: c7cf6e570fe6a1cbba068905ad860c26ad375b5f413d449c8f77f84c4ad81b139585ef2917aa2328dac2b80e0a3f962895a5efb8c5b988a14605e59ccf6eafbc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## 1.2.0
|
2
|
+
|
3
|
+
* Enable compatibility with ActiveSupport::TaggedLogger by calling `tagged_logger!` on a logger.
|
4
|
+
* Add `tag_formatter` to logger to specify formatting of tags for output.
|
5
|
+
* Allow adding and removing classes by name to formatters.
|
6
|
+
* Allow adding and removing multiple classes in a single call to a formatter.
|
7
|
+
* Allow using symbols and strings as log level for silencing a logger.
|
8
|
+
* Ensure flusher thread gets stopped when logger is closed.
|
9
|
+
* Add writer for logger device attribute.
|
10
|
+
* Handle passing an array of devices to a multi device.
|
11
|
+
* Helper method to get a tag with a specified name.
|
12
|
+
* Add strip formatter to strip whitespace from strings.
|
13
|
+
* Support non-alpha numeric characters in template variables.
|
14
|
+
* Add backtrace cleaner to ExceptionFormatter.
|
15
|
+
|
1
16
|
## 1.1.1
|
2
17
|
|
3
18
|
* Replace Procs in tag values with the value of calling the Proc in log entries.
|
@@ -15,6 +30,7 @@
|
|
15
30
|
* Add DateTimeFormatter, IdFormatter, ObjectFormatter, and StructuredFormatter
|
16
31
|
* Add rack Context middleware for setting thread global context
|
17
32
|
* End support for ruby versions < 2.3
|
33
|
+
* Add support for modules in formatters
|
18
34
|
|
19
35
|
## 1.0.13
|
20
36
|
|
data/README.md
CHANGED
@@ -85,6 +85,16 @@ logger.info("no requests") # Will not include the `request_id` tag
|
|
85
85
|
|
86
86
|
Tag keys are always converted to strings. Tags are inherited so that message tags take precedence over block tags which take precedence over global tags.
|
87
87
|
|
88
|
+
#### Compatibility with ActiveSupport::TaggedLogger
|
89
|
+
|
90
|
+
`Lumberjack::Logger` version 1.1.2 or greater is compatible with `ActiveSupport::TaggedLogger`. This is so that other code that expect to have a logger that responds to the `tagged` method will work. Any tags added with the `tagged` method will be appended to an array in the the "tagged" tag. However, if a tagged value has a colon or equals sign in it, it will be parsed to a name value pair.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
logger.tagged("foo", "bar=1", "baz:2", "other") do
|
94
|
+
logger.info("here") # will include tags: {"tagged" => ["foo", "other"], "bar" => "1", "baz" => "2"}
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
88
98
|
#### Templates
|
89
99
|
|
90
100
|
The built in `Lumberjack::Device::Writer` class has built in support for including tags in the output using the `Lumberjack::Template` class.
|
@@ -117,33 +127,57 @@ If you'd like to send you log to a different kind of output, you just need to ex
|
|
117
127
|
|
118
128
|
#### Formatters
|
119
129
|
|
120
|
-
|
130
|
+
The message you send to the logger can be any object type and does not need to be a string. You can specify a `Lumberjack::Formatter` to instruct the logger how to format objects before outputting them to the device. You do this by mapping classes or modules to formatter code. This code can be either a block or an object that responds to the `call` method. The formatter will be called with the object logged as the message and the returned value will be what is sent to the device.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# Format all floating point number with three significant digits.
|
134
|
+
logger.formatter.add(Float) { |value| value.round(3) }
|
135
|
+
|
136
|
+
# Format all enumerable objects as a comma delimited string.
|
137
|
+
logger.formatter.add(Enumerable) { |value| value.join(", ") }
|
138
|
+
```
|
121
139
|
|
122
|
-
There are several built in classes you can add as formatters
|
140
|
+
There are several built in classes you can add as formatters. You can use a symbol to reference built in formatters.
|
123
141
|
|
124
142
|
```ruby
|
125
143
|
logger.formatter.add(Hash, :pretty_print) # use the Formatter::PrettyPrintFormatter for all Hashes
|
126
144
|
logger.formatter.add(Hash, Lumberjack::Formatter::PrettyPrintFormatter.new) # alternative using a formatter instance
|
127
145
|
```
|
128
146
|
|
129
|
-
* `Lumberjack::Formatter::ObjectFormatter` - no op conversion that returns the object itself.
|
130
|
-
* `Lumberjack::Formatter::StringFormatter` - calls `to_s` on the object.
|
131
|
-
* `Lumberjack::Formatter::
|
132
|
-
* `Lumberjack::Formatter::
|
133
|
-
* `Lumberjack::Formatter::
|
134
|
-
* `Lumberjack::Formatter::
|
135
|
-
* `Lumberjack::Formatter::
|
136
|
-
* `Lumberjack::Formatter::
|
147
|
+
* `:object` - `Lumberjack::Formatter::ObjectFormatter` - no op conversion that returns the object itself.
|
148
|
+
* `:string` - `Lumberjack::Formatter::StringFormatter` - calls `to_s` on the object.
|
149
|
+
* `:strip` - `Lumberjack::Formatter::StripFormatter` - calls `to_s.strip` on the object.
|
150
|
+
* `:inspect` - `Lumberjack::Formatter::InspectFormatter` - calls `inspect` on the object.
|
151
|
+
* `:exception` - `Lumberjack::Formatter::ExceptionFormatter` - special formatter for exceptions which logs them as multi line statements with the message and backtrace.
|
152
|
+
* `:date_time` - `Lumberjack::Formatter::DateTimeFormatter` - special formatter for dates and times to format them using `strftime`.
|
153
|
+
* `:pretty_print` - `Lumberjack::Formatter::PrettyPrintFormatter` - returns the pretty print format of the object.
|
154
|
+
* `:id` - `Lumberjack::Formatter::IdFormatter` - returns a hash of the object with keys for the id attribute and class.
|
155
|
+
* `:structured` - `Lumberjack::Formatter::StructuredFormatter` - crawls the object and applies the formatter recursively to Enumerable objects found in it (arrays, hashes, etc.).
|
156
|
+
|
157
|
+
To define your own formatter, either provide a block or an object that responds to `call` with a single argument.
|
158
|
+
|
159
|
+
The default formatter will pass through values for strings, numbers, and booleans, and use the `:inspect` formatter for all objects except for exceptions which will be formatted with the `:exception` formatter.
|
160
|
+
|
161
|
+
#### Tag Formatters
|
162
|
+
|
163
|
+
The `logger.formatter` will only apply to log messages. You can use `logger.tag_formatter` to register formatters for tags. You can register both default formatters that will apply to all tag values, as well as tag specifice formatters that will apply only to objects with a specific tag name.
|
137
164
|
|
138
|
-
|
165
|
+
The fomatter values can be either a `Lumberjack::Formatter` or a block or an object that responds to `call`. If you supply a `Lumberjack::Formatter`, the tag value will be passed through the rules for that formatter. If you supply a block or other object, it will be called with the tag value.
|
139
166
|
|
140
167
|
```ruby
|
141
|
-
|
168
|
+
# These will all do the same thing formatting all tag values with `inspect`
|
169
|
+
logger.tag_formatter.default(Lumberjack::Formatter.new.clear.add(Object, :inspect))
|
170
|
+
logger.tag_formatter.default(Lumberjack::Formatter::InspectFormatter.new)
|
171
|
+
logger.tag_formatter.default { |value| value.inspect }
|
172
|
+
|
173
|
+
# This will register formatters only on specific tag names
|
174
|
+
logger.tag_formatter.add(:thread) { |thread| "Thread(#{thread.name})" }
|
175
|
+
logger.tag_formatter.add(:current_user, Lumberjack::Formatter::IdFormatter.new)
|
142
176
|
```
|
143
177
|
|
144
178
|
#### Templates
|
145
179
|
|
146
|
-
If you use the built in devices, you can also customize the Template used to format the LogEntry.
|
180
|
+
If you use the built in `Lumberjack::Writer` derived devices, you can also customize the Template used to format the LogEntry.
|
147
181
|
|
148
182
|
See `Lumberjack::Template` for a complete list of macros you can use in the template. You can also use a block that receives a `Lumberjack::LogEntry` as a template.
|
149
183
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/lib/lumberjack.rb
CHANGED
@@ -10,12 +10,15 @@ module Lumberjack
|
|
10
10
|
LINE_SEPARATOR = (RbConfig::CONFIG['host_os'].match(/mswin/i) ? "\r\n" : "\n")
|
11
11
|
|
12
12
|
require_relative "lumberjack/severity.rb"
|
13
|
+
require_relative "lumberjack/formatter.rb"
|
14
|
+
|
13
15
|
require_relative "lumberjack/context.rb"
|
14
16
|
require_relative "lumberjack/log_entry.rb"
|
15
|
-
require_relative "lumberjack/formatter.rb"
|
16
17
|
require_relative "lumberjack/device.rb"
|
17
18
|
require_relative "lumberjack/logger.rb"
|
18
19
|
require_relative "lumberjack/tags.rb"
|
20
|
+
require_relative "lumberjack/tag_formatter.rb"
|
21
|
+
require_relative "lumberjack/tagged_logger_support.rb"
|
19
22
|
require_relative "lumberjack/template.rb"
|
20
23
|
require_relative "lumberjack/rack.rb"
|
21
24
|
|
data/lib/lumberjack/formatter.rb
CHANGED
@@ -19,18 +19,24 @@ module Lumberjack
|
|
19
19
|
require_relative "formatter/object_formatter.rb"
|
20
20
|
require_relative "formatter/pretty_print_formatter.rb"
|
21
21
|
require_relative "formatter/string_formatter.rb"
|
22
|
+
require_relative "formatter/strip_formatter.rb"
|
22
23
|
require_relative "formatter/structured_formatter.rb"
|
23
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
|
+
|
24
34
|
def initialize
|
25
35
|
@class_formatters = {}
|
26
36
|
@module_formatters = {}
|
27
|
-
@_default_formatter = InspectFormatter.new
|
28
37
|
structured_formatter = StructuredFormatter.new(self)
|
29
|
-
add(String, :object)
|
30
|
-
add(
|
31
|
-
add(TrueClass, :object)
|
32
|
-
add(FalseClass, :object)
|
33
|
-
add(Object, @_default_formatter)
|
38
|
+
add([String, Numeric, TrueClass, FalseClass], :object)
|
39
|
+
add(Object, InspectFormatter.new)
|
34
40
|
add(Exception, :exception)
|
35
41
|
add(Enumerable, structured_formatter)
|
36
42
|
end
|
@@ -41,6 +47,12 @@ module Lumberjack
|
|
41
47
|
#
|
42
48
|
# The predefined formatters are: :inspect, :string, :exception, and :pretty_print.
|
43
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.
|
55
|
+
#
|
44
56
|
# === Examples
|
45
57
|
#
|
46
58
|
# # Use a predefined formatter
|
@@ -56,24 +68,41 @@ module Lumberjack
|
|
56
68
|
# formatter.add(MyClass, :pretty_print).add(YourClass){|obj| obj.humanize}
|
57
69
|
def add(klass, formatter = nil, &block)
|
58
70
|
formatter ||= block
|
59
|
-
if formatter.
|
60
|
-
|
61
|
-
formatter = Formatter.const_get(formatter_class_name).new
|
62
|
-
end
|
63
|
-
if klass.is_a?(Class)
|
64
|
-
@class_formatters[klass] = formatter
|
71
|
+
if formatter.nil?
|
72
|
+
remove(klass)
|
65
73
|
else
|
66
|
-
|
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
|
67
87
|
end
|
68
88
|
self
|
69
89
|
end
|
70
90
|
|
71
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.
|
72
98
|
def remove(klass)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
77
106
|
end
|
78
107
|
self
|
79
108
|
end
|
@@ -87,7 +116,12 @@ module Lumberjack
|
|
87
116
|
|
88
117
|
# Format a message object as a string.
|
89
118
|
def format(message)
|
90
|
-
formatter_for(message.class)
|
119
|
+
formatter = formatter_for(message.class)
|
120
|
+
if formatter && formatter.respond_to?(:call)
|
121
|
+
formatter.call(message)
|
122
|
+
else
|
123
|
+
message
|
124
|
+
end
|
91
125
|
end
|
92
126
|
|
93
127
|
# Compatibility with the Logger::Formatter signature. This method will just convert the message
|
@@ -102,7 +136,7 @@ module Lumberjack
|
|
102
136
|
def formatter_for(klass) #:nodoc:
|
103
137
|
check_modules = true
|
104
138
|
while klass != nil do
|
105
|
-
formatter = @class_formatters[klass]
|
139
|
+
formatter = @class_formatters[klass.name]
|
106
140
|
return formatter if formatter
|
107
141
|
|
108
142
|
if check_modules
|
@@ -113,7 +147,6 @@ module Lumberjack
|
|
113
147
|
|
114
148
|
klass = klass.superclass
|
115
149
|
end
|
116
|
-
@_default_formatter
|
117
150
|
end
|
118
151
|
end
|
119
152
|
end
|
@@ -2,13 +2,38 @@
|
|
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
|
+
|
11
|
+
attr_accessor :backtrace_cleaner
|
12
|
+
|
13
|
+
def initialize(backtrace_cleaner = nil)
|
14
|
+
self.backtrace_cleaner = backtrace_cleaner
|
15
|
+
end
|
16
|
+
|
7
17
|
def call(exception)
|
8
18
|
message = "#{exception.class.name}: #{exception.message}"
|
9
|
-
|
19
|
+
trace = exception.backtrace
|
20
|
+
if trace
|
21
|
+
trace = clean_backtrace(trace)
|
22
|
+
message << "#{Lumberjack::LINE_SEPARATOR} #{trace.join("#{Lumberjack::LINE_SEPARATOR} ")}"
|
23
|
+
end
|
10
24
|
message
|
11
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def clean_backtrace(trace)
|
30
|
+
if trace && backtrace_cleaner
|
31
|
+
backtrace_cleaner.call(trace)
|
32
|
+
else
|
33
|
+
trace
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
12
37
|
end
|
13
38
|
end
|
14
39
|
end
|
@@ -10,7 +10,7 @@ module Lumberjack
|
|
10
10
|
@formatter = formatter
|
11
11
|
end
|
12
12
|
|
13
|
-
def call(obj)
|
13
|
+
def call(obj)
|
14
14
|
if obj.is_a?(Hash)
|
15
15
|
hash = {}
|
16
16
|
references ||= Set.new
|
@@ -18,7 +18,7 @@ module Lumberjack
|
|
18
18
|
hash[name.to_s] = call(value)
|
19
19
|
end
|
20
20
|
hash
|
21
|
-
elsif obj.is_a?(Enumerable)
|
21
|
+
elsif obj.is_a?(Enumerable) && obj.respond_to?(:size) && obj.size != Float::INFINITY
|
22
22
|
obj.collect { |element| call(element) }
|
23
23
|
elsif @formatter
|
24
24
|
@formatter.format(obj)
|
data/lib/lumberjack/log_entry.rb
CHANGED
data/lib/lumberjack/logger.rb
CHANGED
@@ -37,7 +37,10 @@ module Lumberjack
|
|
37
37
|
attr_writer :progname
|
38
38
|
|
39
39
|
# The device being written to
|
40
|
-
|
40
|
+
attr_accessor :device
|
41
|
+
|
42
|
+
# The TagFormatter used for formatting tags for output
|
43
|
+
attr_accessor :tag_formatter
|
41
44
|
|
42
45
|
# Create a new logger to log to a Device.
|
43
46
|
#
|
@@ -53,6 +56,7 @@ module Lumberjack
|
|
53
56
|
# * :level - The logging level below which messages will be ignored.
|
54
57
|
# * :formatter - The formatter to use for outputting messages to the log.
|
55
58
|
# * :datetime_format - The format to use for log timestamps.
|
59
|
+
# * :tag_formatter - The TagFormatter to use for formatting tags.
|
56
60
|
# * :progname - The name of the program that will be recorded with each log entry.
|
57
61
|
# * :flush_seconds - The maximum number of seconds between flush calls.
|
58
62
|
# * :roll - If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.
|
@@ -66,25 +70,27 @@ module Lumberjack
|
|
66
70
|
max_flush_seconds = options.delete(:flush_seconds).to_f
|
67
71
|
|
68
72
|
@device = open_device(device, options) if device
|
69
|
-
|
73
|
+
self.formatter = (options[:formatter] || Formatter.new)
|
74
|
+
@tag_formatter = (options[:tag_formatter] || TagFormatter.new)
|
70
75
|
time_format = (options[:datetime_format] || options[:time_format])
|
71
76
|
self.datetime_format = time_format if time_format
|
72
77
|
@last_flushed_at = Time.now
|
73
78
|
@silencer = true
|
74
79
|
@tags = {}
|
80
|
+
@closed = false
|
75
81
|
|
76
82
|
create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
|
77
83
|
end
|
78
84
|
|
79
85
|
# Get the timestamp format on the device if it has one.
|
80
86
|
def datetime_format
|
81
|
-
|
87
|
+
device.datetime_format if device.respond_to?(:datetime_format)
|
82
88
|
end
|
83
89
|
|
84
90
|
# Set the timestamp format on the device if it is supported.
|
85
91
|
def datetime_format=(format)
|
86
|
-
if
|
87
|
-
|
92
|
+
if device.respond_to?(:datetime_format=)
|
93
|
+
device.datetime_format = format
|
88
94
|
end
|
89
95
|
end
|
90
96
|
|
@@ -110,12 +116,29 @@ module Lumberjack
|
|
110
116
|
|
111
117
|
# Set the Lumberjack::Formatter used to format objects for logging as messages.
|
112
118
|
def formatter=(value)
|
113
|
-
@_formatter = value
|
119
|
+
@_formatter = (value.is_a?(TaggedLoggerSupport::Formatter) ? value.__formatter : value)
|
114
120
|
end
|
115
121
|
|
116
122
|
# Get the Lumberjack::Formatter used to format objects for logging as messages.
|
117
123
|
def formatter
|
118
|
-
|
124
|
+
if respond_to?(:tagged)
|
125
|
+
# Wrap in an object that supports ActiveSupport::TaggedLogger API
|
126
|
+
TaggedLoggerSupport::Formatter.new(logger: self, formatter: @_formatter)
|
127
|
+
else
|
128
|
+
@_formatter
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Enable this logger to function like an ActiveSupport::TaggedLogger. This will make the logger
|
133
|
+
# API compatible with ActiveSupport::TaggedLogger and is provided as a means of compatibility
|
134
|
+
# with other libraries that assume they can call the `tagged` method on a logger to add tags.
|
135
|
+
#
|
136
|
+
# The tags added with this method are just strings so they are stored in the logger tags
|
137
|
+
# in an array under the "tagged" tag. So calling `logger.tagged("foo", "bar")` will result
|
138
|
+
# in tags `{"tagged" => ["foo", "bar"]}`.
|
139
|
+
def tagged_logger!
|
140
|
+
self.extend(TaggedLoggerSupport)
|
141
|
+
self
|
119
142
|
end
|
120
143
|
|
121
144
|
# Add a message to the log with a given severity. The message can be either
|
@@ -135,7 +158,7 @@ module Lumberjack
|
|
135
158
|
def add_entry(severity, message, progname = nil, tags = nil)
|
136
159
|
severity = Severity.label_to_level(severity) unless severity.is_a?(Integer)
|
137
160
|
|
138
|
-
return true unless
|
161
|
+
return true unless device && severity && severity >= level
|
139
162
|
|
140
163
|
time = Time.now
|
141
164
|
message = message.call if message.is_a?(Proc)
|
@@ -154,6 +177,7 @@ module Lumberjack
|
|
154
177
|
end
|
155
178
|
end
|
156
179
|
tags = Tags.expand_runtime_values(tags)
|
180
|
+
tags = tag_formatter.format(tags) if tag_formatter
|
157
181
|
|
158
182
|
entry = LogEntry.new(time, severity, message, progname, $$, tags)
|
159
183
|
write_to_device(entry)
|
@@ -186,10 +210,16 @@ module Lumberjack
|
|
186
210
|
# Close the logging device.
|
187
211
|
def close
|
188
212
|
flush
|
189
|
-
|
213
|
+
device.close if device.respond_to?(:close)
|
214
|
+
@closed = true
|
215
|
+
end
|
216
|
+
|
217
|
+
def closed?
|
218
|
+
@closed
|
190
219
|
end
|
191
220
|
|
192
221
|
def reopen(logdev = nil)
|
222
|
+
@closed = false
|
193
223
|
device.reopen(logdev) if device.respond_to?(:reopen)
|
194
224
|
end
|
195
225
|
|
@@ -264,6 +294,9 @@ module Lumberjack
|
|
264
294
|
# end
|
265
295
|
def silence(temporary_level = ERROR, &block)
|
266
296
|
if silencer
|
297
|
+
unless temporary_level.is_a?(Integer)
|
298
|
+
temporary_level = Severity::label_to_level(temporary_level)
|
299
|
+
end
|
267
300
|
push_thread_local_value(:lumberjack_logger_level, temporary_level, &block)
|
268
301
|
else
|
269
302
|
yield
|
@@ -393,7 +426,7 @@ module Lumberjack
|
|
393
426
|
|
394
427
|
def write_to_device(entry) #:nodoc:
|
395
428
|
begin
|
396
|
-
|
429
|
+
device.write(entry)
|
397
430
|
rescue => e
|
398
431
|
$stderr.puts("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
|
399
432
|
$stderr.puts(entry.to_s)
|
@@ -406,7 +439,7 @@ module Lumberjack
|
|
406
439
|
begin
|
407
440
|
logger = self
|
408
441
|
Thread.new do
|
409
|
-
|
442
|
+
while !closed?
|
410
443
|
begin
|
411
444
|
sleep(flush_seconds)
|
412
445
|
logger.flush if Time.now - logger.last_flushed_at >= flush_seconds
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lumberjack
|
4
|
+
# Class for formatting tags. You can register a default formatter and tag
|
5
|
+
# name specific formatters. Formatters can be either `Lumberjack::Formatter`
|
6
|
+
# objects or any object that responds to `call`.
|
7
|
+
#
|
8
|
+
# tag_formatter = Lumberjack::TagFormatter.new.default(Lumberjack::Formatter.new)
|
9
|
+
# tag_formatter.add(["password", "email"]) { |value| "***" }
|
10
|
+
# tag_formatter.add("finished_at", Lumberjack::Formatter::DateTimeFormatter.new("%Y-%m-%dT%H:%m:%S%z"))
|
11
|
+
class TagFormatter
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@formatters = {}
|
15
|
+
@default_formatter = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add a default formatter applied to all tag values. This can either be a Lumberjack::Formatter
|
19
|
+
# or an object that responds to `call` or a block.
|
20
|
+
def default(formatter = nil, &block)
|
21
|
+
formatter ||= block
|
22
|
+
formatter = dereference_formatter(formatter)
|
23
|
+
@default_formatter = formatter
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Remove the default formatter.
|
28
|
+
def remove_default
|
29
|
+
@default_formatter = nil
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Add a formatter for specific tag names. This can either be a Lumberjack::Formatter
|
34
|
+
# or an object that responds to `call` or a block. The default formatter will not be
|
35
|
+
# applied.
|
36
|
+
def add(names, formatter = nil, &block)
|
37
|
+
formatter ||= block
|
38
|
+
formatter = dereference_formatter(formatter)
|
39
|
+
if formatter.nil?
|
40
|
+
remove(key)
|
41
|
+
else
|
42
|
+
Array(names).each do |name|
|
43
|
+
@formatters[name.to_s] = formatter
|
44
|
+
end
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Remove formatters for specific tag names. The default formatter will still be applied.
|
50
|
+
def remove(names)
|
51
|
+
Array(names).each do |name|
|
52
|
+
@formatters.delete(name.to_s)
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Remove all formatters.
|
58
|
+
def clear
|
59
|
+
@default_formatter = nil
|
60
|
+
@formatters.clear
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# Format a hash of tags using the formatters
|
65
|
+
def format(tags)
|
66
|
+
return nil if tags.nil?
|
67
|
+
if @default_formatter.nil? && (@formatters.empty? || (@formatters.keys & tags.keys).empty?)
|
68
|
+
tags
|
69
|
+
else
|
70
|
+
formatted = {}
|
71
|
+
tags.each do |name, value|
|
72
|
+
formatter = (@formatters[name.to_s] || @default_formatter)
|
73
|
+
if formatter.is_a?(Lumberjack::Formatter)
|
74
|
+
value = formatter.format(value)
|
75
|
+
elsif formatter.respond_to?(:call)
|
76
|
+
value = formatter.call(value)
|
77
|
+
end
|
78
|
+
formatted[name.to_s] = value
|
79
|
+
end
|
80
|
+
formatted
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def dereference_formatter(formatter)
|
87
|
+
if formatter.is_a?(TaggedLoggerSupport::Formatter)
|
88
|
+
formatter.__formatter
|
89
|
+
elsif formatter.is_a?(Symbol)
|
90
|
+
formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/){|m| $~[2].upcase}}Formatter"
|
91
|
+
Formatter.const_get(formatter_class_name).new
|
92
|
+
else
|
93
|
+
formatter
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module Lumberjack
|
7
|
+
# Methods to make Lumberjack::Logger API compatible with ActiveSupport::TaggedLogger.
|
8
|
+
module TaggedLoggerSupport
|
9
|
+
|
10
|
+
class Formatter < DelegateClass(Lumberjack::Formatter)
|
11
|
+
extend Forwardable
|
12
|
+
def_delegators :@logger, :tagged, :push_tags, :pop_tags, :clear_tags!
|
13
|
+
|
14
|
+
def initialize(formatter:, logger:)
|
15
|
+
@logger = logger
|
16
|
+
@formatter = formatter
|
17
|
+
super(formatter)
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_tags
|
21
|
+
tags = @logger.instance_variable_get(:@tags)
|
22
|
+
if tags.is_a?(Hash)
|
23
|
+
Array(tags["tagged"])
|
24
|
+
else
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def tags_text
|
30
|
+
tags = current_tags
|
31
|
+
if tags.any?
|
32
|
+
tags.collect { |tag| "[#{tag}] " }.join
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def __formatter
|
37
|
+
@formatter
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Compatibility with ActiveSupport::TaggedLogging which only supports adding tags as strings.
|
42
|
+
# If a tag looks like "key:value" or "key=value", it will be added as a key value pair.
|
43
|
+
# Otherwise it will be appended to a list named "tagged".
|
44
|
+
def tagged(*tags, &block)
|
45
|
+
tag_hash = {}
|
46
|
+
tags.flatten.each do |tag|
|
47
|
+
tagged_values = Array(tag_hash["tagged"] || self.tags["tagged"])
|
48
|
+
tag_hash["tagged"] = tagged_values + [tag]
|
49
|
+
end
|
50
|
+
tag(tag_hash, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def push_tags(*tags)
|
54
|
+
tagged(*tags)
|
55
|
+
end
|
56
|
+
|
57
|
+
def pop_tags(size = 1)
|
58
|
+
tagged_values = Array(@tags["tagged"])
|
59
|
+
tagged_values = (tagged_values.size > size ? tagged_values[0, tagged_values.size - size] : nil)
|
60
|
+
tag("tagged" => tagged_values)
|
61
|
+
end
|
62
|
+
|
63
|
+
def clear_tags!
|
64
|
+
tag("tagged" => nil)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/lumberjack/template.rb
CHANGED
@@ -11,10 +11,13 @@ module Lumberjack
|
|
11
11
|
# * :message
|
12
12
|
#
|
13
13
|
# Any other words prefixed with a colon will be substituted with the value of the tag with that name.
|
14
|
+
# If your tag name contains characters other than alpha numerics and the underscore, you must surround it
|
15
|
+
# with curly brackets: `:{http.request-id}`.
|
14
16
|
class Template
|
15
17
|
TEMPLATE_ARGUMENT_ORDER = %w(:time :severity :progname :pid :message :tags).freeze
|
16
18
|
MILLISECOND_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%3N"
|
17
19
|
MICROSECOND_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%6N"
|
20
|
+
PLACEHOLDER_PATTERN = /:(([a-z0-9_]+)|({[^}]+}))/i.freeze
|
18
21
|
|
19
22
|
# Create a new template from the markup. The +first_line+ argument is used to format only the first
|
20
23
|
# line of a message. Additional lines will be added to the message unformatted. If you wish to format
|
@@ -85,8 +88,10 @@ module Lumberjack
|
|
85
88
|
|
86
89
|
tags_string = String.new
|
87
90
|
tags.each do |name, value|
|
88
|
-
unless tag_vars.include?(name)
|
89
|
-
|
91
|
+
unless value.nil? || tag_vars.include?(name)
|
92
|
+
value = value.to_s
|
93
|
+
value = value.gsub(Lumberjack::LINE_SEPARATOR, " ") if value.include?(Lumberjack::LINE_SEPARATOR)
|
94
|
+
tags_string << "[#{name}:#{value}] "
|
90
95
|
end
|
91
96
|
end
|
92
97
|
|
@@ -100,12 +105,13 @@ module Lumberjack
|
|
100
105
|
# Compile the template string into a value that can be used with sprintf.
|
101
106
|
def compile(template) #:nodoc:
|
102
107
|
tag_vars = []
|
103
|
-
template = template.gsub(
|
104
|
-
|
108
|
+
template = template.gsub(PLACEHOLDER_PATTERN) do |match|
|
109
|
+
var_name = match.sub("{", "").sub("}", "")
|
110
|
+
position = TEMPLATE_ARGUMENT_ORDER.index(var_name)
|
105
111
|
if position
|
106
112
|
"%#{position + 1}$s"
|
107
113
|
else
|
108
|
-
tag_vars <<
|
114
|
+
tag_vars << var_name[1, var_name.length]
|
109
115
|
"%#{TEMPLATE_ARGUMENT_ORDER.size + tag_vars.size}$s"
|
110
116
|
end
|
111
117
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lumberjack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- lib/lumberjack/formatter/object_formatter.rb
|
96
96
|
- lib/lumberjack/formatter/pretty_print_formatter.rb
|
97
97
|
- lib/lumberjack/formatter/string_formatter.rb
|
98
|
+
- lib/lumberjack/formatter/strip_formatter.rb
|
98
99
|
- lib/lumberjack/formatter/structured_formatter.rb
|
99
100
|
- lib/lumberjack/log_entry.rb
|
100
101
|
- lib/lumberjack/logger.rb
|
@@ -103,6 +104,8 @@ files:
|
|
103
104
|
- lib/lumberjack/rack/request_id.rb
|
104
105
|
- lib/lumberjack/rack/unit_of_work.rb
|
105
106
|
- lib/lumberjack/severity.rb
|
107
|
+
- lib/lumberjack/tag_formatter.rb
|
108
|
+
- lib/lumberjack/tagged_logger_support.rb
|
106
109
|
- lib/lumberjack/tags.rb
|
107
110
|
- lib/lumberjack/template.rb
|
108
111
|
- lumberjack.gemspec
|