lumberjack 1.1.1 → 1.2.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.
- 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
|