ifm3d
log_writer_file.h
1 // -*- c++ -*-
2 /*
3  * Copyright 2023-present ifm electronic, gmbh
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef IFM3D_COMMON_LOGGING_LOG_WRITER_FILE_H
8 #define IFM3D_COMMON_LOGGING_LOG_WRITER_FILE_H
9 
10 #include <iostream>
11 #include <fstream>
12 #include <cstdio>
13 
14 #include <fmt/color.h>
15 
16 #include <ifm3d/common/logging/log_writer.h>
17 
18 namespace ifm3d
19 {
20  template <class Formatter>
21  class LogWriterFile : public LogWriter
22  {
23  public:
24  LogWriterFile(const std::string& file_name,
25  size_t max_size = 0,
26  int keep_files = 0)
27  : max_size_(max_size),
28  keep_files_(keep_files)
29  {
30  this->SetFileName(file_name);
31  }
32 
33  void
34  SetFileName(const std::string& file_name)
35  {
36  const std::lock_guard<std::mutex> lock(this->mutex_);
37 
38  this->CloseFile();
39 
40  size_t idx_ext = file_name.find_last_of(".");
41  if (idx_ext != std::string::npos)
42  {
43  this->file_stem_ = file_name.substr(0, idx_ext);
44  this->file_ext_ = file_name.substr(idx_ext);
45  }
46  else
47  {
48  this->file_stem_ = file_name;
49  this->file_ext_ = "";
50  }
51  }
52 
53  void
54  SetKeepFiles(int keep_files)
55  {
56  this->keep_files_ = keep_files;
57  }
58 
59  void
60  SetMaxSize(size_t max_size)
61  {
62  this->max_size_ = max_size;
63  }
64 
65  void
66  Write(const LogEntry& entry) override
67  {
68  const auto str = Formatter::format(entry);
69 
70  const std::lock_guard<std::mutex> lock(this->mutex_);
71 
72  if (!this->file_.is_open())
73  {
74  this->OpenFile();
75  }
76 
77  if (this->keep_files_ > 0 && this->max_size_ > 0 &&
78  this->file_size_ > this->max_size_)
79  {
80  this->RotateFiles();
81  }
82 
83  this->file_.write(str.c_str(), str.size());
84  this->file_size_ += str.size();
85 
86 #if defined(_MSC_VER)
87  this->file_.write("\r\n", 2);
88  this->file_size_ += 2;
89 #else
90  this->file_.write("\n", 1);
91  this->file_size_ += 1;
92 #endif
93  }
94 
95  private:
96  void
97  OpenFile()
98  {
99  if (!file_.is_open())
100  {
101  auto file_name = this->GenerateFileName(0);
102  this->file_.open(file_name, std::ios::binary | std::ios::app);
103  if (this->file_)
104  {
105  this->file_.seekp(0, std::ios::beg);
106  auto start = this->file_.tellp();
107  this->file_.seekp(0, std::ios::end);
108  this->file_size_ = this->file_.tellp() - start;
109  }
110  else
111  {
112  this->file_size_ = 0;
113  }
114  }
115  }
116 
117  void
118  CloseFile()
119  {
120  if (this - file_.is_open())
121  {
122  this->file_.close();
123  }
124  }
125 
126  void
127  RotateFiles()
128  {
129  this->CloseFile();
130 
131  std::remove(this->GenerateFileName(this->keep_files_ - 1).c_str());
132 
133  for (int i = this->keep_files_ - 2; i >= 0; --i)
134  {
135  auto cur = this->GenerateFileName(i);
136  auto next = this->GenerateFileName(i + 1);
137 
138  std::rename(cur.c_str(), next.c_str());
139  }
140 
141  this->OpenFile();
142  }
143 
144  std::string
145  GenerateFileName(size_t number)
146  {
147  return number > 0 ?
148  fmt::format("{}.{}{}",
149  this->file_stem_,
150  number,
151  this->file_ext_) :
152  fmt::format("{}{}", this->file_stem_, this->file_ext_);
153  }
154 
155  protected:
156  std::mutex mutex_;
157  std::string file_stem_;
158  std::string file_ext_;
159  std::ofstream file_;
160  size_t file_size_;
161  size_t max_size_;
162  size_t keep_files_;
163  bool firstWrite_;
164  };
165 }
166 #endif // IFM3D_COMMON_LOGGING_LOG_WRITER_FILE_H
ifm3d::LogWriterFile
Definition: log_writer_file.h:21
ifm3d::LogWriter
Definition: log_writer.h:14
ifm3d::LogEntry
Definition: log_entry.h:19