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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/ARCHITECTURE.md +244 -0
  3. data/CHANGELOG.md +251 -56
  4. data/README.md +197 -62
  5. data/VERSION +1 -1
  6. data/lib/lumberjack/context.rb +25 -5
  7. data/lib/lumberjack/device/date_rolling_log_file.rb +17 -8
  8. data/lib/lumberjack/device/log_file.rb +14 -7
  9. data/lib/lumberjack/device/multi.rb +8 -7
  10. data/lib/lumberjack/device/null.rb +2 -2
  11. data/lib/lumberjack/device/rolling_log_file.rb +46 -22
  12. data/lib/lumberjack/device/size_rolling_log_file.rb +10 -10
  13. data/lib/lumberjack/device/writer.rb +45 -21
  14. data/lib/lumberjack/device.rb +28 -13
  15. data/lib/lumberjack/formatter/date_time_formatter.rb +5 -5
  16. data/lib/lumberjack/formatter/exception_formatter.rb +4 -4
  17. data/lib/lumberjack/formatter/id_formatter.rb +4 -3
  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 +7 -5
  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 +3 -1
  27. data/lib/lumberjack/formatter/tagged_message.rb +39 -0
  28. data/lib/lumberjack/formatter/truncate_formatter.rb +27 -0
  29. data/lib/lumberjack/formatter.rb +96 -28
  30. data/lib/lumberjack/log_entry.rb +90 -19
  31. data/lib/lumberjack/logger.rb +318 -86
  32. data/lib/lumberjack/rack/context.rb +21 -2
  33. data/lib/lumberjack/rack/request_id.rb +8 -4
  34. data/lib/lumberjack/rack/unit_of_work.rb +7 -3
  35. data/lib/lumberjack/rack.rb +4 -4
  36. data/lib/lumberjack/severity.rb +22 -3
  37. data/lib/lumberjack/tag_context.rb +78 -0
  38. data/lib/lumberjack/tag_formatter.rb +124 -25
  39. data/lib/lumberjack/tagged_logger_support.rb +26 -12
  40. data/lib/lumberjack/tagged_logging.rb +1 -1
  41. data/lib/lumberjack/tags.rb +8 -8
  42. data/lib/lumberjack/template.rb +17 -5
  43. data/lib/lumberjack/utils.rb +182 -0
  44. data/lib/lumberjack.rb +64 -35
  45. data/lumberjack.gemspec +18 -15
  46. metadata +23 -54
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  class Device
@@ -14,35 +14,43 @@ module Lumberjack
14
14
  class RollingLogFile < LogFile
15
15
  attr_reader :path
16
16
  attr_accessor :keep
17
-
17
+
18
18
  def initialize(path, options = {})
19
19
  @path = File.expand_path(path)
20
20
  @keep = options[:keep]
21
- super(path, options)
22
- @file_inode = stream.lstat.ino rescue nil
21
+ super
22
+ @file_inode = begin
23
+ stream.lstat.ino
24
+ rescue
25
+ nil
26
+ end
23
27
  @@rolls = []
24
28
  @next_stat_check = Time.now.to_f
25
29
  @min_roll_check = (options[:min_roll_check] || 1.0).to_f
26
30
  end
27
-
31
+
28
32
  # Returns a suffix that will be appended to the file name when it is archived.. The suffix should
29
33
  # change after it is time to roll the file. The log file will be renamed when it is rolled.
30
34
  def archive_file_suffix
31
35
  raise NotImplementedError
32
36
  end
33
-
37
+
34
38
  # Return +true+ if the file should be rolled.
35
39
  def roll_file?
36
40
  raise NotImplementedError
37
41
  end
38
-
42
+
39
43
  # Roll the log file by renaming it to the archive file name and then re-opening a stream to the log
40
44
  # file path. Rolling a file is safe in multi-threaded or multi-process environments.
41
- def roll_file! #:nodoc:
45
+ def roll_file! # :nodoc:
42
46
  do_once(stream) do
43
47
  archive_file = "#{path}.#{archive_file_suffix}"
44
48
  stream.flush
45
- current_inode = File.stat(path).ino rescue nil
49
+ current_inode = begin
50
+ File.stat(path).ino
51
+ rescue
52
+ nil
53
+ end
46
54
  if @file_inode && current_inode == @file_inode && !File.exist?(archive_file) && File.exist?(path)
47
55
  begin
48
56
  File.rename(path, archive_file)
@@ -59,41 +67,49 @@ module Lumberjack
59
67
  end
60
68
 
61
69
  protected
62
-
70
+
63
71
  # This method will be called after a file has been rolled. Subclasses can
64
72
  # implement code to reset the state of the device. This method is thread safe.
65
73
  def after_roll
66
74
  end
67
-
75
+
68
76
  # Handle rolling the file before flushing.
69
77
  def before_flush # :nodoc:
70
78
  if @min_roll_check <= 0.0 || Time.now.to_f >= @next_stat_check
71
79
  @next_stat_check += @min_roll_check
72
- path_inode = File.lstat(path).ino rescue nil
80
+ path_inode = begin
81
+ File.lstat(path).ino
82
+ rescue
83
+ nil
84
+ end
73
85
  if path_inode != @file_inode
74
86
  @file_inode = path_inode
75
87
  reopen_file
76
- else
77
- roll_file! if roll_file?
88
+ elsif roll_file?
89
+ roll_file!
78
90
  end
79
91
  end
80
92
  end
81
-
93
+
82
94
  private
83
95
 
84
96
  def reopen_file
85
97
  old_stream = stream
86
- new_stream = File.open(path, 'a', encoding: EXTERNAL_ENCODING)
98
+ new_stream = File.open(path, "a", encoding: EXTERNAL_ENCODING)
87
99
  new_stream.sync = true if buffer_size > 0
88
- @file_inode = new_stream.lstat.ino rescue nil
100
+ @file_inode = begin
101
+ new_stream.lstat.ino
102
+ rescue
103
+ nil
104
+ end
89
105
  self.stream = new_stream
90
106
  old_stream.close
91
107
  end
92
108
  end
93
-
109
+
94
110
  def cleanup_files!
95
111
  if keep
96
- files = Dir.glob("#{path}.*").collect{|f| [f, File.ctime(f)]}.sort{|a,b| b.last <=> a.last}.collect{|a| a.first}
112
+ files = Dir.glob("#{path}.*").collect { |f| [f, File.ctime(f)] }.sort { |a, b| b.last <=> a.last }.collect { |a| a.first }
97
113
  if files.size > keep
98
114
  files[keep, files.length].each do |f|
99
115
  File.delete(f)
@@ -101,7 +117,7 @@ module Lumberjack
101
117
  end
102
118
  end
103
119
  end
104
-
120
+
105
121
  def do_once(file)
106
122
  begin
107
123
  file.flock(File::LOCK_EX)
@@ -110,11 +126,19 @@ module Lumberjack
110
126
  return
111
127
  end
112
128
  begin
113
- verify = file.lstat rescue nil
129
+ verify = begin
130
+ file.lstat
131
+ rescue
132
+ nil
133
+ end
114
134
  # Execute only if the file we locked is still the same one that needed to be rolled
115
135
  yield if verify && verify.ino == @file_inode && verify.size > 0
116
136
  ensure
117
- file.flock(File::LOCK_UN) rescue nil
137
+ begin
138
+ file.flock(File::LOCK_UN)
139
+ rescue
140
+ nil
141
+ end
118
142
  end
119
143
  end
120
144
  end
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  class Device
@@ -8,30 +8,30 @@ module Lumberjack
8
8
  # production.log.1, then production.log.2, etc.
9
9
  class SizeRollingLogFile < RollingLogFile
10
10
  attr_reader :max_size
11
-
11
+
12
12
  # Create an new log device to the specified file. The maximum size of the log file is specified with
13
13
  # the :max_size option. The unit can also be specified: "32K", "100M", "2G" are all valid.
14
14
  def initialize(path, options = {})
15
15
  @manual = options[:manual]
16
16
  @max_size = options[:max_size]
17
17
  if @max_size.is_a?(String)
18
- if @max_size.match(/^(\d+(\.\d+)?)([KMG])?$/i)
18
+ if @max_size =~ /^(\d+(\.\d+)?)([KMG])?$/i
19
19
  @max_size = $~[1].to_f
20
20
  units = $~[3].to_s.upcase
21
21
  case units
22
22
  when "K"
23
23
  @max_size *= 1024
24
24
  when "M"
25
- @max_size *= 1024 ** 2
25
+ @max_size *= 1024**2
26
26
  when "G"
27
- @max_size *= 1024 ** 3
27
+ @max_size *= 1024**3
28
28
  end
29
29
  @max_size = @max_size.round
30
30
  else
31
31
  raise ArgumentError.new("illegal value for :max_size (#{@max_size})")
32
32
  end
33
33
  end
34
-
34
+
35
35
  super
36
36
  end
37
37
 
@@ -44,15 +44,15 @@ module Lumberjack
44
44
  rescue SystemCallError
45
45
  false
46
46
  end
47
-
47
+
48
48
  protected
49
-
49
+
50
50
  # Calculate the next archive file name extension.
51
51
  def next_archive_number # :nodoc:
52
52
  max = 0
53
53
  Dir.glob("#{path}.*").each do |filename|
54
- if filename.match(/\.\d+$/)
55
- suffix = filename.split('.').last.to_i
54
+ if /\.\d+\z/ =~ filename
55
+ suffix = filename.split(".").last.to_i
56
56
  max = suffix if suffix > max
57
57
  end
58
58
  end
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  class Device
@@ -55,34 +55,42 @@ module Lumberjack
55
55
  # :additional_lines and :time_format options will be passed through to the
56
56
  # Template constuctor.
57
57
  #
58
- # The default template is "[:time :severity :progname(:pid) #:unit_of_work_id] :message"
59
- # with additional lines formatted as "\n [#:unit_of_work_id] :message". The unit of
60
- # work id will only appear if it is present.
58
+ # The default template is "[:time :severity :progname(:pid)] :message"
59
+ # with additional lines formatted as "\n :message".
61
60
  #
62
61
  # The size of the internal buffer in bytes can be set by providing :buffer_size (defaults to 32K).
62
+ #
63
+ # @param [IO] stream The stream to write log entries to.
64
+ # @param [Hash] options The options for the device.
63
65
  def initialize(stream, options = {})
64
66
  @lock = Mutex.new
65
67
  @stream = stream
66
68
  @stream.sync = true if @stream.respond_to?(:sync=)
67
69
  @buffer = Buffer.new
68
- @buffer_size = (options[:buffer_size] || 0)
69
- template = (options[:template] || DEFAULT_FIRST_LINE_TEMPLATE)
70
+ @buffer_size = options[:buffer_size] || 0
71
+ template = options[:template] || DEFAULT_FIRST_LINE_TEMPLATE
70
72
  if template.respond_to?(:call)
71
73
  @template = template
72
74
  else
73
- additional_lines = (options[:additional_lines] || DEFAULT_ADDITIONAL_LINES_TEMPLATE)
74
- @template = Template.new(template, :additional_lines => additional_lines, :time_format => options[:time_format])
75
+ additional_lines = options[:additional_lines] || DEFAULT_ADDITIONAL_LINES_TEMPLATE
76
+ @template = Template.new(template, additional_lines: additional_lines, time_format: options[:time_format])
75
77
  end
76
78
  end
77
79
 
78
80
  # Set the buffer size in bytes. The device will only be physically written to when the buffer size
79
81
  # is exceeded.
82
+ #
83
+ # @param [Integer] value The size of the buffer in bytes.
84
+ # @return [void]
80
85
  def buffer_size=(value)
81
86
  @buffer_size = value
82
87
  flush
83
88
  end
84
89
 
85
90
  # Write an entry to the stream. The entry will be converted into a string using the defined template.
91
+ #
92
+ # @param [LogEntry, String] entry The entry to write to the stream.
93
+ # @return [void]
86
94
  def write(entry)
87
95
  string = (entry.is_a?(LogEntry) ? @template.call(entry) : entry)
88
96
  return if string.nil?
@@ -105,12 +113,16 @@ module Lumberjack
105
113
  end
106
114
 
107
115
  # Close the underlying stream.
116
+ #
117
+ # @return [void]
108
118
  def close
109
119
  flush
110
120
  stream.close
111
121
  end
112
122
 
113
123
  # Flush the underlying stream.
124
+ #
125
+ # @return [void]
114
126
  def flush
115
127
  lines = nil
116
128
  @lock.synchronize do
@@ -120,39 +132,47 @@ module Lumberjack
120
132
  write_to_stream(lines) if lines
121
133
  end
122
134
 
135
+ # Get the datetime format.
136
+ #
137
+ # @return [String] The datetime format.
123
138
  def datetime_format
124
139
  @template.datetime_format if @template.respond_to?(:datetime_format)
125
140
  end
126
141
 
142
+ # Set the datetime format.
143
+ #
144
+ # @param [String] format The datetime format.
145
+ # @return [void]
127
146
  def datetime_format=(format)
128
147
  if @template.respond_to?(:datetime_format=)
129
148
  @template.datetime_format = format
130
149
  end
131
150
  end
132
151
 
152
+ # Return the underlying stream. Provided for API compatibility with Logger devices.
153
+ #
154
+ # @return [IO] The underlying stream.
155
+ def dev
156
+ @stream
157
+ end
158
+
133
159
  protected
134
160
 
135
161
  # Set the underlying stream.
136
- def stream=(stream)
137
- @stream = stream
138
- end
162
+ attr_writer :stream
139
163
 
140
164
  # Get the underlying stream.
141
- def stream
142
- @stream
143
- end
165
+ attr_reader :stream
144
166
 
145
167
  private
146
168
 
147
169
  def write_to_stream(lines)
148
170
  return if lines.empty?
149
171
  lines = lines.first if lines.is_a?(Array) && lines.size == 1
150
-
151
- out = nil
152
- if lines.is_a?(Array)
153
- out = "#{lines.join(Lumberjack::LINE_SEPARATOR)}#{Lumberjack::LINE_SEPARATOR}"
172
+ out = if lines.is_a?(Array)
173
+ "#{lines.join(Lumberjack::LINE_SEPARATOR)}#{Lumberjack::LINE_SEPARATOR}"
154
174
  else
155
- out = "#{lines}#{Lumberjack::LINE_SEPARATOR}"
175
+ "#{lines}#{Lumberjack::LINE_SEPARATOR}"
156
176
  end
157
177
 
158
178
  begin
@@ -170,9 +190,13 @@ module Lumberjack
170
190
  end
171
191
  end
172
192
  end
173
- stream.flush rescue nil
193
+ begin
194
+ stream.flush
195
+ rescue
196
+ nil
197
+ end
174
198
  rescue => e
175
- $stderr.write("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
199
+ $stderr.write("#{e.class.name}: #{e.message}#{" at " + e.backtrace.first if e.backtrace}")
176
200
  $stderr.write(out)
177
201
  $stderr.flush
178
202
  end
@@ -1,41 +1,56 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  # This is an abstract class for logging devices. Subclasses must implement the +write+ method and
5
5
  # may implement the +close+ and +flush+ methods if applicable.
6
6
  class Device
7
- require_relative "device/writer.rb"
8
- require_relative "device/log_file.rb"
9
- require_relative "device/rolling_log_file.rb"
10
- require_relative "device/date_rolling_log_file.rb"
11
- require_relative "device/size_rolling_log_file.rb"
12
- require_relative "device/multi.rb"
13
- require_relative "device/null.rb"
7
+ require_relative "device/writer"
8
+ require_relative "device/log_file"
9
+ require_relative "device/rolling_log_file"
10
+ require_relative "device/date_rolling_log_file"
11
+ require_relative "device/size_rolling_log_file"
12
+ require_relative "device/multi"
13
+ require_relative "device/null"
14
14
 
15
15
  # Subclasses must implement this method to write a LogEntry.
16
+ #
17
+ # @param [Lumberjack::LogEntry] entry The entry to write.
18
+ # @return [void]
16
19
  def write(entry)
17
20
  raise NotImplementedError
18
21
  end
19
-
22
+
20
23
  # Subclasses may implement this method to close the device.
24
+ #
25
+ # @return [void]
21
26
  def close
22
27
  flush
23
28
  end
24
-
29
+
25
30
  # Subclasses may implement this method to reopen the device.
31
+ #
32
+ # @param [Object] logdev The log device to use.
33
+ # @return [void]
26
34
  def reopen(logdev = nil)
27
35
  flush
28
36
  end
29
-
37
+
30
38
  # Subclasses may implement this method to flush any buffers used by the device.
39
+ #
40
+ # @return [void]
31
41
  def flush
32
42
  end
33
-
43
+
34
44
  # Subclasses may implement this method to get the format for log timestamps.
45
+ #
46
+ # @return [String] The format for log timestamps.
35
47
  def datetime_format
36
48
  end
37
-
49
+
38
50
  # Subclasses may implement this method to set a format for log timestamps.
51
+ #
52
+ # @param [String] format The format for log timestamps.
53
+ # @return [void]
39
54
  def datetime_format=(format)
40
55
  end
41
56
  end
@@ -1,22 +1,22 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
4
  class Formatter
5
5
  # Format a Date, Time, or DateTime object. If you don't specify a format in the constructor,
6
6
  # it will use the ISO-8601 format.
7
7
  class DateTimeFormatter
8
-
9
8
  attr_reader :format
10
-
9
+
10
+ # @param [String] format The format to use when formatting the date/time object.
11
11
  def initialize(format = nil)
12
12
  @format = format.dup.to_s.freeze unless format.nil?
13
13
  end
14
-
14
+
15
15
  def call(obj)
16
16
  if @format && obj.respond_to?(:strftime)
17
17
  obj.strftime(@format)
18
18
  elsif obj.respond_to?(:iso8601)
19
- obj.iso8601
19
+ obj.iso8601(6)
20
20
  else
21
21
  obj.to_s
22
22
  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
@@ -7,15 +7,16 @@ module Lumberjack
7
7
  # passed to this object and the returned array is what will be logged. You can
8
8
  # use this to clean out superfluous lines.
9
9
  class ExceptionFormatter
10
-
11
10
  attr_accessor :backtrace_cleaner
12
11
 
12
+ # @param [#call] backtrace_cleaner An object that responds to `call` and takes
13
+ # an array of strings (the backtrace) and returns an array of strings (the
13
14
  def initialize(backtrace_cleaner = nil)
14
15
  self.backtrace_cleaner = backtrace_cleaner
15
16
  end
16
17
 
17
18
  def call(exception)
18
- message = "#{exception.class.name}: #{exception.message}"
19
+ message = +"#{exception.class.name}: #{exception.message}"
19
20
  trace = exception.backtrace
20
21
  if trace
21
22
  trace = clean_backtrace(trace)
@@ -33,7 +34,6 @@ module Lumberjack
33
34
  trace
34
35
  end
35
36
  end
36
-
37
37
  end
38
38
  end
39
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
@@ -6,14 +6,15 @@ module Lumberjack
6
6
  # as a default formatter for objects pulled from a data store. By default it will use :id as the
7
7
  # id attribute.
8
8
  class IdFormatter
9
+ # @param [Symbol, String] id_attribute The attribute to use as the id.
9
10
  def initialize(id_attribute = :id)
10
11
  @id_attribute = id_attribute
11
12
  end
12
-
13
+
13
14
  def call(obj)
14
15
  if obj.respond_to?(@id_attribute)
15
16
  id = obj.send(@id_attribute)
16
- { "class" => obj.class.name, "id" => id }
17
+ {"class" => obj.class.name, "id" => id}
17
18
  else
18
19
  obj.to_s
19
20
  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
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lumberjack
4
+ class Formatter
5
+ # This formatter can be used to multiply a numeric value by a specified multiplier and
6
+ # optionally round to a specified number of decimal places.
7
+ class MultiplyFormatter
8
+ # @param multiplier [Numeric] The multiplier to apply to the value.
9
+ # @param decimals [Integer, nil] The number of decimal places to round the result to.
10
+ # If nil, no rounding is applied.
11
+ def initialize(multiplier, decimals = nil)
12
+ @multiplier = multiplier
13
+ @decimals = decimals
14
+ end
15
+
16
+ def call(value)
17
+ return value unless value.is_a?(Numeric)
18
+
19
+ value *= @multiplier
20
+ value = value.round(@decimals) if @decimals
21
+ value
22
+ end
23
+ end
24
+ end
25
+ 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,20 +1,22 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
- require 'pp'
4
- require 'stringio'
3
+ require "pp"
4
+ require "stringio"
5
5
 
6
6
  module Lumberjack
7
7
  class Formatter
8
8
  # Format an object with it's pretty print method.
9
9
  class PrettyPrintFormatter
10
10
  attr_accessor :width
11
-
11
+
12
12
  # Create a new formatter. The maximum width of the message can be specified with the width
13
13
  # parameter (defaults to 79 characters).
14
+ #
15
+ # @param [Integer] width The maximum width of the message.
14
16
  def initialize(width = 79)
15
17
  @width = width
16
18
  end
17
-
19
+
18
20
  def call(obj)
19
21
  s = StringIO.new
20
22
  PP.pp(obj, s)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lumberjack
4
+ class Formatter
5
+ # Log sensitive information in a redacted format showing the firat and last
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
8
+ # not show any characters in order to avoid revealing too much information.
9
+ class RedactFormatter
10
+ def call(obj)
11
+ return obj unless obj.is_a?(String)
12
+
13
+ if obj.length > 8
14
+ "#{obj[0..1]}#{"*" * (obj.length - 4)}#{obj[-2..-1]}"
15
+ elsif obj.length > 5
16
+ "#{obj[0]}#{"*" * (obj.length - 2)}#{obj[-1]}"
17
+ else
18
+ "*****"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lumberjack
4
+ class Formatter
5
+ # Round numeric values to a set number of decimal places. This is useful when logging
6
+ # floating point numbers to reduce noise and rounding errors in the logs.
7
+ class RoundFormatter
8
+ def initialize(precision = 3)
9
+ @precision = precision
10
+ end
11
+
12
+ def call(obj)
13
+ if obj.is_a?(Numeric)
14
+ obj.round(@precision)
15
+ else
16
+ obj
17
+ end
18
+ end
19
+ end
20
+ end
21
+ 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
  class Formatter
@@ -1,4 +1,4 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
4
 
@@ -9,6 +9,8 @@ module Lumberjack
9
9
  class RecusiveReferenceError < StandardError
10
10
  end
11
11
 
12
+ # @param [Formatter] formatter The formatter to call on each element
13
+ # in the structure.
12
14
  def initialize(formatter = nil)
13
15
  @formatter = formatter
14
16
  end