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 verbosity eVerbosity,
143 std::thread::id threadId,
144 std::chrono::system_clock::time_point messageTime,
146 string_view svFunction,
148 string_view svMessage)
150 const size_t nRequiredSize = svMessage.size() * std::min(
sizeof(char_type),
sizeof(char16_t));
151 if (!ensure_capacity(nRequiredSize))
154 #if QX_WIN || QX_CONF_USE_CHAR
155 std::memcpy(m_pData + m_nSize, svMessage.data(), nRequiredSize);
156 m_nSize += nRequiredSize;
158 std::array<char16_t, 2048> chunk;
160 const char_type* pData = svMessage.data();
161 size_t nCharsRemaining = svMessage.size();
163 while (nCharsRemaining > 0)
165 size_t nCharsToTake = std::min(nCharsRemaining, chunk.size());
166 for (
size_t i = 0; i < nCharsToTake; ++i)
168 chunk[i] =
static_cast<char16_t
>(
static_cast<char16_t
>(pData[i]) & 0xFFFFu);
171 std::memcpy(m_pData + m_nSize, chunk.data(), nCharsToTake *
sizeof(char16_t));
173 m_nSize += nCharsToTake *
sizeof(char16_t);
174 pData += nCharsToTake;
175 nCharsRemaining -= nCharsToTake;
182 if (!m_pData || m_nSize == 0)
187 if (!FlushViewOfFile(m_pData, m_nSize))
190 ::msync(m_pData,
static_cast<size_t>(m_nSize), MS_SYNC);
194 if (m_hFile != INVALID_HANDLE_VALUE)
201 FlushFileBuffers(m_hFile);
209 GetSystemTimeAsFileTime(&ft);
210 SetFileTime(m_hFile,
nullptr,
nullptr, &ft);
212 struct timespec ts[2];
213 clock_gettime(CLOCK_REALTIME, &ts[0]);
221 inline bool file_logger_stream_mapping::remap_to_capacity(
size_t nNewCapacity) noexcept
226 UnmapViewOfFile(m_pData);
228 ::munmap(m_pData,
static_cast<size_t>(m_nCapacity));
243 li.QuadPart =
static_cast<LONGLONG
>(nNewCapacity);
244 if (!SetFilePointerEx(m_hFile, li,
nullptr, FILE_BEGIN))
246 if (!SetEndOfFile(m_hFile))
249 if (::ftruncate(m_Fd,
static_cast<off_t
>(nNewCapacity)) != 0)
254 DWORD hi =
static_cast<DWORD
>((nNewCapacity >> 32) & 0xFFFFFFFFu);
255 DWORD lo =
static_cast<DWORD
>(nNewCapacity & 0xFFFFFFFFu);
257 m_hMap = CreateFileMappingW(m_hFile,
nullptr, PAGE_READWRITE, hi, lo,
nullptr);
261 void* p = MapViewOfFile(m_hMap, FILE_MAP_ALL_ACCESS, 0, 0, nNewCapacity);
265 void* p = ::mmap(
nullptr,
static_cast<size_t>(nNewCapacity), PROT_READ | PROT_WRITE, MAP_SHARED, m_Fd, 0);
270 m_pData =
static_cast<std::byte*
>(p);
271 m_nCapacity = nNewCapacity;
276 inline bool file_logger_stream_mapping::ensure_capacity(
size_t nAdditionalBytes) noexcept
278 if (m_nSize + nAdditionalBytes <= m_nCapacity)
282 size_t nRequiredSize = m_nSize + nAdditionalBytes;
283 size_t nNewCapacity = m_nCapacity ? m_nCapacity : m_nGranularity;
284 while (nNewCapacity < nRequiredSize)
287 nNewCapacity = align_up_u64(nNewCapacity, m_nGranularity);
288 return remap_to_capacity(nNewCapacity);
291 inline size_t file_logger_stream_mapping::align_up_u64(
size_t nValue,
size_t nAlignment) noexcept
296 const size_t nResult = nValue % nAlignment;
297 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.