14 const config& streamConfig,
18 const std::filesystem::path path = prepare_folder_and_get_log_file_path(streamConfig);
23 m_nGranularity = si.dwAllocationGranularity ?
static_cast<size_t>(si.dwAllocationGranularity) :
size_t { 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)
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
227 UnmapViewOfFile(m_pData);
229 ::munmap(m_pData,
static_cast<size_t>(m_nCapacity));
244 li.QuadPart =
static_cast<LONGLONG
>(nNewCapacity);
245 if (!SetFilePointerEx(m_hFile, li,
nullptr, FILE_BEGIN))
247 if (!SetEndOfFile(m_hFile))
250 if (::ftruncate(m_Fd,
static_cast<off_t
>(nNewCapacity)) != 0)
255 const u64 nMappingSize =
static_cast<u64>(nNewCapacity);
256 const DWORD hi =
static_cast<DWORD
>(nMappingSize >> std::numeric_limits<DWORD>::digits);
257 const DWORD lo =
static_cast<DWORD
>(nMappingSize);
259 m_hMap = CreateFileMappingW(m_hFile,
nullptr, PAGE_READWRITE, hi, lo,
nullptr);
264 void* p = MapViewOfFile(m_hMap, FILE_MAP_ALL_ACCESS, 0, 0, nNewCapacity);
268 void* p = ::mmap(
nullptr,
static_cast<size_t>(nNewCapacity), PROT_READ | PROT_WRITE, MAP_SHARED, m_Fd, 0);
273 m_pData =
static_cast<std::byte*
>(p);
274 m_nCapacity = nNewCapacity;
279 inline bool file_logger_stream_mapping::ensure_capacity(
size_t nAdditionalBytes) noexcept
281 if (m_nSize + nAdditionalBytes <= m_nCapacity)
285 size_t nRequiredSize = m_nSize + nAdditionalBytes;
286 size_t nNewCapacity = m_nCapacity ? m_nCapacity : m_nGranularity;
287 while (nNewCapacity < nRequiredSize)
290 nNewCapacity = align_up_u64(nNewCapacity, m_nGranularity);
291 return remap_to_capacity(nNewCapacity);
294 inline size_t file_logger_stream_mapping::align_up_u64(
size_t nValue,
size_t nAlignment) noexcept
299 const size_t nResult = nValue % nAlignment;
300 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.