9 #include "quill/core/Attributes.h" 10 #include "quill/core/Common.h" 11 #include "quill/core/Filesystem.h" 12 #include "quill/core/QuillError.h" 13 #include "quill/core/TimeUtilities.h" 14 #include "quill/sinks/StreamSink.h" 25 #include <string_view> 29 #if !defined(WIN32_LEAN_AND_MEAN) 30 #define WIN32_LEAN_AND_MEAN 33 #if !defined(NOMINMAX) 46 enum class FilenameAppendOption : uint8_t
51 StartCustomTimestampFormat
75 FilenameAppendOption value, std::string_view append_filename_format_pattern = std::string_view{})
77 _filename_append_option = value;
79 if (_filename_append_option == FilenameAppendOption::StartCustomTimestampFormat)
81 if (append_filename_format_pattern.empty())
84 "The 'CustomDateTimeFormat' option was specified, but no format pattern was provided. " 85 "Please set a valid strftime format pattern"});
88 _append_filename_format_pattern = append_filename_format_pattern;
90 else if (_filename_append_option == FilenameAppendOption::StartDateTime)
92 _append_filename_format_pattern =
"_%Y%m%d_%H%M%S";
94 else if (_filename_append_option == FilenameAppendOption::StartDate)
96 _append_filename_format_pattern =
"_%Y%m%d";
107 QUILL_ATTRIBUTE_COLD
void set_timezone(Timezone time_zone) { _time_zone = time_zone; }
121 QUILL_ATTRIBUTE_COLD
void set_open_mode(
char open_mode) { _open_mode = open_mode; }
136 _write_buffer_size = (value == 0) ? 0 : ((value < 4096) ? 4096 : value);
160 _minimum_fsync_interval = value;
174 _override_pattern_formatter_options = options;
178 QUILL_NODISCARD
bool fsync_enabled() const noexcept {
return _fsync_enabled; }
179 QUILL_NODISCARD Timezone timezone()
const noexcept {
return _time_zone; }
180 QUILL_NODISCARD FilenameAppendOption filename_append_option()
const noexcept
182 return _filename_append_option;
184 QUILL_NODISCARD std::string
const& append_filename_format_pattern()
const noexcept
186 return _append_filename_format_pattern;
188 QUILL_NODISCARD std::string
const& open_mode()
const noexcept {
return _open_mode; }
189 QUILL_NODISCARD
size_t write_buffer_size()
const noexcept {
return _write_buffer_size; }
190 QUILL_NODISCARD std::chrono::milliseconds minimum_fsync_interval()
const noexcept
192 return _minimum_fsync_interval;
194 QUILL_NODISCARD std::optional<PatternFormatterOptions>
const& override_pattern_formatter_options()
const noexcept
196 return _override_pattern_formatter_options;
200 std::string _open_mode{
'a'};
201 std::string _append_filename_format_pattern;
202 size_t _write_buffer_size{64 * 1024};
203 std::chrono::milliseconds _minimum_fsync_interval{0};
204 std::optional<PatternFormatterOptions> _override_pattern_formatter_options;
205 Timezone _time_zone{Timezone::LocalTime};
206 FilenameAppendOption _filename_append_option{FilenameAppendOption::None};
207 bool _fsync_enabled{
false};
228 std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now())
229 :
StreamSink(_get_updated_filename_with_appended_datetime(filename, config.filename_append_option(),
230 config.append_filename_format_pattern(),
231 config.timezone(), start_time),
232 nullptr, config.override_pattern_formatter_options(), std::move(file_event_notifier)),
235 if (!_config.fsync_enabled() && (_config.minimum_fsync_interval().count() != 0))
238 QuillError{
"Cannot set a non-zero minimum fsync interval when fsync is disabled."});
243 open_file(_filename, _config.open_mode());
247 ~
FileSink()
override { close_file(); }
254 if (!_write_occurred || !_file)
262 if (_config.fsync_enabled())
267 if (!fs::exists(_filename))
274 open_file(_filename,
"w");
287 std::string
const& append_format_pattern)
290 auto const time_now =
static_cast<time_t
>(timestamp_ns / 1000000000);
293 if (time_zone == Timezone::GmtTime)
303 static constexpr
size_t buffer_size{128};
304 char buffer[buffer_size];
305 std::strftime(buffer, buffer_size, append_format_pattern.data(), &now_tm);
307 return std::string{buffer};
318 return std::make_pair((filename.parent_path() / filename.stem()).
string(), filename.extension().string());
330 std::string
const& append_filename_format_pattern,
332 std::chrono::system_clock::time_point timestamp) noexcept
335 auto const [stem, ext] = extract_stem_and_extension(filename);
338 uint64_t
const timestamp_ns =
static_cast<uint64_t
>(
339 std::chrono::duration_cast<std::chrono::nanoseconds>(timestamp.time_since_epoch()).count());
342 return stem + format_datetime_string(timestamp_ns, time_zone, append_filename_format_pattern) + ext;
350 void open_file(fs::path
const& filename, std::string
const& mode)
352 if (_file_event_notifier.before_open)
354 _file_event_notifier.before_open(filename);
357 _file = fopen(filename.string().data(), mode.data());
361 QUILL_THROW(
QuillError{std::string{
"fopen failed failed path: "} + filename.string() +
" mode: " + mode +
362 " errno: " + std::to_string(errno) +
" error: " + strerror(errno)});
365 if (_config.write_buffer_size() != 0)
367 _write_buffer = std::make_unique<char[]>(_config.write_buffer_size());
369 if (setvbuf(_file, _write_buffer.get(), _IOFBF, _config.write_buffer_size()) != 0)
371 QUILL_THROW(
QuillError{std::string{
"setvbuf failed error: "} + strerror(errno)});
375 if (_file_event_notifier.after_open)
377 _file_event_notifier.after_open(filename, _file);
391 if (_file_event_notifier.before_close)
393 _file_event_notifier.before_close(_filename, _file);
399 if (_file_event_notifier.after_close)
401 _file_event_notifier.after_close(_filename);
412 auto const now = std::chrono::steady_clock::now();
413 if ((now - _last_fsync_timestamp) < _config.minimum_fsync_interval())
417 _last_fsync_timestamp = now;
421 FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(_file))));
423 ::fsync(fileno(_file));
437 QUILL_NODISCARD
static fs::path _get_updated_filename_with_appended_datetime(
438 fs::path
const& filename, FilenameAppendOption append_to_filename_option,
439 std::string
const& append_filename_format_pattern, Timezone time_zone,
440 std::chrono::system_clock::time_point timestamp)
442 if ((append_to_filename_option == FilenameAppendOption::None) || (filename ==
"/dev/null"))
447 if ((append_to_filename_option == FilenameAppendOption::StartCustomTimestampFormat) ||
448 (append_to_filename_option == FilenameAppendOption::StartDate) ||
449 (append_to_filename_option == FilenameAppendOption::StartDateTime))
451 return append_datetime_to_filename(filename, append_filename_format_pattern, time_zone, timestamp);
459 std::chrono::steady_clock::time_point _last_fsync_timestamp{};
460 std::unique_ptr<char[]> _write_buffer;
static QUILL_NODISCARD std::string format_datetime_string(uint64_t timestamp_ns, Timezone time_zone, std::string const &append_format_pattern)
Format a datetime string.
Definition: FileSink.h:286
FileSink Writes the log messages to a file.
Definition: FileSink.h:214
QUILL_NODISCARD bool fsync_enabled() const noexcept
Getters.
Definition: FileSink.h:178
QUILL_ATTRIBUTE_COLD void set_override_pattern_formatter_options(std::optional< PatternFormatterOptions > const &options)
Sets custom pattern formatter options for this sink.
Definition: FileSink.h:172
QUILL_ATTRIBUTE_COLD void set_filename_append_option(FilenameAppendOption value, std::string_view append_filename_format_pattern=std::string_view{})
Sets the append type for the file name.
Definition: FileSink.h:74
QUILL_ATTRIBUTE_COLD void set_fsync_enabled(bool value)
Sets whether fsync should be performed when flushing.
Definition: FileSink.h:114
tm * localtime_rs(time_t const *timer, tm *buf)
Portable localtime_r or _s per operating system.
Definition: TimeUtilities.h:59
void fsync_file(bool force_fsync=false) noexcept
Fsync the file descriptor.
Definition: FileSink.h:408
static QUILL_NODISCARD std::pair< std::string, std::string > extract_stem_and_extension(fs::path const &filename) noexcept
Extract stem and extension from a filename.
Definition: FileSink.h:315
QUILL_ATTRIBUTE_COLD void set_write_buffer_size(size_t value)
Sets the user-defined buffer size for fwrite operations.
Definition: FileSink.h:134
void open_file(fs::path const &filename, std::string const &mode)
Open a file.
Definition: FileSink.h:350
QUILL_ATTRIBUTE_HOT void flush_sink() override
Flushes the stream and optionally fsyncs it.
Definition: FileSink.h:252
tm * gmtime_rs(time_t const *timer, tm *buf)
Portable gmtime_r or _s per operating system.
Definition: TimeUtilities.h:31
void close_file()
Close the file.
Definition: FileSink.h:384
FileSink(fs::path const &filename, FileSinkConfig const &config=FileSinkConfig{}, FileEventNotifier file_event_notifier=FileEventNotifier{}, bool do_fopen=true, std::chrono::system_clock::time_point start_time=std::chrono::system_clock::now())
Construct a FileSink object.
Definition: FileSink.h:226
static QUILL_NODISCARD fs::path append_datetime_to_filename(fs::path const &filename, std::string const &append_filename_format_pattern, Timezone time_zone, std::chrono::system_clock::time_point timestamp) noexcept
Append date and/or time to a filename.
Definition: FileSink.h:329
custom exception
Definition: QuillError.h:45
QUILL_ATTRIBUTE_COLD void set_timezone(Timezone time_zone)
Sets the timezone to use for time-based operations e.g.
Definition: FileSink.h:107
The FileSinkConfig class holds the configuration options for the FileSink.
Definition: FileSink.h:57
QUILL_ATTRIBUTE_HOT void flush_sink() override
Flushes the stream.
Definition: StreamSink.h:166
Notifies on file events by calling the appropriate callback, the callback is executed on the backend ...
Definition: StreamSink.h:36
QUILL_ATTRIBUTE_COLD void set_minimum_fsync_interval(std::chrono::milliseconds value)
Sets the minimum interval between fsync calls.
Definition: FileSink.h:158
StreamSink class for handling log messages.
Definition: StreamSink.h:48
QUILL_ATTRIBUTE_COLD void set_open_mode(char open_mode)
Sets the open mode for the file.
Definition: FileSink.h:121