Skip to content

Commit

Permalink
wip: logger: Compressed rotated file for spdlog
Browse files Browse the repository at this point in the history
  • Loading branch information
shramov committed Dec 23, 2024
1 parent 0f968e5 commit 57b9c75
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 16 deletions.
56 changes: 51 additions & 5 deletions src/logger/rotating_file_sink-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/rotating_file_sink.h>
#include <logger/rotating_file_sink.h>
#endif

#include <spdlog/common.h>
Expand All @@ -20,6 +20,11 @@
#include <string>
#include <tuple>

#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <zlib.h>

namespace spdlog {
namespace sinks {

Expand All @@ -29,11 +34,13 @@ SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
std::size_t max_size,
std::size_t max_files,
bool rotate_on_open,
std::size_t compress_begin,
const file_event_handlers &event_handlers)
: base_filename_(std::move(base_filename)),
max_size_(max_size),
max_files_(max_files),
file_helper_{event_handlers} {
file_helper_{event_handlers},
compress_begin_(compress_begin) {
if (max_size == 0) {
throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
}
Expand All @@ -60,6 +67,8 @@ SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename

filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
if (compress_begin_ > 0 && index >= compress_begin_)
ext += ".gz";
return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
}

Expand Down Expand Up @@ -110,14 +119,17 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_() {
if (!path_exists(src)) {
continue;
}

filename_t target = calc_filename(base_filename_, i);

if (!rename_file_(src, target)) {
bool compress = i == compress_begin_;

if (!rename_file_(src, target, compress)) {
// if failed try again after a small delay.
// this is a workaround to a windows issue, where very high rotation
// rates can cause the rename to fail with permission denied (because of antivirus?).
details::os::sleep_for_millis(100);
if (!rename_file_(src, target)) {
if (!rename_file_(src, target, compress)) {
file_helper_.reopen(
true); // truncate the log file anyway to prevent it to grow beyond its limit!
current_size_ = 0;
Expand All @@ -134,11 +146,45 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_() {
// return true on success, false otherwise.
template <typename Mutex>
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename,
const filename_t &target_filename) {
const filename_t &target_filename, bool compress) {
// try to delete the target file in case it already exists.
(void)details::os::remove(target_filename);
if (compress)
compress_file_(src_filename, target_filename);

return details::os::rename(src_filename, target_filename) == 0;
}

template <typename Mutex>
SPDLOG_INLINE bool rotating_file_sink<Mutex>::compress_file_(const filename_t &src_filename,
const filename_t &target_filename) {
auto ifd = open(src_filename.c_str(), O_RDONLY);
if (!ifd)
return false;
struct stat istat;
fstat(ifd, &istat);
auto ptr = mmap(nullptr, istat.st_size, PROT_READ, MAP_PRIVATE, ifd, 0);
if (ptr == MAP_FAILED) {
close(ifd);
return false;
}
bool failed = false;
auto fp = gzopen(target_filename.c_str(), "wb");
if (fp) {
failed = gzwrite(fp, ptr, istat.st_size) == 0;
} else
failed = true;
munmap(ptr, istat.st_size);
close(ifd);
if (fp && gzclose(fp))
failed = true;

if (failed) {
(void)details::os::remove(target_filename);
return false;
}
return true;
}

} // namespace sinks
} // namespace spdlog
14 changes: 10 additions & 4 deletions src/logger/rotating_file_sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class rotating_file_sink final : public base_sink<Mutex> {
std::size_t max_size,
std::size_t max_files,
bool rotate_on_open = false,
size_t compress_begin = 0,
const file_event_handlers &event_handlers = {});
static filename_t calc_filename(const filename_t &filename, std::size_t index);
filename_t calc_filename(const filename_t &filename, std::size_t index);
filename_t filename();

protected:
Expand All @@ -41,15 +42,20 @@ class rotating_file_sink final : public base_sink<Mutex> {
// log.3.txt -> delete
void rotate_();

// delete the target if exists, and rename the src file to target
// delete the target if exists, and rename (with possible compression) the src file to target
// return true on success, false otherwise.
bool rename_file_(const filename_t &src_filename, const filename_t &target_filename);
bool rename_file_(const filename_t &src_filename, const filename_t &target_filename, bool compress);

// Compress file
// return true on success, false otherwise.
bool compress_file_(const filename_t &src_filename, const filename_t &target_filename);

filename_t base_filename_;
std::size_t max_size_;
std::size_t max_files_;
std::size_t current_size_;
details::file_helper file_helper_;
std::size_t compress_begin_ = 0;
};

using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
Expand Down Expand Up @@ -85,5 +91,5 @@ inline std::shared_ptr<logger> rotating_logger_st(const std::string &logger_name
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "rotating_file_sink-inl.h"
#include "logger/rotating_file_sink-inl.h"
#endif
12 changes: 5 additions & 7 deletions src/logger/spdlog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
#include <spdlog/sinks/ansicolor_sink.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#ifndef _WIN32
#include <spdlog/sinks/syslog_sink.h>
#endif

#include <logger/rotating_file_sink.h>
#include <logger/rotating_file_sink-inl.h>

namespace {
spdlog::level::level_enum tll2spdlog(tll_logger_level_t l)
{
Expand Down Expand Up @@ -286,14 +288,10 @@ struct spdlog_impl_t : public tll_logger_impl_t
auto max_size = reader.getT<tll::util::Size>("max-size", 64 * 1024 * 1024);
auto max_files = reader.getT("max-files", 5u);
auto rotate_on_open = reader.getT("rotate-on-open", false);
auto compress_begin = reader.getT("compress-begin", 0u);
if (!reader)
return log.fail(EINVAL, "Invalid parameters for sink {}: {}", *type, reader.error());
#if SPDLOG_VERSION < 10500
sink.sink.reset(new spdlog::sinks::rotating_file_sink_mt(filename, max_size, max_files));
(void) rotate_on_open;
#else
sink.sink.reset(new spdlog::sinks::rotating_file_sink_mt(filename, max_size, max_files, rotate_on_open));
#endif
sink.sink.reset(new spdlog::sinks::rotating_file_sink_mt(filename, max_size, max_files, rotate_on_open, compress_begin));
#ifndef _WIN32
} else if (*type == "syslog") {
auto ident = reader.getT<std::string>("ident", "");
Expand Down

0 comments on commit 57b9c75

Please sign in to comment.