1 #include <log4cpp/Category.hh>
2 #include <log4cpp/PropertyConfigurator.hh>
3 #include <log4cpp/DailyRollingFileAppender.hh>
4 #include <log4cpp/PatternLayout.hh>
5 #include <log4cpp/OstreamAppender.hh>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <iostream>
10 #include <ctime>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <memory>
14 #include <cstring>
15 
16 #ifdef LOG4CPP_HAVE_IO_H
17 #    include <io.h>
18 #endif
19 #ifdef LOG4CPP_HAVE_UNISTD_H
20 #    include <unistd.h>
21 #endif
22 
23 #ifndef WIN32    // only available on Win32
24 #include <dirent.h>
25 #else
26 #include <direct.h>
27 #endif
28 
29 #ifdef WIN32
30 #pragma comment(lib, "Ws2_32.lib")
31 #endif
32 
33 using namespace log4cpp;
34 using namespace std;
35 static const char* const test_message = "message";
36 static const char* const daily_file_prefix = "dailyrolling_file.log";
37 static const char* const nestedDir = "nesteddir";
38 #ifndef WIN32
39 #define PATHDELIMITER "/"
40 #else
41 #define PATHDELIMITER "\\"
42 #endif
43 const char* const nesteddirname = "nesteddir"PATHDELIMITER;
44 
45 
46 class DailyRollingTest {
47 	DailyRollingFileAppender* dailyApp, *nestedDirDailyApp;
48 public:
remove_impl(const char * filename)49 	bool remove_impl(const char* filename)
50 	{
51 	   int res = remove(filename);
52 
53 	   if (res != 0 && errno != ENOENT)
54 		  cout << "Can't remove file '" << filename << "'.\n";
55 
56 	   return res == 0 || (res != 0 && errno == ENOENT);
57 	}
58 
remove_files()59 	bool remove_files()
60 	{
61 //	   if (!remove_impl(daily_file_prefix))
62 //		  return false;
63 
64 	   return true;
65 	}
66 
setup()67 	bool setup()
68 	{
69 	   if (!remove_files())
70 		  return false;
71 
72 	   Category& root = Category::getRoot();
73 	   dailyApp = new DailyRollingFileAppender("daily-rolling-appender", daily_file_prefix, 1);
74 	   nestedDirDailyApp = new DailyRollingFileAppender("nesteddir-daily-rolling-appender", std::string(nesteddirname).append(daily_file_prefix), 1);
75 	   root.addAppender(dailyApp);
76 	   root.addAppender(nestedDirDailyApp);
77 	   root.setPriority(Priority::DEBUG);
78 
79 	   return true;
80 	}
81 
make_log_files()82 	void make_log_files()
83 	{
84 	   Category::getRoot().debugStream() << test_message << 1;
85 	   Category::getRoot().debugStream() << test_message << 2;
86 	   Category::getRoot().debugStream() << test_message << 3;
87 	   Category::getRoot().debugStream() << "The message before rolling over attempt";
88 	   dailyApp->rollOver();
89 	   nestedDirDailyApp->rollOver();
90 	   Category::getRoot().debugStream() << "The message after rolling over attempt";
91 	   Category::getRoot().debugStream() << test_message << 4;
92 	   Category::getRoot().debugStream() << test_message << 5;
93 	}
94 
exists(const char * filename)95 	bool exists(const char* filename)
96 	{
97 	   FILE* f = fopen(filename, "r");
98 	   if (f == NULL)
99 	   {
100 		  cout << "File '" << filename << "' doesn't exists.\n";
101 		  return false;
102 	   }
103 
104 	   fclose(f);
105 
106 	   return true;
107 	}
108 
check_log_files()109 	bool check_log_files()
110 	{
111 	   bool result = exists(daily_file_prefix);
112 
113 	   Category::shutdown();
114 	   return result && remove_files();
115 	}
116 };
117 
testOnlyDailyRollingFileAppender()118 int testOnlyDailyRollingFileAppender() {
119 	DailyRollingTest dailyTest;
120 	   if (!dailyTest.setup())
121 	   {
122 	      cout << "Setup has failed. Check for permissions on files " << daily_file_prefix << "*'.\n";
123 	      return -1;
124 	   }
125 
126 	   dailyTest.make_log_files();
127 
128 	   if (dailyTest.check_log_files())
129 	      return 0;
130 	   else
131 	      return -1;
132 }
133 
testConfigDailyRollingFileAppender()134 int testConfigDailyRollingFileAppender()
135 {
136 		/* looking for the init file in $srcdir is a requirement of
137 		   automake's distcheck target.
138 		*/
139 		const char* srcdir = getenv("srcdir");
140 		std::string initFileName;
141 	   try {
142 #if defined(WIN32)
143         initFileName = "./testConfig.log4cpp.dailyroll.nt.properties";
144 #else
145         initFileName = "./testConfig.log4cpp.dailyroll.properties";
146 #endif
147 			if (srcdir != NULL) {
148 	            initFileName = std::string(srcdir) + PATHDELIMITER + initFileName;
149 	        }
150 
151 	        log4cpp::PropertyConfigurator::configure(initFileName);
152 	    } catch(log4cpp::ConfigureFailure& f) {
153 	        std::cout << "Configure Problem " << f.what() << "($srcdir=" << ((srcdir != NULL)?srcdir:"NULL") << ")" << std::endl;
154 	        return -1;
155 	    }
156 
157 	    log4cpp::Category& root = log4cpp::Category::getRoot();
158 
159 	    log4cpp::Category& sub1 =
160 	        log4cpp::Category::getInstance(std::string("sub1"));
161 
162 	    root.error("root error");
163 	    root.warn("root warn");
164 	    sub1.error("sub1 error");
165 	    sub1.warn("sub1 warn");
166 
167 	    log4cpp::Category::shutdown();
168 	    return 0;
169 }
170 
171 //  Note: this test changes system time. Run it only manually
172 namespace OnlyManualTesting {
173 
174 	const char* absolutePathCategoryName = "absolutePathCategory";
175 	const int maxDaysToKeep = 3;
176 
177 #if defined(WIN32)
178     const char *logFilename = "C:\\Temp\\log4cpp\\dailyrolling_abs_path_file.log";
179     const char *logPathname = "C:\\Temp\\log4cpp";
180 #else
181     const char *logFilename = "/var/log/log4cpp/dailyrolling_abs_path_file.log";
182     const char *logPathname = "/var/log/log4cpp";
183 #endif
184 
setupManualEntryLog()185     void setupManualEntryLog() {
186 #if defined(WIN32)
187 		if (access(logPathname, 0) != 0) {
188 			mkdir(logPathname);
189 		}
190 #else
191 		if (access(logPathname, F_OK) != 0) {
192 			mkdir(logPathname, 644);
193 		}
194 #endif
195 
196 		log4cpp::PatternLayout *ostreamLayout = new log4cpp::PatternLayout();
197 		ostreamLayout->setConversionPattern("%d: %p %c %x: %m %n");
198 		log4cpp::Appender *ostreamAppender = new log4cpp::OstreamAppender("ostreamAppender", &std::cout);
199 		ostreamAppender->setLayout(ostreamLayout);
200 
201 		log4cpp::PatternLayout *fileLayout = new log4cpp::PatternLayout();
202 		fileLayout->setConversionPattern("%d: %p %c %x: %m %n");
203 		log4cpp::Appender *fileAppender = new log4cpp::DailyRollingFileAppender("fileAppender", logFilename, maxDaysToKeep);
204 		fileAppender->setLayout(fileLayout);
205 
206 		log4cpp::Category& absolutePathCategory =
207 				log4cpp::Category::getInstance(std::string(absolutePathCategoryName));
208 		absolutePathCategory.setAdditivity(false);
209 
210 		absolutePathCategory.addAppender(ostreamAppender);
211 		absolutePathCategory.addAppender(fileAppender);
212 		absolutePathCategory.setPriority(log4cpp::Priority::DEBUG);
213 	}
214 
215     int checkThatNoMoreThanNLogFilesPresent(const std::string _fileName, int n);
216 
jumpToFuture(int seconds)217 	int jumpToFuture(int seconds) {
218 
219 #if defined(WIN32)
220 		SYSTEMTIME now;
221 		GetSystemTime(&now);
222 		now.wDay += seconds / (24*60*60);
223 		now.wSecond += 1;
224 		if (SetSystemTime(&now) == 0) {
225 			std::cerr << "Can not change system time. Probably not today... Try running as admin? Err: " << GetLastError() << std::endl;
226 			return -1;
227 		}
228 #else
229 		time_t  now;
230 		if (time(&now) == -1)
231 			return -1;
232 
233 		now += seconds;
234 
235 		if (stime(&now) == -1) {
236 			std::cerr << "Can not set date. Need admin privileges?" << std::endl;
237 			return -1;
238 		}
239 #endif
240 		return 0;
241 	}
242 
makeManualEntryLog()243 	int makeManualEntryLog()
244 	{
245 		const int totalLinesCount = 14, linesPerDay=3, jumpPeriod=24*60*60 + 1;
246 		int i = 0, future = 0;
247 
248 		log4cpp::Category& absolutePathCategory =
249 				log4cpp::Category::getInstance(std::string(absolutePathCategoryName));
250 
251 		// 1. update system time (eg: use 'date' command on Linux) manually when test program is running here (at least 4 times)
252         absolutePathCategory.debugStream() << "debug line " << i;
253 		while (++i <= totalLinesCount) {
254 			if (i % linesPerDay == 0) {
255 				if (jumpToFuture(jumpPeriod) == -1)
256 					return -1;
257 				future += jumpPeriod;
258 			}
259             absolutePathCategory.debugStream() << "debug line " << i;
260 		}
261 
262 		if (jumpToFuture(0-future) == -1)
263 			return -1;
264 
265         // 2. check the number of files in /var/log/log4cpp ( <= maxDaysToKeep) (+1 to allow consequent runs of test)
266         if (checkThatNoMoreThanNLogFilesPresent(std::string(logFilename), maxDaysToKeep + 1) == -1)
267             return -1;
268 
269 		return 0;
270 	}
271 
272 //  Note: this test changes system time. Run it only manually
checkThatNoMoreThanNLogFilesPresent(const std::string _fileName,int n)273     int checkThatNoMoreThanNLogFilesPresent(const std::string _fileName, int n) {
274         // iterate over files around log file and count files with same prefix
275         const std::string::size_type last_delimiter = _fileName.rfind(PATHDELIMITER);
276         const std::string dirname((last_delimiter == std::string::npos)? "." : _fileName.substr(0, last_delimiter));
277         const std::string filname((last_delimiter == std::string::npos)? _fileName : _fileName.substr(last_delimiter+1, _fileName.size()-last_delimiter-1));
278         int logFilesCount(0);
279 #ifndef WIN32    // only available on Win32
280         struct dirent **entries;
281         int nentries = scandir(dirname.c_str(), &entries, 0, alphasort);
282         if (nentries < 0)
283             return -1;
284         for (int i = 0; i < nentries; i++) {
285             if (strstr(entries[i]->d_name, filname.c_str())) {
286                 ++logFilesCount;
287             }
288             free(entries[i]);
289         }
290         free(entries);
291 #else
292     HANDLE hFind = INVALID_HANDLE_VALUE;
293     WIN32_FIND_DATA ffd;
294 	const std::string pattern = _fileName + "*";
295 
296     hFind = FindFirstFile(pattern.c_str(), &ffd);
297     if (hFind != INVALID_HANDLE_VALUE) {
298         do {
299             if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
300                 ++logFilesCount;
301             }
302         } while (FindNextFile(hFind, &ffd) != 0);
303 		FindClose(hFind);
304 		hFind = INVALID_HANDLE_VALUE;
305 	}
306 #endif
307         if (logFilesCount > n) {
308             std::cerr << "Too many log files in the dir " << dirname << ": " << logFilesCount << std::endl;
309         } else {
310             std::cout << "Daily log files in the dir " << dirname << ": " << logFilesCount << std::endl;
311         }
312 
313         return (logFilesCount <= n) ? 0 : -1;
314     }
315 
testDailyRollingFileAppenderChangeDateManualOnly()316 	int testDailyRollingFileAppenderChangeDateManualOnly() {
317 		setupManualEntryLog();
318 		return makeManualEntryLog();
319 	}
320 }
321 
main()322 int main()
323 {
324 	int res = testOnlyDailyRollingFileAppender();
325 	if (!res)
326 		res = testConfigDailyRollingFileAppender();
327 
328 //  Note: this test changes system time. Run it only manually
329 //	if (!res)
330 //		res = OnlyManualTesting::testDailyRollingFileAppenderChangeDateManualOnly();
331 
332 	return res;
333 }
334