14 const config& streamConfig,
18 const std::filesystem::path path =
19 create_folder_and_get_log_file_path(streamConfig.eLogFilePolicy, streamConfig.svFileName);
24 m_nGranularity = si.dwAllocationGranularity ? si.dwAllocationGranularity : 65536;
26 m_hFile = CreateFileW(
28 GENERIC_READ | GENERIC_WRITE,
31 streamConfig.eLogFilePolicy == log_file_policy::clear_then_uppend ? CREATE_ALWAYS : OPEN_ALWAYS,
32 FILE_ATTRIBUTE_NORMAL,
35 if (m_hFile == INVALID_HANDLE_VALUE)
39 if (!GetFileSizeEx(m_hFile, &sz))
42 m_nSize =
static_cast<size_t>(sz.QuadPart);
44 const long ps = ::sysconf(_SC_PAGESIZE);
45 m_nGranularity = ps > 0 ?
static_cast<size_t>(ps) : 4096ull;
47 int flags = O_RDWR | O_CREAT;
48 if (streamConfig.eLogFilePolicy == log_file_policy::clear_then_uppend)
51 m_Fd = ::open(path.c_str(),
flags, 0644);
56 if (::fstat(m_Fd, &st) != 0)
59 m_nSize =
static_cast<size_t>(st.st_size);
62 const size_t nDefaultStartSize = initialMapSize ?
convert(initialMapSize).to(units::data::bytes) : m_nGranularity;
63 size_t nInitialSize = m_nSize < nDefaultStartSize ? nDefaultStartSize : m_nSize;
64 nInitialSize = align_up_u64(nInitialSize, m_nGranularity);
66 remap_to_capacity(nInitialSize);
73 std::swap(m_hFile, other.m_hFile);
74 std::swap(m_hMap, other.m_hMap);
76 std::swap(m_Fd, other.m_Fd);
78 std::swap(m_pData, other.m_pData);
79 std::swap(m_nSize, other.m_nSize);
80 std::swap(m_nCapacity, other.m_nCapacity);
81 std::swap(m_nGranularity, other.m_nGranularity);
84 inline file_logger_stream_mapping::~file_logger_stream_mapping() noexcept
87 if (m_hFile == INVALID_HANDLE_VALUE)
99 UnmapViewOfFile(m_pData);
101 ::munmap(m_pData,
static_cast<size_t>(m_nCapacity));
116 li.QuadPart =
static_cast<LONGLONG
>(m_nSize);
118 if (!SetFilePointerEx(m_hFile, li,
nullptr, FILE_BEGIN))
121 if (!SetEndOfFile(m_hFile))
124 if (!FlushFileBuffers(m_hFile))
127 CloseHandle(m_hFile);
128 m_hFile = INVALID_HANDLE_VALUE;
130 ::ftruncate(m_Fd,
static_cast<off_t
>(m_nSize));
142 const size_t nRequiredSize = svMessage.size() * std::min(
sizeof(char_type),
sizeof(char16_t));
143 if (!ensure_capacity(nRequiredSize))
146 #if QX_WIN || QX_CONF_USE_CHAR
147 std::memcpy(m_pData + m_nSize, svMessage.data(), nRequiredSize);
148 m_nSize += nRequiredSize;
150 std::array<char16_t, 2048> chunk;
152 const char_type* pData = svMessage.data();
153 size_t nCharsRemaining = svMessage.size();
155 while (nCharsRemaining > 0)
157 size_t nCharsToTake = std::min(nCharsRemaining, chunk.size());
158 for (
size_t i = 0; i < nCharsToTake; ++i)
160 chunk[i] =
static_cast<char16_t
>(
static_cast<char16_t
>(pData[i]) & 0xFFFFu);
163 std::memcpy(m_pData + m_nSize, chunk.data(), nCharsToTake *
sizeof(char16_t));
165 m_nSize += nCharsToTake *
sizeof(char16_t);
166 pData += nCharsToTake;
167 nCharsRemaining -= nCharsToTake;
174 if (!m_pData || m_nSize == 0)
179 if (!FlushViewOfFile(m_pData, m_nSize))
182 ::msync(m_pData,
static_cast<size_t>(m_nSize), MS_SYNC);
186 if (m_hFile != INVALID_HANDLE_VALUE)
193 FlushFileBuffers(m_hFile);
201 GetSystemTimeAsFileTime(&ft);
202 SetFileTime(m_hFile,
nullptr,
nullptr, &ft);
204 struct timespec ts[2];
205 clock_gettime(CLOCK_REALTIME, &ts[0]);
213 inline bool file_logger_stream_mapping::remap_to_capacity(
size_t nNewCapacity) noexcept
218 UnmapViewOfFile(m_pData);
220 ::munmap(m_pData,
static_cast<size_t>(m_nCapacity));
235 li.QuadPart =
static_cast<LONGLONG
>(nNewCapacity);
236 if (!SetFilePointerEx(m_hFile, li,
nullptr, FILE_BEGIN))
238 if (!SetEndOfFile(m_hFile))
241 if (::ftruncate(m_Fd,
static_cast<off_t
>(nNewCapacity)) != 0)
246 DWORD hi =
static_cast<DWORD
>((nNewCapacity >> 32) & 0xFFFFFFFFu);
247 DWORD lo =
static_cast<DWORD
>(nNewCapacity & 0xFFFFFFFFu);
249 m_hMap = CreateFileMappingW(m_hFile,
nullptr, PAGE_READWRITE, hi, lo,
nullptr);
253 void* p = MapViewOfFile(m_hMap, FILE_MAP_ALL_ACCESS, 0, 0, nNewCapacity);
257 void* p = ::mmap(
nullptr,
static_cast<size_t>(nNewCapacity), PROT_READ | PROT_WRITE, MAP_SHARED, m_Fd, 0);
262 m_pData =
static_cast<std::byte*
>(p);
263 m_nCapacity = nNewCapacity;
268 inline bool file_logger_stream_mapping::ensure_capacity(
size_t nAdditionalBytes) noexcept
270 if (m_nSize + nAdditionalBytes <= m_nCapacity)
274 size_t nRequiredSize = m_nSize + nAdditionalBytes;
275 size_t nNewCapacity = m_nCapacity ? m_nCapacity : m_nGranularity;
276 while (nNewCapacity < nRequiredSize)
279 nNewCapacity = align_up_u64(nNewCapacity, m_nGranularity);
280 return remap_to_capacity(nNewCapacity);
283 inline size_t file_logger_stream_mapping::align_up_u64(
size_t nValue,
size_t nAlignment) noexcept
288 const size_t nResult = nValue % nAlignment;
289 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, 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.