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
@@ -1,87 +1,149 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lumberjack
4
- # Logger is a thread safe logging object. It has a compatible API with the Ruby
5
- # standard library Logger class, the Log4r gem, and ActiveSupport::BufferedLogger.
4
+ # Lumberjack::Logger is a thread-safe, feature-rich logging implementation that extends Ruby's standard
5
+ # library Logger class with advanced capabilities for structured logging.
6
6
  #
7
- # === Example
7
+ # Key features include:
8
+ # - Structured logging with attributes (key-value pairs) attached to log entries
9
+ # - Context isolation for scoping logging behavior to specific code blocks
10
+ # - Flexible output devices supporting files, streams, and custom destinations
11
+ # - Customizable formatters for messages and attributes
8
12
  #
9
- # logger = Lumberjack::Logger.new
13
+ # The Logger maintains full API compatibility with Ruby's standard Logger while adding
14
+ # powerful extensions for modern logging needs.
15
+ #
16
+ # @example Basic usage
17
+ # logger = Lumberjack::Logger.new(STDOUT)
10
18
  # logger.info("Starting processing")
11
19
  # logger.debug("Processing options #{options.inspect}")
12
20
  # logger.fatal("OMG the application is on fire!")
13
21
  #
14
- # Log entries are written to a logging Device if their severity meets or exceeds the log level.
22
+ # @example Structured logging with attributes
23
+ # logger = Lumberjack::Logger.new("/var/log/app.log")
24
+ # logger.tag(request_id: "abc123") do
25
+ # logger.info("User logged in", user_id: 123, ip: "192.168.1.1")
26
+ # logger.info("Processing request") # Will include request_id: "abc123"
27
+ # end
28
+ #
29
+ # @example Log rotation
30
+ # # Keep 10 files, rotate when each reaches 10MB
31
+ # logger = Lumberjack::Logger.new("/var/log/app.log", 10, 10 * 1024 * 1024)
32
+ #
33
+ # @example Using different devices
34
+ # logger = Lumberjack::Logger.new("logs/application.log") # Log to file
35
+ # logger = Lumberjack::Logger.new(STDOUT, template: "{{severity}} - {{message}}") # Log to a stream with a template
36
+ # logger = Lumberjack::Logger.new(:test) # Log to an in memory buffer for testing
37
+ # logger = Lumberjack::Logger.new(another_logger) # Proxy logs to another logger
38
+ # logger = Lumberjack::Logger.new(MyDevice.new) # Log to a custom Lumberjack::Device
15
39
  #
16
- # Devices may use buffers internally and the log entries are not guaranteed to be written until you call
17
- # the +flush+ method. Sometimes this can result in problems when trying to track down extraordinarily
18
- # long running sections of code since it is likely that none of the messages logged before the long
19
- # running code will appear in the log until the entire process finishes. You can set the +:flush_seconds+
20
- # option on the constructor to force the device to be flushed periodically. This will create a new
21
- # monitoring thread, but its use is highly recommended.
40
+ # @example Logging to multiple devices with an array
41
+ # logger = Lumberjack::Logger.new(["/var/log/app.log", [:stdout, {template: "{{message}}"}]])
22
42
  #
43
+ # Log entries are written to a logging Device if their severity meets or exceeds the log level.
23
44
  # Each log entry records the log message and severity along with the time it was logged, the
24
- # program name, process id, and an optional hash of tags. The message will be converted to a string, but
25
- # otherwise, it is up to the device how these values are recorded. Messages are converted to strings
45
+ # program name, process id, and an optional hash of attributes. Messages are converted to strings
26
46
  # using a Formatter associated with the logger.
27
- class Logger
28
- include Severity
47
+ #
48
+ # @see Lumberjack::ContextLogger
49
+ # @see Lumberjack::Device
50
+ # @see Lumberjack::Template
51
+ # @see Lumberjack::EntryFormatter
52
+ class Logger < ::Logger
53
+ include ContextLogger
29
54
 
30
- # The time that the device was last flushed.
31
- attr_reader :last_flushed_at
55
+ # Create a new logger to log to a Device.
56
+ #
57
+ # The +device+ argument can be in any one of several formats:
58
+ # - A symbol for a device name (e.g. :null, :test). You can call +Lumberjack::DeviceRegistry.registered_devices+ for a list.
59
+ # - A stream
60
+ # - A file path string or +Pathname+
61
+ # - A +Lumberjack::Device+ object
62
+ # - An object with a +write+ method will be wrapped in a Device::Writer
63
+ # - An array of any of the above will open a Multi device that will send output to all devices.
64
+ #
65
+ # @param logdev [Lumberjack::Device, IO, Symbol, String, Pathname] The device to log to.
66
+ # If this is a symbol, the device will be looked up from the DeviceRegistry. If it is
67
+ # a string or a Pathname, the logs will be sent to the corresponding file path.
68
+ # @param shift_age [Integer, String, Symbol] If this is an integer greater than zero, then
69
+ # log files will be rolled when they get to the size specified in shift_size and the number of
70
+ # files to keep will be determined by this value. Otherwise it will be interpreted as a date
71
+ # rolling value and must be one of "daily", "weekly", or "monthly". This parameter has no
72
+ # effect unless the device parameter is a file path or file stream. This can also be
73
+ # specified with the :roll keyword argument.
74
+ # @param shift_size [Integer] The size in bytes of the log files before rolling them. This can
75
+ # be passed as a string with a unit suffix of K, M, or G (e.g. "10M" for 10 megabytes).
76
+ # This can also be specified with the :max_size keyword argument.
77
+ # @param level [Integer, Symbol, String] The logging level below which messages will be ignored.
78
+ # @param progname [String] The name of the program that will be recorded with each log entry.
79
+ # @param formatter [Lumberjack::EntryFormatter, Lumberjack::Formatter, ::Logger::Formatter, :default, #call]
80
+ # The formatter to use for outputting messages to the log. If this is a Lumberjack::EntryFormatter
81
+ # or a Lumberjack::Formatter, it will be used to format structured log entries.
82
+ # You can also pass the value +:default+ to use the default message formatter which formats
83
+ # non-primitive objects with +inspect+ and includes the backtrace in exceptions.
84
+ #
85
+ # For compatibility with the standard library Logger when writing to a stream, you can also
86
+ # pass in a +::Logger::Formatter+ object or a callable object that takes exactly 4 arguments
87
+ # (severity, time, progname, msg).
88
+ # @param datetime_format [String] The format to use for log timestamps.
89
+ # @param binmode [Boolean] Whether to open the log file in binary mode.
90
+ # @param shift_period_suffix [String] The suffix to use for the shifted log file names.
91
+ # @param kwargs [Hash] Additional device-specific options. These will be passed through when creating
92
+ # a device from the logdev argument.
93
+ # @return [Lumberjack::Logger] A new logger instance.
94
+ def initialize(logdev, shift_age = 0, shift_size = 1048576,
95
+ level: DEBUG, progname: nil, formatter: nil, datetime_format: nil,
96
+ binmode: false, shift_period_suffix: "%Y%m%d", **kwargs)
97
+ init_fiber_locals!
98
+
99
+ if shift_age.is_a?(Hash)
100
+ Lumberjack::Utils.deprecated("Logger.new(options)", "Passing a Hash as the second argument to Logger.new is deprecated and will be removed in version 2.1; use keyword arguments instead.")
101
+ options = shift_age
102
+ level = options[:level] if options.include?(:level)
103
+ progname = options[:progname] if options.include?(:progname)
104
+ formatter = options[:formatter] if options.include?(:formatter)
105
+ datetime_format = options[:datetime_format] if options.include?(:datetime_format)
106
+ kwargs = options.merge(kwargs)
107
+ end
32
108
 
33
- # Set +silencer+ to false to disable silencing the log.
34
- attr_accessor :silencer
109
+ # Include standard args that affect devices with the optional kwargs which may
110
+ # contain device specific options.
111
+ device_options = kwargs.merge(shift_age: shift_age, shift_size: size_with_units(shift_size), binmode: binmode, shift_period_suffix: shift_period_suffix)
112
+ device_options[:standard_logger_formatter] = formatter if standard_logger_formatter?(formatter)
35
113
 
36
- # Set the name of the program to attach to log entries.
37
- attr_writer :progname
114
+ if device_options.include?(:roll)
115
+ Utils.deprecated("Logger.options(:roll)", "Lumberjack::Logger :roll option is deprecated and will be removed in version 2.1; use the shift_age argument instead.")
116
+ device_options[:shift_age] = device_options.delete(:roll) unless shift_age != 0
117
+ end
38
118
 
39
- # The Formatter used only for log entry messages.
40
- attr_accessor :message_formatter
119
+ if device_options.include?(:max_size)
120
+ Utils.deprecated("Logger.options(:max_size)", "Lumberjack::Logger :max_size option is deprecated and will be removed in version 2.1; use the shift_size argument instead.")
121
+ device_options[:shift_age] = 10 if shift_age == 0
122
+ device_options[:shift_size] = device_options.delete(:max_size)
123
+ end
41
124
 
42
- # The TagFormatter used for formatting tags for output
43
- attr_accessor :tag_formatter
125
+ message_formatter = nil
126
+ if device_options.include?(:message_formatter)
127
+ Utils.deprecated("Logger.options(:message_formatter)", "Lumberjack::Logger :message_formatter option is deprecated and will be removed in version 2.1; use the formatter argument instead to specify an EntryFormatter.")
128
+ message_formatter = device_options.delete(:message_formatter)
129
+ end
44
130
 
45
- # Create a new logger to log to a Device.
46
- #
47
- # The +device+ argument can be in any one of several formats.
48
- #
49
- # If it is a Device object, that object will be used.
50
- # If it has a +write+ method, it will be wrapped in a Device::Writer class.
51
- # If it is :null, it will be a Null device that won't record any output.
52
- # Otherwise, it will be assumed to be file path and wrapped in a Device::LogFile class.
53
- #
54
- # All other options are passed to the device constuctor.
55
- #
56
- # @param [Lumberjack::Device, Object, Symbol, String] device The device to log to.
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.
67
- def initialize(device = $stdout, options = {})
68
- options = options.dup
69
- self.level = options.delete(:level) || INFO
70
- self.progname = options.delete(:progname)
71
- max_flush_seconds = options.delete(:flush_seconds).to_f
72
-
73
- @logdev = open_device(device, options) if device
74
- self.formatter = (options[:formatter] || Formatter.new)
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]
78
- self.datetime_format = time_format if time_format
79
- @last_flushed_at = Time.now
80
- @silencer = true
81
- @tags = {}
82
- @closed = false
131
+ attribute_formatter = nil
132
+ if device_options.include?(:tag_formatter)
133
+ Utils.deprecated("Logger.options(:tag_formatter)", "Lumberjack::Logger :tag_formatter option is deprecated and will be removed in version 2.1; use the formatter argument instead to specify an EntryFormatter.")
134
+ attribute_formatter = device_options.delete(:tag_formatter)
135
+ end
83
136
 
84
- create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
137
+ @logdev = Device.open_device(logdev, device_options)
138
+
139
+ @context = Context.new
140
+ self.level = level || DEBUG
141
+ self.progname = progname
142
+
143
+ self.formatter = build_entry_formatter(formatter, message_formatter, attribute_formatter)
144
+ self.datetime_format = datetime_format if datetime_format
145
+
146
+ @closed = false
85
147
  end
86
148
 
87
149
  # Get the logging device that is used to write log entries.
@@ -93,10 +155,19 @@ module Lumberjack
93
155
 
94
156
  # Set the logging device to a new device.
95
157
  #
96
- # @param [Lumberjack::Device] device The new logging device.
158
+ # @param device [Lumberjack::Device] The new logging device.
97
159
  # @return [void]
98
160
  def device=(device)
99
- @logdev = device.nil? ? nil : open_device(device, {})
161
+ @logdev = Device.open_device(device, {})
162
+ end
163
+
164
+ # Set the formatter used for log entries. This can be an EntryFormatter, a standard Logger::Formatter,
165
+ # or any callable object that formats log entries.
166
+ #
167
+ # @param value [Lumberjack::EntryFormatter, ::Logger::Formatter, #call] The formatter to use.
168
+ # @return [void]
169
+ def formatter=(value)
170
+ @formatter = build_entry_formatter(value, nil, nil)
100
171
  end
101
172
 
102
173
  # Get the timestamp format on the device if it has one.
@@ -108,7 +179,7 @@ module Lumberjack
108
179
 
109
180
  # Set the timestamp format on the device if it is supported.
110
181
  #
111
- # @param [String] format The timestamp format.
182
+ # @param format [String] The timestamp format.
112
183
  # @return [void]
113
184
  def datetime_format=(format)
114
185
  if device.respond_to?(:datetime_format=)
@@ -116,156 +187,55 @@ module Lumberjack
116
187
  end
117
188
  end
118
189
 
119
- # Get the level of severity of entries that are logged. Entries with a lower
120
- # severity level will be ignored.
190
+ # Get the message formatter used to format log messages.
121
191
  #
122
- # @return [Integer] The severity level.
123
- def level
124
- thread_local_value(:lumberjack_logger_level) || @level
192
+ # @return [Lumberjack::Formatter] The message formatter.
193
+ def message_formatter
194
+ formatter.message_formatter
125
195
  end
126
196
 
127
- alias_method :sev_threshold, :level
128
-
129
- # Set the log level using either an integer level like Logger::INFO or a label like
130
- # :info or "info"
197
+ # Set the message formatter used to format log messages.
131
198
  #
132
- # @param [Integer, Symbol, String] value The severity level.
199
+ # @param value [Lumberjack::Formatter] The message formatter to use.
133
200
  # @return [void]
134
- def level=(value)
135
- @level = Severity.coerce(value)
201
+ def message_formatter=(value)
202
+ formatter.message_formatter = value
136
203
  end
137
204
 
138
- alias_method :sev_threshold=, :level=
139
-
140
- # Adjust the log level during the block execution for the current Fiber only.
205
+ # Get the attribute formatter used to format log entry attributes.
141
206
  #
142
- # @param [Integer, Symbol, String] severity The severity level.
143
- # @return [Object] The result of the block.
144
- def with_level(severity, &block)
145
- push_thread_local_value(:lumberjack_logger_level, Severity.coerce(severity), &block)
207
+ # @return [Lumberjack::AttributeFormatter] The attribute formatter.
208
+ def attribute_formatter
209
+ formatter.attribute_formatter
146
210
  end
147
211
 
148
- # Set the Lumberjack::Formatter used to format objects for logging as messages.
212
+ # Set the attribute formatter used to format log entry attributes.
149
213
  #
150
- # @param [Lumberjack::Formatter, Object] value The formatter to use.
214
+ # @param value [Lumberjack::AttributeFormatter] The attribute formatter to use.
151
215
  # @return [void]
152
- def formatter=(value)
153
- @_formatter = (value.is_a?(TaggedLoggerSupport::Formatter) ? value.__formatter : value)
154
- end
155
-
156
- # Get the Lumberjack::Formatter used to format objects for logging as messages.
157
- #
158
- # @return [Lumberjack::Formatter] The formatter.
159
- def formatter
160
- if respond_to?(:tagged)
161
- # Wrap in an object that supports ActiveSupport::TaggedLogger API
162
- TaggedLoggerSupport::Formatter.new(logger: self, formatter: @_formatter)
163
- else
164
- @_formatter
165
- end
166
- end
167
-
168
- # Enable this logger to function like an ActiveSupport::TaggedLogger. This will make the logger
169
- # API compatible with ActiveSupport::TaggedLogger and is provided as a means of compatibility
170
- # with other libraries that assume they can call the `tagged` method on a logger to add tags.
171
- #
172
- # The tags added with this method are just strings so they are stored in the logger tags
173
- # in an array under the "tagged" tag. So calling `logger.tagged("foo", "bar")` will result
174
- # in tags `{"tagged" => ["foo", "bar"]}`.
175
- #
176
- # @return [Lumberjack::Logger] self.
177
- def tagged_logger!
178
- extend(TaggedLoggerSupport)
179
- self
216
+ def attribute_formatter=(value)
217
+ formatter.attribute_formatter = value
180
218
  end
181
219
 
182
- # Add a message to the log with a given severity. The message can be either
183
- # passed in the +message+ argument or supplied with a block. This method
184
- # is not normally called. Instead call one of the helper functions
185
- # +fatal+, +error+, +warn+, +info+, or +debug+.
186
- #
187
- # The severity can be passed in either as one of the Severity constants,
188
- # or as a Severity label.
189
- #
190
- # @param [Integer, Symbol, String] severity The severity of the message.
191
- # @param [Object] message The message to log.
192
- # @param [String] progname The name of the program that is logging the message.
193
- # @param [Hash] tags The tags to add to the log entry.
194
- # @return [void]
195
- #
196
- # @example
197
- #
198
- # logger.add_entry(Logger::ERROR, exception)
199
- # logger.add_entry(Logger::INFO, "Request completed")
200
- # logger.add_entry(:warn, "Request took a long time")
201
- # logger.add_entry(Logger::DEBUG){"Start processing with options #{options.inspect}"}
202
- def add_entry(severity, message, progname = nil, tags = nil)
203
- severity = Severity.label_to_level(severity) unless severity.is_a?(Integer)
204
- return true unless device && severity && severity >= level
205
- return true if Thread.current[:lumberjack_logging]
206
-
207
- begin
208
- Thread.current[:lumberjack_logging] = true # Prevent circular calls to add_entry
209
-
210
- time = Time.now
211
-
212
- message = message.call if message.is_a?(Proc)
213
- msg_class_formatter = message_formatter&.formatter_for(message.class)
214
- if msg_class_formatter
215
- message = msg_class_formatter.call(message)
216
- elsif formatter
217
- message = formatter.format(message)
218
- end
219
- message_tags = nil
220
- if message.is_a?(Formatter::TaggedMessage)
221
- message_tags = message.tags
222
- message = message.message
223
- end
224
-
225
- progname ||= self.progname
226
- message_tags = Utils.flatten_tags(message_tags) if message_tags
227
-
228
- current_tags = self.tags
229
- tags = nil unless tags.is_a?(Hash)
230
- tags = merge_tags(current_tags, tags)
231
- tags = merge_tags(tags, message_tags) if message_tags
232
- tags = Tags.expand_runtime_values(tags)
233
- tags = tag_formatter.format(tags) if tag_formatter
234
-
235
- entry = LogEntry.new(time, severity, message, progname, Process.pid, tags)
236
- write_to_device(entry)
237
- ensure
238
- Thread.current[:lumberjack_logging] = nil
220
+ # @deprecated Use {#attribute_formatter} instead.
221
+ def tag_formatter
222
+ Utils.deprecated("Logger#tag_formatter", "Lumberjack::Logger#tag_formatter is deprecated and will be removed in version 2.1; use attribute_formatter instead.") do
223
+ formatter.attributes.attribute_formatter
239
224
  end
240
- true
241
225
  end
242
226
 
243
- # ::Logger compatible method to add a log entry.
244
- #
245
- # @param [Integer, Symbol, String] severity The severity of the message.
246
- # @param [Object] message The message to log.
247
- # @param [String] progname The name of the program that is logging the message.
248
- # @return [void]
249
- def add(severity, message = nil, progname = nil, &block)
250
- if message.nil?
251
- if block
252
- message = block
253
- else
254
- message = progname
255
- progname = nil
256
- end
227
+ # @deprecated Use {#attribute_formatter=} instead.
228
+ def tag_formatter=(value)
229
+ Utils.deprecated("Logger#tag_formatter=", "Lumberjack::Logger#tag_formatter= is deprecated and will be removed in version 2.1; use attribute_formatter= instead.") do
230
+ formatter.attributes.attribute_formatter = value
257
231
  end
258
- add_entry(severity, message, progname)
259
232
  end
260
233
 
261
- alias_method :log, :add
262
-
263
234
  # Flush the logging device. Messages are not guaranteed to be written until this method is called.
264
235
  #
265
236
  # @return [void]
266
237
  def flush
267
238
  device.flush
268
- @last_flushed_at = Time.now
269
239
  nil
270
240
  end
271
241
 
@@ -282,454 +252,256 @@ module Lumberjack
282
252
  #
283
253
  # @return [Boolean] +true+ if the logging device is closed.
284
254
  def closed?
285
- @closed
255
+ return true if @closed
256
+
257
+ device.respond_to?(:closed?) && device.closed?
286
258
  end
287
259
 
288
260
  # Reopen the logging device.
289
261
  #
290
- # @param [Object] logdev passed through to the logging device.
262
+ # @param logdev [Object] passed through to the logging device.
263
+ # @return [Lumberjack::Logger] self
291
264
  def reopen(logdev = nil)
292
265
  @closed = false
293
266
  device.reopen(logdev) if device.respond_to?(:reopen)
267
+ self
294
268
  end
295
269
 
296
- # Log a +FATAL+ message. The message can be passed in either the +message+ argument or in a block.
270
+ # Set the program name that is associated with log messages. If a block
271
+ # is given, the program name will be valid only within the block.
297
272
  #
298
- # @param [Object] message_or_progname_or_tags The message to log or progname
299
- # if the message is passed in a block.
300
- # @param [String, Hash] progname_or_tags The name of the program that is logging the message or tags
301
- # if the message is passed in a block.
273
+ # @param value [String] The program name to use.
302
274
  # @return [void]
303
- def fatal(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
304
- call_add_entry(FATAL, message_or_progname_or_tags, progname_or_tags, &block)
275
+ # @deprecated Use with_progname or progname= instead.
276
+ def set_progname(value, &block)
277
+ Utils.deprecated("Logger#set_progname", "Lumberjack::Logger#set_progname is deprecated and will be removed in version 2.1; use with_progname or progname= instead.") do
278
+ if block
279
+ with_progname(value, &block)
280
+ else
281
+ self.progname = value
282
+ end
283
+ end
305
284
  end
306
285
 
307
- # Return +true+ if +FATAL+ messages are being logged.
286
+ # Alias method for #attributes to provide backward compatibility with version 1.x API. This
287
+ # method will eventually be removed.
308
288
  #
309
- # @return [Boolean]
310
- def fatal?
311
- level <= FATAL
289
+ # @return [Hash]
290
+ # @deprecated Use {#attributes} instead
291
+ def tags
292
+ Utils.deprecated("Logger#tags", "Lumberjack::Logger#tags is deprecated and will be removed in version 2.1; use attributes instead.") do
293
+ attributes
294
+ end
312
295
  end
313
296
 
314
- # Set the log level to fatal.
297
+ # Alias method for #attribute_value to provide backward compatibility with version 1.x API. This
298
+ # method will eventually be removed.
315
299
  #
316
- # @return [void]
317
- def fatal!
318
- self.level = FATAL
300
+ # @return [Hash]
301
+ # @deprecated Use {#attribute_value} instead
302
+ def tag_value(name)
303
+ Utils.deprecated("Logger#tag_value", "Lumberjack::Logger#tag_value is deprecated and will be removed in version 2.1; use attribute_value instead.") do
304
+ attribute_value(name)
305
+ end
319
306
  end
320
307
 
321
- # Log an +ERROR+ message. The message can be passed in either the +message+ argument or in a block.
308
+ # Use tag! instead
322
309
  #
323
- # @param [Object] message_or_progname_or_tags The message to log or progname
324
- # if the message is passed in a block.
325
- # @param [String, Hash] progname_or_tags The name of the program that is logging the message or tags
326
- # if the message is passed in a block.
327
310
  # @return [void]
328
- def error(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
329
- call_add_entry(ERROR, message_or_progname_or_tags, progname_or_tags, &block)
311
+ # @deprecated Use {#tag!} instead.
312
+ def tag_globally(tags)
313
+ Utils.deprecated("Logger#tag_globally", "Lumberjack::Logger#tag_globally is deprecated and will be removed in version 2.1; use tag! instead.") do
314
+ tag!(tags)
315
+ end
330
316
  end
331
317
 
332
- # Return +true+ if +ERROR+ messages are being logged.
318
+ # Use context? instead
333
319
  #
334
320
  # @return [Boolean]
335
- def error?
336
- level <= ERROR
337
- end
338
-
339
- # Set the log level to error.
340
- #
341
- # @return [void]
342
- def error!
343
- self.level = ERROR
321
+ # @deprecated Use {#in_context?} instead.
322
+ def in_tag_context?
323
+ Utils.deprecated("Logger#in_tag_context?", "Lumberjack::Logger#in_tag_context? is deprecated and will be removed in version 2.1; use in_context? instead.") do
324
+ context?
325
+ end
344
326
  end
345
327
 
346
- # Log a +WARN+ message. The message can be passed in either the +message+ argument or in a block.
328
+ # Remove a tag from the current context block. If this is called inside a context block,
329
+ # the attributes will only be removed for the duration of that block. Otherwise they will be removed
330
+ # from the global attributes.
347
331
  #
348
- # @param [Object] message_or_progname_or_tags The message to log or progname
349
- # if the message is passed in a block.
350
- # @param [String, Hash] progname_or_tags The name of the program that is logging the message or tags
351
- # if the message is passed in a block.
332
+ # @param tag_names [Array<String, Symbol>] The attributes to remove.
352
333
  # @return [void]
353
- def warn(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
354
- call_add_entry(WARN, message_or_progname_or_tags, progname_or_tags, &block)
355
- end
356
-
357
- # Return +true+ if +WARN+ messages are being logged.
358
- #
359
- # @return [Boolean]
360
- def warn?
361
- level <= WARN
334
+ # @deprecated Use untag or untag! instead.
335
+ def remove_tag(*tag_names)
336
+ Utils.deprecated("Logger#remove_tag", "Lumberjack::Logger#remove_tag is deprecated and will be removed in version 2.1; use untag or untag! instead.") do
337
+ attributes = current_context&.attributes
338
+ AttributesHelper.new(attributes).delete(*tag_names) if attributes
339
+ end
362
340
  end
363
341
 
364
- # Set the log level to warn.
342
+ # Alias for append_to(:tagged) for compatibility with ActiveSupport support in Lumberjack 1.x.
343
+ # This functionality has been moved to the lumberjack_rails gem. Note that in that gem the
344
+ # tags are added to the :tags attribute instead of the :tagged attribute.
365
345
  #
366
- # @return [void]
367
- def warn!
368
- self.level = WARN
346
+ # @see append_to
347
+ # @deprecated This implementation is deprecated. Install the lumberjack_rails gem for full support.
348
+ def tagged(*tags, &block)
349
+ deprecation_message = "Install the lumberjack_rails gem for full support of the tagged method."
350
+ Utils.deprecated("Logger#tagged", deprecation_message) do
351
+ append_to(:tagged, *tags, &block)
352
+ end
369
353
  end
370
354
 
371
- # Log an +INFO+ message. The message can be passed in either the +message+ argument or in a block.
355
+ # Alias for clear_attributes.
372
356
  #
373
- # @param [Object] message_or_progname_or_tags The message to log or progname
374
- # if the message is passed in a block.
375
- # @param [String] progname_or_tags The name of the program that is logging the message or tags
376
- # if the message is passed in a block.
377
- # @return [void]
378
- def info(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
379
- call_add_entry(INFO, message_or_progname_or_tags, progname_or_tags, &block)
357
+ # @see clear_attributes
358
+ # @deprecated Use clear_attributes instead.
359
+ def untagged(&block)
360
+ Utils.deprecated("Logger#untagged", "Lumberjack::Logger#untagged is deprecated and will be removed in version 2.1; use clear_attributes instead.") do
361
+ clear_attributes(&block)
362
+ end
380
363
  end
381
364
 
382
- # Return +true+ if +INFO+ messages are being logged.
365
+ # Alias for with_level for compatibility with ActiveSupport loggers. This functionality
366
+ # has been moved to the lumberjack_rails gem.
383
367
  #
384
- # @return [Boolean]
385
- def info?
386
- level <= INFO
368
+ # @see with_level
369
+ # @deprecated This implementation is deprecated. Install the lumberjack_rails gem for full support.
370
+ def log_at(level, &block)
371
+ deprecation_message = "Install the lumberjack_rails gem for full support of the log_at method."
372
+ Utils.deprecated("Logger#log_at", deprecation_message) do
373
+ with_level(level, &block)
374
+ end
387
375
  end
388
376
 
389
- # Set the log level to info.
377
+ # Alias for with_level for compatibilty with ActiveSupport loggers. This functionality
378
+ # has been moved to the lumberjack_rails gem.
390
379
  #
391
- # @return [void]
392
- def info!
393
- self.level = INFO
380
+ # @see with_level
381
+ # @deprecated This implementation is deprecated. Install the lumberjack_rails gem for full support.
382
+ def silence(level = Logger::ERROR, &block)
383
+ deprecation_message = "Install the lumberjack_rails gem for full support of the silence method."
384
+ Utils.deprecated("Logger#silence", deprecation_message) do
385
+ with_level(level, &block)
386
+ end
394
387
  end
395
388
 
396
- # Log a +DEBUG+ message. The message can be passed in either the +message+ argument or in a block.
389
+ # Add an entry to the log.
397
390
  #
398
- # @param [Object] message_or_progname_or_tags The message to log or progname
399
- # if the message is passed in a block.
400
- # @param [String, Hash] progname_or_tags The name of the program that is logging the message or tags
401
- # if the message is passed in a block.
391
+ # @param severity [Integer, Symbol, String] The severity of the message.
392
+ # @param message [Object] The message to log.
393
+ # @param progname [String] The name of the program that is logging the message.
394
+ # @param attributes [Hash] The attributes to add to the log entry.
402
395
  # @return [void]
403
- def debug(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
404
- call_add_entry(DEBUG, message_or_progname_or_tags, progname_or_tags, &block)
405
- end
396
+ # @api private
397
+ def add_entry(severity, message, progname = nil, attributes = nil)
398
+ return false unless device
399
+ return false if fiber_local&.logging
406
400
 
407
- # Return +true+ if +DEBUG+ messages are being logged.
408
- #
409
- # @return [Boolean]
410
- def debug?
411
- level <= DEBUG
412
- end
401
+ severity = Severity.label_to_level(severity) unless severity.is_a?(Integer)
413
402
 
414
- # Set the log level to debug.
415
- #
416
- # @return [void]
417
- def debug!
418
- self.level = DEBUG
419
- end
403
+ fiber_locals do |locals|
404
+ locals.logging = true # protection from infinite loops
420
405
 
421
- # Log a message when the severity is not known. Unknown messages will always appear in the log.
422
- # The message can be passed in either the +message+ argument or in a block.
423
- #
424
- # @param [Object] message_or_progname_or_tags The message to log or progname
425
- # if the message is passed in a block.
426
- # @param [String, Hash] progname_or_tags The name of the program that is logging the message or tags
427
- # if the message is passed in a block.
428
- # @return [void]
429
- def unknown(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
430
- call_add_entry(UNKNOWN, message_or_progname_or_tags, progname_or_tags, &block)
431
- end
406
+ time = Time.now
407
+ progname ||= self.progname
408
+ attributes = nil unless attributes.is_a?(Hash)
409
+ attributes = merge_attributes(merge_all_attributes, attributes)
410
+ message, attributes = formatter.format(message, attributes) if formatter
432
411
 
433
- # Add a message when the severity is not known.
434
- #
435
- # @param [Object] msg The message to log.
436
- # @return [void]
437
- def <<(msg)
438
- add_entry(UNKNOWN, msg)
439
- end
412
+ entry = Lumberjack::LogEntry.new(time, severity, message, progname, Process.pid, attributes)
440
413
 
441
- # Silence the logger by setting a new log level inside a block. By default, only +ERROR+ or +FATAL+
442
- # messages will be logged.
443
- #
444
- # @param [Integer, String, Symbol] temporary_level The log level to use inside the block.
445
- # @return [Object] The result of the block.
446
- #
447
- # @example
448
- #
449
- # logger.level = Logger::INFO
450
- # logger.silence do
451
- # do_something # Log level inside the block is +ERROR+
452
- # end
453
- def silence(temporary_level = ERROR, &block)
454
- if silencer
455
- unless temporary_level.is_a?(Integer)
456
- temporary_level = Severity.label_to_level(temporary_level)
457
- end
458
- push_thread_local_value(:lumberjack_logger_level, temporary_level, &block)
459
- else
460
- yield
414
+ write_to_device(entry)
461
415
  end
462
- end
463
416
 
464
- # Provided for compatibility with ActiveSupport::LoggerThreadSafeLevel to temporarily set the log level.
465
- #
466
- # @param [Integer, String, Symbol] level The log level to use inside the block.
467
- # @return [Object] The result of the block.
468
- def log_at(level, &block)
469
- with_level(level, &block)
417
+ true
470
418
  end
471
419
 
472
- # Set the program name that is associated with log messages. If a block
473
- # is given, the program name will be valid only within the block.
420
+ # Return a human-readable representation of the logger showing its key configuration.
474
421
  #
475
- # @param [String] value The program name to use.
476
- # @return [void]
477
- def set_progname(value, &block)
478
- if block
479
- push_thread_local_value(:lumberjack_logger_progname, value, &block)
480
- else
481
- self.progname = value
482
- end
422
+ # @return [String] A string representation of the logger.
423
+ def inspect
424
+ formatted_object_id = object_id.to_s(16).rjust(16, "0")
425
+ "#<Lumberjack::Logger:0x#{formatted_object_id} level:#{Severity.level_to_label(level)} device:#{device.class.name} progname:#{progname.inspect} attributes:#{attributes.inspect}>"
483
426
  end
484
427
 
485
- # Set the logger progname for the duration of the block.
486
- #
487
- # @yield [Object] The block to execute with the program name set.
488
- # @param [String] value The program name to use.
489
- # @return [Object] The result of the block.
490
- def with_progname(value, &block)
491
- set_progname(value, &block)
492
- end
428
+ private
493
429
 
494
- # Get the program name associated with log messages.
495
- #
496
- # @return [String]
497
- def progname
498
- thread_local_value(:lumberjack_logger_progname) || @progname
430
+ def default_context
431
+ @context
499
432
  end
500
433
 
501
- # Set a hash of tags on logger. If a block is given, the tags will only be set
502
- # for the duration of the block. Otherwise the tags will be applied on the current
503
- # logger context for the duration of that context.
504
- #
505
- # If there is no block or context, the tags will be applied to the global context.
506
- # This behavior is deprecated. Use the `tag_globally` method to set global tags instead.
507
- #
508
- # @param [Hash] tags The tags to set.
509
- # @return [void]
510
- def tag(tags, &block)
511
- thread_tags = thread_local_value(:lumberjack_logger_tags)
512
- if block
513
- merged_tags = (thread_tags ? thread_tags.dup : {})
514
- TagContext.new(merged_tags).tag(tags)
515
- push_thread_local_value(:lumberjack_logger_tags, merged_tags, &block)
516
- elsif thread_tags
517
- TagContext.new(thread_tags).tag(tags)
518
- nil
519
- else
520
- 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
521
- tag_globally(tags)
522
- end
523
- end
434
+ def write_to_device(entry) # :nodoc:
435
+ device.write(entry)
436
+ rescue => e
437
+ err = e.class.name.dup
438
+ err << ": #{e.message}" unless e.message.to_s.empty?
439
+ err << " at #{e.backtrace.first}" if e.backtrace
440
+ $stderr.write("#{err}#{Lumberjack::LINE_SEPARATOR}#{entry}#{Lumberjack::LINE_SEPARATOR}") # rubocop:disable Style/StderrPuts
441
+
442
+ raise e if Lumberjack.raise_logger_errors?
524
443
  end
525
444
 
526
- # Set up a context block for the logger. All tags added within the block will be cleared when
527
- # the block exits.
528
- #
529
- # @param [Proc] block The block to execute with the tag context.
530
- # @return [TagContext] If no block is passed, then a Lumberjack::TagContext is returned that can be used
531
- # to interact with the tags (add, remove, etc.).
532
- # @yield [TagContext] If a block is passed, it will be yielded a TagContext object that can be used to
533
- # add or remove tags within the context.
534
- def context(&block)
535
- if block
536
- thread_tags = thread_local_value(:lumberjack_logger_tags)&.dup
537
- thread_tags ||= {}
538
- push_thread_local_value(:lumberjack_logger_tags, thread_tags) do
539
- block.call(TagContext.new(thread_tags))
540
- end
541
- else
542
- TagContext.new(thread_local_value(:lumberjack_logger_tags) || {})
445
+ def build_entry_formatter(formatter, message_formatter, attribute_formatter) # :nodoc:
446
+ entry_formatter = formatter if formatter.is_a?(Lumberjack::EntryFormatter)
447
+
448
+ unless entry_formatter
449
+ message_formatter ||= formatter if formatter.is_a?(Lumberjack::Formatter) || formatter == :default
450
+ entry_formatter = Lumberjack::EntryFormatter.new
543
451
  end
544
- end
545
452
 
546
- # Add global tags to the logger that will appear on all log entries.
547
- #
548
- # @param [Hash] tags The tags to set.
549
- # @return [void]
550
- def tag_globally(tags)
551
- TagContext.new(@tags).tag(tags)
552
- nil
553
- end
453
+ message_formatter = Lumberjack::Formatter.default if message_formatter == :default
554
454
 
555
- # Remove a tag from the current tag context. If this is called inside a tag context,
556
- # the tags will only be removed for the duration of that block. Otherwise they will be removed
557
- # from the global tags.
558
- #
559
- # @param [Array<String, Symbol>] tag_names The tags to remove.
560
- # @return [void]
561
- def remove_tag(*tag_names)
562
- tags = thread_local_value(:lumberjack_logger_tags) || @tags
563
- TagContext.new(tags).delete(*tag_names)
564
- end
455
+ entry_formatter.message_formatter = message_formatter if message_formatter
456
+ entry_formatter.attribute_formatter = attribute_formatter if attribute_formatter
565
457
 
566
- # Return all tags in scope on the logger including global tags set on the Lumberjack
567
- # context, tags set on the logger, and tags set on the current block for the logger.
568
- #
569
- # @return [Hash]
570
- def tags
571
- tags = {}
572
- context_tags = Lumberjack.context_tags
573
- tags.merge!(context_tags) if context_tags && !context_tags.empty?
574
- tags.merge!(@tags) if !@tags.empty? && !thread_local_value(:lumberjack_logger_untagged)
575
- scope_tags = thread_local_value(:lumberjack_logger_tags)
576
- tags.merge!(scope_tags) if scope_tags && !scope_tags.empty?
577
- tags
458
+ entry_formatter
578
459
  end
579
460
 
580
- # Get the value of a tag by name from the current tag context.
581
- #
582
- # @param [String, Symbol] name The name of the tag to get.
583
- # @return [Object, nil] The value of the tag or nil if the tag does not exist.
584
- def tag_value(name)
585
- name = name.join(".") if name.is_a?(Array)
586
- TagContext.new(tags)[name]
587
- end
461
+ def standard_logger_formatter?(formatter)
462
+ return false if formatter.is_a?(Lumberjack::EntryFormatter)
463
+ return false if formatter.is_a?(Lumberjack::Formatter)
464
+ return true if formatter.is_a?(::Logger::Formatter)
588
465
 
589
- # Remove all tags on the current logger and logging context within a block.
590
- # You can still set new block scoped tags within theuntagged block and provide
591
- # tags on individual log methods.
592
- #
593
- # @return [void]
594
- def untagged(&block)
595
- Lumberjack.use_context(nil) do
596
- scope_tags = thread_local_value(:lumberjack_logger_tags)
597
- untagged = thread_local_value(:lumberjack_logger_untagged)
598
- begin
599
- set_thread_local_value(:lumberjack_logger_untagged, true)
600
- set_thread_local_value(:lumberjack_logger_tags, nil)
601
- tag({}, &block)
602
- ensure
603
- set_thread_local_value(:lumberjack_logger_untagged, untagged)
604
- set_thread_local_value(:lumberjack_logger_tags, scope_tags)
605
- end
606
- end
466
+ takes_exactly_n_call_args?(formatter, 4)
607
467
  end
608
468
 
609
- # Return true if the thread is currently in a Lumberjack::Context block.
610
- # When the logger is in a context block, tagging will only apply to that block.
469
+ # Convert a size string with optional unit suffix to an integer size in bytes.
470
+ # Allowed suffixes are K, M, and G (case insensitive) for kilobytes, megabytes, and gigabytes.
611
471
  #
612
- # @return [Boolean]
613
- def in_tag_context?
614
- !!thread_local_value(:lumberjack_logger_tags)
615
- end
616
-
617
- private
472
+ # @param size [String, Integer] The size string to convert.
473
+ # @return [Integer] The size in bytes.
474
+ def size_with_units(size)
475
+ return size unless size.is_a?(String) && size.match?(/\A\d+(\.\d+)?[KMG]?\z/i)
618
476
 
619
- # Dereference arguments to log calls so we can have methods with compatibility with ::Logger
620
- def call_add_entry(severity, message_or_progname_or_tags, progname_or_tags, &block) # :nodoc:
621
- message = nil
622
- progname = nil
623
- tags = nil
624
- if block
625
- message = block
626
- if message_or_progname_or_tags.is_a?(Hash)
627
- tags = message_or_progname_or_tags
628
- progname = progname_or_tags
629
- else
630
- progname = message_or_progname_or_tags
631
- tags = progname_or_tags if progname_or_tags.is_a?(Hash)
632
- end
633
- else
634
- message = message_or_progname_or_tags
635
- if progname_or_tags.is_a?(Hash)
636
- tags = progname_or_tags
637
- else
638
- progname = progname_or_tags
639
- end
477
+ multiplier = case size[-1].upcase
478
+ when "K" then 1024
479
+ when "M" then 1024 * 1024
480
+ when "G" then 1024 * 1024 * 1024
481
+ else 1
640
482
  end
641
- add_entry(severity, message, progname, tags)
642
- end
643
483
 
644
- # Merge a tags hash into an existing tags hash.
645
- def merge_tags(current_tags, tags)
646
- if current_tags.nil? || current_tags.empty?
647
- tags
648
- elsif tags.nil?
649
- current_tags
650
- else
651
- current_tags.merge(tags)
652
- end
484
+ (size.to_f * multiplier).round
653
485
  end
654
486
 
655
- # Set a local value for a thread tied to this object.
656
- def set_thread_local_value(name, value) # :nodoc:
657
- values = Thread.current[name]
658
- unless values
659
- values = {}
660
- Thread.current[name] = values
487
+ def takes_exactly_n_call_args?(callable, count)
488
+ params = if callable.is_a?(Proc)
489
+ callable.parameters
490
+ elsif callable.respond_to?(:call)
491
+ callable.method(:call).parameters
661
492
  end
662
- if value.nil?
663
- values.delete(self)
664
- Thread.current[name] = nil if values.empty?
665
- else
666
- values[self] = value
667
- end
668
- end
669
493
 
670
- # Get a local value for a thread tied to this object.
671
- def thread_local_value(name) # :nodoc:
672
- values = Thread.current[name]
673
- values[self] if values
674
- end
494
+ return false unless params
675
495
 
676
- # Set a local value for a thread tied to this object within a block.
677
- def push_thread_local_value(name, value) # :nodoc:
678
- save_val = thread_local_value(name)
679
- set_thread_local_value(name, value)
680
- begin
681
- yield
682
- ensure
683
- set_thread_local_value(name, save_val)
496
+ positional_arg_count = params.count do |type, _name|
497
+ type == :req || type == :opt
684
498
  end
685
- end
686
499
 
687
- # Open a logging device.
688
- def open_device(device, options) # :nodoc:
689
- if device.nil?
690
- nil
691
- elsif device.is_a?(Device)
692
- device
693
- elsif device.respond_to?(:write) && device.respond_to?(:flush)
694
- Device::Writer.new(device, options)
695
- elsif device == :null
696
- Device::Null.new
697
- else
698
- device = device.to_s
699
- if options[:roll]
700
- Device::DateRollingLogFile.new(device, options)
701
- elsif options[:max_size]
702
- Device::SizeRollingLogFile.new(device, options)
703
- else
704
- Device::LogFile.new(device, options)
705
- end
500
+ has_forbidden_args = params.any? do |type, _name|
501
+ [:rest, :keyreq, :key, :keyrest].include?(type)
706
502
  end
707
- end
708
503
 
709
- def write_to_device(entry) # :nodoc:
710
- device.write(entry)
711
- rescue => e
712
- # rubocop:disable Style/StderrPuts
713
- $stderr.puts("#{e.class.name}: #{e.message}#{" at " + e.backtrace.first if e.backtrace}")
714
- $stderr.puts(entry.to_s)
715
- # rubocop:enable Style/StderrPuts
716
- end
717
-
718
- # Create a thread that will periodically call flush.
719
- def create_flusher_thread(flush_seconds) # :nodoc:
720
- if flush_seconds > 0
721
- logger = self
722
- Thread.new do
723
- until closed?
724
- begin
725
- sleep(flush_seconds)
726
- logger.flush if Time.now - logger.last_flushed_at >= flush_seconds
727
- rescue => e
728
- warn("Error flushing log: #{e.inspect}")
729
- end
730
- end
731
- end
732
- end
504
+ positional_arg_count == 4 && !has_forbidden_args
733
505
  end
734
506
  end
735
507
  end