lumberjack 1.2.7 → 1.4.2
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/ARCHITECTURE.md +244 -0
- data/CHANGELOG.md +251 -56
- data/README.md +197 -62
- data/VERSION +1 -1
- data/lib/lumberjack/context.rb +25 -5
- data/lib/lumberjack/device/date_rolling_log_file.rb +17 -8
- data/lib/lumberjack/device/log_file.rb +14 -7
- data/lib/lumberjack/device/multi.rb +8 -7
- data/lib/lumberjack/device/null.rb +2 -2
- data/lib/lumberjack/device/rolling_log_file.rb +46 -22
- data/lib/lumberjack/device/size_rolling_log_file.rb +10 -10
- data/lib/lumberjack/device/writer.rb +45 -21
- data/lib/lumberjack/device.rb +28 -13
- data/lib/lumberjack/formatter/date_time_formatter.rb +5 -5
- data/lib/lumberjack/formatter/exception_formatter.rb +4 -4
- data/lib/lumberjack/formatter/id_formatter.rb +4 -3
- data/lib/lumberjack/formatter/inspect_formatter.rb +1 -1
- data/lib/lumberjack/formatter/multiply_formatter.rb +25 -0
- data/lib/lumberjack/formatter/object_formatter.rb +1 -1
- data/lib/lumberjack/formatter/pretty_print_formatter.rb +7 -5
- data/lib/lumberjack/formatter/redact_formatter.rb +23 -0
- data/lib/lumberjack/formatter/round_formatter.rb +21 -0
- data/lib/lumberjack/formatter/string_formatter.rb +1 -1
- data/lib/lumberjack/formatter/strip_formatter.rb +1 -1
- data/lib/lumberjack/formatter/structured_formatter.rb +3 -1
- data/lib/lumberjack/formatter/tagged_message.rb +39 -0
- data/lib/lumberjack/formatter/truncate_formatter.rb +27 -0
- data/lib/lumberjack/formatter.rb +96 -28
- data/lib/lumberjack/log_entry.rb +90 -19
- data/lib/lumberjack/logger.rb +318 -86
- data/lib/lumberjack/rack/context.rb +21 -2
- data/lib/lumberjack/rack/request_id.rb +8 -4
- data/lib/lumberjack/rack/unit_of_work.rb +7 -3
- data/lib/lumberjack/rack.rb +4 -4
- data/lib/lumberjack/severity.rb +22 -3
- data/lib/lumberjack/tag_context.rb +78 -0
- data/lib/lumberjack/tag_formatter.rb +124 -25
- data/lib/lumberjack/tagged_logger_support.rb +26 -12
- data/lib/lumberjack/tagged_logging.rb +1 -1
- data/lib/lumberjack/tags.rb +8 -8
- data/lib/lumberjack/template.rb +17 -5
- data/lib/lumberjack/utils.rb +182 -0
- data/lib/lumberjack.rb +64 -35
- data/lumberjack.gemspec +18 -15
- metadata +23 -54
|
@@ -1,18 +1,37 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Lumberjack
|
|
4
4
|
module Rack
|
|
5
5
|
# Middleware to create a global context for Lumberjack for the scope of a rack request.
|
|
6
|
+
#
|
|
7
|
+
# The optional `env_tags` parameter can be used to set up global tags from the request
|
|
8
|
+
# environment. This is useful for setting tags that are relevant to the entire request
|
|
9
|
+
# like the request id, host, etc.
|
|
6
10
|
class Context
|
|
7
|
-
|
|
11
|
+
# @param [Object] app The rack application.
|
|
12
|
+
# @param [Hash] env_tags A hash of tags to set from the request environment. If a tag value is
|
|
13
|
+
# a Proc, it will be called with the request `env` as an argument to allow dynamic tag values
|
|
14
|
+
# based on request data.
|
|
15
|
+
def initialize(app, env_tags = nil)
|
|
8
16
|
@app = app
|
|
17
|
+
@env_tags = env_tags
|
|
9
18
|
end
|
|
10
19
|
|
|
11
20
|
def call(env)
|
|
12
21
|
Lumberjack.context do
|
|
22
|
+
apply_tags(env) if @env_tags
|
|
13
23
|
@app.call(env)
|
|
14
24
|
end
|
|
15
25
|
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def apply_tags(env)
|
|
30
|
+
tags = @env_tags.transform_values do |value|
|
|
31
|
+
value.is_a?(Proc) ? value.call(env) : value
|
|
32
|
+
end
|
|
33
|
+
Lumberjack.tag(tags)
|
|
34
|
+
end
|
|
16
35
|
end
|
|
17
36
|
end
|
|
18
37
|
end
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Lumberjack
|
|
4
4
|
module Rack
|
|
5
5
|
# Support for using the Rails ActionDispatch request id in the log.
|
|
6
6
|
# The format is expected to be a random UUID and only the first chunk is used for terseness
|
|
7
7
|
# if the abbreviated argument is true.
|
|
8
|
+
#
|
|
9
|
+
# @deprecated Use tags instead of request id for unit of work. Will be removed in version 2.0.
|
|
8
10
|
class RequestId
|
|
9
11
|
REQUEST_ID = "action_dispatch.request_id"
|
|
10
12
|
|
|
11
13
|
def initialize(app, abbreviated = false)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
Lumberjack::Utils.deprecated("Lumberjack::Rack::RequestId", "Lumberjack::Rack::RequestId will be removed in version 2.0") do
|
|
15
|
+
@app = app
|
|
16
|
+
@abbreviated = abbreviated
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def call(env)
|
|
17
21
|
request_id = env[REQUEST_ID]
|
|
18
22
|
if request_id && @abbreviated
|
|
19
|
-
request_id = request_id.split(
|
|
23
|
+
request_id = request_id.split("-", 2).first
|
|
20
24
|
end
|
|
21
25
|
Lumberjack.unit_of_work(request_id) do
|
|
22
26
|
@app.call(env)
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Lumberjack
|
|
4
4
|
module Rack
|
|
5
|
+
# @deprecated Use the Lumberjack::Rack::Context middleware instead to set a global tag
|
|
6
|
+
# with an identifier to tie log entries together in a unit of work. Will be removed in version 2.0.
|
|
5
7
|
class UnitOfWork
|
|
6
8
|
def initialize(app)
|
|
7
|
-
|
|
9
|
+
Lumberjack::Utils.deprecated("Lumberjack::Rack::UnitOfWork", "Lumberjack::Rack::UnitOfWork will be removed in version 2.0") do
|
|
10
|
+
@app = app
|
|
11
|
+
end
|
|
8
12
|
end
|
|
9
|
-
|
|
13
|
+
|
|
10
14
|
def call(env)
|
|
11
15
|
Lumberjack.unit_of_work do
|
|
12
16
|
@app.call(env)
|
data/lib/lumberjack/rack.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Lumberjack
|
|
4
4
|
module Rack
|
|
5
|
-
require_relative "rack/unit_of_work
|
|
6
|
-
require_relative "rack/request_id
|
|
7
|
-
require_relative "rack/context
|
|
5
|
+
require_relative "rack/unit_of_work"
|
|
6
|
+
require_relative "rack/request_id"
|
|
7
|
+
require_relative "rack/context"
|
|
8
8
|
end
|
|
9
9
|
end
|
data/lib/lumberjack/severity.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Lumberjack
|
|
4
4
|
# The standard severity levels for logging messages.
|
|
@@ -11,17 +11,36 @@ module Lumberjack
|
|
|
11
11
|
FATAL = ::Logger::Severity::FATAL
|
|
12
12
|
UNKNOWN = ::Logger::Severity::UNKNOWN
|
|
13
13
|
|
|
14
|
-
SEVERITY_LABELS = %w
|
|
14
|
+
SEVERITY_LABELS = %w[DEBUG INFO WARN ERROR FATAL UNKNOWN].freeze
|
|
15
15
|
|
|
16
16
|
class << self
|
|
17
|
+
# Convert a severity level to a label.
|
|
18
|
+
#
|
|
19
|
+
# @param [Integer] severity The severity level to convert.
|
|
20
|
+
# @return [String] The severity label.
|
|
17
21
|
def level_to_label(severity)
|
|
18
22
|
SEVERITY_LABELS[severity] || SEVERITY_LABELS.last
|
|
19
23
|
end
|
|
20
24
|
|
|
25
|
+
# Convert a severity label to a level.
|
|
26
|
+
#
|
|
27
|
+
# @param [String, Symbol] label The severity label to convert.
|
|
28
|
+
# @return [Integer] The severity level.
|
|
21
29
|
def label_to_level(label)
|
|
22
30
|
SEVERITY_LABELS.index(label.to_s.upcase) || UNKNOWN
|
|
23
31
|
end
|
|
24
|
-
end
|
|
25
32
|
|
|
33
|
+
# Coerce a value to a severity level.
|
|
34
|
+
#
|
|
35
|
+
# @param [Integer, String, Symbol] value The value to coerce.
|
|
36
|
+
# @return [Integer] The severity level.
|
|
37
|
+
def coerce(value)
|
|
38
|
+
if value.is_a?(Integer)
|
|
39
|
+
value
|
|
40
|
+
else
|
|
41
|
+
label_to_level(value)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
26
45
|
end
|
|
27
46
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lumberjack
|
|
4
|
+
# A tag context provides an interface for manipulating a tag hash.
|
|
5
|
+
class TagContext
|
|
6
|
+
def initialize(tags)
|
|
7
|
+
@tags = tags
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Merge new tags into the context tags. Tag values will be flattened using dot notation
|
|
11
|
+
# on the keys. So `{ a: { b: 'c' } }` will become `{ 'a.b' => 'c' }`.
|
|
12
|
+
#
|
|
13
|
+
# If a block is given, then the tags will only be added for the duration of the block.
|
|
14
|
+
#
|
|
15
|
+
# @param tags [Hash] The tags to set.
|
|
16
|
+
# @return [void]
|
|
17
|
+
def tag(tags)
|
|
18
|
+
@tags.merge!(Utils.flatten_tags(tags))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Get a tag value.
|
|
22
|
+
#
|
|
23
|
+
# @param name [String, Symbol] The tag key.
|
|
24
|
+
# @return [Object] The tag value.
|
|
25
|
+
def [](name)
|
|
26
|
+
return nil if @tags.empty?
|
|
27
|
+
|
|
28
|
+
name = name.to_s
|
|
29
|
+
return @tags[name] if @tags.include?(name)
|
|
30
|
+
|
|
31
|
+
# Check for partial matches in dot notation and return the hash representing the partial match.
|
|
32
|
+
prefix_key = "#{name}."
|
|
33
|
+
matching_tags = {}
|
|
34
|
+
@tags.each do |key, value|
|
|
35
|
+
if key.start_with?(prefix_key)
|
|
36
|
+
# Remove the prefix to get the relative key
|
|
37
|
+
relative_key = key[prefix_key.length..-1]
|
|
38
|
+
matching_tags[relative_key] = value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return nil if matching_tags.empty?
|
|
43
|
+
matching_tags
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Set a tag value.
|
|
47
|
+
#
|
|
48
|
+
# @param name [String, Symbol] The tag name.
|
|
49
|
+
# @param value [Object] The tag value.
|
|
50
|
+
# @return [void]
|
|
51
|
+
def []=(name, value)
|
|
52
|
+
if value.is_a?(Hash)
|
|
53
|
+
@tags.merge!(Utils.flatten_tags(name => value))
|
|
54
|
+
else
|
|
55
|
+
@tags[name.to_s] = value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Remove tags from the context.
|
|
60
|
+
#
|
|
61
|
+
# @param names [Array<String, Symbol>] The tag names to remove.
|
|
62
|
+
# @return [void]
|
|
63
|
+
def delete(*names)
|
|
64
|
+
names.each do |name|
|
|
65
|
+
prefix_key = "#{name}."
|
|
66
|
+
@tags.delete_if { |k, _| k == name.to_s || k.start_with?(prefix_key) }
|
|
67
|
+
end
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Return a copy of the tags as a hash.
|
|
72
|
+
#
|
|
73
|
+
# @return [Hash]
|
|
74
|
+
def to_h
|
|
75
|
+
@tags.dup
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -9,14 +9,18 @@ module Lumberjack
|
|
|
9
9
|
# tag_formatter.add(["password", "email"]) { |value| "***" }
|
|
10
10
|
# tag_formatter.add("finished_at", Lumberjack::Formatter::DateTimeFormatter.new("%Y-%m-%dT%H:%m:%S%z"))
|
|
11
11
|
class TagFormatter
|
|
12
|
-
|
|
13
12
|
def initialize
|
|
14
13
|
@formatters = {}
|
|
14
|
+
@class_formatters = {}
|
|
15
15
|
@default_formatter = nil
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Add a default formatter applied to all tag values. This can either be a Lumberjack::Formatter
|
|
19
19
|
# or an object that responds to `call` or a block.
|
|
20
|
+
#
|
|
21
|
+
# @param formatter [Lumberjack::Formatter, #call, nil] The formatter to use.
|
|
22
|
+
# If this is nil, then the block will be used as the formatter.
|
|
23
|
+
# @return [Lumberjack::TagFormatter] self
|
|
20
24
|
def default(formatter = nil, &block)
|
|
21
25
|
formatter ||= block
|
|
22
26
|
formatter = dereference_formatter(formatter)
|
|
@@ -25,36 +29,67 @@ module Lumberjack
|
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
# Remove the default formatter.
|
|
32
|
+
#
|
|
33
|
+
# @return [Lumberjack::TagFormatter] self
|
|
28
34
|
def remove_default
|
|
29
35
|
@default_formatter = nil
|
|
30
36
|
self
|
|
31
37
|
end
|
|
32
38
|
|
|
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
|
|
35
|
-
#
|
|
36
|
-
|
|
39
|
+
# Add a formatter for specific tag names or object classes. This can either be a Lumberjack::Formatter
|
|
40
|
+
# or an object that responds to `call` or a block. The formatter will be applied if it matches either a tag name
|
|
41
|
+
# or if the tag value is an instance of a registered class. Tag name formatters will take precedence
|
|
42
|
+
# over class formatters. The default formatter will not be applied to a value if a tag formatter
|
|
43
|
+
# is applied to it.
|
|
44
|
+
#
|
|
45
|
+
# Name formatters can be applied to nested hashes using dot syntax. For example, if you add a formatter
|
|
46
|
+
# for "foo.bar", it will be applied to the value of the "bar" key in the "foo" tag if that value is a hash.
|
|
47
|
+
#
|
|
48
|
+
# Class formatters will be applied recursively to nested hashes and arrays.
|
|
49
|
+
#
|
|
50
|
+
# @param names_or_classes [String, Module, Array<String, Module>] The tag names or object classes
|
|
51
|
+
# to apply the formatter to.
|
|
52
|
+
# @param formatter [Lumberjack::Formatter, #call, nil] The formatter to use.
|
|
53
|
+
# If this is nil, then the block will be used as the formatter.
|
|
54
|
+
# @return [Lumberjack::TagFormatter] self
|
|
55
|
+
#
|
|
56
|
+
# @example
|
|
57
|
+
# tag_formatter.add("password", &:redact)
|
|
58
|
+
def add(names_or_classes, formatter = nil, &block)
|
|
37
59
|
formatter ||= block
|
|
38
60
|
formatter = dereference_formatter(formatter)
|
|
39
61
|
if formatter.nil?
|
|
40
62
|
remove(key)
|
|
41
63
|
else
|
|
42
|
-
Array(
|
|
43
|
-
|
|
64
|
+
Array(names_or_classes).each do |key|
|
|
65
|
+
if key.is_a?(Module)
|
|
66
|
+
@class_formatters[key] = formatter
|
|
67
|
+
else
|
|
68
|
+
@formatters[key.to_s] = formatter
|
|
69
|
+
end
|
|
44
70
|
end
|
|
45
71
|
end
|
|
46
72
|
self
|
|
47
73
|
end
|
|
48
74
|
|
|
49
75
|
# Remove formatters for specific tag names. The default formatter will still be applied.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
#
|
|
77
|
+
# @param names_or_classes [String, Module, Array<String, Module>] The tag names or classes to remove the formatter from.
|
|
78
|
+
# @return [Lumberjack::TagFormatter] self
|
|
79
|
+
def remove(names_or_classes)
|
|
80
|
+
Array(names_or_classes).each do |key|
|
|
81
|
+
if key.is_a?(Module)
|
|
82
|
+
@class_formatters.delete(key)
|
|
83
|
+
else
|
|
84
|
+
@formatters.delete(key.to_s)
|
|
85
|
+
end
|
|
53
86
|
end
|
|
54
87
|
self
|
|
55
88
|
end
|
|
56
89
|
|
|
57
90
|
# Remove all formatters.
|
|
91
|
+
#
|
|
92
|
+
# @return [Lumberjack::TagFormatter] self
|
|
58
93
|
def clear
|
|
59
94
|
@default_formatter = nil
|
|
60
95
|
@formatters.clear
|
|
@@ -62,37 +97,101 @@ module Lumberjack
|
|
|
62
97
|
end
|
|
63
98
|
|
|
64
99
|
# Format a hash of tags using the formatters
|
|
100
|
+
#
|
|
101
|
+
# @param tags [Hash] The tags to format.
|
|
102
|
+
# @return [Hash] The formatted tags.
|
|
65
103
|
def format(tags)
|
|
66
104
|
return nil if tags.nil?
|
|
67
|
-
if @default_formatter.nil? &&
|
|
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
|
|
105
|
+
if @default_formatter.nil? && @formatters.empty? && @class_formatters.empty?
|
|
106
|
+
return tags
|
|
81
107
|
end
|
|
108
|
+
|
|
109
|
+
formatted_tags(tags)
|
|
82
110
|
end
|
|
83
111
|
|
|
84
112
|
private
|
|
85
113
|
|
|
114
|
+
def formatted_tags(tags, skip_classes: nil, prefix: nil)
|
|
115
|
+
formatted = {}
|
|
116
|
+
|
|
117
|
+
tags.each do |name, value|
|
|
118
|
+
name = name.to_s
|
|
119
|
+
formatted[name] = formatted_tag_value(name, value, skip_classes: skip_classes, prefix: prefix)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
formatted
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def formatted_tag_value(name, value, skip_classes: nil, prefix: nil)
|
|
126
|
+
prefixed_name = prefix ? "#{prefix}#{name}" : name
|
|
127
|
+
using_class_formatter = false
|
|
128
|
+
|
|
129
|
+
formatter = @formatters[prefixed_name]
|
|
130
|
+
if formatter.nil? && (skip_classes.nil? || !skip_classes.include?(value.class))
|
|
131
|
+
formatter = class_formatter(value.class)
|
|
132
|
+
using_class_formatter = true if formatter
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
formatter ||= @default_formatter
|
|
136
|
+
|
|
137
|
+
formatted_value = begin
|
|
138
|
+
if formatter.is_a?(Lumberjack::Formatter)
|
|
139
|
+
formatter.format(value)
|
|
140
|
+
elsif formatter.respond_to?(:call)
|
|
141
|
+
formatter.call(value)
|
|
142
|
+
else
|
|
143
|
+
value
|
|
144
|
+
end
|
|
145
|
+
rescue SystemStackError, StandardError => e
|
|
146
|
+
error_message = e.class.name
|
|
147
|
+
error_message = "#{error_message} #{e.message}" if e.message && e.message != ""
|
|
148
|
+
warn("<Error formatting #{value.class.name}: #{error_message}>")
|
|
149
|
+
"<Error formatting #{value.class.name}: #{error_message}>"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
if formatted_value.is_a?(Enumerable)
|
|
153
|
+
skip_classes ||= []
|
|
154
|
+
skip_classes << value.class if using_class_formatter
|
|
155
|
+
sub_prefix = "#{prefixed_name}."
|
|
156
|
+
|
|
157
|
+
formatted_value = if formatted_value.is_a?(Hash)
|
|
158
|
+
formatted_tags(formatted_value, skip_classes: skip_classes, prefix: sub_prefix)
|
|
159
|
+
else
|
|
160
|
+
formatted_value.collect do |item|
|
|
161
|
+
formatted_tag_value(nil, item, skip_classes: skip_classes, prefix: sub_prefix)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
formatted_value
|
|
167
|
+
end
|
|
168
|
+
|
|
86
169
|
def dereference_formatter(formatter)
|
|
87
170
|
if formatter.is_a?(TaggedLoggerSupport::Formatter)
|
|
88
171
|
formatter.__formatter
|
|
89
172
|
elsif formatter.is_a?(Symbol)
|
|
90
|
-
formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/){|m| $~[2].upcase}}Formatter"
|
|
173
|
+
formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/) { |m| $~[2].upcase }}Formatter"
|
|
91
174
|
Formatter.const_get(formatter_class_name).new
|
|
92
175
|
else
|
|
93
176
|
formatter
|
|
94
177
|
end
|
|
95
178
|
end
|
|
96
179
|
|
|
180
|
+
def class_formatter(klass)
|
|
181
|
+
formatter = @class_formatters[klass]
|
|
182
|
+
return formatter if formatter
|
|
183
|
+
|
|
184
|
+
formatters = @class_formatters.select { |k, _| klass <= k }
|
|
185
|
+
return formatters.values.first if formatters.length <= 1
|
|
186
|
+
|
|
187
|
+
superclass = klass.superclass
|
|
188
|
+
while superclass
|
|
189
|
+
formatter = formatters[superclass]
|
|
190
|
+
return formatter if formatter
|
|
191
|
+
superclass = superclass.superclass
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
formatters.values.first
|
|
195
|
+
end
|
|
97
196
|
end
|
|
98
197
|
end
|
|
@@ -6,7 +6,6 @@ require "forwardable"
|
|
|
6
6
|
module Lumberjack
|
|
7
7
|
# Methods to make Lumberjack::Logger API compatible with ActiveSupport::TaggedLogger.
|
|
8
8
|
module TaggedLoggerSupport
|
|
9
|
-
|
|
10
9
|
class Formatter < DelegateClass(Lumberjack::Formatter)
|
|
11
10
|
extend Forwardable
|
|
12
11
|
def_delegators :@logger, :tagged, :push_tags, :pop_tags, :clear_tags!
|
|
@@ -39,15 +38,19 @@ module Lumberjack
|
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
# Compatibility with ActiveSupport::TaggedLogging which only supports adding tags as strings.
|
|
42
|
-
#
|
|
43
|
-
# Otherwise it will be appended to a list named "tagged".
|
|
41
|
+
# Tags will be added to the "tagged" key in the logger's tags hash as an array.
|
|
44
42
|
def tagged(*tags, &block)
|
|
45
|
-
|
|
46
|
-
tags.flatten.
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
tagged_values = Array(tag_value("tagged"))
|
|
44
|
+
flattened_tags = tags.flatten.collect(&:to_s).reject do |tag|
|
|
45
|
+
tag.respond_to?(:blank?) ? tag.blank? : tag.empty?
|
|
46
|
+
end
|
|
47
|
+
tagged_values += flattened_tags unless flattened_tags.empty?
|
|
48
|
+
|
|
49
|
+
if block || in_tag_context?
|
|
50
|
+
tag("tagged" => tagged_values, &block)
|
|
51
|
+
else
|
|
52
|
+
tag_globally("tagged" => tagged_values)
|
|
49
53
|
end
|
|
50
|
-
tag(tag_hash, &block)
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
def push_tags(*tags)
|
|
@@ -55,13 +58,24 @@ module Lumberjack
|
|
|
55
58
|
end
|
|
56
59
|
|
|
57
60
|
def pop_tags(size = 1)
|
|
58
|
-
tagged_values =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
tagged_values = tag_value("tagged")
|
|
62
|
+
return unless tagged_values.is_a?(Array)
|
|
63
|
+
|
|
64
|
+
tagged_values = ((tagged_values.size > size) ? tagged_values[0, tagged_values.size - size] : nil)
|
|
65
|
+
|
|
66
|
+
if in_tag_context?
|
|
67
|
+
tag("tagged" => tagged_values)
|
|
68
|
+
else
|
|
69
|
+
tag_globally("tagged" => tagged_values)
|
|
70
|
+
end
|
|
61
71
|
end
|
|
62
72
|
|
|
63
73
|
def clear_tags!
|
|
64
|
-
|
|
74
|
+
if in_tag_context?
|
|
75
|
+
tag("tagged" => nil)
|
|
76
|
+
else
|
|
77
|
+
tag_globally("tagged" => nil)
|
|
78
|
+
end
|
|
65
79
|
end
|
|
66
80
|
end
|
|
67
81
|
end
|
data/lib/lumberjack/tags.rb
CHANGED
|
@@ -5,29 +5,29 @@ module Lumberjack
|
|
|
5
5
|
class << self
|
|
6
6
|
# Transform hash keys to strings. This method exists for optimization and backward compatibility.
|
|
7
7
|
# If a hash already has string keys, it will be returned as is.
|
|
8
|
+
#
|
|
9
|
+
# @param [Hash] hash The hash to transform.
|
|
10
|
+
# @return [Hash] The hash with string keys.
|
|
8
11
|
def stringify_keys(hash)
|
|
9
12
|
return nil if hash.nil?
|
|
10
13
|
if hash.keys.all? { |key| key.is_a?(String) }
|
|
11
14
|
hash
|
|
12
|
-
elsif hash.respond_to?(:transform_keys)
|
|
13
|
-
hash.transform_keys(&:to_s)
|
|
14
15
|
else
|
|
15
|
-
|
|
16
|
-
hash.each do |key, value|
|
|
17
|
-
copy[key.to_s] = value
|
|
18
|
-
end
|
|
19
|
-
copy
|
|
16
|
+
hash.transform_keys(&:to_s)
|
|
20
17
|
end
|
|
21
18
|
end
|
|
22
19
|
|
|
23
20
|
# Ensure keys are strings and expand any values in a hash that are Proc's by calling them and replacing
|
|
24
21
|
# the value with the result. This allows setting global tags with runtime values.
|
|
22
|
+
#
|
|
23
|
+
# @param [Hash] hash The hash to transform.
|
|
24
|
+
# @return [Hash] The hash with string keys and expanded values.
|
|
25
25
|
def expand_runtime_values(hash)
|
|
26
26
|
return nil if hash.nil?
|
|
27
27
|
if hash.all? { |key, value| key.is_a?(String) && !value.is_a?(Proc) }
|
|
28
28
|
return hash
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
copy = {}
|
|
32
32
|
hash.each do |key, value|
|
|
33
33
|
if value.is_a?(Proc) && (value.arity == 0 || value.arity == -1)
|
data/lib/lumberjack/template.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Lumberjack
|
|
4
4
|
# A template converts entries to strings. Templates can contain the following place holders to
|
|
@@ -14,7 +14,7 @@ module Lumberjack
|
|
|
14
14
|
# If your tag name contains characters other than alpha numerics and the underscore, you must surround it
|
|
15
15
|
# with curly brackets: `:{http.request-id}`.
|
|
16
16
|
class Template
|
|
17
|
-
TEMPLATE_ARGUMENT_ORDER = %w
|
|
17
|
+
TEMPLATE_ARGUMENT_ORDER = %w[:time :severity :progname :pid :message :tags].freeze
|
|
18
18
|
MILLISECOND_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%3N"
|
|
19
19
|
MICROSECOND_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%6N"
|
|
20
20
|
PLACEHOLDER_PATTERN = /:(([a-z0-9_]+)|({[^}]+}))/i.freeze
|
|
@@ -30,6 +30,9 @@ module Lumberjack
|
|
|
30
30
|
# specified precision.
|
|
31
31
|
#
|
|
32
32
|
# Messages will have white space stripped from both ends.
|
|
33
|
+
#
|
|
34
|
+
# @param [String] first_line The template to use to format the first line of a message.
|
|
35
|
+
# @param [Hash] options The options for the template.
|
|
33
36
|
def initialize(first_line, options = {})
|
|
34
37
|
@first_line_template, @first_line_tags = compile(first_line)
|
|
35
38
|
additional_lines = options[:additional_lines] || "#{Lumberjack::LINE_SEPARATOR}:message"
|
|
@@ -39,6 +42,9 @@ module Lumberjack
|
|
|
39
42
|
self.datetime_format = (options[:time_format] || :milliseconds)
|
|
40
43
|
end
|
|
41
44
|
|
|
45
|
+
# Set the format used to format the time.
|
|
46
|
+
#
|
|
47
|
+
# @param [String] format The format to use to format the time.
|
|
42
48
|
def datetime_format=(format)
|
|
43
49
|
if format == :milliseconds
|
|
44
50
|
format = MILLISECOND_TIME_FORMAT
|
|
@@ -48,11 +54,17 @@ module Lumberjack
|
|
|
48
54
|
@time_formatter = Formatter::DateTimeFormatter.new(format)
|
|
49
55
|
end
|
|
50
56
|
|
|
57
|
+
# Get the format used to format the time.
|
|
58
|
+
#
|
|
59
|
+
# @return [String]
|
|
51
60
|
def datetime_format
|
|
52
61
|
@time_formatter.format
|
|
53
62
|
end
|
|
54
63
|
|
|
55
64
|
# Convert an entry into a string using the template.
|
|
65
|
+
#
|
|
66
|
+
# @param [Lumberjack::LogEntry] entry The entry to convert to a string.
|
|
67
|
+
# @return [String] The entry converted to a string.
|
|
56
68
|
def call(entry)
|
|
57
69
|
return entry unless entry.is_a?(LogEntry)
|
|
58
70
|
|
|
@@ -86,8 +98,8 @@ module Lumberjack
|
|
|
86
98
|
def tag_args(tags, tag_vars)
|
|
87
99
|
return [nil] * (tag_vars.size + 1) if tags.nil? || tags.size == 0
|
|
88
100
|
|
|
89
|
-
tags_string =
|
|
90
|
-
tags.each do |name, value|
|
|
101
|
+
tags_string = +""
|
|
102
|
+
Lumberjack::Utils.flatten_tags(tags).each do |name, value|
|
|
91
103
|
unless value.nil? || tag_vars.include?(name)
|
|
92
104
|
value = value.to_s
|
|
93
105
|
value = value.gsub(Lumberjack::LINE_SEPARATOR, " ") if value.include?(Lumberjack::LINE_SEPARATOR)
|
|
@@ -103,7 +115,7 @@ module Lumberjack
|
|
|
103
115
|
end
|
|
104
116
|
|
|
105
117
|
# Compile the template string into a value that can be used with sprintf.
|
|
106
|
-
def compile(template)
|
|
118
|
+
def compile(template) # :nodoc:
|
|
107
119
|
tag_vars = []
|
|
108
120
|
template = template.gsub(PLACEHOLDER_PATTERN) do |match|
|
|
109
121
|
var_name = match.sub("{", "").sub("}", "")
|