The global default (unless overwritten by an adapter class) is to use
the JSON encoder since it's the most versatile and flexible option for a
logger today.
An ada pert wraps a log device (e.g. a file, an underlying logger, ...)
and provides an uniform interface to write the encoded log event to its
final target.
By using a registry, we can create the required adapter instance for a
provided log device automatically.
`each_with_object` allocates an array for each kv pair. Switching to
the slightly more verbose but less allocatey `each_pair` eliminates
array allocations.
This follows the similar change in Rails:
960de47f0e
Since a flow will significantly affect the representation of a log event
over its life before being persistet by the final adapter, the new term
better reflects what it is responsible for.
It also helps to avoid the dupliction of meaning between Sink, Target,
Adapter.
Since the Message class now formats the passed message on initialization
anyways, there is no need anymore to retain the formatter there.
Instead, we can just format the message string in the Logger before
creating the Message instance.
This significantly simplifies the Message class and better encapsulates
the knowledge about the line formatter into the Logger class.
Since we can not guarantee that a user-supplied formatter is side-effect
free, a delayed formatting might result in unexpected results. An
example of such a formatter is the one used by
ActiveSupport::TaggedLogging.
These methods are useful convenience methods to add (nested) fields to a
hash while optionally retaining existing values. Usually, `#deep_merge!`
or `#set` will be the commonly used ways to set fields to a hash.
This follows the rule that two objects which are eql? also have the same
hash value. It is also required to ensure that we can use collections in
sets, hashes and arrays where the hash-equality is checked for certain
operations.
This allows to define default values for certain fields which can be
inserted just before a Buffer is flushed. They won;t overwrite prior
user-provided fields.
Through the use of a block, expensive calculations for a field could be
avoided if the field is not going to be inserted, e.g. because it exists
already or is forbidden from being set.
That way, we can ensure that the BufferStack and the Buffers themselves
including their nested fields can not be accessed by different threads,
providing some thread safety for non malicious users.
Using a block here is unnecessary and doesn't help us with any
thread-safty guarantees on deeply nested fields or tags. Thus, we can
just remove this and replace it with a simpler method returning the
top-most buffer.
When using this buffer, we still have to ensure that only a single
Thread can access it.
Generally, a non-buffering Buffer will eventually be flushed to the sink
after each logged message. This thus mostly resembles the way
traditional loggers work in Ruby. A buffering Buffer however holds log
messages, fields and tags for a longer time. Only at a specific time,
all log messages and stored fields will be flushed to the Sink as a
single log event. A common scope for such an event is a full request to
a Rack app.
Each buffer instance can hold messages, fields and tags. These together
form the log event which will eventually be written to the log target.
By adding fields and tags, you can add highly details structured
information to your logs which allow to filter and analyze the logs
without having to parse complex multi-line logs.