scclib
Stable Cloud Computing C++ Library
logger.cc
Go to the documentation of this file.
1 /*
2 BSD 3-Clause License
3 
4 Copyright (c) 2022, Stable Cloud Computing, Inc.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 1. Redistributions of source code must retain the above copyright notice, this
10  list of conditions and the following disclaimer.
11 
12 2. Redistributions in binary form must reproduce the above copyright notice,
13  this list of conditions and the following disclaimer in the documentation
14  and/or other materials provided with the distribution.
15 
16 3. Neither the name of the copyright holder nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <util/logger.h>
32 #include <gtest/gtest.h>
33 #include <cstring>
34 #include <iostream>
35 #include <fstream>
36 #include <sstream>
37 #include <thread>
38 #include <list>
39 #include <util/fs.h>
40 
47 using std::cout;
48 using std::endl;
49 using std::vector;
50 using std::thread;
51 using std::string;
52 using std::stringstream;
53 using std::ifstream;
54 using std::ofstream;
55 using std::shared_ptr;
56 using std::system_error;
57 using std::make_shared;
58 using std::istreambuf_iterator;
59 using std::set;
61 using scc::util::Logger;
62 
63 struct LoggerTest : public testing::Test
64 {
65  shared_ptr<stringstream> strings;
66  string fname;
67  shared_ptr<ofstream> files;
68  Logger log;
69 
70  string m_curdir;
71  LoggerTest()
72  {
73  system_error err;
74  m_curdir = fs::get_current_dir();
75  fs::remove_all("sandbox", &err);
76  fs::create_dir("sandbox");
77  fs::change_dir("sandbox");
78 
79  strings.reset(new stringstream());
80  fname = "ofstream_test";
81  files.reset(new ofstream(fname));
82 
83  log.add_cout();
84  log.add(strings);
85  log.add(files);
86  }
87  virtual ~LoggerTest()
88  {
89  fs::change_dir(m_curdir);
90  system_error err;
91  fs::remove_all("sandbox", &err);
92  }
93 
94  std::string read_file()
95  {
96  ifstream f(fname);
97  // load the file contents using an iterator to the ifstream's streambuf
98  // the default-constructed iterator is the stream end
99  // see: https://en.cppreference.com/w/cpp/iterator/istreambuf_iterator
100  istreambuf_iterator<char> it{f}, end;
101  string ss{it, end};
102  return ss;
103  }
104 
105  void validate(const string& logs)
106  {
107  ASSERT_EQ(strings->str(), logs);
108  ASSERT_EQ(read_file(), logs);
109  }
110 };
111 
112 TEST(LoggerTestBasic, move_and_construct) // basic test not attached to the test class
113 {
114  stringstream ver;
115 
116  auto s = make_shared<stringstream>();
117  Logger log(s);
118  log.add_cout();
119 
120  log.add(s);
121 
122  log << "Original 1" << endl;
123  ver << "Original 1" << endl;
124 
125  Logger cc(log);
126  cc << "Copy constructed" << endl;
127  ver << "Copy constructed" << endl;
128  log << "Original 2" << endl;
129  ver << "Original 2" << endl;
130 
131  Logger ca = log;
132  ca << "Copy assigned" << endl;
133  ver << "Copy assigned" << endl;
134  log << "Original 3" << endl;
135  ver << "Original 3" << endl;
136 
137  Logger mc(std::move(cc));
138  mc << "Move constructed" << endl;
139  ver << "Move constructed" << endl;
140  cc << "Original 4 (won't see this)" << endl;
141 
142  Logger ma = std::move(ca);
143  ma << "Move assigned" << endl;
144  ver << "Move assigned" << endl;
145  ca << "Original 5 (won't see this)" << endl;
146 
147  cout << "** start readback from log stringstream" << endl;
148  cout << s->str();
149  cout << "** end readback from log stringstream" << endl;
150 
151  cout << "** start readback from verify stringstream" << endl;
152  cout << ver.str();
153  cout << "** end readback from verify stringstream" << endl;
154 
155  ASSERT_EQ(s->str(), ver.str());
156 }
157 
158 TEST_F(LoggerTest, id)
159 {
160  log.id("test");
161  log << "line" << endl;
162  validate("[test] line\n");
163 }
164 
165 TEST_F(LoggerTest, timestamp)
166 {
167  log.timestamp_iso();
168  log << "line" << endl;
169 
170  ASSERT_EQ(strings->str().size(), 26);
171  ASSERT_EQ(strings->str().substr(20), " line\n");
172 }
173 
174 TEST_F(LoggerTest, multiline)
175 {
176  stringstream s;
177  log.multiline(5);
178  log.id(1);
179 
180  log << "m 1" << endl;
181  s << "[1] m 1" << endl;
182  log << " m 2" << endl;
183  s << " m 2" << endl;
184  log << " m 3" << endl;
185  s << " m 3" << endl;
186  log << " m 4" << endl;
187  s << " m 4" << endl;
188  log << " m 5" << endl;
189  s << " m 5" << endl;
190  log << " m 6" << endl;
191  s << "[1] m 6" << endl;
192 
193  validate(s.str());
194 }
195 
196 TEST_F(LoggerTest, multithread_multi_loggers)
197 {
199 
200  /*
201  The test class has the following global objects
202 
203  shared_ptr<stringstream> strings;
204  shared_ptr<ofstream> files;
205  Logger log; // logger with strings, files, and cout attached
206 
207  This test will create 5 threads, each thread create a logger and attach to the three streams.
208 
209  Each thread will write 5 log strings (with id set to the thread number)
210 
211  The main thread will write 5 log strings (with id set to 0).
212  */
213 
214  auto f = [&](int n)
215  {
216  Logger tlog(strings);
217  tlog.add(files);
218  tlog.add_cout();
219  tlog.id(n);
220  for (int i = 1; i <= 5; i++)
221  {
222  tlog << "this is log number " << i << " from thread number " << n << endl;
223  }
224  };
225 
226  vector<thread> tv;
227 
228  for (int n = 1; n <= 5; n++)
229  {
230  tv.emplace_back(f, n);
231  }
232 
233  log.id(0);
234  for (int i = 1; i <= 5; i++)
235  {
236  log << "this is log number " << i << " from thread number " << 0 << endl;
237  }
238 
239  for (auto& t : tv)
240  {
241  t.join();
242  }
243 
244  /*
245  Read the lines back and put them in order
246  */
247 
248  string line;
249  set<string> ordered_strings;
250  while (getline(*strings, line))
251  {
252  ordered_strings.insert(line);
253  }
254 
255  stringstream fs(read_file());
256  set<string> ordered_files;
257  while (getline(fs, line))
258  {
259  ordered_files.insert(line);
260  }
261 
262  cout << "* ordered stringstream" << endl;
263  for (auto& s : ordered_strings)
264  {
265  cout << s << endl;
266  }
267  cout << "* ordered filestream" << endl;
268  for (auto& s : ordered_strings)
269  {
270  cout << s << endl;
271  }
272 
273  ASSERT_EQ(ordered_strings, ordered_files);
274 
275  int cur_thread = 0;
276  int cur_line = 1;
277 
278  cout << "validate the ordered logs:" << endl;
279  for (auto& s : ordered_strings)
280  {
281  cout << s << endl;
282 
283  stringstream val;
284 
285  val << "[" << cur_thread << "] this is log number " << cur_line << " from thread number " << cur_thread;
286 
287  ASSERT_EQ(val.str(), s);
288 
289  cur_line++;
290  if (cur_line > 5)
291  {
292  cur_thread++;
293  cur_line = 1;
294  }
295  }
296 
297  /*
298  Make sure we found all the lines
299  */
300 
301  ASSERT_EQ(cur_thread, 6);
302  ASSERT_EQ(cur_line, 1);
304 }
Common file system utilities.
Definition: fs.h:103
static std::string get_current_dir(std::system_error *=nullptr)
Get working directory.
Definition: fs.cc:454
static void change_dir(const std::string &, std::system_error *=nullptr)
Change working directory.
Definition: fs.cc:440
static void create_dir(const std::string &, std::system_error *=nullptr)
Create a directory.
Definition: fs.cc:284
Thread-safe stream logger.
Definition: logger.h:86
void multiline(unsigned int max=0)
Set multiline mode.
void add(const std::shared_ptr< std::ostream > &)
Add a shared stream.
Definition: logger.cc:404
void timestamp_iso(bool utc_on=true)
Set iso 8601 timestamp.
Definition: logger.h:188
void id(const std::string &id="")
Set the id.
Definition: logger.cc:425
void add_cout()
Add std::cout console stream.
Definition: logger.cc:418
Common file system utilities.
Thread safe logging.
TEST(inet_example, client_server_stream_test)
[Inet client server]
Definition: inet.cc:521
TEST_F(LoggerTest, multithread_multi_loggers)
Definition: logger.cc:196