FileOutputBackend#

mw::log File Output Backend Detailed Design
status: draft
security: YES
safety: QM
version: 1

FileOutputBackend implements mw::log::detail::Backend. It is responsible for providing buffers by means of ReserveSlot() call and writing data to the file descriptor passed to its constructor when the buffer is returned by the user by means of calling FlushSlot(). It assumes non-blocking calls and implements measures to store and manage buffers till all the data is pushed out in subsequent calls. Logs are stored in a circular buffer in the case when the current LogRecord is not completely flushed due to the file descriptor not accepting all the data. The rest of the data is to be pushed in subsequent calls to ReserveSlot() and FlushSlot(), which both start by trying to flush the pending data, looping over a circular buffer to reduce the queue of unprocessed logs in the order provided by the caller. Whenever each LogRecord is flushed completely, the buffer is released to the allocator by ReleaseSlot(). In case of depletion of a fixed number of buffers, none is returned to the caller rather than overwriting old ones. The zero-copy approach was used inside the backend design. Circular buffers store objects of SlotHandle type which is just a handle to buffers storing data. It is part of the job of the Recorder to format and copy content into the buffers. The zero-copy principle does not hold for the data provided at a level of and above Recorder. Configuration of the backend is done through resources passed down in constructor arguments consisting of the message builder, the file descriptor, the slot allocator and the OSAL Fcntl and Unistd objects. The constructor sets the file descriptor to non-blocking and close-on-exec. The file is opened by FileRecorderFactory, which also creates the DltMessageBuilder.

Design decision has been made to make backend independent of loosely coupled to details of data it processes which is presented by LogRecord type. To do so object providing IMessageBuilder interface is passed down by the recorder factory to allow access data in polymorphic way. This interface makes it possible to access set of sequential data buffers in order they should be serialized into a file or onto the console. DltMessageBuilder (file) and TextMessageBuilder (console) implement IMessageBuilder. They are responsible for serving data in correct order of flushing and store some of common parts of the header.

SlotDrainer is responsible for storing and disposal of already serialized data. First data gets inserted into circular buffer. After that step program flow enters a loop that iterates over available slots in ring buffer and then by means of NonBlockingWriter iterates over all spans of each message. A flush first continues to write the pending data of the current message, then releases its slot and loads the next message.

Flush procedure exits whenever all available data is written to the file, the writing procedure would block i.e. write operation reports that number of bytes written is less then requested, or the number of slots processed in one cycle (limit_slots_in_one_cycle, default 32) is exceeded.