Class: Lumberjack::AttributeFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/lumberjack/attribute_formatter.rb

Overview

AttributeFormatter provides flexible formatting control for log entry attributes (key-value pairs). It allows you to specify different formatting rules for attribute names, object classes, or provide a default formatter for all attributes.

The formatter system works in a hierarchical manner:

  1. Attribute-specific formatters - Applied to specific attribute names (highest priority)
  2. Class-specific formatters - Applied based on the attribute value's class
  3. Default formatter - Applied to all other attributes (lowest priority)

Formatters can be specified as:

  • Lumberjack::Formatter objects: Full formatter instances with complex logic
  • Callable objects: Any object responding to #call(value)
  • Blocks: Inline formatting logic
  • Symbols: References to predefined formatter classes (e.g., :strip, :truncate)

If the value returned by a formatter is a Lumberjack::RemapAttributes instance, then the attributes will be remapped to the new attributes.

Examples:

Basic usage with build

formatter = Lumberjack::AttributeFormatter.build do |config|
  config.add_attribute(["password", "secret", "token"]) { |value| "[REDACTED]" }
  config.add_attribute("user.email") { |email| email.downcase }
  config.add_class(Time, :date_time, "%Y-%m-%d %H:%M:%S")
end
formatter = Lumberjack::AttributeFormatter.new
formatter.add_attribute("duration_ms") { |value| Lumberjack::RemapAttributes.new(duration: value.to_f / 1000) }
formatter.format({ "duration_ms" => 1234 }) # => { "duration" => 1.234 }

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeLumberjack::AttributeFormatter

Create a new attribute formatter with no default formatters configured. You'll need to add specific formatters using #add_class, #add_attribute, or #default.



64
65
66
67
68
# File 'lib/lumberjack/attribute_formatter.rb', line 64

def initialize
  @attribute_formatter = {}
  @class_formatter = Formatter.new
  @default_formatter = nil
end

Class Method Details

.build {|formatter| ... } ⇒ Lumberjack::AttributeFormatter

Build a new attribute formatter using a configuration block. The block receives the new formatter as a parameter, allowing you to configure it with methods like add_attribute, add_class, default, etc.

Examples:

formatter = Lumberjack::AttributeFormatter.build do |config|
  config.default { |value| value.to_s.strip }
  config.add_attribute(["password", "secret"]) { |value| "[REDACTED]" }
  config.add_attribute("email") { |email| email.downcase }
  config.add_class(Time, :date_time, "%Y-%m-%d %H:%M:%S")
end

Yields:

  • (formatter)

    A block that configures the attribute formatter.

Returns:



53
54
55
56
57
# File 'lib/lumberjack/attribute_formatter.rb', line 53

def build(&block)
  formatter = new
  block&.call(formatter)
  formatter
end

Instance Method Details

#add_attribute(attribute_names, formatter = nil, *args) {|value| ... } ⇒ Lumberjack::AttributeFormatter

Add formatters for specific attribute names. These formatters take precedence over class formatters and the default formatter.

Supports dot notation for nested attributes (e.g., "user.profile.email"). This allows you to format specific values deep within nested hash structures.

Examples:

Basic attribute formatting

formatter.add_attribute("password") { |pwd| "[REDACTED]" }
formatter.add_attribute("email") { |email| email.downcase }

Nested attribute formatting

formatter.add_attribute("user.profile.email") { |email| email.downcase }
formatter.add_attribute("config.database.password") { "[HIDDEN]" }

Multiple attributes

formatter.add_attribute(["secret", "token", "api_key"]) { "[REDACTED]" }

Parameters:

  • attribute_names (String, Symbol, Array<String, Symbol>)

    The attribute names to format.

  • formatter (Lumberjack::Formatter, #call, Symbol, nil) (defaults to: nil)

    The formatter to use.

Yields:

  • (value)

    Block-based formatter that receives the attribute value.

Yield Parameters:

  • value (Object)

    The attribute value to format.

Yield Returns:

  • (Object)

    The formatted attribute value.

Returns:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/lumberjack/attribute_formatter.rb', line 156

def add_attribute(attribute_names, formatter = nil, *args, &block)
  formatter ||= block
  formatter = dereference_formatter(formatter, args)

  Array(attribute_names).collect(&:to_s).each do |attribute_name|
    if attribute_name.is_a?(Module)
      raise ArgumentError.new("attribute_name cannot be a Module/Class; use #add_class to add class-based formatters")
    end

    if formatter.nil?
      @attribute_formatter.delete(attribute_name)
    else
      @attribute_formatter[attribute_name] = formatter
    end
  end

  self
end

#add_class(classes_or_names, formatter = nil, *args) {|value| ... } ⇒ Lumberjack::AttributeFormatter

Add formatters for specific object classes. The formatter will be applied to any attribute value that is an instance of the registered class. This is particularly useful for formatting all instances of specific data types consistently across your logs.

Class formatters are recursive - they will be applied to matching objects found within nested hashes and arrays.

Examples:

Time formatting

formatter.add_class(Time, :date_time, "%Y-%m-%d %H:%M:%S")
formatter.add_class([Date, DateTime]) { |dt| dt.strftime("%Y-%m-%d") }

Parameters:

  • classes_or_names (String, Module, Array<String, Module>)

    Class names or modules.

  • formatter (Lumberjack::Formatter, #call, Symbol, Class, nil) (defaults to: nil)

    The formatter to use. If a Class is provided, it will be instantiated with the provided args.

  • args (Array)

    The arguments to pass to the constructor if formatter is a Class.

Yields:

  • (value)

    Block-based formatter that receives the attribute value.

Yield Parameters:

  • value (Object)

    The attribute value to format.

Yield Returns:

  • (Object)

    The formatted attribute value.

Returns:



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/lumberjack/attribute_formatter.rb', line 117

def add_class(classes_or_names, formatter = nil, *args, &block)
  formatter ||= block
  formatter = dereference_formatter(formatter, args)

  Array(classes_or_names).each do |class_or_name|
    class_name = class_or_name.to_s
    if formatter.nil?
      @class_formatter.remove(class_name)
    else
      @class_formatter.add(class_name, formatter)
    end
  end

  self
end

#clearLumberjack::AttributeFormatter

Remove all configured formatters, including the default formatter. This resets the formatter to a completely empty state where all attributes pass through unchanged.

Returns:



242
243
244
245
246
247
# File 'lib/lumberjack/attribute_formatter.rb', line 242

def clear
  @default_formatter = nil
  @attribute_formatter.clear
  @class_formatter.clear
  self
end

#default(formatter = nil, *args) {|value| ... } ⇒ Lumberjack::AttributeFormatter

Set a default formatter applied to all attribute values that don't have specific formatters. This serves as the fallback formatting behavior for any attributes not covered by attribute-specific or class-specific formatters.

Parameters:

  • formatter (Lumberjack::Formatter, #call, Class, nil) (defaults to: nil)

    The formatter to use. If nil, the block will be used as the formatter. If a class is passed, it will be instantiated with the args passed in.

  • args (Array)

    The arguments to pass to the constructor if formatter is a Class.

Yields:

  • (value)

    Block-based formatter that receives the attribute value.

Yield Parameters:

  • value (Object)

    The attribute value to format.

Yield Returns:

  • (Object)

    The formatted attribute value.

Returns:



82
83
84
85
86
87
# File 'lib/lumberjack/attribute_formatter.rb', line 82

def default(formatter = nil, *args, &block)
  formatter ||= block
  formatter = dereference_formatter(formatter, args)
  @default_formatter = formatter
  self
end

#empty?Boolean

Check if the formatter has any configured formatters (attribute, class, or default).

Returns:

  • (Boolean)

    true if no formatters are configured, false otherwise.



252
253
254
# File 'lib/lumberjack/attribute_formatter.rb', line 252

def empty?
  @attribute_formatter.empty? && @class_formatter.empty? && @default_formatter.nil?
end

#format(attributes) ⇒ Hash?

Format a hash of attributes using the configured formatters. This is the main method that applies all formatting rules to transform attribute values.

The formatting process follows this precedence:

  1. Attribute-specific formatters (highest priority)
  2. Class-specific formatters
  3. Default formatter (lowest priority)

Nested hashes and arrays are processed recursively, and dot notation attribute formatters are applied to nested structures.

Parameters:

  • attributes (Hash, nil)

    The attributes hash to format.

Returns:

  • (Hash, nil)

    The formatted attributes hash, or nil if input was nil.



269
270
271
272
273
274
# File 'lib/lumberjack/attribute_formatter.rb', line 269

def format(attributes)
  return nil if attributes.nil?
  return attributes if empty?

  formated_attributes(attributes)
end

#formatter_for_attribute(name) ⇒ #call?

Get the formatter for a specific attribute.

Parameters:

  • name (String, Symbol)

    The attribute name to get the formatter for.

Returns:

  • (#call, nil)

    The formatter for the attribute, or nil if not found.



288
289
290
# File 'lib/lumberjack/attribute_formatter.rb', line 288

def formatter_for_attribute(name)
  @attribute_formatter[name.to_s]
end

#formatter_for_class(klass) ⇒ #call?

Get the formatter for a specific class or class name.

Parameters:

  • klass (String, Module)

    The class or class name to get the formatter for.

Returns:

  • (#call, nil)

    The formatter for the class, or nil if not found.



280
281
282
# File 'lib/lumberjack/attribute_formatter.rb', line 280

def formatter_for_class(klass)
  @class_formatter.formatter_for(klass)
end

#include(formatter) ⇒ self

Extend this formatter by merging the formats defined in the provided formatter into this one.

Parameters:

Returns:

  • (self)

    Returns self for method chaining.



203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/lumberjack/attribute_formatter.rb', line 203

def include(formatter)
  unless formatter.is_a?(Lumberjack::AttributeFormatter)
    raise ArgumentError.new("formatter must be a Lumberjack::AttributeFormatter")
  end

  @class_formatter.include(formatter.instance_variable_get(:@class_formatter))
  @attribute_formatter.merge!(formatter.instance_variable_get(:@attribute_formatter))

  default_formatter = formatter.instance_variable_get(:@default_formatter)
  @default_formatter = default_formatter if default_formatter

  self
end

#include_attribute?(name) ⇒ Boolean

Check if a formatter exists for a specific attribute name.

Parameters:

  • name (String, Symbol)

    The attribute name to check.

Returns:

  • (Boolean)

    true if a formatter exists, false otherwise.



304
305
306
# File 'lib/lumberjack/attribute_formatter.rb', line 304

def include_attribute?(name)
  @attribute_formatter.include?(name.to_s)
end

#include_class?(class_or_name) ⇒ Boolean

Check if a formatter exists for a specific class or class name.

Parameters:

  • class_or_name (Class, Module, String)

    The class or class name to check.

Returns:

  • (Boolean)

    true if a formatter exists, false otherwise.



296
297
298
# File 'lib/lumberjack/attribute_formatter.rb', line 296

def include_class?(class_or_name)
  @class_formatter.include?(class_or_name.to_s)
end

#prepend(formatter) ⇒ self

Extend this formatter by merging the formats defined in the provided formatter into this one. Formats defined in this formatter will take precedence and not be overridden.

Parameters:

Returns:

  • (self)

    Returns self for method chaining.



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/lumberjack/attribute_formatter.rb', line 222

def prepend(formatter)
  unless formatter.is_a?(Lumberjack::AttributeFormatter)
    raise ArgumentError.new("formatter must be a Lumberjack::AttributeFormatter")
  end

  @class_formatter.prepend(formatter.instance_variable_get(:@class_formatter))

  formatter.instance_variable_get(:@attribute_formatter).each do |key, value|
    @attribute_formatter[key] = value unless @attribute_formatter.include?(key)
  end

  @default_formatter ||= formatter.instance_variable_get(:@default_formatter)

  self
end

#remove_attribute(attribute_names) ⇒ Lumberjack::AttributeFormatter

Remove formatters for specific attribute names. This reverts the specified attributes to use the default formatter (if configured) or no formatting.

Parameters:

  • attribute_names (String, Symbol, Array<String, Symbol>)

    The attribute names to remove.

Returns:



192
193
194
195
196
197
# File 'lib/lumberjack/attribute_formatter.rb', line 192

def remove_attribute(attribute_names)
  Array(attribute_names).collect(&:to_s).each do |attribute_name|
    @attribute_formatter.delete(attribute_name)
  end
  self
end

#remove_class(classes_or_names) ⇒ Lumberjack::AttributeFormatter

Remove formatters for specific object classes. This reverts the specified classes to use the default formatter (if configured) or no formatting.

Parameters:

  • classes_or_names (String, Module, Array<String, Module>)

    The classes or names to remove.

Returns:



180
181
182
183
184
185
# File 'lib/lumberjack/attribute_formatter.rb', line 180

def remove_class(classes_or_names)
  Array(classes_or_names).each do |class_or_name|
    @class_formatter.remove(class_or_name)
  end
  self
end

#remove_defaultLumberjack::AttributeFormatter

Remove the default formatter. After calling this, attributes without specific formatters will be passed through unchanged.

Returns:



93
94
95
96
# File 'lib/lumberjack/attribute_formatter.rb', line 93

def remove_default
  @default_formatter = nil
  self
end