14 const config& streamConfig,
18 const std::filesystem::path path = prepare_folder_and_get_log_file_path(streamConfig);
23 m_nGranularity = si.dwAllocationGranularity ? si.dwAllocationGranularity : 65536;
25 m_hFile = CreateFileW(
27 GENERIC_READ | GENERIC_WRITE,
30 streamConfig.eLogFilePolicy == log_file_policy::clear_then_upend ? CREATE_ALWAYS : OPEN_ALWAYS,
31 FILE_ATTRIBUTE_NORMAL,
34 if (m_hFile == INVALID_HANDLE_VALUE)
38 if (!GetFileSizeEx(m_hFile, &sz))
41 m_nSize =
static_cast<size_t>(sz.QuadPart);
43 const long ps = ::sysconf(_SC_PAGESIZE);
44 m_nGranularity = ps > 0 ?
static_cast<size_t>(ps) : 4096ull;
46 int flags = O_RDWR | O_CREAT;
47 if (streamConfig.eLogFilePolicy == log_file_policy::clear_then_upend)
50 m_Fd = ::open(path.c_str(),
flags, 0644);
55 if (::fstat(m_Fd, &st) != 0)
58 m_nSize =
static_cast<size_t>(st.st_size);
61 const size_t nDefaultStartSize = initialMapSize ?
convert(initialMapSize).to(units::data::bytes) : m_nGranularity;
62 size_t nInitialSize = m_nSize < nDefaultStartSize ? nDefaultStartSize : m_nSize;
63 nInitialSize = align_up_u64(nInitialSize, m_nGranularity);
65 remap_to_capacity(nInitialSize);
72 std::swap(m_hFile, other.m_hFile);
73 std::swap(m_hMap, other.m_hMap);
75 std::swap(m_Fd, other.m_Fd);
77 std::swap(m_pData, other.m_pData);
78 std::swap(m_nSize, other.m_nSize);
79 std::swap(m_nCapacity, other.m_nCapacity);
80 std::swap(m_nGranularity, other.m_nGranularity);
83 inline file_logger_stream_mapping::~file_logger_stream_mapping() noexcept
86 if (m_hFile == INVALID_HANDLE_VALUE)
98 UnmapViewOfFile(m_pData);
100 ::munmap(m_pData,
static_cast<size_t>(m_nCapacity));
115 li.QuadPart =
static_cast<LONGLONG
>(m_nSize);
117 if (!SetFilePointerEx(m_hFile, li,
nullptr, FILE_BEGIN))
120 if (!SetEndOfFile(m_hFile))
123 if (!FlushFileBuffers(m_hFile))
126 CloseHandle(m_hFile);
127 m_hFile = INVALID_HANDLE_VALUE;
129 ::ftruncate(m_Fd,
static_cast<off_t
>(m_nSize));
141 verbosity eVerbosity,
142 std::thread::id threadId,
143 std::chrono::system_clock::time_point messageTime,
145 string_view svFunction,
147 string_view svMessage)
149 const size_t nRequiredSize = svMessage.size() * std::min(
sizeof(char_type),
sizeof(char16_t));
150 if (!ensure_capacity(nRequiredSize))
153 #if QX_WIN || QX_CONF_USE_CHAR
154 std::memcpy(m_pData + m_nSize, svMessage.data(), nRequiredSize);
155 m_nSize += nRequiredSize;
157 std::array<char16_t, 2048> chunk;
159 const char_type* pData = svMessage.data();
160 size_t nCharsRemaining = svMessage.size();
162 while (nCharsRemaining > 0)
164 size_t nCharsToTake = std::min(nCharsRemaining, chunk.size());
165 for (
size_t i = 0; i < nCharsToTake; ++i)
167 chunk[i] =
static_cast<char16_t
>(
static_cast<char16_t
>(pData[i]) & 0xFFFFu);
170 std::memcpy(m_pData + m_nSize, chunk.data(), nCharsToTake *
sizeof(char16_t));
172 m_nSize += nCharsToTake *
sizeof(char16_t);
173 pData += nCharsToTake;
174 nCharsRemaining -= nCharsToTake;
181 if (!m_pData || m_nSize == 0)
186 if (!FlushViewOfFile(m_pData, m_nSize))
189 ::msync(m_pData,
static_cast<size_t>(m_nSize), MS_SYNC);
193 if (m_hFile != INVALID_HANDLE_VALUE)
200 FlushFileBuffers(m_hFile);
208 GetSystemTimeAsFileTime(&ft);
209 SetFileTime(m_hFile,
nullptr,
nullptr, &ft);
211 struct timespec ts[2];
212 clock_gettime(CLOCK_REALTIME, &ts[0]);
220 inline bool file_logger_stream_mapping::remap_to_capacity(
size_t nNewCapacity) noexcept
225 UnmapViewOfFile(m_pData);
227 ::munmap(m_pData,
static_cast<size_t>(m_nCapacity));
242 li.QuadPart =
static_cast<LONGLONG
>(nNewCapacity);
243 if (!SetFilePointerEx(m_hFile, li,
nullptr, FILE_BEGIN))
245 if (!SetEndOfFile(m_hFile))
248 if (::ftruncate(m_Fd,
static_cast<off_t
>(nNewCapacity)) != 0)
253 DWORD hi =
static_cast<DWORD
>((nNewCapacity >> 32) & 0xFFFFFFFFu);
254 DWORD lo =
static_cast<DWORD
>(nNewCapacity & 0xFFFFFFFFu);
256 m_hMap = CreateFileMappingW(m_hFile,
nullptr, PAGE_READWRITE, hi, lo,
nullptr);
260 void* p = MapViewOfFile(m_hMap, FILE_MAP_ALL_ACCESS, 0, 0, nNewCapacity);
264 void* p = ::mmap(
nullptr,
static_cast<size_t>(nNewCapacity), PROT_READ | PROT_WRITE, MAP_SHARED, m_Fd, 0);
269 m_pData =
static_cast<std::byte*
>(p);
270 m_nCapacity = nNewCapacity;
275 inline bool file_logger_stream_mapping::ensure_capacity(
size_t nAdditionalBytes) noexcept
277 if (m_nSize + nAdditionalBytes <= m_nCapacity)
281 size_t nRequiredSize = m_nSize + nAdditionalBytes;
282 size_t nNewCapacity = m_nCapacity ? m_nCapacity : m_nGranularity;
283 while (nNewCapacity < nRequiredSize)
286 nNewCapacity = align_up_u64(nNewCapacity, m_nGranularity);
287 return remap_to_capacity(nNewCapacity);
290 inline size_t file_logger_stream_mapping::align_up_u64(
size_t nValue,
size_t nAlignment) noexcept
295 const size_t nResult = nValue % nAlignment;
296 return nResult ? (nValue + (nAlignment - nResult)) : nValue;
Base class for all file logger streams.
A category is a class that identifies a particular piece of code. This code can be located in differe...
High-performance file logger based on memory-mapped I/O.
virtual void do_log(const category &category, verbosity eVerbosity, std::thread::id threadId, std::chrono::system_clock::time_point messageTime, string_view svFile, string_view svFunction, int nLine, string_view svMessage) override
Proceed stream logging.
virtual void do_flush() override
Flush the stream.
file_logger_stream_mapping(const config &streamConfig=config(), unit< size_t, units::data > initialMapSize={ 1, units::data::mebibytes }) noexcept
file_logger_stream_mapping object constructor
Wrapper for enumerations to be used as a list of flags.