Class: Lumberjack::Formatter

Inherits:
Object
  • Object
show all
Defined in:
lib/lumberjack/formatter.rb,
lib/lumberjack/formatter/id_formatter.rb,
lib/lumberjack/formatter/round_formatter.rb,
lib/lumberjack/formatter/strip_formatter.rb,
lib/lumberjack/formatter/object_formatter.rb,
lib/lumberjack/formatter/redact_formatter.rb,
lib/lumberjack/formatter/string_formatter.rb,
lib/lumberjack/formatter/inspect_formatter.rb,
lib/lumberjack/formatter/multiply_formatter.rb,
lib/lumberjack/formatter/truncate_formatter.rb,
lib/lumberjack/formatter/date_time_formatter.rb,
lib/lumberjack/formatter/exception_formatter.rb,
lib/lumberjack/formatter/structured_formatter.rb,
lib/lumberjack/formatter/pretty_print_formatter.rb

Overview

Formatter controls the conversion of log entry messages into a loggable format, allowing you to log any object type and have the logging system handle the string conversion automatically.

The formatter system works by associating formatting rules with specific classes using the #add method. When an object is logged, the formatter finds the most specific formatter for that object’s class hierarchy and applies it to convert the object into a string representation.

Formatters can be:

  • Predefined formatters: Accessed by symbol (e.g., :pretty_print, :truncate)

  • Custom objects: Any object responding to #call(object)

  • Blocks: Inline formatting logic

  • Classes: Instantiated automatically with optional arguments

The formatter includes optimizations for common primitive types (String, Integer, Float, Boolean) to avoid unnecessary formatting overhead when custom formatters aren’t defined for these types.

Examples:

Basic formatter usage

formatter = Lumberjack::Formatter.new
formatter.add(MyClass, :pretty_print)
formatter.add(Array) { |vals| vals.join(", ") }
result = formatter.format(my_object)

Building a custom formatter

formatter = Lumberjack::Formatter.build do |config|
  config.add(User, :id)  # Only log user IDs
  config.add(BigDecimal, :round, 2)  # Round decimals to 2 places
end

Defined Under Namespace

Classes: DateTimeFormatter, ExceptionFormatter, IdFormatter, InspectFormatter, MultiplyFormatter, ObjectFormatter, PrettyPrintFormatter, RedactFormatter, RoundFormatter, StringFormatter, StripFormatter, StructuredFormatter, TaggedMessage, TagsFormatter, TruncateFormatter

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeLumberjack::Formatter

Create a new formatter with default mappings for common Ruby types. The default configuration provides sensible formatting for most use cases:

  • Object: Uses inspect for debugging-friendly output

  • Exception: Formats with stack trace details

  • Enumerable: Recursively formats collections (Arrays, Hashes, etc.)



103
104
105
106
107
108
# File 'lib/lumberjack/formatter.rb', line 103

def initialize
  @class_formatters = {}
  @has_string_formatter = false
  @has_numeric_formatter = false
  @has_boolean_formatter = false
end

Class Method Details

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

Build a new formatter using a configuration block. The block receives the new formatter as a parameter, allowing you to configure it with methods like add, remove, etc.

Examples:

formatter = Lumberjack::Formatter.build do |config|
  config.add(User, :id)  # Only show user IDs
  config.add(SecretToken) { |token| "[REDACTED]" }
  config.remove(Exception)  # Don't format exceptions specially
end

Yields:

  • (formatter)

    A block that configures the formatter.

Returns:



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

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

.defaultLumberjack::Formatter

Create a new formatter with default mappings.

Object: inspect formatter
Exception: exception formatter
Enumerable: structured formatter

Returns:



87
88
89
90
91
92
93
# File 'lib/lumberjack/formatter.rb', line 87

def default
  build do |config|
    config.add(Object, :inspect)
    config.add(Exception, :exception)
    config.add(Enumerable, :structured)
  end
end

.emptyLumberjack::Formatter

Deprecated.

Use #new instead.

Create a new empty formatter with no mappings. This is an alias for #new.

Returns:



74
75
76
77
78
# File 'lib/lumberjack/formatter.rb', line 74

def empty
  Utils.deprecated("Formatter.empty", "Lumberjack::Formatter.empty is deprecated and will be removed in version 2.1; use new instead.") do
    new
  end
end

Instance Method Details

#add(klass, formatter = nil, *args) {|obj| ... } ⇒ self

Add a formatter for a specific class or classes. The formatter determines how objects of that class will be converted to strings when logged.

The formatter can be specified in several ways:

  • Symbol: References a predefined formatter (see list below)

  • Class: Will be instantiated with optional arguments

  • Object: Must respond to #call(object) method

  • Block: Inline formatting logic

Formatters can be referenced by name from the formatter registry. These formatters are available out of the box. Some of them require an argument to be provided as well.

  • :date_time - Formats time objects with a customizable format (takes the format string as an argument)

  • :exception - Formats exceptions with stack trace details

  • :id - Extracts object ID or specified ID field

  • :inspect - Uses Ruby’s inspect method for debugging output

  • :multiply - Multiplies numeric values by a factor (requires the factor as an argument)

  • :object - Generic object formatter with custom methods

  • :pretty_print - Pretty-prints objects using PP library

  • :redact - Redacts sensitive information from objects

  • :round - Rounds numeric values to specified precision (takes the precision as an argument; defaults to 3 decimal places)

  • :string - Converts objects to strings using to_s

  • :strip - Strips whitespace from string representations

  • :structured - Recursively formats structured data (Arrays, Hashes)

  • :tags - Formats an array or hash of values in the format “[a] [b] [c=d]”

  • :truncate - Truncates long strings to specified length (takes the length as an argument)

Classes can be specified as:

  • Class objects: Direct class references

  • Arrays: Multiple classes at once

  • Strings: Class names to avoid loading dependencies

Examples:

Using predefined formatters

formatter.add(Float, :round, 2)  # Round floats to 2 decimal places
formatter.add(Time, :date_time, "%Y-%m-%d")  # Custom time format
formatter.add([User, Admin], :id)  # Show only IDs for user objects

Using custom formatters

formatter.add(MyClass, MyFormatter.new)  # Custom formatter object
formatter.add("BigDecimal", RoundFormatter, 4)  # Class with arguments

Method chaining

formatter.add(User, :id)
         .add(BigDecimal, :round, 2)

Parameters:

  • klass (Class, Module, String, Array<Class, Module, String>)

    The class(es) to format.

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

    The formatter to use.

  • args (Array)

    Arguments passed to formatter constructor (when formatter is a Class).

Yields:

  • (obj)

    Block-based formatter that receives the object to format.

Yield Parameters:

  • obj (Object)

    The object to format.

Yield Returns:

  • (String)

    The formatted string representation.

Returns:

  • (self)

    Returns self for method chaining.

Raises:

  • (ArgumentError)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/lumberjack/formatter.rb', line 163

def add(klass, formatter = nil, *args, &block)
  formatter ||= block

  return remove(klass) if formatter.nil?

  if formatter.is_a?(Symbol)
    formatter = FormatterRegistry.formatter(formatter, *args)
  elsif formatter.is_a?(Class)
    formatter = formatter.new(*args)
  end

  raise ArgumentError.new("formatter must respond to call") unless formatter.respond_to?(:call)

  Array(klass).each do |k|
    @class_formatters[k.to_s] = formatter
  end

  set_optimized_flags!

  self
end

#call(severity, timestamp, progname, msg) ⇒ String

Compatibility method for Ruby’s standard Logger::Formatter interface. This allows the Formatter to be used directly as a logger formatter, though it only uses the message parameter and ignores severity, timestamp, and progname.

Parameters:

  • severity (Integer, String, Symbol)

    The log severity (ignored).

  • timestamp (Time)

    The log timestamp (ignored).

  • progname (String)

    The program name (ignored).

  • msg (Object)

    The message object to format.

Returns:

  • (String)

    The formatted message with line separator.



295
296
297
298
299
# File 'lib/lumberjack/formatter.rb', line 295

def call(severity, timestamp, progname, msg)
  formatted_message = format(msg)
  formatted_message = formatted_message.message if formatted_message.is_a?(MessageAttributes)
  "#{formatted_message}#{Lumberjack::LINE_SEPARATOR}"
end

#clearself

Remove all formatter associations, including defaults. This creates a completely empty formatter where all objects will be passed through unchanged.

Returns:

  • (self)

    Returns self for method chaining.



237
238
239
240
241
242
# File 'lib/lumberjack/formatter.rb', line 237

def clear
  @class_formatters.clear
  set_optimized_flags!

  self
end

#empty?Boolean

Check if the formatter has any registered formatters.

Returns:

  • (Boolean)

    true if no formatters are registered, false otherwise.



247
248
249
# File 'lib/lumberjack/formatter.rb', line 247

def empty?
  @class_formatters.empty?
end

#format(value) ⇒ Object

Format an object by applying the appropriate formatter based on its class hierarchy. The formatter searches up the class hierarchy to find the most specific formatter available.

Parameters:

  • value (Object)

    The object to format.

Returns:

  • (Object)

    The formatted representation (usually a String).



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/lumberjack/formatter.rb', line 256

def format(value)
  # These primitive types are the most common in logs and so are optimized here
  # for the normal case where a custom formatter has not been defined.
  case value
  when String
    return value unless @has_string_formatter
  when Integer, Float
    return value unless @has_numeric_formatter
  when Numeric
    if defined?(BigDecimal) && value.is_a?(BigDecimal)
      return value unless @has_numeric_formatter
    end
  when true, false
    return value unless @has_boolean_formatter
  end

  if value.respond_to?(:to_log_format) && !@class_formatters.include?(value.class.name)
    return value.to_log_format
  end

  formatter = formatter_for(value.class)
  value = formatter.call(value) if formatter&.respond_to?(:call)
  value
rescue SystemStackError, StandardError => e
  error_message = e.class.name
  error_message = "#{error_message} #{e.message}" if e.message && e.message != ""
  warn("<Error formatting #{value.class.name}: #{error_message}>")
  "<Error formatting #{value.class.name}: #{error_message}>"
end

#formatter_for(klass) ⇒ #call?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find the most appropriate formatter for a class by searching up the class hierarchy. Returns the first formatter found by walking through the class’s ancestors.

Parameters:

  • klass (Class)

    The class to find a formatter for.

Returns:

  • (#call, nil)

    The formatter object, or nil if no formatter is found.



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/lumberjack/formatter.rb', line 307

def formatter_for(klass)
  return nil if @class_formatters.empty?

  unless klass.is_a?(Module)
    begin
      klass = Object.const_get(klass.to_s)
    rescue NameError
      return @class_formatters[klass.to_s]
    end
  end

  formatter = nil
  has_to_log_format = klass.public_method_defined?(:to_log_format) if klass.is_a?(Module)
  klass.ancestors.detect do |ancestor|
    break if has_to_log_format && ancestor == Object

    formatter = @class_formatters[ancestor.name]
    break if formatter
  end
  formatter
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.



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

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

  formatter.instance_variable_get(:@class_formatters).each do |class_name, fmttr|
    add(class_name, fmttr)
  end

  self
end

#include?(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.



333
334
335
# File 'lib/lumberjack/formatter.rb', line 333

def include?(class_or_name)
  @class_formatters.include?(class_or_name.to_s)
end

#prepend(formatter) ⇒ self

Extend this formatter by adding 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.



221
222
223
224
225
226
227
228
229
230
231
# File 'lib/lumberjack/formatter.rb', line 221

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

  formatter.instance_variable_get(:@class_formatters).each do |class_name, fmttr|
    add(class_name, fmttr) unless @class_formatters.include?(class_name)
  end

  self
end

#remove(klass) ⇒ self

Remove formatter associations for one or more classes. This reverts the classes to use the default Object formatter (inspect method) or no formatting if no default exists.

Parameters:

  • klass (Class, Module, String, Array<Class, Module, String>)

    The class(es) to remove formatters for.

Returns:

  • (self)

    Returns self for method chaining.



190
191
192
193
194
195
196
197
198
# File 'lib/lumberjack/formatter.rb', line 190

def remove(klass)
  Array(klass).each do |k|
    @class_formatters.delete(k.to_s)
  end

  set_optimized_flags!

  self
end