LogStorage

Memory-mapped daily log file storage.

Declared in src/libs/liblog/log/LogStorage.hpp
Inherits QObject
Group log

Detailed description

LogStorage provides efficient log file management using memory-mapped files. Creates daily rotating log files in a configurable directory. Supports appending new entries, reading recent logs, clearing, and reloading.

The storage never changes the size of a file while it holds a view of that file. Windows refuses to truncate or shrink a file that any view or section maps (ERROR_USER_MAPPED_FILE), and two LogStorage instances can map the same day file at the same time. So:

  • rotateLog() only grows a file, and only before it maps the file. If the grow fails, it maps the length the file already has.
  • clearLogs() writes zeros over the mapped data. It does not truncate.
  • When the file is full on the same day, appendLog() moves the newest lines to the start of the view and zeros the rest. It never writes past the view.

A day file is whole lines, then zeros up to maxSize. A reader of a day file uses readDayFileLines(), which stops at the first zero. A reader that reads to the end of the file reads all of the zeros.

All public methods are thread-safe. One mutex guards the mapped view, the line index and the file handle, so two threads cannot copy to the same offset, and readLogs() never sees a half-written line. Node::log() can run on any thread that holds the node, while a LogWidget reads on the GUI thread. The class emits logChanged() after it releases the lock, and prints its own warnings after it releases the lock. So a slot or a message handler that calls back into the same LogStorage does not deadlock.

The lock is per instance. Two LogStorage instances that map the same day file do not share it.

Run files

With Options::runFile set, the storage keeps one file for one run of a program, at that path (diagnostic-reports §DGC-1). The message handler's file sink uses it. A run file differs from a day file in three ways:

  • It never changes with the day.
  • It starts at kRunFileFirstSize and doubles when it is full, up to maxSize. Only this instance maps it, so the storage can unmap, grow and map again. A file on disk is then at most twice its data, and prune() can count the bytes that the files really use.
  • With Options::ownPrefix false, no prefix and no "Log opened" line is added: the caller formats every line.

prune() deletes the oldest run files of one program.

See also LogHandler.

Member function documentation

readData

[static] QByteArray LogStorage::readData(QIODevice &device, qint64 maxBytes = -1)

Reads the data of a log file from device, from its current position: the bytes before the first NUL byte. With maxBytes not negative, it reads at most that many bytes. It reads in chunks, so it reads at most one chunk of the zero padding.

readDayFileLines

[static] QList<QByteArray> LogStorage::readDayFileLines(QIODevice &device)

Reads the lines of a day file from device, from its current position.

A day file holds whole lines, then zeros up to its full size (see rotateLog()). The zeros are not data, and they are most of the file, which is maxSize bytes long. So this function reads in chunks and stops at the first NUL byte. It reads at most one chunk of the zeros. rebuildIndex() uses the same rule for the end of the data.

Returns each line without its '\\n' and without a trailing '\\r'. Empty lines are not returned. A read error ends the read, so the function always returns.

In this section