9 #include "quill/core/Attributes.h" 10 #include "quill/core/Filesystem.h" 11 #include "quill/core/LogLevel.h" 12 #include "quill/core/QuillError.h" 13 #include "quill/sinks/Sink.h" 22 #include <string_view> 23 #include <system_error> 28 #if !defined(WIN32_LEAN_AND_MEAN) 29 #define WIN32_LEAN_AND_MEAN 32 #if !defined(NOMINMAX) 43 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 45 #pragma warning(disable : 4996) 54 using FileEventNotifierHandle = HANDLE;
56 using FileEventNotifierHandle = FILE*;
70 std::function<void(fs::path const& file_path)> before_open;
71 std::function<void(fs::path const& file_path, FileEventNotifierHandle f)> after_open;
72 std::function<void(fs::path const& file_path, FileEventNotifierHandle f)> before_close;
73 std::function<void(fs::path const& file_path)> after_close;
74 std::function<std::string(std::string_view message)> before_write;
91 explicit StreamSink(fs::path stream, FILE* file =
nullptr,
92 std::optional<PatternFormatterOptions>
const& override_pattern_formatter_options = std::nullopt,
94 :
Sink(override_pattern_formatter_options),
95 _filename(std::move(stream)),
97 _file_event_notifier(std::move(file_event_notifier))
100 if (_filename == std::string{
"stdout"})
104 else if (_filename == std::string{
"stderr"})
108 else if (_filename == std::string{
"/dev/null"})
114 _filename = detail::normalize_file_sink_path(_filename);
124 uint64_t , std::string_view ,
125 std::string_view , std::string
const& ,
126 std::string_view , LogLevel ,
129 std::vector<std::pair<std::string, std::string>>
const* ,
130 std::string_view , std::string_view log_statement)
override 132 if (QUILL_UNLIKELY(!_file))
138 if (_file_event_notifier.before_write)
140 std::string
const user_log_statement = _file_event_notifier.before_write(log_statement);
141 safe_fwrite(user_log_statement.data(),
sizeof(char), user_log_statement.size(), _file);
142 _file_size += user_log_statement.size();
146 safe_fwrite(log_statement.data(),
sizeof(char), log_statement.size(), _file);
147 _file_size += log_statement.size();
150 _write_occurred =
true;
158 if (!_write_occurred || !_file)
170 QUILL_NODISCARD
virtual fs::path
const&
get_filename() const noexcept {
return _filename; }
176 QUILL_NODISCARD
bool is_null() const noexcept {
return _is_null; }
185 QUILL_ATTRIBUTE_HOT
static void safe_fwrite(
void const* ptr,
size_t size,
size_t count, FILE* stream)
187 size_t const total_bytes = size * count;
188 size_t bytes_written = 0;
190 while (bytes_written < total_bytes)
192 auto const* current_ptr =
static_cast<char const*
>(ptr) + bytes_written;
193 size_t const remaining = total_bytes - bytes_written;
198 if ((stream == stdout) || (stream == stderr))
200 HANDLE
const handle =
reinterpret_cast<HANDLE
>(::_get_osfhandle(::_fileno(stream)));
202 if (QUILL_LIKELY(handle != INVALID_HANDLE_VALUE))
204 constexpr
size_t max_dword =
static_cast<size_t>(~DWORD{0});
205 auto const total_bytes_remaining =
static_cast<DWORD
>(std::min<size_t>(remaining, max_dword));
206 DWORD bytes_written_this_call = 0;
208 if (QUILL_UNLIKELY(::WriteFile(handle, current_ptr, total_bytes_remaining,
209 &bytes_written_this_call,
nullptr) == 0))
211 QUILL_THROW(
QuillError{std::string{
"WriteFile failed. GetLastError: "} +
212 std::to_string(::GetLastError())});
215 if (QUILL_UNLIKELY(bytes_written_this_call == 0))
217 QUILL_THROW(
QuillError{
"WriteFile returned 0 bytes written without error"});
220 bytes_written += bytes_written_this_call;
228 size_t const written = std::fwrite(current_ptr, 1, remaining, stream);
230 if (QUILL_UNLIKELY(written < remaining))
235 int const saved_errno = errno;
236 std::clearerr(stream);
237 QUILL_THROW(
QuillError{std::string{
"fwrite failed errno: "} + std::to_string(saved_errno) +
238 " error: " + std::strerror(saved_errno)});
245 QuillError{std::string{
"fwrite returned 0 bytes written without error - stream may be " 246 "at EOF or in invalid state"}});
252 bytes_written += written;
257 QUILL_NODISCARD
virtual size_t estimate_write_size(
259 std::string_view , std::string_view ,
260 std::string
const& , std::string_view , LogLevel ,
261 std::string_view , std::string_view ,
262 std::vector<std::pair<std::string, std::string>>
const* ,
263 std::string_view , std::string_view log_statement)
265 return log_statement.size();
277 result = std::fflush(_file);
278 }
while (result != 0 && errno == EINTR);
280 if (QUILL_LIKELY(result == 0))
282 _write_occurred =
false;
286 int const saved_errno = errno;
287 std::clearerr(_file);
288 QUILL_THROW(
QuillError{std::string{
"fflush failed errno: "} + std::to_string(saved_errno) +
289 " error: " + std::strerror(saved_errno)});
295 FILE* _file{
nullptr};
296 size_t _file_size{0};
298 bool _is_null{
false};
299 bool _write_occurred{
false};
304 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) QUILL_ATTRIBUTE_HOT void write_log(MacroMetadata const *, uint64_t, std::string_view, std::string_view, std::string const &, std::string_view, LogLevel, std::string_view, std::string_view, std::vector< std::pair< std::string, std::string >> const *, std::string_view, std::string_view log_statement) override
Writes a formatted log message to the stream.
Definition: StreamSink.h:123
Base class for sinks.
Definition: Sink.h:46
QUILL_NODISCARD bool is_null() const noexcept
Checks if the stream is null.
Definition: StreamSink.h:176
StreamSink(fs::path stream, FILE *file=nullptr, std::optional< PatternFormatterOptions > const &override_pattern_formatter_options=std::nullopt, FileEventNotifier file_event_notifier=FileEventNotifier{})
Constructor for StreamSink.
Definition: StreamSink.h:91
static QUILL_ATTRIBUTE_HOT void safe_fwrite(void const *ptr, size_t size, size_t count, FILE *stream)
Writes data safely to the stream.
Definition: StreamSink.h:185
custom exception
Definition: QuillError.h:47
QUILL_ATTRIBUTE_HOT void flush_sink() override
Flushes the stream.
Definition: StreamSink.h:156
Notifies on file events by calling the appropriate callback.
Definition: StreamSink.h:68
StreamSink class for handling log messages.
Definition: StreamSink.h:80
QUILL_ATTRIBUTE_HOT void flush()
Flushes the stream.
Definition: StreamSink.h:271
virtual QUILL_NODISCARD fs::path const & get_filename() const noexcept
Returns the name of the file.
Definition: StreamSink.h:170