├── CMakeLists.txt ├── README.md └── lib ├── filebuffer.cpp ├── filebuffer.h └── utils.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.11) 2 | 3 | project(FastLogger) 4 | 5 | file(GLOB MY_SRCS "lib/*.cpp" "lib/*.h") 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 8 | 9 | add_library(${PROJECT_NAME} STATIC ${MY_SRCS}) 10 | 11 | target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/lib") 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fast logger 2 | -------------------------------------------------------------------------------- /lib/filebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "filebuffer.h" 2 | #include "utils.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /// The Pimpl implementation for a file buffer 15 | struct FileBuffer::Impl final 16 | { 17 | // one megabyte for now 18 | static constexpr size_t FILESIZE = 1024 * 1024; 19 | 20 | // counter for each unique file buffer object within the process 21 | static std::atomic mThreadId; 22 | 23 | // the address of the memory mapped file 24 | char* mAddress = nullptr; 25 | 26 | Impl(const Impl&) = delete; 27 | Impl() = default; 28 | Impl(Impl&&) noexcept = default; 29 | Impl& operator=(const Impl&) = delete; 30 | Impl& operator=(Impl&&) noexcept = default; 31 | }; 32 | 33 | // static initialisation 34 | std::atomic FileBuffer::Impl::mThreadId{0}; 35 | 36 | FileBuffer::FileBuffer() 37 | : mImpl(utilities::make_unique()) 38 | { 39 | std::ostringstream filename; 40 | filename << "/tmp/fastlogger-" << getpid() << "-" << mImpl->mThreadId++; 41 | int fd = open(filename.str().c_str(), O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR ); 42 | if (fd == -1) 43 | { 44 | auto errorText = "Failed to open log file for writing: " + std::string(strerror(errno)); 45 | throw std::runtime_error(errorText); 46 | } 47 | if ((mImpl->mAddress = (char*)mmap(nullptr, mImpl->FILESIZE, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) 48 | { 49 | auto errorText = "Failed to memory map log file: " + std::string(strerror(errno)); 50 | throw std::runtime_error(errorText); 51 | } 52 | if (ftruncate(fd, mImpl->FILESIZE) == -1) 53 | { 54 | auto errorText = "Failed to truncate log file: " + std::string(strerror(errno)); 55 | throw std::runtime_error(errorText); 56 | } 57 | if (close(fd) == -1) 58 | { 59 | auto errorText = "Failed to close log file: " + std::string(strerror(errno)); 60 | throw std::runtime_error(errorText); 61 | } 62 | bzero(mImpl->mAddress, mImpl->FILESIZE); // rely on all unwritten bytes being zero 63 | } 64 | 65 | FileBuffer::FileBuffer(FileBuffer&& buffer) noexcept 66 | { 67 | mImpl = std::move(buffer.mImpl); 68 | } 69 | 70 | FileBuffer::~FileBuffer() noexcept 71 | { 72 | CloseNoThrow(); 73 | } 74 | 75 | FileBuffer& FileBuffer::operator= (FileBuffer&& buffer) noexcept 76 | { 77 | if (this != &buffer) 78 | { 79 | CloseNoThrow(); // close previously mapped file (if exists) 80 | mImpl = std::move(buffer.mImpl); 81 | } 82 | return *this; 83 | } 84 | 85 | char*& FileBuffer::GetInternalBuffer() 86 | { 87 | return mImpl->mAddress; 88 | } 89 | 90 | void FileBuffer::WriteTimestamp() 91 | { 92 | struct timespec now; 93 | clock_gettime(CLOCK_MONOTONIC_COARSE, &now); 94 | memcpy(mImpl->mAddress, &now, sizeof(now)); 95 | mImpl->mAddress += sizeof(now); 96 | } 97 | 98 | void FileBuffer::Close() 99 | { 100 | if (mImpl) 101 | { 102 | if (munmap(mImpl->mAddress, Impl::FILESIZE) == -1) 103 | { 104 | auto errorText = "Failed to unmap log file: " + std::string(strerror(errno)); 105 | throw std::runtime_error(errorText); 106 | } 107 | } 108 | } 109 | 110 | void FileBuffer::CloseNoThrow() noexcept 111 | { 112 | try 113 | { 114 | Close(); 115 | } 116 | catch (...) 117 | { 118 | } 119 | } 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /lib/filebuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef FILEBUFFER_H 2 | #define FILEBUFFER_H 3 | 4 | #include 5 | #include 6 | 7 | class FileBuffer final 8 | { 9 | private: 10 | /// data members 11 | struct Impl; 12 | std::unique_ptr mImpl; 13 | 14 | /// useful helper methods 15 | void CloseNoThrow() noexcept; 16 | char*& GetInternalBuffer(); 17 | void WriteTimestamp(); 18 | 19 | public: 20 | /// constructors 21 | FileBuffer(); // once constructed, file has been mapped 22 | FileBuffer(const FileBuffer&) = delete; // copying a buffer makes no sense 23 | FileBuffer(FileBuffer&& buffer) noexcept; // transferring ownership is allowed 24 | virtual ~FileBuffer() noexcept; 25 | FileBuffer& operator= (const FileBuffer&) = delete; // can't copy by way of assignment (copies are not allowed) 26 | FileBuffer& operator= (FileBuffer&& buffer) noexcept; // can move by way of assignment 27 | 28 | /// serialisation routines that messages will call within their serialisation routine 29 | template FileBuffer& operator<< (const T& type) noexcept; 30 | 31 | /// call this to serialise an entire message (sort of a template method pattern) 32 | template void Serialise(const T& message); 33 | 34 | /// ask the buffer to flush to disk and clean up 35 | void Close(); 36 | }; 37 | 38 | template 39 | void FileBuffer::Serialise(const T& message) 40 | { 41 | // TODO journalling (just do a munmap and create new buffer), otherwise we will segfault 42 | // Maybe ask for serialised size, and if not enough space, flush out (or use hardcoded limit) 43 | 44 | if (mImpl) 45 | { 46 | WriteTimestamp(); 47 | message.Serialise(GetInternalBuffer()); 48 | } 49 | } 50 | 51 | #endif // FILEBUFFER_H 52 | -------------------------------------------------------------------------------- /lib/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | 6 | namespace utilities 7 | { 8 | 9 | template 10 | std::unique_ptr make_unique(Ts&&... params) 11 | { 12 | return std::unique_ptr(new T(std::forward(params)...)); 13 | } 14 | 15 | } 16 | #endif // UTILS_H 17 | --------------------------------------------------------------------------------