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