Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <cstdio>
16 : : #include <fstream>
17 : : #include <hpactor/log/log_config.hpp>
18 : : #include <hpactor/log/log_sink.hpp>
19 : : #include <mutex>
20 : :
21 : : namespace hpactor::log {
22 : :
23 : : class RotatingFileSink : public ILogSink {
24 : : public:
25 : 4 : explicit RotatingFileSink(const RotatingFileConfig& cfg)
26 : 4 : : cfg_(cfg), file_(cfg.path, std::ios::app | std::ios::out) {
27 : 4 : if (file_.is_open()) {
28 : 4 : file_.seekp(0, std::ios::end);
29 : 4 : bytes_written_ = static_cast<uint64_t>(file_.tellp());
30 : : }
31 : 4 : }
32 : :
33 : 4 : result<void> write(std::string_view line) noexcept override {
34 : 4 : std::lock_guard<std::mutex> lock(mutex_);
35 : 4 : if (!file_.is_open())
36 : 0 : return result<void>::make(error(errors::unknown, "rotating file "
37 : 0 : "sink not open"));
38 : 4 : file_.write(line.data(), static_cast<std::streamsize>(line.size()));
39 : 4 : file_.put('\n');
40 : 4 : if (file_.fail())
41 : 0 : return result<void>::make(error(errors::unknown, "rotating file "
42 : : "sink write "
43 : 0 : "failed"));
44 : 4 : bytes_written_ += line.size() + 1;
45 : :
46 : 4 : if (bytes_written_ >= cfg_.max_bytes) {
47 : 1 : rotate();
48 : : }
49 : 4 : return result<void>::make();
50 : 4 : }
51 : :
52 : 3 : result<void> flush() noexcept override {
53 : 3 : std::lock_guard<std::mutex> lock(mutex_);
54 : 3 : if (file_.is_open())
55 : 3 : file_.flush();
56 : 3 : return result<void>::make();
57 : 3 : }
58 : :
59 : : private:
60 : 1 : void rotate() {
61 : 1 : file_.close();
62 : : // Remove oldest file
63 : 1 : std::string oldest = cfg_.path + "." + std::to_string(cfg_.max_files);
64 : 1 : std::remove(oldest.c_str());
65 : : // Rotate: file.log.N -> file.log.(N+1), file.log -> file.log.1
66 : 2 : for (int i = static_cast<int>(cfg_.max_files) - 1; i >= 1; --i) {
67 : 1 : std::string from = cfg_.path + "." + std::to_string(i);
68 : 1 : std::string to = cfg_.path + "." + std::to_string(i + 1);
69 : 1 : std::rename(from.c_str(), to.c_str());
70 : 1 : }
71 : 1 : std::rename(cfg_.path.c_str(), (cfg_.path + ".1").c_str());
72 : : // Open fresh file
73 : 1 : file_.open(cfg_.path, std::ios::out | std::ios::trunc);
74 : 1 : bytes_written_ = 0;
75 : 1 : }
76 : :
77 : : RotatingFileConfig cfg_;
78 : : std::ofstream file_;
79 : : std::mutex mutex_;
80 : : uint64_t bytes_written_ = 0;
81 : : };
82 : :
83 : 4 : std::unique_ptr<ILogSink> make_rotating_file_sink(const RotatingFileConfig& cfg) {
84 : 4 : return std::make_unique<RotatingFileSink>(cfg);
85 : : }
86 : :
87 : : } // namespace hpactor::log
|