lumberjack 1.2.9 → 1.3.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/ARCHITECTURE.md +244 -0
  3. data/CHANGELOG.md +78 -2
  4. data/README.md +176 -58
  5. data/VERSION +1 -1
  6. data/lib/lumberjack/context.rb +5 -5
  7. data/lib/lumberjack/device/date_rolling_log_file.rb +1 -1
  8. data/lib/lumberjack/device/log_file.rb +1 -1
  9. data/lib/lumberjack/device/multi.rb +1 -1
  10. data/lib/lumberjack/device/null.rb +1 -1
  11. data/lib/lumberjack/device/rolling_log_file.rb +2 -2
  12. data/lib/lumberjack/device/size_rolling_log_file.rb +1 -1
  13. data/lib/lumberjack/device/writer.rb +13 -9
  14. data/lib/lumberjack/device.rb +1 -1
  15. data/lib/lumberjack/formatter/date_time_formatter.rb +2 -2
  16. data/lib/lumberjack/formatter/exception_formatter.rb +2 -2
  17. data/lib/lumberjack/formatter/id_formatter.rb +1 -1
  18. data/lib/lumberjack/formatter/inspect_formatter.rb +1 -1
  19. data/lib/lumberjack/formatter/multiply_formatter.rb +25 -0
  20. data/lib/lumberjack/formatter/object_formatter.rb +1 -1
  21. data/lib/lumberjack/formatter/pretty_print_formatter.rb +1 -1
  22. data/lib/lumberjack/formatter/redact_formatter.rb +23 -0
  23. data/lib/lumberjack/formatter/round_formatter.rb +21 -0
  24. data/lib/lumberjack/formatter/string_formatter.rb +1 -1
  25. data/lib/lumberjack/formatter/strip_formatter.rb +1 -1
  26. data/lib/lumberjack/formatter/structured_formatter.rb +1 -1
  27. data/lib/lumberjack/formatter/tagged_message.rb +39 -0
  28. data/lib/lumberjack/formatter/truncate_formatter.rb +1 -1
  29. data/lib/lumberjack/formatter.rb +29 -14
  30. data/lib/lumberjack/log_entry.rb +25 -15
  31. data/lib/lumberjack/logger.rb +132 -40
  32. data/lib/lumberjack/rack/context.rb +21 -2
  33. data/lib/lumberjack/rack/request_id.rb +7 -3
  34. data/lib/lumberjack/rack/unit_of_work.rb +6 -2
  35. data/lib/lumberjack/rack.rb +1 -1
  36. data/lib/lumberjack/severity.rb +13 -1
  37. data/lib/lumberjack/tag_formatter.rb +102 -27
  38. data/lib/lumberjack/tagged_logger_support.rb +6 -1
  39. data/lib/lumberjack/tags.rb +1 -7
  40. data/lib/lumberjack/template.rb +3 -3
  41. data/lib/lumberjack/utils.rb +133 -0
  42. data/lib/lumberjack.rb +12 -6
  43. data/lumberjack.gemspec +2 -2
  44. metadata +12 -6
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
4
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lumberjack
4
+ class Formatter
5
+ # This class can be used as the return value from a formatter `call` method to
6
+ # extract additional tags from an object being logged. This can be useful when there
7
+ # using structured logging to include important metadata in the log message.
8
+ #
9
+ # @example
10
+ # # Automatically add tags with error details when logging an exception.
11
+ # logger.add_formatter(Exception, ->(e) {
12
+ # Lumberjack::Formatter::TaggedMessage.new(e.message, {
13
+ # error: {
14
+ # message: e.message,
15
+ # class: e.class.name,
16
+ # trace: e.backtrace
17
+ # }
18
+ # })
19
+ # })
20
+ class TaggedMessage
21
+ attr_reader :message, :tags
22
+
23
+ # @param [Formatter] formatter The formatter to apply the transformation to.
24
+ # @param [Proc] transform The transformation function to apply to the formatted string.
25
+ def initialize(message, tags)
26
+ @message = message
27
+ @tags = tags || {}
28
+ end
29
+
30
+ def to_s
31
+ inspect
32
+ end
33
+
34
+ def inspect
35
+ {message: @message, tags: @tags}.inspect
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  # This class controls the conversion of log entry messages into a loggable format. This allows you
@@ -16,12 +16,16 @@ module Lumberjack
16
16
  require_relative "formatter/exception_formatter"
17
17
  require_relative "formatter/id_formatter"
18
18
  require_relative "formatter/inspect_formatter"
19
+ require_relative "formatter/multiply_formatter"
19
20
  require_relative "formatter/object_formatter"
20
21
  require_relative "formatter/pretty_print_formatter"
22
+ require_relative "formatter/redact_formatter"
23
+ require_relative "formatter/round_formatter"
21
24
  require_relative "formatter/string_formatter"
22
25
  require_relative "formatter/strip_formatter"
23
26
  require_relative "formatter/structured_formatter"
24
27
  require_relative "formatter/truncate_formatter"
28
+ require_relative "formatter/tagged_message"
25
29
 
26
30
  class << self
27
31
  # Returns a new empty formatter with no mapping. For historical reasons, a formatter
@@ -66,12 +70,12 @@ module Lumberjack
66
70
  # help avoid loading dependency issues. This applies only to classes; modules cannot be
67
71
  # passed in as strings.
68
72
  #
69
- # @param [Class, Module, String, Array<Class, Module, String>] klass The class or module to add a formatter for.
70
- # @param [Symbol, Class, String, #call] formatter The formatter to use for the class.
73
+ # @param klass [Class, Module, String, Array<Class, Module, String>] The class or module to add a formatter for.
74
+ # @param formatter [Symbol, Class, String, #call] The formatter to use for the class.
71
75
  # If a symbol is passed in, it will be used to load one of the predefined formatters.
72
76
  # If a class is passed in, it will be initialized with the args passed in.
73
77
  # Otherwise, the object will be used as the formatter and must respond to call method.
74
- # @param [Array] args Arguments to pass to the formatter when it is initialized.
78
+ # @param args [Array] Arguments to pass to the formatter when it is initialized.
75
79
  # @yield [obj] A block that will be used as the formatter for the class.
76
80
  # @yieldparam [Object] obj The object to format.
77
81
  # @yieldreturn [String] The formatted string.
@@ -129,7 +133,7 @@ module Lumberjack
129
133
  # help avoid loading dependency issues. This applies only to classes; modules cannot be
130
134
  # passed in as strings.
131
135
  #
132
- # @param [Class, Module, String, Array<Class, Module, String>] klass The class or module to remove the formatters for.
136
+ # @param klass [Class, Module, String, Array<Class, Module, String>] The class or module to remove the formatters for.
133
137
  # @return [self] Returns itself so that remove statements can be chained together.
134
138
  def remove(klass)
135
139
  Array(klass).each do |k|
@@ -152,9 +156,16 @@ module Lumberjack
152
156
  self
153
157
  end
154
158
 
159
+ # Return true if their are no registered formatters.
160
+ #
161
+ # @return [Boolean] true if there are no registered formatters, false otherwise.
162
+ def empty?
163
+ @class_formatters.empty? && @module_formatters.empty?
164
+ end
165
+
155
166
  # Format a message object by applying all formatters attached to it.
156
167
  #
157
- # @param [Object] message The message object to format.
168
+ # @param message [Object] The message object to format.
158
169
  # @return [Object] The formatted object.
159
170
  def format(message)
160
171
  formatter = formatter_for(message.class)
@@ -168,18 +179,22 @@ module Lumberjack
168
179
  # Compatibility with the Logger::Formatter signature. This method will just convert the message
169
180
  # object to a string and ignores the other parameters.
170
181
  #
171
- # @param [Integer, String, Symbol] severity The severity of the message.
172
- # @param [Time] timestamp The time the message was logged.
173
- # @param [String] progname The name of the program logging the message.
174
- # @param [Object] msg The message object to format.
182
+ # @param severity [Integer, String, Symbol] The severity of the message.
183
+ # @param timestamp [Time] The time the message was logged.
184
+ # @param progname [String] The name of the program logging the message.
185
+ # @param msg [Object] The message object to format.
175
186
  def call(severity, timestamp, progname, msg)
176
- "#{format(msg)}#{Lumberjack::LINE_SEPARATOR}"
187
+ formatted_message = format(msg)
188
+ formatted_message = formatted_message.message if formatted_message.is_a?(TaggedMessage)
189
+ "#{formatted_message}#{Lumberjack::LINE_SEPARATOR}"
177
190
  end
178
191
 
179
- private
180
-
181
192
  # Find the formatter for a class by looking it up using the class hierarchy.
182
- def formatter_for(klass) # :nodoc:
193
+ #
194
+ # @api private
195
+ def formatter_for(klass)
196
+ return nil if empty?
197
+
183
198
  check_modules = true
184
199
  until klass.nil?
185
200
  formatter = @class_formatters[klass.name]
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  # An entry in a log is a data structure that captures the log message as well as
@@ -8,16 +8,17 @@ module Lumberjack
8
8
 
9
9
  TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
10
10
 
11
+ # @deprecated Will be removed in version 2.0.
11
12
  UNIT_OF_WORK_ID = "unit_of_work_id"
12
13
 
13
14
  # Create a new log entry.
14
15
  #
15
- # @param [Time] time The time the log entry was created.
16
- # @param [Integer, String] severity The severity of the log entry.
17
- # @param [String] message The message to log.
18
- # @param [String] progname The name of the program that created the log entry.
19
- # @param [Integer] pid The process id of the program that created the log entry.
20
- # @param [Hash] tags A hash of tags to associate with the log entry.
16
+ # @param time [Time] The time the log entry was created.
17
+ # @param severity [Integer, String] The severity of the log entry.
18
+ # @param message [String] The message to log.
19
+ # @param progname [String] The name of the program that created the log entry.
20
+ # @param pid [Integer] The process id of the program that created the log entry.
21
+ # @param tags [Hash<String, Object>] A hash of tags to associate with the log entry.
21
22
  def initialize(time, severity, message, progname, pid, tags)
22
23
  @time = time
23
24
  @severity = (severity.is_a?(Integer) ? severity : Severity.label_to_level(severity))
@@ -44,17 +45,21 @@ module Lumberjack
44
45
  to_s
45
46
  end
46
47
 
47
- # Deprecated - backward compatibility with 1.0 API
48
+ # @deprecated - backward compatibility with 1.0 API. Will be removed in version 2.0.
48
49
  def unit_of_work_id
49
- tags[UNIT_OF_WORK_ID] if tags
50
+ Lumberjack::Utils.deprecated("Lumberjack::LogEntry#unit_of_work_id", "Lumberjack::LogEntry#unit_of_work_id will be removed in version 2.0") do
51
+ tags[UNIT_OF_WORK_ID] if tags
52
+ end
50
53
  end
51
54
 
52
- # Deprecated - backward compatibility with 1.0 API
55
+ # @deprecated - backward compatibility with 1.0 API. Will be removed in version 2.0.
53
56
  def unit_of_work_id=(value)
54
- if tags
55
- tags[UNIT_OF_WORK_ID] = value
56
- else
57
- @tags = {UNIT_OF_WORK_ID => value}
57
+ Lumberjack::Utils.deprecated("Lumberjack::LogEntry#unit_of_work_id=", "Lumberjack::LogEntry#unit_of_work_id= will be removed in version 2.0") do
58
+ if tags
59
+ tags[UNIT_OF_WORK_ID] = value
60
+ else
61
+ @tags = {UNIT_OF_WORK_ID => value}
62
+ end
58
63
  end
59
64
  end
60
65
 
@@ -63,10 +68,15 @@ module Lumberjack
63
68
  tags[name.to_s] if tags
64
69
  end
65
70
 
71
+ # Return true if the log entry has no message and no tags.
72
+ def empty?
73
+ (message.nil? || message == "") && (tags.nil? || tags.empty?)
74
+ end
75
+
66
76
  private
67
77
 
68
78
  def tags_to_s
69
- tags_string = ""
79
+ tags_string = +""
70
80
  tags&.each { |name, value| tags_string << " #{name}:#{value.inspect}" }
71
81
  tags_string
72
82
  end
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  # Logger is a thread safe logging object. It has a compatible API with the Ruby
@@ -21,7 +21,7 @@ module Lumberjack
21
21
  # monitoring thread, but its use is highly recommended.
22
22
  #
23
23
  # Each log entry records the log message and severity along with the time it was logged, the
24
- # program name, process id, and unit of work id. The message will be converted to a string, but
24
+ # program name, process id, and an optional hash of tags. The message will be converted to a string, but
25
25
  # otherwise, it is up to the device how these values are recorded. Messages are converted to strings
26
26
  # using a Formatter associated with the logger.
27
27
  class Logger
@@ -36,8 +36,8 @@ module Lumberjack
36
36
  # Set the name of the program to attach to log entries.
37
37
  attr_writer :progname
38
38
 
39
- # The device being written to
40
- attr_accessor :device
39
+ # The Formatter used only for log entry messages.
40
+ attr_accessor :message_formatter
41
41
 
42
42
  # The TagFormatter used for formatting tags for output
43
43
  attr_accessor :tag_formatter
@@ -51,31 +51,30 @@ module Lumberjack
51
51
  # If it is :null, it will be a Null device that won't record any output.
52
52
  # Otherwise, it will be assumed to be file path and wrapped in a Device::LogFile class.
53
53
  #
54
- # This method can take the following options:
55
- #
56
- # * :level - The logging level below which messages will be ignored.
57
- # * :formatter - The formatter to use for outputting messages to the log.
58
- # * :datetime_format - The format to use for log timestamps.
59
- # * :tag_formatter - The TagFormatter to use for formatting tags.
60
- # * :progname - The name of the program that will be recorded with each log entry.
61
- # * :flush_seconds - The maximum number of seconds between flush calls.
62
- # * :roll - If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.
63
- # * :max_size - If the log device is a file path, it will be a Device::SizeRollingLogFile if this is set.
64
- #
65
54
  # All other options are passed to the device constuctor.
66
55
  #
67
56
  # @param [Lumberjack::Device, Object, Symbol, String] device The device to log to.
68
57
  # @param [Hash] options The options for the logger.
58
+ # @option options [Integer, Symbol, String] :level The logging level below which messages will be ignored.
59
+ # @option options [Lumberjack::Formatter] :formatter The formatter to use for outputting messages to the log.
60
+ # @option options [String] :datetime_format The format to use for log timestamps.
61
+ # @option options [Lumberjack::Formatter] :message_formatter The MessageFormatter to use for formatting log messages.
62
+ # @option options [Lumberjack::TagFormatter] :tag_formatter The TagFormatter to use for formatting tags.
63
+ # @option options [String] :progname The name of the program that will be recorded with each log entry.
64
+ # @option options [Numeric] :flush_seconds The maximum number of seconds between flush calls.
65
+ # @option options [Boolean] :roll If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.
66
+ # @option options [Integer] :max_size If the log device is a file path, it will be a Device::SizeRollingLogFile if this is set.
69
67
  def initialize(device = $stdout, options = {})
70
68
  options = options.dup
71
69
  self.level = options.delete(:level) || INFO
72
70
  self.progname = options.delete(:progname)
73
71
  max_flush_seconds = options.delete(:flush_seconds).to_f
74
72
 
75
- @device = open_device(device, options) if device
73
+ @logdev = open_device(device, options) if device
76
74
  self.formatter = (options[:formatter] || Formatter.new)
77
- @tag_formatter = (options[:tag_formatter] || TagFormatter.new)
78
- time_format = (options[:datetime_format] || options[:time_format])
75
+ @message_formatter = options[:message_formatter] || Formatter.empty
76
+ @tag_formatter = options[:tag_formatter] || TagFormatter.new
77
+ time_format = options[:datetime_format] || options[:time_format]
79
78
  self.datetime_format = time_format if time_format
80
79
  @last_flushed_at = Time.now
81
80
  @silencer = true
@@ -85,6 +84,23 @@ module Lumberjack
85
84
  create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
86
85
  end
87
86
 
87
+ # Get the logging device that is used to write log entries.
88
+ #
89
+ # @return [Lumberjack::Device] The logging device.
90
+ def device
91
+ @logdev
92
+ end
93
+
94
+ # Set the logging device to a new device.
95
+ #
96
+ # @param [Lumberjack::Device] device The new logging device.
97
+ # @return [void]
98
+ def device=(device)
99
+ @logdev = if device
100
+ open_device(device, options)
101
+ end
102
+ end
103
+
88
104
  # Get the timestamp format on the device if it has one.
89
105
  #
90
106
  # @return [String, nil] The timestamp format or nil if the device doesn't support it.
@@ -118,15 +134,19 @@ module Lumberjack
118
134
  # @param [Integer, Symbol, String] value The severity level.
119
135
  # @return [void]
120
136
  def level=(value)
121
- @level = if value.is_a?(Integer)
122
- value
123
- else
124
- Severity.label_to_level(value)
125
- end
137
+ @level = Severity.coerce(value)
126
138
  end
127
139
 
128
140
  alias_method :sev_threshold=, :level=
129
141
 
142
+ # Adjust the log level during the block execution for the current Fiber only.
143
+ #
144
+ # @param [Integer, Symbol, String] severity The severity level.
145
+ # @return [Object] The result of the block.
146
+ def with_level(severity, &block)
147
+ push_thread_local_value(:lumberjack_logger_level, Severity.coerce(severity), &block)
148
+ end
149
+
130
150
  # Set the Lumberjack::Formatter used to format objects for logging as messages.
131
151
  #
132
152
  # @param [Lumberjack::Formatter, Object] value The formatter to use.
@@ -190,25 +210,30 @@ module Lumberjack
190
210
  Thread.current[:lumberjack_logging] = true
191
211
 
192
212
  time = Time.now
213
+
193
214
  message = message.call if message.is_a?(Proc)
194
- message = formatter.format(message)
215
+ msg_class_formatter = message_formatter&.formatter_for(message.class)
216
+ if msg_class_formatter
217
+ message = msg_class_formatter.call(message)
218
+ elsif formatter
219
+ message = formatter.format(message)
220
+ end
221
+ message_tags = nil
222
+ if message.is_a?(Formatter::TaggedMessage)
223
+ message_tags = message.tags
224
+ message = message.message
225
+ end
226
+
195
227
  progname ||= self.progname
196
228
 
197
229
  current_tags = self.tags
198
230
  tags = nil unless tags.is_a?(Hash)
199
- if current_tags.empty?
200
- tags = Tags.stringify_keys(tags) unless tags.nil?
201
- else
202
- tags = if tags.nil?
203
- current_tags.dup
204
- else
205
- current_tags.merge(Tags.stringify_keys(tags))
206
- end
207
- end
231
+ tags = merge_tags(current_tags, tags)
232
+ tags = merge_tags(tags, message_tags) if message_tags
208
233
  tags = Tags.expand_runtime_values(tags)
209
234
  tags = tag_formatter.format(tags) if tag_formatter
210
235
 
211
- entry = LogEntry.new(time, severity, message, progname, $$, tags)
236
+ entry = LogEntry.new(time, severity, message, progname, Process.pid, tags)
212
237
  write_to_device(entry)
213
238
  ensure
214
239
  Thread.current[:lumberjack_logging] = nil
@@ -437,6 +462,14 @@ module Lumberjack
437
462
  end
438
463
  end
439
464
 
465
+ # Provided for compatibility with ActiveSupport::LoggerThreadSafeLevel to temporarily set the log level.
466
+ #
467
+ # @param [Integer, String, Symbol] level The log level to use inside the block.
468
+ # @return [Object] The result of the block.
469
+ def log_at(level, &block)
470
+ silence(level, &block)
471
+ end
472
+
440
473
  # Set the program name that is associated with log messages. If a block
441
474
  # is given, the program name will be valid only within the block.
442
475
  #
@@ -458,10 +491,11 @@ module Lumberjack
458
491
  end
459
492
 
460
493
  # Set a hash of tags on logger. If a block is given, the tags will only be set
461
- # for the duration of the block. If this method is called inside such a block,
462
- # the tags will only be defined on the tags in that block. When the parent block
463
- # exits, all the tags will be reverted. If there is no block, then the tags will
464
- # be defined as global and apply to all log statements.
494
+ # for the duration of the block. Otherwise the tags will be applied on the current
495
+ # logger context for the duration of that context.
496
+ #
497
+ # If there is no block or context, the tags will be applied to the global context.
498
+ # This behavior is deprecated. Use the `tag_globally` method to set global tags instead.
465
499
  #
466
500
  # @param [Hash] tags The tags to set.
467
501
  # @return [void]
@@ -475,11 +509,22 @@ module Lumberjack
475
509
  thread_tags.merge!(tags)
476
510
  nil
477
511
  else
478
- @tags.merge!(tags)
479
- nil
512
+ Lumberjack::Utils.deprecated("Lumberjack::Logger#tag", "Lumberjack::Logger#tag must be called with a block or inside a context block. In version 2.0 it will no longer be used for setting global tags. Use Lumberjack::Logger#tag_globally instead.") do
513
+ tag_globally(tags)
514
+ end
480
515
  end
481
516
  end
482
517
 
518
+ # Add global tags to the logger that will appear on all log entries.
519
+ #
520
+ # @param [Hash] tags The tags to set.
521
+ # @return [void]
522
+ def tag_globally(tags)
523
+ tags = Tags.stringify_keys(tags)
524
+ @tags.merge!(tags)
525
+ nil
526
+ end
527
+
483
528
  # Remove a tag from the current tag context. If this is called inside a block to a
484
529
  # call to `tag`, the tags will only be removed for the duration of that block. Otherwise
485
530
  # they will be removed from the global tags.
@@ -509,6 +554,31 @@ module Lumberjack
509
554
  tags
510
555
  end
511
556
 
557
+ # Get the value of a tag by name from the current tag context.
558
+ #
559
+ # @param [String, Symbol] name The name of the tag to get.
560
+ # @return [Object, nil] The value of the tag or nil if the tag does not exist.
561
+ def tag_value(name)
562
+ all_tags = tags
563
+ return nil if tags.empty?
564
+
565
+ name = name.join(".") if name.is_a?(Array)
566
+ name = name.to_s
567
+ return all_tags[name] if all_tags.include?(name)
568
+
569
+ flattened_tags = Lumberjack::Utils.flatten_tags(all_tags)
570
+ return flattened_tags[name] if flattened_tags.include?(name)
571
+
572
+ flattened_tags.keys.select { |key| key.include?(".") }.each do |key|
573
+ parts = key.split(".")
574
+ while (subkey = parts.pop)
575
+ flattened_tags[parts.join(".")] = {subkey => flattened_tags[(parts + [subkey]).join(".")]}
576
+ end
577
+ end
578
+
579
+ flattened_tags[name]
580
+ end
581
+
512
582
  # Remove all tags on the current logger and logging context within a block.
513
583
  # You can still set new block scoped tags within theuntagged block and provide
514
584
  # tags on individual log methods.
@@ -529,6 +599,14 @@ module Lumberjack
529
599
  end
530
600
  end
531
601
 
602
+ # Return true if the thread is currently in a Lumberjack::Context block.
603
+ # When the logger is in a context block, tagging will only apply to that block.
604
+ #
605
+ # @return [Boolean]
606
+ def in_tag_context?
607
+ !!thread_local_value(:lumberjack_logger_tags)
608
+ end
609
+
532
610
  private
533
611
 
534
612
  # Dereference arguments to log calls so we can have methods with compatibility with ::Logger
@@ -556,6 +634,20 @@ module Lumberjack
556
634
  add_entry(severity, message, progname, tags)
557
635
  end
558
636
 
637
+ # Merge a tags hash into an existing tags hash.
638
+ def merge_tags(current_tags, tags)
639
+ if current_tags.nil? || current_tags.empty?
640
+ tags = Tags.stringify_keys(tags) unless tags.nil?
641
+ else
642
+ tags = if tags.nil?
643
+ current_tags.dup
644
+ else
645
+ current_tags.merge(Tags.stringify_keys(tags))
646
+ end
647
+ end
648
+ tags
649
+ end
650
+
559
651
  # Set a local value for a thread tied to this object.
560
652
  def set_thread_local_value(name, value) # :nodoc:
561
653
  values = Thread.current[name]
@@ -1,18 +1,37 @@
1
- # frozen_string_literals: true
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
- def initialize(app)
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,16 +1,20 @@
1
- # frozen_string_literals: true
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
- @app = app
13
- @abbreviated = abbreviated
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)
@@ -1,10 +1,14 @@
1
- # frozen_string_literals: true
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
- @app = app
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)
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  module Rack
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  # The standard severity levels for logging messages.
@@ -29,6 +29,18 @@ module Lumberjack
29
29
  def label_to_level(label)
30
30
  SEVERITY_LABELS.index(label.to_s.upcase) || UNKNOWN
31
31
  end
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
32
44
  end
33
45
  end
34
46
  end