Splitted file sinks to seperate headers
This commit is contained in:
		
							parent
							
								
									99ca7f1cbe
								
							
						
					
					
						commit
						60f8a68ae0
					
				@ -7,7 +7,9 @@
 | 
			
		||||
// bench.cpp : spdlog benchmarks
 | 
			
		||||
//
 | 
			
		||||
#include "spdlog/async.h"
 | 
			
		||||
#include "spdlog/sinks/file_sinks.h"
 | 
			
		||||
#include "spdlog/sinks/file/simple_file_sink.h"
 | 
			
		||||
#include "spdlog/sinks/file/daily_file_sink.h"
 | 
			
		||||
#include "spdlog/sinks/file/rotating_file_sink.h"
 | 
			
		||||
#include "spdlog/sinks/null_sink.h"
 | 
			
		||||
#include "spdlog/spdlog.h"
 | 
			
		||||
 | 
			
		||||
@ -33,7 +35,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
 | 
			
		||||
    int queue_size = 1048576;
 | 
			
		||||
    int howmany = 1000000;
 | 
			
		||||
    int threads = 1;
 | 
			
		||||
    int threads = 10;
 | 
			
		||||
    int file_size = 30 * 1024 * 1024;
 | 
			
		||||
    int rotating_files = 5;
 | 
			
		||||
 | 
			
		||||
@ -46,7 +48,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
            threads = atoi(argv[2]);
 | 
			
		||||
        if (argc > 3)
 | 
			
		||||
            queue_size = atoi(argv[3]);
 | 
			
		||||
	/*
 | 
			
		||||
	
 | 
			
		||||
        cout << "*******************************************************************************\n";
 | 
			
		||||
        cout << "Single thread, " << format(howmany) << " iterations" << endl;
 | 
			
		||||
        cout << "*******************************************************************************\n";
 | 
			
		||||
@ -67,12 +69,12 @@ int main(int argc, char *argv[])
 | 
			
		||||
        auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
 | 
			
		||||
        bench_mt(howmany, daily_mt, threads);
 | 
			
		||||
        bench(howmany, spdlog::create<null_sink_st>("null_mt"));
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
        cout << "\n*******************************************************************************\n";
 | 
			
		||||
        cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
 | 
			
		||||
        cout << "*******************************************************************************\n";
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < 300; ++i)
 | 
			
		||||
        for (int i = 0; i < 3; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::init_thread_pool(queue_size, 1);
 | 
			
		||||
            auto as = spdlog::basic_logger_mt<spdlog::create_async>("as", "logs/basic_async.log", true);
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,9 @@
 | 
			
		||||
#define SPDLOG_TRACE_ON
 | 
			
		||||
#define SPDLOG_DEBUG_ON
 | 
			
		||||
 | 
			
		||||
#include "spdlog/sinks/file_sinks.h"
 | 
			
		||||
#include "spdlog/sinks/file/simple_file_sink.h"
 | 
			
		||||
#include "spdlog/sinks/file/daily_file_sink.h"
 | 
			
		||||
#include "spdlog/sinks/file/rotating_file_sink.h"
 | 
			
		||||
#include "spdlog/sinks/stdout_color_sinks.h"
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										139
									
								
								include/spdlog/sinks/file/daily_file_sink.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								include/spdlog/sinks/file/daily_file_sink.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,139 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright(c) 2015 Gabi Melman.
 | 
			
		||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "spdlog/spdlog.h"
 | 
			
		||||
#include "spdlog/details/file_helper.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/fmt/fmt.h"
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Default generator of daily log file names.
 | 
			
		||||
 */
 | 
			
		||||
struct default_daily_file_name_calculator
 | 
			
		||||
{
 | 
			
		||||
    // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
 | 
			
		||||
    static filename_t calc_filename(const filename_t &filename)
 | 
			
		||||
    {
 | 
			
		||||
        std::tm tm = spdlog::details::os::localtime();
 | 
			
		||||
        filename_t basename, ext;
 | 
			
		||||
        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
 | 
			
		||||
        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
 | 
			
		||||
        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 | 
			
		||||
            tm.tm_hour, tm.tm_min, ext);
 | 
			
		||||
        return w.str();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Generator of daily log file names in format basename.YYYY-MM-DD.ext
 | 
			
		||||
 */
 | 
			
		||||
struct dateonly_daily_file_name_calculator
 | 
			
		||||
{
 | 
			
		||||
    // Create filename for the form basename.YYYY-MM-DD
 | 
			
		||||
    static filename_t calc_filename(const filename_t &filename)
 | 
			
		||||
    {
 | 
			
		||||
        std::tm tm = spdlog::details::os::localtime();
 | 
			
		||||
        filename_t basename, ext;
 | 
			
		||||
        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
 | 
			
		||||
        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
 | 
			
		||||
        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
 | 
			
		||||
        return w.str();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Rotating file sink based on date. rotates at midnight
 | 
			
		||||
 */
 | 
			
		||||
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
 | 
			
		||||
class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    // create daily file sink which rotates on given time
 | 
			
		||||
    daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute)
 | 
			
		||||
        : _base_filename(std::move(base_filename))
 | 
			
		||||
        , _rotation_h(rotation_hour)
 | 
			
		||||
        , _rotation_m(rotation_minute)
 | 
			
		||||
    {
 | 
			
		||||
        if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
 | 
			
		||||
        {
 | 
			
		||||
            throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
 | 
			
		||||
        }
 | 
			
		||||
        _rotation_tp = _next_rotation_tp();
 | 
			
		||||
        _file_helper.open(FileNameCalc::calc_filename(_base_filename));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void _sink_it(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
        if (std::chrono::system_clock::now() >= _rotation_tp)
 | 
			
		||||
        {
 | 
			
		||||
            _file_helper.open(FileNameCalc::calc_filename(_base_filename));
 | 
			
		||||
            _rotation_tp = _next_rotation_tp();
 | 
			
		||||
        }
 | 
			
		||||
        _file_helper.write(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void _flush() override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::chrono::system_clock::time_point _next_rotation_tp()
 | 
			
		||||
    {
 | 
			
		||||
        auto now = std::chrono::system_clock::now();
 | 
			
		||||
        time_t tnow = std::chrono::system_clock::to_time_t(now);
 | 
			
		||||
        tm date = spdlog::details::os::localtime(tnow);
 | 
			
		||||
        date.tm_hour = _rotation_h;
 | 
			
		||||
        date.tm_min = _rotation_m;
 | 
			
		||||
        date.tm_sec = 0;
 | 
			
		||||
        auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
 | 
			
		||||
        if (rotation_time > now)
 | 
			
		||||
        {
 | 
			
		||||
            return rotation_time;
 | 
			
		||||
        }
 | 
			
		||||
        return {rotation_time + std::chrono::hours(24)};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    filename_t _base_filename;
 | 
			
		||||
    int _rotation_h;
 | 
			
		||||
    int _rotation_m;
 | 
			
		||||
    std::chrono::system_clock::time_point _rotation_tp;
 | 
			
		||||
    details::file_helper _file_helper;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
 | 
			
		||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
 | 
			
		||||
 | 
			
		||||
} // namespace sinks
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// factory functions 
 | 
			
		||||
//
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
 | 
			
		||||
}
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
							
								
								
									
										134
									
								
								include/spdlog/sinks/file/rotating_file_sink.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								include/spdlog/sinks/file/rotating_file_sink.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,134 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright(c) 2015 Gabi Melman.
 | 
			
		||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "spdlog/spdlog.h"
 | 
			
		||||
#include "spdlog/details/file_helper.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/fmt/fmt.h"
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Rotating file sink based on size
 | 
			
		||||
//
 | 
			
		||||
template<class Mutex>
 | 
			
		||||
class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
 | 
			
		||||
        : _base_filename(std::move(base_filename))
 | 
			
		||||
        , _max_size(max_size)
 | 
			
		||||
        , _max_files(max_files)
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.open(calc_filename(_base_filename, 0));
 | 
			
		||||
        _current_size = _file_helper.size(); // expensive. called only once
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // calc filename according to index and file extension if exists.
 | 
			
		||||
    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
 | 
			
		||||
    static filename_t calc_filename(const filename_t &filename, std::size_t index)
 | 
			
		||||
    {
 | 
			
		||||
        typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
 | 
			
		||||
        if (index != 0u)
 | 
			
		||||
        {
 | 
			
		||||
            filename_t basename, ext;
 | 
			
		||||
            std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
 | 
			
		||||
            w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            w.write(SPDLOG_FILENAME_T("{}"), filename);
 | 
			
		||||
        }
 | 
			
		||||
        return w.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void _sink_it(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
        _current_size += msg.formatted.size();
 | 
			
		||||
        if (_current_size > _max_size)
 | 
			
		||||
        {
 | 
			
		||||
            _rotate();
 | 
			
		||||
            _current_size = msg.formatted.size();
 | 
			
		||||
        }
 | 
			
		||||
        _file_helper.write(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void _flush() override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Rotate files:
 | 
			
		||||
    // log.txt -> log.1.txt
 | 
			
		||||
    // log.1.txt -> log.2.txt
 | 
			
		||||
    // log.2.txt -> log.3.txt
 | 
			
		||||
    // log.3.txt -> delete
 | 
			
		||||
    void _rotate()
 | 
			
		||||
    {
 | 
			
		||||
        using details::os::filename_to_str;
 | 
			
		||||
        _file_helper.close();
 | 
			
		||||
        for (auto i = _max_files; i > 0; --i)
 | 
			
		||||
        {
 | 
			
		||||
            filename_t src = calc_filename(_base_filename, i - 1);
 | 
			
		||||
            filename_t target = calc_filename(_base_filename, i);
 | 
			
		||||
 | 
			
		||||
            if (details::file_helper::file_exists(target))
 | 
			
		||||
            {
 | 
			
		||||
                if (details::os::remove(target) != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        _file_helper.reopen(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    filename_t _base_filename;
 | 
			
		||||
    std::size_t _max_size;
 | 
			
		||||
    std::size_t _max_files;
 | 
			
		||||
    std::size_t _current_size;
 | 
			
		||||
    details::file_helper _file_helper;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
 | 
			
		||||
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
 | 
			
		||||
 | 
			
		||||
} // namespace sinks
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// factory functions
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> rotating_logger_mt(
 | 
			
		||||
    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> rotating_logger_st(
 | 
			
		||||
    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
 | 
			
		||||
}
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
							
								
								
									
										78
									
								
								include/spdlog/sinks/file/simple_file_sink.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								include/spdlog/sinks/file/simple_file_sink.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright(c) 2015-2018 Gabi Melman.
 | 
			
		||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "spdlog/spdlog.h"
 | 
			
		||||
#include "spdlog/details/file_helper.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
/*
 | 
			
		||||
 * Trivial file sink with single file as target
 | 
			
		||||
 */
 | 
			
		||||
template<class Mutex>
 | 
			
		||||
class simple_file_sink SPDLOG_FINAL : public base_sink<Mutex>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    explicit simple_file_sink(const filename_t &filename, bool truncate = false)
 | 
			
		||||
        : _force_flush(false)
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.open(filename, truncate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void set_force_flush(bool force_flush)
 | 
			
		||||
    {
 | 
			
		||||
        _force_flush = force_flush;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void _sink_it(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.write(msg);
 | 
			
		||||
        if (_force_flush)
 | 
			
		||||
        {
 | 
			
		||||
            _file_helper.flush();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void _flush() override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    details::file_helper _file_helper;
 | 
			
		||||
    bool _force_flush;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using simple_file_sink_mt = simple_file_sink<std::mutex>;
 | 
			
		||||
using simple_file_sink_st = simple_file_sink<details::null_mutex>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace sinks
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// factory functions
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Basic logger simply writes to given file without any limitations or rotations.
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::simple_file_sink_mt>(logger_name, filename, truncate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::simple_file_sink_st>(logger_name, filename, truncate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
@ -1,304 +0,0 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright(c) 2015 Gabi Melman.
 | 
			
		||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "../details/file_helper.h"
 | 
			
		||||
#include "../details/null_mutex.h"
 | 
			
		||||
#include "../fmt/fmt.h"
 | 
			
		||||
#include "../spdlog.h"
 | 
			
		||||
#include "base_sink.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
/*
 | 
			
		||||
 * Trivial file sink with single file as target
 | 
			
		||||
 */
 | 
			
		||||
template<class Mutex>
 | 
			
		||||
class simple_file_sink SPDLOG_FINAL : public base_sink<Mutex>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    explicit simple_file_sink(const filename_t &filename, bool truncate = false)
 | 
			
		||||
        : _force_flush(false)
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.open(filename, truncate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void set_force_flush(bool force_flush)
 | 
			
		||||
    {
 | 
			
		||||
        _force_flush = force_flush;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void _sink_it(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.write(msg);
 | 
			
		||||
        if (_force_flush)
 | 
			
		||||
        {
 | 
			
		||||
            _file_helper.flush();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void _flush() override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    details::file_helper _file_helper;
 | 
			
		||||
    bool _force_flush;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using simple_file_sink_mt = simple_file_sink<std::mutex>;
 | 
			
		||||
using simple_file_sink_st = simple_file_sink<details::null_mutex>;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Rotating file sink based on size
 | 
			
		||||
 */
 | 
			
		||||
template<class Mutex>
 | 
			
		||||
class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
 | 
			
		||||
        : _base_filename(std::move(base_filename))
 | 
			
		||||
        , _max_size(max_size)
 | 
			
		||||
        , _max_files(max_files)
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.open(calc_filename(_base_filename, 0));
 | 
			
		||||
        _current_size = _file_helper.size(); // expensive. called only once
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // calc filename according to index and file extension if exists.
 | 
			
		||||
    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
 | 
			
		||||
    static filename_t calc_filename(const filename_t &filename, std::size_t index)
 | 
			
		||||
    {
 | 
			
		||||
        typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
 | 
			
		||||
        if (index != 0u)
 | 
			
		||||
        {
 | 
			
		||||
            filename_t basename, ext;
 | 
			
		||||
            std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
 | 
			
		||||
            w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            w.write(SPDLOG_FILENAME_T("{}"), filename);
 | 
			
		||||
        }
 | 
			
		||||
        return w.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void _sink_it(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
        _current_size += msg.formatted.size();
 | 
			
		||||
        if (_current_size > _max_size)
 | 
			
		||||
        {
 | 
			
		||||
            _rotate();
 | 
			
		||||
            _current_size = msg.formatted.size();
 | 
			
		||||
        }
 | 
			
		||||
        _file_helper.write(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void _flush() override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Rotate files:
 | 
			
		||||
    // log.txt -> log.1.txt
 | 
			
		||||
    // log.1.txt -> log.2.txt
 | 
			
		||||
    // log.2.txt -> log.3.txt
 | 
			
		||||
    // log.3.txt -> delete
 | 
			
		||||
    void _rotate()
 | 
			
		||||
    {
 | 
			
		||||
        using details::os::filename_to_str;
 | 
			
		||||
        _file_helper.close();
 | 
			
		||||
        for (auto i = _max_files; i > 0; --i)
 | 
			
		||||
        {
 | 
			
		||||
            filename_t src = calc_filename(_base_filename, i - 1);
 | 
			
		||||
            filename_t target = calc_filename(_base_filename, i);
 | 
			
		||||
 | 
			
		||||
            if (details::file_helper::file_exists(target))
 | 
			
		||||
            {
 | 
			
		||||
                if (details::os::remove(target) != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        _file_helper.reopen(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    filename_t _base_filename;
 | 
			
		||||
    std::size_t _max_size;
 | 
			
		||||
    std::size_t _max_files;
 | 
			
		||||
    std::size_t _current_size;
 | 
			
		||||
    details::file_helper _file_helper;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
 | 
			
		||||
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Default generator of daily log file names.
 | 
			
		||||
 */
 | 
			
		||||
struct default_daily_file_name_calculator
 | 
			
		||||
{
 | 
			
		||||
    // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
 | 
			
		||||
    static filename_t calc_filename(const filename_t &filename)
 | 
			
		||||
    {
 | 
			
		||||
        std::tm tm = spdlog::details::os::localtime();
 | 
			
		||||
        filename_t basename, ext;
 | 
			
		||||
        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
 | 
			
		||||
        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
 | 
			
		||||
        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 | 
			
		||||
            tm.tm_hour, tm.tm_min, ext);
 | 
			
		||||
        return w.str();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Generator of daily log file names in format basename.YYYY-MM-DD.ext
 | 
			
		||||
 */
 | 
			
		||||
struct dateonly_daily_file_name_calculator
 | 
			
		||||
{
 | 
			
		||||
    // Create filename for the form basename.YYYY-MM-DD
 | 
			
		||||
    static filename_t calc_filename(const filename_t &filename)
 | 
			
		||||
    {
 | 
			
		||||
        std::tm tm = spdlog::details::os::localtime();
 | 
			
		||||
        filename_t basename, ext;
 | 
			
		||||
        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
 | 
			
		||||
        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
 | 
			
		||||
        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
 | 
			
		||||
        return w.str();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Rotating file sink based on date. rotates at midnight
 | 
			
		||||
 */
 | 
			
		||||
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
 | 
			
		||||
class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    // create daily file sink which rotates on given time
 | 
			
		||||
    daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute)
 | 
			
		||||
        : _base_filename(std::move(base_filename))
 | 
			
		||||
        , _rotation_h(rotation_hour)
 | 
			
		||||
        , _rotation_m(rotation_minute)
 | 
			
		||||
    {
 | 
			
		||||
        if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
 | 
			
		||||
        {
 | 
			
		||||
            throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
 | 
			
		||||
        }
 | 
			
		||||
        _rotation_tp = _next_rotation_tp();
 | 
			
		||||
        _file_helper.open(FileNameCalc::calc_filename(_base_filename));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void _sink_it(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
        if (std::chrono::system_clock::now() >= _rotation_tp)
 | 
			
		||||
        {
 | 
			
		||||
            _file_helper.open(FileNameCalc::calc_filename(_base_filename));
 | 
			
		||||
            _rotation_tp = _next_rotation_tp();
 | 
			
		||||
        }
 | 
			
		||||
        _file_helper.write(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void _flush() override
 | 
			
		||||
    {
 | 
			
		||||
        _file_helper.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::chrono::system_clock::time_point _next_rotation_tp()
 | 
			
		||||
    {
 | 
			
		||||
        auto now = std::chrono::system_clock::now();
 | 
			
		||||
        time_t tnow = std::chrono::system_clock::to_time_t(now);
 | 
			
		||||
        tm date = spdlog::details::os::localtime(tnow);
 | 
			
		||||
        date.tm_hour = _rotation_h;
 | 
			
		||||
        date.tm_min = _rotation_m;
 | 
			
		||||
        date.tm_sec = 0;
 | 
			
		||||
        auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
 | 
			
		||||
        if (rotation_time > now)
 | 
			
		||||
        {
 | 
			
		||||
            return rotation_time;
 | 
			
		||||
        }
 | 
			
		||||
        return {rotation_time + std::chrono::hours(24)};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    filename_t _base_filename;
 | 
			
		||||
    int _rotation_h;
 | 
			
		||||
    int _rotation_m;
 | 
			
		||||
    std::chrono::system_clock::time_point _rotation_tp;
 | 
			
		||||
    details::file_helper _file_helper;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
 | 
			
		||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
 | 
			
		||||
 | 
			
		||||
} // namespace sinks
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// factory functions to create and register file loggers
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Basic logger simply writes to given file without any limitations or rotations.
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::simple_file_sink_mt>(logger_name, filename, truncate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::simple_file_sink_st>(logger_name, filename, truncate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Create and register multi/single threaded rotating file logger
 | 
			
		||||
//
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> rotating_logger_mt(
 | 
			
		||||
    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> rotating_logger_st(
 | 
			
		||||
    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Create file logger which creates new file on the given time (default in midnight):
 | 
			
		||||
//
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename Factory = default_factory>
 | 
			
		||||
inline std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0)
 | 
			
		||||
{
 | 
			
		||||
    return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
 | 
			
		||||
}
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user