source: trunk/GDE/SINA/builddir/include/spdlog/sinks/rotating_file_sink.h

Last change on this file was 19170, checked in by westram, 2 years ago
  • sina source
    • unpack + remove tarball
    • no longer ignore sina builddir.
File size: 4.8 KB
Line 
1//
2// Copyright(c) 2015 Gabi Melman.
3// Distributed under the MIT License (http://opensource.org/licenses/MIT)
4//
5
6#pragma once
7
8#ifndef SPDLOG_H
9#include "spdlog/spdlog.h"
10#endif
11
12#include "spdlog/details/file_helper.h"
13#include "spdlog/details/null_mutex.h"
14#include "spdlog/fmt/fmt.h"
15#include "spdlog/sinks/base_sink.h"
16
17#include <cerrno>
18#include <chrono>
19#include <ctime>
20#include <mutex>
21#include <string>
22#include <tuple>
23
24namespace spdlog {
25namespace sinks {
26
27//
28// Rotating file sink based on size
29//
30template<typename Mutex>
31class rotating_file_sink final : public base_sink<Mutex>
32{
33public:
34    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
35        : base_filename_(std::move(base_filename))
36        , max_size_(max_size)
37        , max_files_(max_files)
38    {
39        file_helper_.open(calc_filename(base_filename_, 0));
40        current_size_ = file_helper_.size(); // expensive. called only once
41    }
42
43    // calc filename according to index and file extension if exists.
44    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
45    static filename_t calc_filename(const filename_t &filename, std::size_t index)
46    {
47        typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
48        if (index != 0u)
49        {
50            filename_t basename, ext;
51            std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
52            fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
53        }
54        else
55        {
56            fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
57        }
58        return fmt::to_string(w);
59    }
60
61protected:
62    void sink_it_(const details::log_msg &msg) override
63    {
64        fmt::memory_buffer formatted;
65        sink::formatter_->format(msg, formatted);
66        current_size_ += formatted.size();
67        if (current_size_ > max_size_)
68        {
69            rotate_();
70            current_size_ = formatted.size();
71        }
72        file_helper_.write(formatted);
73    }
74
75    void flush_() override
76    {
77        file_helper_.flush();
78    }
79
80private:
81    // Rotate files:
82    // log.txt -> log.1.txt
83    // log.1.txt -> log.2.txt
84    // log.2.txt -> log.3.txt
85    // log.3.txt -> delete
86    void rotate_()
87    {
88        using details::os::filename_to_str;
89        file_helper_.close();
90        for (auto i = max_files_; i > 0; --i)
91        {
92            filename_t src = calc_filename(base_filename_, i - 1);
93            if (!details::file_helper::file_exists(src))
94            {
95                continue;
96            }
97            filename_t target = calc_filename(base_filename_, i);
98
99            if (!rename_file(src, target))
100            {
101                // if failed try again after a small delay.
102                // this is a workaround to a windows issue, where very high rotation
103                // rates can cause the rename to fail with permission denied (because of antivirus?).
104                details::os::sleep_for_millis(100);
105                if (!rename_file(src, target))
106                {
107                    file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
108                    current_size_ = 0;
109                    throw spdlog_ex(
110                        "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
111                }
112            }
113        }
114        file_helper_.reopen(true);
115    }
116
117    // delete the target if exists, and rename the src file  to target
118    // return true on success, false otherwise.
119    bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
120    {
121        // try to delete the target file in case it already exists.
122        (void)details::os::remove(target_filename);
123        return details::os::rename(src_filename, target_filename) == 0;
124    }
125
126    filename_t base_filename_;
127    std::size_t max_size_;
128    std::size_t max_files_;
129    std::size_t current_size_;
130    details::file_helper file_helper_;
131};
132
133using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
134using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
135
136} // namespace sinks
137
138//
139// factory functions
140//
141
142template<typename Factory = default_factory>
143inline std::shared_ptr<logger> rotating_logger_mt(
144    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
145{
146    return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
147}
148
149template<typename Factory = default_factory>
150inline std::shared_ptr<logger> rotating_logger_st(
151    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
152{
153    return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
154}
155} // namespace spdlog
Note: See TracBrowser for help on using the repository browser.