winss
log.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016-2017 Morgan Stanley
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef LIB_WINSS_LOG_LOG_HPP_
18 #define LIB_WINSS_LOG_LOG_HPP_
19 
20 #include <filesystem>
21 #include <algorithm>
22 #include <vector>
23 #include <regex>
24 #include <chrono>
25 #include <utility>
26 #include <string>
27 #include "easylogging/easylogging++.hpp"
28 #include "../filesystem_interface.hpp"
29 #include "../not_owning_ptr.hpp"
30 #include "../path_mutex.hpp"
31 #include "../utils.hpp"
32 #include "log_settings.hpp"
33 #include "log_stream_wrapper.hpp"
34 
35 namespace fs = std::experimental::filesystem;
36 
37 namespace winss {
38 
45  fs::path file;
46  unsigned __int64 time;
47 };
48 
55 template<typename TMutex>
56 class LogTmpl {
57  protected:
61  fs::path current;
62  TMutex mutex;
63  std::regex pattern;
72  bool Rotate() const {
73  writer->Close();
74 
75  auto now = std::chrono::system_clock::now();
76  std::ostringstream os;
77  os << kArchivePrefix << now.time_since_epoch().count() << ".u";
78 
79  fs::path archive = settings.log_dir / os.str();
80 
81  FILESYSTEM.Rename(current, archive);
82 
83  return writer->Open(current);
84  }
85 
92  void CleanArchives() const {
93  VLOG(3) << "Cleaning old archives";
94 
95  std::vector<fs::path> files = FILESYSTEM.GetFiles(settings.log_dir);
96  if (files.size() <= settings.number) {
97  VLOG(3) << "Skipping archive clean up";
98  return;
99  }
100 
101  std::vector<winss::LogArchiveFile> archives;
102  for (const fs::path& file : files) {
103  std::string s = file.filename().string();
104  std::smatch match;
105  if (std::regex_search(s, match, pattern) && match.size() > 1) {
106  std::string m = match[1].str();
107  unsigned __int64 time = std::strtoull(m.c_str(), nullptr, 10);
108  winss::LogArchiveFile archive{ file, time };
109  archives.push_back(std::move(archive));
110  }
111  }
112 
113  std::sort(archives.begin(), archives.end(),
114  [](const winss::LogArchiveFile& f1,
115  const winss::LogArchiveFile& f2) {
116  return f1.time < f2.time;
117  });
118 
119  __int64 to_delete = archives.size() - settings.number;
120  if (to_delete <= 0) {
121  VLOG(3)
122  << "Not cleaning up any archives because there are only "
123  << archives.size();
124  return;
125  }
126 
127  VLOG(3) << "Removing " << to_delete << " archives";
128  for (size_t i = 0; to_delete; ++i, --to_delete) {
129  FILESYSTEM.Remove(archives.at(i).file);
130  }
131  }
132 
133  public:
134  static const int kMutexTaken = 100;
135  static const int kFatalExitCode = 111;
136  static constexpr const char kCurrentLog[8] = "current";
137  static constexpr const char kArchivePrefix[2] = "@";
138  static constexpr const char kMutexName[4] = "log";
149  const winss::LogSettings& settings) : reader(reader), writer(writer),
150  settings(settings), mutex(settings.log_dir, kMutexName),
151  pattern("^" + std::string(kArchivePrefix) + "([\\d]+)\\.\\w$") {
152  current = settings.log_dir / fs::path(kCurrentLog);
153  }
154 
155  LogTmpl(const LogTmpl&) = delete;
156  LogTmpl(LogTmpl&&) = delete;
167  int Start() {
168  if (!FILESYSTEM.DirectoryExists(settings.log_dir)) {
169  LOG(ERROR)
170  << "Directory "
171  << settings.log_dir
172  << " does not exist";
173  return kFatalExitCode;
174  }
175 
176  if (!mutex.Lock()) {
177  return kMutexTaken;
178  }
179 
180  if (!writer->Open(current)) {
181  return kFatalExitCode;
182  }
183 
184  unsigned int size = 0;
185  while (!reader->IsEOF()) {
186  std::streamoff pos = writer->GetPos();
187 
188  if (pos > size) {
189  if (!Rotate()) {
190  return kFatalExitCode;
191  }
192 
193  CleanArchives();
194  }
195 
196  size = settings.file_size;
197 
198  std::string line = reader->GetLine();
199 
200  if (settings.timestamp) {
201  auto now = std::chrono::system_clock::now();
203  writer->Write(" ");
204  }
205 
206  writer->Write(line);
207  writer->WriteLine();
208  }
209 
210  writer->Close();
211  return 0;
212  }
213 
214  LogTmpl& operator=(const LogTmpl&) = delete;
215  LogTmpl& operator=(LogTmpl&&) = delete;
216 };
217 
222 } // namespace winss
223 
224 #endif // LIB_WINSS_LOG_LOG_HPP_
static std::string ConvertToISOString(const std::chrono::system_clock::time_point &time_point)
Convert the time to a ISO string.
Definition: utils.cpp:104
virtual bool Open(fs::path log_path)
Opens the file stream at the given location.
Definition: log_stream_wrapper.cpp:37
winss::NotOwningPtr< winss::LogStreamWriter > writer
Log output.
Definition: log.hpp:59
bool Rotate() const
Rotates the current log file.
Definition: log.hpp:72
virtual void Close()
Closes the currently open stream.
Definition: log_stream_wrapper.cpp:61
virtual void Write(const std::string &line)
Writes the given string to the log stream.
Definition: log_stream_wrapper.cpp:49
void CleanArchives() const
Deletes old archive files.
Definition: log.hpp:92
Definition: case_ignore.hpp:23
winss::NotOwningPtr< winss::LogStreamReader > reader
Log input.
Definition: log.hpp:58
const winss::LogSettings & settings
Logger settings.
Definition: log.hpp:60
fs::path file
The archive file name.
Definition: log.hpp:45
virtual bool IsEOF() const
Gets the end of file state.
Definition: log_stream_wrapper.cpp:25
fs::path current
Current log file.
Definition: log.hpp:61
unsigned __int64 time
The time the archive was taken.
Definition: log.hpp:46
Settings for the logger.
Definition: log_settings.hpp:28
TMutex mutex
Log dir global mutex.
Definition: log.hpp:62
LogTmpl< winss::PathMutex > Log
Concrete log implementation.
Definition: log.hpp:221
fs::path log_dir
The log directory.
Definition: log_settings.hpp:32
virtual std::streamoff GetPos()
Gets the current position in the stream.
Definition: log_stream_wrapper.cpp:57
The logger template.
Definition: log.hpp:56
std::regex pattern
Log file pattern when rotating files.
Definition: log.hpp:63
#define FILESYSTEM
Definition: filesystem_interface.hpp:26
unsigned int file_size
The max file size in bytes.
Definition: log_settings.hpp:30
LogTmpl(winss::NotOwningPtr< winss::LogStreamReader > reader, winss::NotOwningPtr< winss::LogStreamWriter > writer, const winss::LogSettings &settings)
Log template constructor.
Definition: log.hpp:147
virtual std::string GetLine()
Blocks for the next log line.
Definition: log_stream_wrapper.cpp:29
A container for pointers where the lifetime should be owned by the caller.
Definition: not_owning_ptr.hpp:33
unsigned int number
The number of archives to keep.
Definition: log_settings.hpp:29
An archived log file.
Definition: log.hpp:44
virtual void WriteLine()
Writes a line terminator to the stream.
Definition: log_stream_wrapper.cpp:53
bool timestamp
Prepend a ISO 8601 timestamp.
Definition: log_settings.hpp:31
int Start()
Starts the logging implementation.
Definition: log.hpp:167