logger_manager_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // Copyright (C) 2011,2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <stdio.h>
  15. #include <unistd.h>
  16. #include <fstream>
  17. #include <iostream>
  18. #include <string>
  19. #include <gtest/gtest.h>
  20. #include <boost/scoped_array.hpp>
  21. #include <boost/lexical_cast.hpp>
  22. #include <exceptions/exceptions.h>
  23. #include <log/macros.h>
  24. #include <log/log_messages.h>
  25. #include <log/logger.h>
  26. #include <log/logger_level.h>
  27. #include <log/logger_manager.h>
  28. #include <log/logger_specification.h>
  29. #include <log/output_option.h>
  30. #include "tempdir.h"
  31. #include <sys/types.h>
  32. #include <regex.h>
  33. using namespace isc;
  34. using namespace isc::log;
  35. using namespace std;
  36. /// \brief LoggerManager Test
  37. class LoggerManagerTest : public ::testing::Test {
  38. public:
  39. LoggerManagerTest() {
  40. // Initialization of logging is done in main()
  41. }
  42. ~LoggerManagerTest() {
  43. LoggerManager::reset();
  44. }
  45. };
  46. // Convenience class to create the specification for the logger "filelogger",
  47. // which, as the name suggests, logs to a file. It remembers the file name and
  48. // deletes the file when instance of the class is destroyed.
  49. class SpecificationForFileLogger {
  50. public:
  51. // Constructor - allocate file and create the specification object
  52. SpecificationForFileLogger() : spec_(), name_(""), logname_("filelogger") {
  53. // Set the output to a temporary file.
  54. OutputOption option;
  55. option.destination = OutputOption::DEST_FILE;
  56. option.filename = name_ = createTempFilename();
  57. // Set target output to the file logger. The defauls indicate
  58. // INFO severity.
  59. spec_.setName(logname_);
  60. spec_.addOutputOption(option);
  61. }
  62. // Destructor, remove the file. This is only a test, so ignore failures
  63. ~SpecificationForFileLogger() {
  64. if (! name_.empty()) {
  65. static_cast<void>(unlink(name_.c_str()));
  66. // Depending on the log4cplus version, a lock file may also be
  67. // created.
  68. static_cast<void>(unlink((name_ + ".lock").c_str()));
  69. }
  70. }
  71. // Return reference to the logging specification for this loggger
  72. LoggerSpecification& getSpecification() {
  73. return spec_;
  74. }
  75. // Return name of the logger
  76. string getLoggerName() const {
  77. return logname_;
  78. }
  79. // Return name of the file
  80. string getFileName() const {
  81. return name_;
  82. }
  83. // Create temporary filename
  84. //
  85. // The compiler warns against tmpnam() and suggests mkstemp instead.
  86. // Unfortunately, this creates the filename and opens it. So we need to
  87. // close and delete the file before returning the name. Also, the name
  88. // is based on the template supplied and the name of the temporary
  89. // directory may vary between systems. So translate TMPDIR and if that
  90. // does not exist, use /tmp.
  91. //
  92. // \return Temporary file name
  93. static std::string createTempFilename() {
  94. string filename = TEMP_DIR + "/kea_logger_manager_test_XXXXXX";
  95. // Copy into writeable storage for the call to mkstemp
  96. boost::scoped_array<char> tname(new char[filename.size() + 1]);
  97. strcpy(tname.get(), filename.c_str());
  98. // Create file, close and delete it, and store the name for later.
  99. // There is still a race condition here, albeit a small one.
  100. int filenum = mkstemp(tname.get());
  101. if (filenum == -1) {
  102. isc_throw(Exception, "Unable to obtain unique filename");
  103. }
  104. close(filenum);
  105. return (string(tname.get()));
  106. }
  107. private:
  108. LoggerSpecification spec_; // Specification for this file logger
  109. string name_; // Name of the output file
  110. string logname_; // Name of this logger
  111. };
  112. // Convenience function to read an output log file and check that each line
  113. // contains the expected message ID
  114. //
  115. // \param filename Name of the file to check
  116. // \param start Iterator pointing to first expected message ID
  117. // \param finish Iterator pointing to last expected message ID
  118. template <typename T>
  119. void checkFileContents(const std::string& filename, T start, T finish) {
  120. // Access the file for input
  121. ifstream infile(filename.c_str());
  122. if (! infile.good()) {
  123. FAIL() << "Unable to open the logging file " << filename;
  124. }
  125. // Iterate round the expected message IDs and check that they appear in
  126. // the string.
  127. string line; // Line read from the file
  128. T i = start; // Iterator
  129. getline(infile, line);
  130. int lineno = 1;
  131. while ((i != finish) && (infile.good())) {
  132. // Check that the message ID appears in the line.
  133. EXPECT_TRUE(line.find(string(*i)) != string::npos)
  134. << "Expected to find " << string(*i) << " on line " << lineno
  135. << " of logging file " << filename;
  136. // Go for the next line
  137. ++i;
  138. getline(infile, line);
  139. ++lineno;
  140. }
  141. // Why did the loop end?
  142. EXPECT_TRUE(i == finish) << "Did not reach the end of the message ID list";
  143. EXPECT_TRUE(infile.eof()) << "Did not reach the end of the logging file";
  144. // File will close when the instream is deleted at the end of this
  145. // function.
  146. }
  147. // Check that the logger correctly creates something logging to a file.
  148. TEST_F(LoggerManagerTest, FileLogger) {
  149. // Create a specification for the file logger and use the manager to
  150. // connect the "filelogger" logger to it.
  151. SpecificationForFileLogger file_spec;
  152. // For the first test, we want to check that the file is created
  153. // if it does not already exist. So delete the temporary file before
  154. // logging the first message.
  155. unlink(file_spec.getFileName().c_str());
  156. // Set up the file appenders.
  157. LoggerManager manager;
  158. manager.process(file_spec.getSpecification());
  159. // Try logging to the file. Local scope is set to ensure that the logger
  160. // is destroyed before we reset the global logging. We record what we
  161. // put in the file for a later comparison.
  162. vector<MessageID> ids;
  163. {
  164. // Scope-limit the logger to ensure it is destroyed after the brief
  165. // check. This adds weight to the idea that the logger will not
  166. // keep the file open.
  167. Logger logger(file_spec.getLoggerName().c_str());
  168. LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
  169. ids.push_back(LOG_DUPLICATE_MESSAGE_ID);
  170. LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg("test");
  171. ids.push_back(LOG_DUPLICATE_NAMESPACE);
  172. }
  173. LoggerManager::reset();
  174. // At this point, the output file should contain two lines with messages
  175. // LOG_DUPLICATE_MESSAGE_ID and LOG_DUPLICATE_NAMESPACE messages - test this.
  176. checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
  177. // Re-open the file (we have to assume that it was closed when we
  178. // reset the logger - there is no easy way to check) and check that
  179. // new messages are appended to it. We use the alternative
  180. // invocation of process() here to check it works.
  181. vector<LoggerSpecification> spec(1, file_spec.getSpecification());
  182. manager.process(spec.begin(), spec.end());
  183. // Create a new instance of the logger and log three more messages.
  184. Logger logger(file_spec.getLoggerName().c_str());
  185. LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg("test");
  186. ids.push_back(LOG_NO_SUCH_MESSAGE);
  187. LOG_FATAL(logger, LOG_INVALID_MESSAGE_ID).arg("test").arg("test2");
  188. ids.push_back(LOG_INVALID_MESSAGE_ID);
  189. LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("42");
  190. ids.push_back(LOG_NO_MESSAGE_ID);
  191. // Close the file and check again
  192. LoggerManager::reset();
  193. checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
  194. }
  195. // Check if the file rolls over when it gets above a certain size.
  196. TEST_F(LoggerManagerTest, FileSizeRollover) {
  197. // Set to a suitable minimum that log4cplus can copy with
  198. static const size_t SIZE_LIMIT = 204800;
  199. // Set up the name of the file.
  200. SpecificationForFileLogger file_spec;
  201. LoggerSpecification& spec = file_spec.getSpecification();
  202. // Expand the option to ensure that a maximum version size is set.
  203. LoggerSpecification::iterator opt = spec.begin();
  204. EXPECT_TRUE(opt != spec.end());
  205. opt->maxsize = SIZE_LIMIT; // Bytes
  206. opt->maxver = 2;
  207. // The current current output file does not exist (the creation of file_spec
  208. // ensures that. Check that previous versions don't either.
  209. vector<string> prev_name;
  210. for (int i = 0; i < 3; ++i) {
  211. prev_name.push_back(file_spec.getFileName() + "." +
  212. boost::lexical_cast<string>(i + 1));
  213. (void) unlink(prev_name[i].c_str());
  214. }
  215. // Generate an argument for a message that ensures that the message when
  216. // logged will be over that size.
  217. string big_arg(SIZE_LIMIT, 'x');
  218. // Set up the file logger
  219. LoggerManager manager;
  220. manager.process(spec);
  221. // Log the message twice using different message IDs. This should generate
  222. // three files as for the log4cplus implementation, the files appear to
  223. // be rolled after the message is logged.
  224. {
  225. Logger logger(file_spec.getLoggerName().c_str());
  226. LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg(big_arg);
  227. LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg(big_arg);
  228. }
  229. // Check them.
  230. LoggerManager::reset(); // Ensure files are closed
  231. vector<MessageID> ids;
  232. ids.push_back(LOG_NO_SUCH_MESSAGE);
  233. checkFileContents(prev_name[1], ids.begin(), ids.end());
  234. ids.clear();
  235. ids.push_back(LOG_DUPLICATE_NAMESPACE);
  236. checkFileContents(prev_name[0], ids.begin(), ids.end());
  237. // Log another message and check that the files have rotated and that
  238. // a .3 version does not exist.
  239. manager.process(spec);
  240. {
  241. Logger logger(file_spec.getLoggerName().c_str());
  242. LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg("42").arg(big_arg);
  243. }
  244. LoggerManager::reset(); // Ensure files are closed
  245. // Check that the files have moved.
  246. ids.clear();
  247. ids.push_back(LOG_DUPLICATE_NAMESPACE);
  248. checkFileContents(prev_name[1], ids.begin(), ids.end());
  249. ids.clear();
  250. ids.push_back(LOG_NO_MESSAGE_TEXT);
  251. checkFileContents(prev_name[0], ids.begin(), ids.end());
  252. // ... and check that the .3 version does not exist.
  253. ifstream file3(prev_name[2].c_str(), ifstream::in);
  254. EXPECT_FALSE(file3.good());
  255. // Tidy up
  256. for (vector<string>::size_type i = 0; i < prev_name.size(); ++i) {
  257. (void) unlink(prev_name[i].c_str());
  258. }
  259. }
  260. namespace { // begin unnamed namespace
  261. // When we begin to use C++11, we could replace use of POSIX API with
  262. // <regex>.
  263. class RegexHolder {
  264. public:
  265. RegexHolder(const char* expr, const int flags = REG_EXTENDED) {
  266. const int rc = regcomp(&regex_, expr, flags);
  267. if (rc) {
  268. regfree(&regex_);
  269. throw;
  270. }
  271. }
  272. ~RegexHolder() {
  273. regfree(&regex_);
  274. }
  275. regex_t* operator*() {
  276. return (&regex_);
  277. }
  278. private:
  279. regex_t regex_;
  280. };
  281. } // end of unnamed namespace
  282. // Check that the logger correctly outputs the full formatted layout
  283. // pattern.
  284. TEST_F(LoggerManagerTest, checkLayoutPattern) {
  285. // Create a specification for the file logger and use the manager to
  286. // connect the "filelogger" logger to it.
  287. SpecificationForFileLogger file_spec;
  288. // For the first test, we want to check that the file is created
  289. // if it does not already exist. So delete the temporary file before
  290. // logging the first message.
  291. unlink(file_spec.getFileName().c_str());
  292. // Set up the file appenders.
  293. LoggerManager manager;
  294. manager.process(file_spec.getSpecification());
  295. // Try logging to the file. Local scope is set to ensure that the logger
  296. // is destroyed before we reset the global logging.
  297. {
  298. Logger logger(file_spec.getLoggerName().c_str());
  299. LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
  300. }
  301. LoggerManager::reset();
  302. // Access the file for input
  303. const std::string& filename = file_spec.getFileName();
  304. ifstream infile(filename.c_str());
  305. if (! infile.good()) {
  306. FAIL() << "Unable to open the logging file " << filename;
  307. }
  308. std::string line;
  309. std::getline(infile, line);
  310. RegexHolder regex(// %D{%Y-%m-%d %H:%M:%S.%q}
  311. "^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}[[:space:]]"
  312. "[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\\.[[:digit:]]+[[:space:]]"
  313. // %-5p
  314. "[[:alpha:]]{1,5}[[:space:]]"
  315. // [%c/%i]
  316. "\\[[[:alnum:]\\-\\.]+/[[:digit:]]+\\][[:space:]]"
  317. );
  318. const int re = regexec(*regex, line.c_str(), 0, NULL, 0);
  319. ASSERT_EQ(0, re)
  320. << "Logged message does not match expected layout pattern";
  321. }