Add memory mapped file based logging

pull/2604/head
Yuval Deutscher 3 years ago
parent 05e3a73b16
commit a17b085cda

@ -34,7 +34,7 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
close();
filename_ = fname;
auto *mode = SPDLOG_FILENAME_T("ab");
auto *mode = SPDLOG_FILENAME_T("a+b");
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
if (event_handlers_.before_open)
@ -133,6 +133,11 @@ SPDLOG_INLINE const filename_t &file_helper::filename() const
return filename_;
}
SPDLOG_INLINE int file_helper::fileno() const
{
return ::fileno(fd_);
}
//
// return file path and its extension:
//

@ -30,6 +30,7 @@ public:
void write(const memory_buf_t &buf);
size_t size() const;
const filename_t &filename() const;
int fileno() const;
//
// return file path and its extension:

@ -0,0 +1,103 @@
#include "spdlog/common.h"
#include <spdlog/details/file_helper.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/synchronous_factory.h>
#include <mutex>
#include <string>
#include <algorithm>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <cstring>
#include <cstddef>
namespace spdlog {
namespace sinks {
template<typename Mutex>
class mapped_file_sink final : public base_sink<Mutex>
{
private:
static constexpr std::size_t mem_buffer_size = 2 * 1024 * 1024;
struct mmap_deleter
{
void operator()(char *p) const
{
if (munmap(p, mem_buffer_size) != 0)
throw_spdlog_ex("munmap failed", errno);
}
};
std::string path;
std::size_t file_offset = 0;
std::size_t mem_offset = 0;
std::unique_ptr<char[], mmap_deleter> buffer;
details::file_helper file_helper_;
public:
explicit mapped_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
: file_helper_{event_handlers}
{
file_helper_.open(filename, truncate);
map_buffer();
}
mapped_file_sink(const mapped_file_sink &) = delete;
mapped_file_sink(mapped_file_sink &&) noexcept = default;
mapped_file_sink &operator=(const mapped_file_sink &) = delete;
mapped_file_sink &operator=(mapped_file_sink &&) noexcept = default;
~mapped_file_sink() override
{
ftruncate(file_helper_.fileno(), file_offset + mem_offset);
}
protected:
void map_buffer()
{
ftruncate(file_helper_.fileno(), file_offset + mem_buffer_size);
void *addr = mmap(nullptr, mem_buffer_size, PROT_WRITE, MAP_SHARED, file_helper_.fileno(), file_offset);
if (addr == MAP_FAILED)
throw_spdlog_ex("mmap failed", errno);
memset(addr, 0, mem_buffer_size);
buffer.reset(static_cast<char *>(addr));
mem_offset = 0;
}
void sink_it_(const details::log_msg &msg) override
{
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
size_t written = 0;
while (written < formatted.size())
{
auto to_write = std::min(formatted.size() - written, mem_buffer_size - mem_offset);
std::memcpy(buffer.get() + mem_offset, formatted.data() + written, to_write);
written += to_write;
mem_offset += to_write;
if (mem_offset == mem_buffer_size)
{
file_offset += mem_buffer_size;
map_buffer();
}
}
}
void flush_() override
{
if (msync(buffer.get(), mem_buffer_size, MS_SYNC) != 0)
throw_spdlog_ex("msync failed", errno);
}
};
using mapped_file_sink_mt = mapped_file_sink<std::mutex>;
using mapped_file_sink_st = mapped_file_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

@ -32,5 +32,6 @@
#include "spdlog/sinks/ostream_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/mapped_file_sink.h"
#include "spdlog/sinks/msvc_sink.h"
#include "spdlog/pattern_formatter.h"

@ -2,9 +2,11 @@
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
#include "spdlog/spdlog.h"
#define SIMPLE_LOG "test_logs/simple_log"
#define ROTATING_LOG "test_logs/rotating_log"
#define MAPPED_LOG "test_logs/mapped_log"
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
@ -107,3 +109,22 @@ TEST_CASE("rotating_file_logger3", "[rotating_logger]]")
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
REQUIRE_THROWS_AS(spdlog::rotating_logger_mt("logger", basename, max_size, 0), spdlog::spdlog_ex);
}
TEST_CASE("mapped_file_logger", "[mapped_logger]]")
{
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(MAPPED_LOG);
auto logger = spdlog::create<spdlog::sinks::mapped_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
spdlog::shutdown();
logger.reset();
require_message_count(MAPPED_LOG, 2);
using spdlog::details::os::default_eol;
REQUIRE(file_contents(MAPPED_LOG) == spdlog::fmt_lib::format("Test message 1{}Test message 2{}", default_eol, default_eol));
}

Loading…
Cancel
Save