cloudFPGA (cF) API  1.0
The documentation of the source code of cloudFPGA (cF)
ologger.cpp
Go to the documentation of this file.
1 
17 /*
18  * Copyright 2020, arcomber, https://codereview.stackexchange.com/questions/254134/cout-style-logger-in-c
19  */
20 
21 #ifdef _WIN32
22 #define _CRT_SECURE_NO_WARNINGS
23 #define SEPARATOR ('\\')
24 #else
25 #define SEPARATOR ('/')
26 #endif
27 
28 #include "ologger.hpp"
29 
30 #include <cstring>
31 #include <ctime>
32 #include <chrono>
33 #include <stdexcept> // domain_error for bad path
34 
35 static size_t get_next_file_suffix(const std::string& path) {
36  size_t next { 0 };
37  std::fstream fstrm(path + SEPARATOR + "next", std::ofstream::in | std::ofstream::out);
38  if (fstrm) {
39  fstrm >> next;
40  }
41  return next;
42 }
43 
44 // convert blank string to . and remove trailing path separator
45 static const std::string make_path(const std::string& original_path) {
46  std::string massaged_path{ original_path };
47  if (massaged_path.empty()) {
48  massaged_path = ".";
49  }
50 
51  if (massaged_path[massaged_path.size() - 1] == SEPARATOR) {
52  massaged_path = massaged_path.substr(0, massaged_path.size() - 1);
53  }
54  return massaged_path;
55 }
56 
57 const std::string ologger::to_string(ologger::log_level level) {
58  switch (level) {
59  case ologger::log_level::LOG_NONE: return "";
60  case ologger::log_level::LOG_ERROR: return "error";
61  case ologger::log_level::LOG_INFO: return "info";
62  case ologger::log_level::LOG_DEBUG: return "debug";
63  default:
64  return "";
65  }
66 }
67 
68 void ologger::make_logger(const std::string& path, const std::string& file_prefix, const std::string& file_suffix) {
69 
70  size_t next_id = get_next_file_suffix(path);
71  log_stream_.open(path + SEPARATOR + file_prefix + std::to_string(next_id) + "." + file_suffix, std::ofstream::out | std::ofstream::app);
72 
73  if (!log_stream_.good()) {
74  throw std::domain_error("ologger error: unable to open log file: " + path + SEPARATOR + file_prefix + std::to_string(next_id) + "." + file_suffix);
75  }
76 }
77 
78 ologger::ologger(const std::string& path,
79  const std::string& file_prefix,
80  const std::string& file_suffix,
81  size_t max_file_size,
82  size_t max_files,
84  :
85  path_(make_path(path)),
86  file_prefix_(file_prefix),
87  file_suffix_(file_suffix),
88  max_file_size_(max_file_size),
89  max_files_(max_files),
90  level_(level),
91  debug(*this), info(*this), error(*this) {
92 
93  make_logger(path_, file_prefix_, file_suffix);
94 }
95 
96 size_t ologger::changeover_if_required() {
97  size_t next_id{0};
98 
99  if (log_stream_) {
100  const std::streampos pos = log_stream_.tellp();
101 
102  if (static_cast<size_t>(pos) > max_file_size_) {
103  next_id = get_next_file_suffix(path_);
104  next_id = (next_id + 1) % max_files_;
105 
106  std::fstream fstrm(path_ + SEPARATOR + "next", std::ofstream::out | std::ofstream::trunc);
107  if (fstrm) {
108  fstrm << next_id;
109  }
110 
111  log_stream_.close();
112 
113  log_stream_.clear();
114 
115  // if next file exists, delete so we start with empty file
116  const std::string next_file{ path_ + SEPARATOR + file_prefix_ + std::to_string(next_id) + "." + file_suffix_ };
117 
118  std::remove(next_file.c_str());
119 
120  log_stream_.open(next_file, std::ofstream::out | std::ofstream::app);
121  }
122  }
123 
124  return next_id;
125 }
126 
127 
128 std::string get_time_stamp()
129 {
130  const auto now = std::chrono::system_clock::now();
131  const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
132 
133  char timestamp[50]{};
134  std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d,%H:%M:%S", std::localtime(&now_time_t));
135 
136  const int millis = std::chrono::time_point_cast<std::chrono::milliseconds>(now).time_since_epoch().count() % 100;
137  snprintf(timestamp + strlen(timestamp), sizeof(timestamp) - strlen(timestamp), ".%03d,", millis);
138  return timestamp;
139 }
140 
141 void ologger::prefix_message() {
142  log_stream_ << get_time_stamp() << to_string(level_) << ',';
143 }
144 
145 //inner logging level classes for ostream overloading
146 ologger::ologger::Debug::Debug(ologger& parent)
147  : parent_(parent), start_of_line_(true)
148 {}
149 
151 {
152  if (parent_.level_ >= log_level::LOG_INFO) {
153  parent_.log_stream_ << endl;
154  }
155 
156  parent_.changeover_if_required();
157  start_of_line_ = true;
158  return *this;
159 }
160 
161 ologger::ologger::Info::Info(ologger& parent)
162  : parent_(parent), start_of_line_(true)
163 {}
164 
166 {
167  if (parent_.level_ >= log_level::LOG_INFO) {
168  parent_.log_stream_ << endl;
169  }
170 
171  parent_.changeover_if_required();
172  start_of_line_ = true;
173  return *this;
174 }
175 
176 ologger::ologger::Error::Error(ologger& parent)
177  : parent_(parent), start_of_line_(true)
178 {}
179 
181 {
182  if (parent_.level_ >= log_level::LOG_ERROR) {
183  parent_.log_stream_ << endl;
184  }
185 
186  parent_.changeover_if_required();
187  start_of_line_ = true;
188  return *this;
189 }
Debug & operator<<(const T &data)
Definition: ologger.hpp:79
Error & operator<<(const T &data)
Definition: ologger.hpp:131
Info & operator<<(const T &data)
Definition: ologger.hpp:106
std::ostream &(std::ostream &) endl_type
Definition: ologger.hpp:50
ologger(const std::string &path, const std::string &file_prefix, const std::string &file_suffix, size_t max_file_size, size_t max_files, log_level level=log_level::LOG_NONE)
Definition: ologger.cpp:78
out
Definition: test.py:12
std::string get_time_stamp()
Definition: ologger.cpp:128
#define SEPARATOR
Definition: ologger.cpp:25