lumberjack 1.4.2 → 2.0.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/ARCHITECTURE.md +524 -176
  3. data/CHANGELOG.md +89 -0
  4. data/README.md +604 -211
  5. data/UPGRADE_GUIDE.md +80 -0
  6. data/VERSION +1 -1
  7. data/lib/lumberjack/attribute_formatter.rb +451 -0
  8. data/lib/lumberjack/attributes_helper.rb +100 -0
  9. data/lib/lumberjack/context.rb +120 -23
  10. data/lib/lumberjack/context_logger.rb +620 -0
  11. data/lib/lumberjack/device/buffer.rb +209 -0
  12. data/lib/lumberjack/device/date_rolling_log_file.rb +10 -62
  13. data/lib/lumberjack/device/log_file.rb +76 -29
  14. data/lib/lumberjack/device/logger_wrapper.rb +137 -0
  15. data/lib/lumberjack/device/multi.rb +92 -30
  16. data/lib/lumberjack/device/null.rb +26 -8
  17. data/lib/lumberjack/device/size_rolling_log_file.rb +13 -54
  18. data/lib/lumberjack/device/test.rb +337 -0
  19. data/lib/lumberjack/device/writer.rb +184 -176
  20. data/lib/lumberjack/device.rb +134 -15
  21. data/lib/lumberjack/device_registry.rb +90 -0
  22. data/lib/lumberjack/entry_formatter.rb +357 -0
  23. data/lib/lumberjack/fiber_locals.rb +55 -0
  24. data/lib/lumberjack/forked_logger.rb +143 -0
  25. data/lib/lumberjack/formatter/date_time_formatter.rb +14 -3
  26. data/lib/lumberjack/formatter/exception_formatter.rb +12 -2
  27. data/lib/lumberjack/formatter/id_formatter.rb +13 -1
  28. data/lib/lumberjack/formatter/inspect_formatter.rb +14 -1
  29. data/lib/lumberjack/formatter/multiply_formatter.rb +10 -0
  30. data/lib/lumberjack/formatter/object_formatter.rb +13 -1
  31. data/lib/lumberjack/formatter/pretty_print_formatter.rb +15 -2
  32. data/lib/lumberjack/formatter/redact_formatter.rb +18 -3
  33. data/lib/lumberjack/formatter/round_formatter.rb +12 -0
  34. data/lib/lumberjack/formatter/string_formatter.rb +9 -1
  35. data/lib/lumberjack/formatter/strip_formatter.rb +13 -1
  36. data/lib/lumberjack/formatter/structured_formatter.rb +18 -2
  37. data/lib/lumberjack/formatter/tagged_message.rb +10 -32
  38. data/lib/lumberjack/formatter/tags_formatter.rb +32 -0
  39. data/lib/lumberjack/formatter/truncate_formatter.rb +8 -1
  40. data/lib/lumberjack/formatter.rb +271 -141
  41. data/lib/lumberjack/formatter_registry.rb +84 -0
  42. data/lib/lumberjack/io_compatibility.rb +133 -0
  43. data/lib/lumberjack/local_log_template.rb +209 -0
  44. data/lib/lumberjack/log_entry.rb +154 -79
  45. data/lib/lumberjack/log_entry_matcher/score.rb +276 -0
  46. data/lib/lumberjack/log_entry_matcher.rb +126 -0
  47. data/lib/lumberjack/logger.rb +328 -556
  48. data/lib/lumberjack/message_attributes.rb +38 -0
  49. data/lib/lumberjack/rack/context.rb +66 -15
  50. data/lib/lumberjack/rack.rb +0 -2
  51. data/lib/lumberjack/remap_attribute.rb +24 -0
  52. data/lib/lumberjack/severity.rb +52 -15
  53. data/lib/lumberjack/tag_context.rb +8 -71
  54. data/lib/lumberjack/tag_formatter.rb +22 -188
  55. data/lib/lumberjack/tags.rb +15 -21
  56. data/lib/lumberjack/template.rb +252 -62
  57. data/lib/lumberjack/template_registry.rb +60 -0
  58. data/lib/lumberjack/utils.rb +198 -48
  59. data/lib/lumberjack.rb +167 -59
  60. data/lumberjack.gemspec +4 -2
  61. metadata +41 -15
  62. data/lib/lumberjack/device/rolling_log_file.rb +0 -145
  63. data/lib/lumberjack/rack/request_id.rb +0 -31
  64. data/lib/lumberjack/rack/unit_of_work.rb +0 -21
  65. data/lib/lumberjack/tagged_logger_support.rb +0 -81
  66. data/lib/lumberjack/tagged_logging.rb +0 -29
@@ -4,7 +4,12 @@ module Lumberjack
4
4
  class Formatter
5
5
  # This formatter can be used to multiply a numeric value by a specified multiplier and
6
6
  # optionally round to a specified number of decimal places.
7
+ #
8
+ # This is useful for unit conversions (e.g., converting seconds to milliseconds)
9
+ # or scaling values for display purposes. Non-numeric values are passed through unchanged.
7
10
  class MultiplyFormatter
11
+ FormatterRegistry.add(:multiply, self)
12
+
8
13
  # @param multiplier [Numeric] The multiplier to apply to the value.
9
14
  # @param decimals [Integer, nil] The number of decimal places to round the result to.
10
15
  # If nil, no rounding is applied.
@@ -13,6 +18,11 @@ module Lumberjack
13
18
  @decimals = decimals
14
19
  end
15
20
 
21
+ # Multiply a numeric value by the configured multiplier and optionally round.
22
+ #
23
+ # @param value [Object] The value to format. Only numeric values are processed.
24
+ # @return [Numeric, Object] The multiplied (and optionally rounded) value if numeric,
25
+ # otherwise returns the original value unchanged.
16
26
  def call(value)
17
27
  return value unless value.is_a?(Numeric)
18
28
 
@@ -2,8 +2,20 @@
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
5
- # No-op formatter that just returns the object itself.
5
+ # No-op formatter that returns the object unchanged. This formatter is useful
6
+ # as a default or fallback formatter when you want to preserve the original
7
+ # object without any transformation.
8
+ #
9
+ # The ObjectFormatter is commonly used in scenarios where you want to maintain
10
+ # the original data structure and let downstream components handle the actual
11
+ # formatting, or when you need a placeholder formatter in the formatter chain.
6
12
  class ObjectFormatter
13
+ FormatterRegistry.add(:object, self)
14
+
15
+ # Return the object unchanged.
16
+ #
17
+ # @param obj [Object] The object to format.
18
+ # @return [Object] The original object without any modifications.
7
19
  def call(obj)
8
20
  obj
9
21
  end
@@ -5,18 +5,31 @@ require "stringio"
5
5
 
6
6
  module Lumberjack
7
7
  class Formatter
8
- # Format an object with it's pretty print method.
8
+ # Format an object with its pretty print method. This formatter provides multi-line,
9
+ # indented output that makes complex data structures easier to read and debug.
10
+ # It's particularly useful for logging hashes, arrays, and other nested objects.
11
+ #
12
+ # The formatter uses Ruby's built-in PP (Pretty Print) library to generate
13
+ # well-formatted output with appropriate indentation and line breaks.
9
14
  class PrettyPrintFormatter
15
+ FormatterRegistry.add(:pretty_print, self)
16
+
17
+ # @!attribute [rw] width
18
+ # @return [Integer] The maximum width of the message.
10
19
  attr_accessor :width
11
20
 
12
21
  # Create a new formatter. The maximum width of the message can be specified with the width
13
22
  # parameter (defaults to 79 characters).
14
23
  #
15
- # @param [Integer] width The maximum width of the message.
24
+ # @param width [Integer] The maximum width of the message.
16
25
  def initialize(width = 79)
17
26
  @width = width
18
27
  end
19
28
 
29
+ # Format an object using pretty print with the configured width.
30
+ #
31
+ # @param obj [Object] The object to format.
32
+ # @return [String] The pretty-printed representation of the object.
20
33
  def call(obj)
21
34
  s = StringIO.new
22
35
  PP.pp(obj, s)
@@ -2,16 +2,31 @@
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
5
- # Log sensitive information in a redacted format showing the firat and last
5
+ # Log sensitive information in a redacted format showing the first and last
6
6
  # characters of the value, with the rest replaced by asterisks. The number of
7
- # characters shown is dependent onthe length of the value; short values will
7
+ # characters shown is dependent on the length of the value; short values will
8
8
  # not show any characters in order to avoid revealing too much information.
9
+ #
10
+ # This formatter is useful for logging sensitive data while still providing
11
+ # enough context to distinguish between different values during debugging.
9
12
  class RedactFormatter
13
+ FormatterRegistry.add(:redact, self)
14
+
15
+ # Redact a string value by showing only the first and last characters.
16
+ #
17
+ # @param obj [Object] The object to format. Only strings are redacted.
18
+ # @return [String, Object] The redacted string if the object is a string,
19
+ # otherwise returns the object unchanged.
20
+ #
21
+ # @example Different string lengths
22
+ # formatter.call("password123") # => "pa*******23"
23
+ # formatter.call("secret") # => "s****t"
24
+ # formatter.call("abc") # => "*****"
10
25
  def call(obj)
11
26
  return obj unless obj.is_a?(String)
12
27
 
13
28
  if obj.length > 8
14
- "#{obj[0..1]}#{"*" * (obj.length - 4)}#{obj[-2..-1]}"
29
+ "#{obj[0..1]}#{"*" * (obj.length - 4)}#{obj[-2..]}"
15
30
  elsif obj.length > 5
16
31
  "#{obj[0]}#{"*" * (obj.length - 2)}#{obj[-1]}"
17
32
  else
@@ -4,11 +4,23 @@ module Lumberjack
4
4
  class Formatter
5
5
  # Round numeric values to a set number of decimal places. This is useful when logging
6
6
  # floating point numbers to reduce noise and rounding errors in the logs.
7
+ #
8
+ # The formatter only affects numeric values, leaving other object types unchanged.
9
+ # This makes it safe to use as a general-purpose formatter for attributes that
10
+ # might contain various data types.
7
11
  class RoundFormatter
12
+ FormatterRegistry.add(:round, self)
13
+
14
+ # @param precision [Integer] The number of decimal places to round to (defaults to 3).
8
15
  def initialize(precision = 3)
9
16
  @precision = precision
10
17
  end
11
18
 
19
+ # Round a numeric value to the configured precision.
20
+ #
21
+ # @param obj [Object] The object to format. Only numeric values are rounded.
22
+ # @return [Numeric, Object] The rounded number if the object is numeric,
23
+ # otherwise returns the object unchanged.
12
24
  def call(obj)
13
25
  if obj.is_a?(Numeric)
14
26
  obj.round(@precision)
@@ -2,8 +2,16 @@
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
5
- # Format an object by calling `to_s` on it.
5
+ # Format an object by calling +to_s+ on it. This is the simplest formatter
6
+ # implementation and is commonly used as a fallback for objects that don't
7
+ # have specialized formatters.
6
8
  class StringFormatter
9
+ FormatterRegistry.add(:string, self)
10
+
11
+ # Convert an object to its string representation.
12
+ #
13
+ # @param obj [Object] The object to format.
14
+ # @return [String] The string representation of the object.
7
15
  def call(obj)
8
16
  obj.to_s
9
17
  end
@@ -2,8 +2,20 @@
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
5
- # Format an object by calling `to_s` on it and stripping leading and trailing whitespace.
5
+ # Format an object by calling +to_s+ on it and stripping leading and trailing whitespace.
6
+ # This formatter is useful for cleaning up string values that may have unwanted whitespace
7
+ # from user input, file processing, or other sources.
8
+ #
9
+ # The StripFormatter combines string conversion with whitespace normalization,
10
+ # making it ideal for attribute values that should be clean and consistent
11
+ # in log output.
6
12
  class StripFormatter
13
+ FormatterRegistry.add(:strip, self)
14
+
15
+ # Convert an object to a string and remove leading and trailing whitespace.
16
+ #
17
+ # @param obj [Object] The object to format.
18
+ # @return [String] The string representation with whitespace stripped.
7
19
  def call(obj)
8
20
  obj.to_s.strip
9
21
  end
@@ -5,16 +5,31 @@ require "set"
5
5
  module Lumberjack
6
6
  class Formatter
7
7
  # Dereference arrays and hashes and recursively call formatters on each element.
8
+ # This formatter provides deep traversal of nested data structures, applying
9
+ # formatting to all contained elements while handling circular references safely.
10
+ #
11
+ # The StructuredFormatter is essential for formatting complex nested objects
12
+ # like configuration hashes, API responses, or any hierarchical data structures
13
+ # that need consistent formatting throughout their entire structure.
8
14
  class StructuredFormatter
15
+ FormatterRegistry.add(:structured, self)
16
+
17
+ # Exception raised when a circular reference is detected during traversal.
18
+ # This prevents infinite recursion when formatting objects that reference themselves.
9
19
  class RecusiveReferenceError < StandardError
10
20
  end
11
21
 
12
- # @param [Formatter] formatter The formatter to call on each element
13
- # in the structure.
22
+ # @param formatter [Formatter, nil] The formatter to call on each element
23
+ # in the structure. If nil, elements are returned unchanged.
14
24
  def initialize(formatter = nil)
15
25
  @formatter = formatter
16
26
  end
17
27
 
28
+ # Format a structured object by recursively processing all nested elements.
29
+ #
30
+ # @param obj [Object] The object to format. Arrays and hashes are traversed
31
+ # recursively, while other objects are passed to the configured formatter.
32
+ # @return [Object] The formatted structure with all nested elements processed.
18
33
  def call(obj)
19
34
  call_with_references(obj, Set.new)
20
35
  end
@@ -50,6 +65,7 @@ module Lumberjack
50
65
  def with_object_reference(obj, references)
51
66
  if obj.is_a?(Enumerable)
52
67
  return RecusiveReferenceError.new if references.include?(obj.object_id)
68
+
53
69
  references << obj.object_id
54
70
  begin
55
71
  yield
@@ -1,38 +1,16 @@
1
1
  # frozen_string_literal: true
2
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
3
+ require_relative "../message_attributes"
29
4
 
30
- def to_s
31
- inspect
32
- end
33
-
34
- def inspect
35
- {message: @message, tags: @tags}.inspect
5
+ module Lumberjack
6
+ # This is a deprecated alias for Lumberjack::MessageAttributes.
7
+ #
8
+ # @deprecated Use Lumberjack::MessageAttributes instead.
9
+ # @see MessageAttributes
10
+ class Formatter::TaggedMessage < MessageAttributes
11
+ def initialize(message, attributes)
12
+ Utils.deprecated("Lumberjack::Formatter::TaggedMessage", "Use Lumberjack::MessageAttributes instead.") do
13
+ super
36
14
  end
37
15
  end
38
16
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lumberjack
4
+ # This formatter is designed to output tags in a specific format.
5
+ #
6
+ # - Simple values will be formatted as "[value]"
7
+ # - Arrays will be formatted as "[value1] [value2] [value3]"
8
+ # - Hashes will be formatted as "[key1=value1] [key2=value2]"
9
+ # - Hashes in arrays will be formatted as "[key=value]"
10
+ class Formatter::TagsFormatter
11
+ FormatterRegistry.add(:tags, self)
12
+
13
+ def call(tags)
14
+ tags = tags.collect { |key, value| "#{key}=#{value}" } if tags.is_a?(Hash)
15
+ if tags.is_a?(Array)
16
+ tags.collect { |tag| format_tag(tag) }.join(" ") unless tags.empty?
17
+ else
18
+ format_tag(tags)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def format_tag(tag)
25
+ if tag.is_a?(Hash)
26
+ tag.collect { |key, value| "[#{key}=#{value.strip}]" }.join(" ")
27
+ else
28
+ "[#{tag.strip}]"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -10,11 +10,18 @@ module Lumberjack
10
10
  # When a string is truncated, it will have a unicode ellipsis
11
11
  # character (U+2026) appended to the end of the string.
12
12
  class TruncateFormatter
13
- # @param [Integer] length The maximum length of the string (defaults to 32K)
13
+ FormatterRegistry.add(:truncate, self)
14
+
15
+ # @param length [Integer] The maximum length of the string (defaults to 32K).
14
16
  def initialize(length = 32768)
15
17
  @length = length
16
18
  end
17
19
 
20
+ # Truncate a string if it exceeds the maximum length.
21
+ #
22
+ # @param obj [Object] The object to format. If it's a string longer than the maximum
23
+ # length, it will be truncated. Other objects are returned unchanged.
24
+ # @return [String, Object] The truncated string or original object.
18
25
  def call(obj)
19
26
  if obj.is_a?(String) && obj.length > @length
20
27
  "#{obj[0, @length - 1]}…"