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 #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->CloseFile();
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->OpenFile();
76  }
77 
78  if (this->keep_files_ > 0 && this->max_size_ > 0 &&
79  this->file_size_ > this->max_size_)
80  {
81  this->RotateFiles();
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  OpenFile()
99  {
100  if (!file_.is_open())
101  {
102  auto file_name = this->GenerateFileName(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  CloseFile()
120  {
121  if (this - file_.is_open())
122  {
123  this->file_.close();
124  }
125  }
126 
127  void
128  RotateFiles()
129  {
130  this->CloseFile();
131 
132  std::remove(this->GenerateFileName(this->keep_files_ - 1).c_str());
133 
134  for (int i = this->keep_files_ - 2; i >= 0; --i)
135  {
136  auto cur = this->GenerateFileName(i);
137  auto next = this->GenerateFileName(i + 1);
138 
139  std::rename(cur.c_str(), next.c_str());
140  }
141 
142  this->OpenFile();
143  }
144 
145  std::string
146  GenerateFileName(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 firstWrite_;
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