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) 57 std::function<void(fs::path const& file_path)> before_open;
58 std::function<void(fs::path const& file_path, FILE* f)> after_open;
59 std::function<void(fs::path const& file_path, FILE* f)> before_close;
60 std::function<void(fs::path const& file_path)> after_close;
61 std::function<std::string(std::string_view message)> before_write;
78 explicit StreamSink(fs::path stream, FILE* file =
nullptr,
79 std::optional<PatternFormatterOptions>
const& override_pattern_formatter_options = std::nullopt,
81 :
Sink(override_pattern_formatter_options),
82 _filename(std::move(stream)),
84 _file_event_notifier(std::move(file_event_notifier))
87 if (_filename == std::string{
"stdout"})
91 else if (_filename == std::string{
"stderr"})
95 else if (_filename == std::string{
"/dev/null"})
103 fs::path parent_path;
105 if (!_filename.parent_path().empty())
107 parent_path = _filename.parent_path();
115 auto const st = fs::status(parent_path, ec);
116 if (!is_directory(st))
118 fs::create_directories(parent_path, ec);
122 QUILL_THROW(
QuillError{std::string{
"cannot create directories for path "} +
123 parent_path.string() + std::string{
" - error: "} + ec.message()});
129 parent_path = fs::current_path();
133 fs::path
const canonical_path = fs::canonical(parent_path, ec);
138 QUILL_THROW(
QuillError{std::string{
"cannot create canonical path for path "} +
139 parent_path.string() + std::string{
" - error: "} + ec.message()});
143 _filename = canonical_path / _filename.filename();
153 uint64_t , std::string_view ,
154 std::string_view , std::string
const& ,
155 std::string_view , LogLevel ,
158 std::vector<std::pair<std::string, std::string>>
const* ,
159 std::string_view , std::string_view log_statement)
override 161 if (QUILL_UNLIKELY(!_file))
167 if (_file_event_notifier.before_write)
169 std::string
const user_log_statement = _file_event_notifier.before_write(log_statement);
170 safe_fwrite(user_log_statement.data(),
sizeof(char), user_log_statement.size(), _file);
171 _file_size += user_log_statement.size();
175 safe_fwrite(log_statement.data(),
sizeof(char), log_statement.size(), _file);
176 _file_size += log_statement.size();
179 _write_occurred =
true;
187 if (!_write_occurred || !_file)
199 QUILL_NODISCARD
virtual fs::path
const&
get_filename() const noexcept {
return _filename; }
205 QUILL_NODISCARD
bool is_null() const noexcept {
return _is_null; }
214 QUILL_ATTRIBUTE_HOT
static void safe_fwrite(
void const* ptr,
size_t size,
size_t count, FILE* stream)
216 size_t const total_bytes = size * count;
217 size_t bytes_written = 0;
219 while (bytes_written < total_bytes)
221 auto const* current_ptr =
static_cast<char const*
>(ptr) + bytes_written;
222 size_t const remaining = total_bytes - bytes_written;
227 if ((stream == stdout) || (stream == stderr))
229 HANDLE
const handle =
reinterpret_cast<HANDLE
>(::_get_osfhandle(::_fileno(stream)));
231 if (QUILL_LIKELY(handle != INVALID_HANDLE_VALUE))
233 auto const total_bytes_remaining =
static_cast<DWORD
>(remaining);
234 DWORD bytes_written_this_call = 0;
236 if (QUILL_UNLIKELY((::WriteFile(handle, current_ptr, total_bytes_remaining,
237 &bytes_written_this_call,
nullptr) == 0) ||
238 (bytes_written_this_call != total_bytes_remaining)))
240 QUILL_THROW(
QuillError{std::string{
"WriteFile failed. GetLastError: "} +
241 std::to_string(::GetLastError())});
244 bytes_written += bytes_written_this_call;
252 size_t const written = std::fwrite(current_ptr, 1, remaining, stream);
254 if (QUILL_UNLIKELY(written < remaining))
259 int const saved_errno = errno;
260 std::clearerr(stream);
261 QUILL_THROW(
QuillError{std::string{
"fwrite failed errno: "} + std::to_string(saved_errno) +
262 " error: " + std::strerror(saved_errno)});
269 QuillError{std::string{
"fwrite returned 0 bytes written without error - stream may be " 270 "at EOF or in invalid state"}});
276 bytes_written += written;
286 int const result = std::fflush(_file);
288 if (QUILL_LIKELY(result == 0))
290 _write_occurred =
false;
294 int const saved_errno = errno;
295 std::clearerr(_file);
296 QUILL_THROW(
QuillError{std::string{
"fflush failed errno: "} + std::to_string(saved_errno) +
297 " error: " + std::strerror(saved_errno)});
303 FILE* _file{
nullptr};
304 size_t _file_size{0};
306 bool _is_null{
false};
307 bool _write_occurred{
false};
310 #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:152
Base class for sinks.
Definition: Sink.h:40
QUILL_NODISCARD bool is_null() const noexcept
Checks if the stream is null.
Definition: StreamSink.h:205
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:78
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:214
custom exception
Definition: QuillError.h:45
QUILL_ATTRIBUTE_HOT void flush_sink() override
Flushes the stream.
Definition: StreamSink.h:185
Notifies on file events by calling the appropriate callback, the callback is executed on the backend ...
Definition: StreamSink.h:55
StreamSink class for handling log messages.
Definition: StreamSink.h:67
QUILL_ATTRIBUTE_HOT void flush()
Flushes the stream.
Definition: StreamSink.h:284
virtual QUILL_NODISCARD fs::path const & get_filename() const noexcept
Returns the name of the file.
Definition: StreamSink.h:199