1 
2 /* ****************************************************************************
3 
4  * eID Middleware Project.
5  * Copyright (C) 2008-2009 FedICT.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License version
9  * 3.0 as published by the Free Software Foundation.
10  *
11  * This software is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this software; if not, see
18  * http://www.gnu.org/licenses/.
19 
20 **************************************************************************** */
21 #include "logbase.h"
22 #include "eiderrors.h"
23 #include "mwexception.h"
24 
25 #include <time.h>
26 #include <errno.h>
27 #include "thread.h"
28 #include "configuration.h"
29 #include "util.h"
30 #include "mw_util.h"
31 
32 #ifndef WIN32
33 #include <stdlib.h>
34 #ifdef __APPLE__
35 #include <PCSC/wintypes.h>
36 #else
37 #include <wintypes.h>
38 #endif
39 #include "sys/stat.h"
40 #include "util.h"
41 
42 #define fwprintf_s fwprintf
43 #define vfwprintf_s vfwprintf
44 #define swprintf_s swprintf
45 #define _stat stat
46 
47 #define LOG_DIRECTORY_DEFAULT  L"/tmp"
48 #endif
49 
50 #define LOG_OPENFAILED_MAXALLOWED  10
51 
52 namespace eIDMW
53 {
54 
MapLevel(const wchar_t * level)55 	tLOG_Level MapLevel(const wchar_t * level)
56 	{
57 		if (wcscmp(L"critical", level) == 0)
58 			return LOG_LEVEL_CRITICAL;
59 		else if (wcscmp(L"error", level) == 0)
60 			return LOG_LEVEL_ERROR;
61 		else if (wcscmp(L"warning", level) == 0)
62 			return LOG_LEVEL_WARNING;
63 		else if (wcscmp(L"info", level) == 0)
64 			return LOG_LEVEL_INFO;
65 		else if (wcscmp(L"debug", level) == 0)
66 			return LOG_LEVEL_DEBUG;
67 		else if (wcscmp(L"none", level) == 0)
68 			return LEV_LEVEL_NOLOG;
69 		else
70 			return LOG_LEVEL_DEFAULT;
71 	}
72 
73 /* ******************
74 *** CLogger Class ***
75 ******************* */
76 //-----------------------------------------------
77 // The logger needs a systemwide mutex to control the access to the
78 // logfile. A systemwide mutex on Windows is a named mutex.
79 // For Linux/Mac we don't change the current code.
80 //-----------------------------------------------
81 #ifdef WIN32
82 	HANDLE LogMutex;     // named mutex for Windows
83 #endif
84 
85 	static CMutex m_mutex;	// mutex for:
86 
87 	//   - non-windows
88 	//   - used as automutex for creating a logger instance
89 
90 	std::unique_ptr < CLogger > CLogger::m_instance;
91 	bool CLogger::m_bApplicationLeaving = false;
92 
93 //Default constructor
CLogger()94 	CLogger::CLogger()
95 	{
96 		m_directory = L".";
97 		m_prefix = L"ZS";
98 		m_filesize = 100000;
99 		m_filenr = 2;
100 		m_groupinnewfile = false;
101 		m_maxlevel = LOG_LEVEL_DEFAULT;
102 
103 		initFromConfig();
104 	}
105 
106 //Copy constructor
CLogger(const CLogger & logger)107 	CLogger::CLogger(const CLogger & logger)
108 	{
109 		*this = logger;
110 	}
111 
operator =(const CLogger & logger)112 	CLogger & CLogger::operator=(const CLogger & logger)
113 	{
114 		if (this != &logger)
115 		{
116 			m_directory = logger.m_directory;
117 			m_prefix = logger.m_prefix;
118 			m_filesize = logger.m_filesize;
119 			m_filenr = logger.m_filenr;
120 			m_maxlevel = logger.m_maxlevel;
121 			m_groupinnewfile = logger.m_groupinnewfile;
122 		}
123 		return *this;
124 	}
125 
126 //Destructor
~CLogger()127 	CLogger::~CLogger()
128 	{
129 		m_bApplicationLeaving = true;
130 
131 		while (m_logStore.size() > 0)
132 		{
133 			delete m_logStore[m_logStore.size() - 1];
134 
135 			m_logStore.pop_back();
136 		}
137 #ifdef WIN32
138 		//--------------------------------
139 		// Close the mutex handle. The last instance using the named mutex will
140 		// destroy the mutex
141 		//--------------------------------
142 		CloseHandle(LogMutex);
143 #endif
144 	}
145 
146 //Get the singleton instance of the logger
instance()147 	CLogger & CLogger::instance()
148 	{
149 		if (m_bApplicationLeaving)
150 			throw CMWEXCEPTION(EIDMW_ERR_LOGGER_APPLEAVING);
151 
152 
153 		if (m_instance.get() == 0)
154 		{
155 #ifdef WIN32
156 			//----------------------------------------------
157 			// always create the logger mutex. Only the first time the named mutex is
158 			// used, it is created. After that it is equivalent to opening the mutex
159 			//----------------------------------------------
160 			LogMutex = CreateMutex(0, FALSE, L"LogMutex");
161 #endif
162 			CAutoMutex autoMutex(&m_mutex);
163 
164 			m_instance.reset(new CLogger);
165 		}
166 		return *m_instance;
167 	}
168 
169 //Set the default values
init(const wchar_t * directory,const wchar_t * prefix,long filesize,long filenr,tLOG_Level maxlevel,bool groupinnewfile)170 	void CLogger::init(const wchar_t * directory, const wchar_t * prefix,
171 			   long filesize, long filenr, tLOG_Level maxlevel,
172 			   bool groupinnewfile)
173 	{
174 		m_directory = directory;
175 		m_prefix = prefix;
176 		m_filesize = filesize;
177 		m_filenr = filenr;
178 		m_maxlevel = maxlevel;
179 		m_groupinnewfile = groupinnewfile;
180 	}
181 
init(const char * directory,const char * prefix,long filesize,long filenr,tLOG_Level maxlevel,bool groupinnewfile)182 	void CLogger::init(const char *directory, const char *prefix,
183 			   long filesize, long filenr, tLOG_Level maxlevel,
184 			   bool groupinnewfile)
185 	{
186 		init(utilStringWiden(directory).c_str(),
187 		     utilStringWiden(prefix).c_str(), filesize, filenr,
188 		     maxlevel, groupinnewfile);
189 	}
190 
191 //Set the default values
initFromConfig()192 	void CLogger::initFromConfig()
193 	{
194 		CConfig config;
195 
196 		std::wstring wcsLogDir =
197 			config.
198 			GetString(CConfig::
199 				  EIDMW_CONFIG_PARAM_LOGGING_DIRNAME);
200 		std::wstring wcsPrefix =
201 			config.
202 			GetString(CConfig::EIDMW_CONFIG_PARAM_LOGGING_PREFIX);
203 		long lFileNbr =
204 			config.
205 			GetLong(CConfig::
206 				EIDMW_CONFIG_PARAM_LOGGING_FILENUMBER);
207 		long lFileSize =
208 			config.
209 			GetLong(CConfig::EIDMW_CONFIG_PARAM_LOGGING_FILESIZE);
210 		std::wstring wcsMaxLevel =
211 			config.
212 			GetString(CConfig::EIDMW_CONFIG_PARAM_LOGGING_LEVEL);
213 		tLOG_Level maxLevel = MapLevel(wcsMaxLevel.c_str());
214 		long lGroup =
215 			config.
216 			GetLong(CConfig::EIDMW_CONFIG_PARAM_LOGGING_GROUP);
217 
218 		init(wcsLogDir.c_str(), wcsPrefix.c_str(), lFileSize,
219 		     lFileNbr, maxLevel, (lGroup ? true : false));
220 	}
221 
222 //Retrieve a CLog object by is group name
getLogW(const wchar_t * group)223 	CLog & CLogger::getLogW(const wchar_t * group)
224 	{
225 		bool find = false;
226 		unsigned int i;
227 
228 		for (i = 0; i < m_logStore.size(); i++)
229 		{
230 			if (m_logStore[i]->m_group.compare(group) == 0)
231 			{
232 				find = true;
233 				break;
234 			}
235 		}
236 
237 		if (!find)
238 		{
239 			CLog *log =
240 				new CLog(m_directory.c_str(),
241 					 m_prefix.c_str(), group, m_filesize,
242 					 m_filenr, m_maxlevel,
243 					 m_groupinnewfile);
244 			m_logStore.push_back(log);
245 			return *log;
246 		}
247 
248 		return *m_logStore[i];
249 	}
250 
getLogA(const char * group)251 	CLog & CLogger::getLogA(const char *group)
252 	{
253 		return getLogW(utilStringWiden(group).c_str());
254 	}
255 
256 //Write into the log of the group
writeToGroup(const wchar_t * group,tLOG_Level level,const wchar_t * format,...)257 	void CLogger::writeToGroup(const wchar_t * group, tLOG_Level level,
258 				   const wchar_t * format, ...)
259 	{
260 		CLog & log = getLogW(group);
261 
262 		if (log.writeLineHeaderW(level))
263 		{
264 			va_list args;
265 
266 			va_start(args, format);
267 			log.writeLineMessageW(format, args);
268 			va_end(args);
269 		}
270 	}
271 
writeToGroup(const char * group,tLOG_Level level,const char * format,...)272 	void CLogger::writeToGroup(const char *group, tLOG_Level level,
273 				   const char *format, ...)
274 	{
275 		CLog & log = getLogA(group);
276 
277 		if (log.writeLineHeaderA(level))
278 		{
279 			va_list args;
280 
281 			va_start(args, format);
282 			log.writeLineMessageA(format, args);
283 			va_end(args);
284 		}
285 	}
286 
287 //Write into the log of the group with filename and line number
288 //use with __LINE__,__WFILE__
writeToGroup(const wchar_t * group,tLOG_Level level,const int line,const wchar_t * file,const wchar_t * format,...)289 	void CLogger::writeToGroup(const wchar_t * group, tLOG_Level level,
290 				   const int line, const wchar_t * file,
291 				   const wchar_t * format, ...)
292 	{
293 		CLog & log = getLogW(group);
294 
295 		if (log.writeLineHeaderW(level, line, file))
296 		{
297 			va_list args;
298 
299 			va_start(args, format);
300 			log.writeLineMessageW(format, args);
301 			va_end(args);
302 		}
303 	}
304 
writeToGroup(const char * group,tLOG_Level level,const int line,const char * file,const char * format,...)305 	void CLogger::writeToGroup(const char *group, tLOG_Level level,
306 				   const int line, const char *file,
307 				   const char *format, ...)
308 	{
309 		CLog & log = getLogA(group);
310 
311 		if (log.writeLineHeaderA(level, line, file))
312 		{
313 			va_list args;
314 
315 			va_start(args, format);
316 			log.writeLineMessageA(format, args);
317 			va_end(args);
318 		}
319 	}
320 
321 //Write into the default log (no group)
write(tLOG_Level level,const wchar_t * format,...)322 	void CLogger::write(tLOG_Level level, const wchar_t * format, ...)
323 	{
324 		CLog & log = getLogW(L"");
325 
326 		if (log.writeLineHeaderW(level))
327 		{
328 			va_list args;
329 
330 			va_start(args, format);
331 			log.writeLineMessageW(format, args);
332 			va_end(args);
333 		}
334 	}
335 
write(tLOG_Level level,const char * format,...)336 	void CLogger::write(tLOG_Level level, const char *format, ...)
337 	{
338 		CLog & log = getLogA("");
339 
340 		if (log.writeLineHeaderA(level))
341 		{
342 			va_list args;
343 
344 			va_start(args, format);
345 			log.writeLineMessageA(format, args);
346 			va_end(args);
347 		}
348 	}
349 
350 //Write into the default log (no group) with filename and line number
351 //use with  __LINE__,__WFILE__
write(tLOG_Level level,const int line,const wchar_t * file,const wchar_t * format,...)352 	void CLogger::write(tLOG_Level level, const int line,
353 			    const wchar_t * file, const wchar_t * format, ...)
354 	{
355 		CLog & log = getLogW(L"");
356 
357 		if (log.writeLineHeaderW(level, line, file))
358 		{
359 			va_list args;
360 
361 			va_start(args, format);
362 			log.writeLineMessageW(format, args);
363 			va_end(args);
364 		}
365 	}
366 
write(tLOG_Level level,const int line,const char * file,const char * format,...)367 	void CLogger::write(tLOG_Level level, const int line,
368 			    const char *file, const char *format, ...)
369 	{
370 		CLog & log = getLogA("");
371 
372 		if (log.writeLineHeaderA(level, line, file))
373 		{
374 			va_list args;
375 
376 			va_start(args, format);
377 			log.writeLineMessageA(format, args);
378 			va_end(args);
379 		}
380 	}
381 
382 /* ***************
383 *** CLog Class ***
384 **************** */
385 	long CLog::m_sopenfailed = 0;
386 
387 //PRIVATE: Default constructor
CLog(const wchar_t * directory,const wchar_t * prefix,const wchar_t * group,long filesize,long filenr,tLOG_Level maxlevel,bool groupinnewfile)388 	CLog::CLog(const wchar_t * directory, const wchar_t * prefix,
389 		   const wchar_t * group, long filesize, long filenr,
390 		   tLOG_Level maxlevel, bool groupinnewfile)
391 	{
392 		m_f = NULL;
393 		m_directory = directory;
394 		m_prefix = prefix;
395 		m_group = group;
396 		m_filesize = filesize;
397 		m_filenr = filenr;
398 		m_maxlevel = maxlevel;
399 		m_groupinnewfile = groupinnewfile;
400 		m_openfailed = 0;
401 #ifndef WIN32
402 		m_flock = NULL;
403 #endif
404 	}
405 
406 //Copy constructor
CLog(const CLog & log)407 	CLog::CLog(const CLog & log) : m_directory(log.m_directory),
408 		m_prefix(log.m_prefix), m_group(log.m_group),
409 		m_filesize(log.m_filesize), m_filenr(log.m_filenr),
410 		m_maxlevel(log.m_maxlevel), m_groupinnewfile(log.m_groupinnewfile),
411 		m_openfailed(log.m_openfailed), m_f(log.m_f)
412 #ifndef WIN32
413 		, m_flock(log.m_flock)
414 #endif
415 	{ }
416 
operator =(const CLog & log)417 	CLog & CLog::operator=(const CLog & log)
418 	{
419 		if (this != &log)
420 		{
421 			m_f = log.m_f;
422 			m_directory = log.m_directory;
423 			m_prefix = log.m_prefix;
424 			m_group = log.m_group;
425 			m_filesize = log.m_filesize;
426 			m_filenr = log.m_filenr;
427 			m_maxlevel = log.m_maxlevel;
428 			m_groupinnewfile = log.m_groupinnewfile;
429 			m_openfailed = log.m_openfailed;
430 #ifndef WIN32
431 			m_flock = NULL;
432 #endif
433 		}
434 		return *this;
435 	}
436 
437 //Destructor
~CLog()438 	CLog::~CLog()
439 	{
440 	}
441 
442 //PRIVATE: Return the name of to file to write into
getFilename(std::wstring & filename)443 	void CLog::getFilename(std::wstring & filename)
444 	{
445 		//Test if the directory exist
446 		std::wstring directory;
447 
448 #ifdef WIN32
449 		DWORD dwError = 0;
450 
451 		directory = m_directory + L"\\";
452 		DWORD dwAttr = GetFileAttributes(directory.c_str());
453 
454 		if (dwAttr == INVALID_FILE_ATTRIBUTES)
455 			dwError = GetLastError();
456 		if (dwError == ERROR_FILE_NOT_FOUND
457 		    || dwError == ERROR_PATH_NOT_FOUND)
458 		{
459 			m_directory = L".";
460 			directory = m_directory + L"\\";
461 		}
462 #else
463 		//      --> TODO : Test if the directory exist
464 		directory = m_directory;
465 		struct stat buffer;
466 
467 		if (stat(utilStringNarrow(directory).c_str(), &buffer))
468 		{
469 			// check error code
470 			m_directory = LOG_DIRECTORY_DEFAULT;
471 		}
472 		directory = m_directory + L"/";
473 #endif
474 
475 		//Initialize the root filename
476 		std::wstring root_filename;
477 		root_filename = directory + m_prefix + L"_";
478 		if (m_groupinnewfile && m_group.size() > 0)
479 			root_filename += m_group + L"_";
480 
481 		wchar_t index[5];
482 
483 		swprintf_s(index, 5, L"%d", 0);
484 
485 		//If there is a maximal file size,
486 		//  we parse the file from index 0 to m_filenr-1
487 		//  until we find one -that doesn't exist
488 		//                    or
489 		//                    -with a size smaller than m_filesize
490 		//  If we don't, we have to rename the files
491 		//
492 		//Else If there is only one file its index is 0
493 		if (m_filesize > 0)
494 		{
495 			//There must be at least 2 files
496 			if (m_filenr < 2)
497 				m_filenr = 2;
498 
499 			std::wstring file;
500 #ifdef WIN32
501 			struct _stat results;
502 #else
503 			struct stat results;
504 #endif
505 			bool find = false;
506 
507 			for (int i = 0; i < m_filenr; i++)
508 			{
509 
510 				swprintf_s(index, 5, L"%d", i);
511 
512 				file = root_filename + index + L".log";
513 #ifdef WIN32
514 				if (_wstat(file.c_str(), &results) != 0
515 				    || results.st_size < m_filesize)
516 #else
517 				if (stat
518 				    (utilStringNarrow(file).c_str(),
519 				     &results) != 0
520 				    || results.st_size < m_filesize)
521 #endif
522 				{
523 					find = true;
524 					break;
525 				}
526 			}
527 			if (!find)
528 			{
529 				renameFiles(root_filename.c_str());
530 				swprintf_s(index, 5, L"%ld", m_filenr - 1);
531 			}
532 		}
533 
534 		filename = root_filename + index + L".log";
535 
536 	}
537 
538 //PRIVATE: Delete file with index 0 et rename all file i to i-1 until m_filenr-1
539 //After this function, the file with index m_filenr-1 is free (it doesn't exist)
renameFiles(const wchar_t * root_filename)540 	void CLog::renameFiles(const wchar_t * root_filename)
541 	{
542 		//We remove the file 0
543 		std::wstring src;
544 		src = root_filename;
545 		src += L"0.log";
546 		int removed;
547 
548 #ifdef WIN32
549 		removed = _wremove(src.c_str());
550 #else
551 		removed = remove(utilStringNarrow(src).c_str());
552 #endif
553 
554 		if(removed != 0) {
555 			// file could not be removed, abort
556 			return;
557 		}
558 		std::wstring dest;
559 		wchar_t isrc[5];
560 		wchar_t idest[5];
561 
562 		//For all file until m_filenr-1
563 		// 1 become 0
564 		// 2 become 1
565 		//i+1 become i
566 		//m_filenr-1 become m_filenr-2
567 		for (int i = 0; i < m_filenr; i++)
568 		{
569 			int ret;
570 
571 			swprintf_s(isrc, 5, L"%d", i + 1);
572 			swprintf_s(idest, 5, L"%d", i);
573 
574 			//if the source does not exist, we stop
575 			src = root_filename;
576 			src += isrc;
577 			src += L".log";
578 
579 			dest = root_filename;
580 			dest += idest;
581 			dest += L".log";
582 
583 			//Rename of the file
584 #ifdef WIN32
585 			ret = _wrename(src.c_str(), dest.c_str());
586 #else
587 			ret = rename(utilStringNarrow(src).c_str(),
588 			       utilStringNarrow(dest).c_str());
589 #endif
590 			if(ret < 0) {
591 				if(errno == ENOENT) {
592 					break;
593 				}
594 				continue;
595 			}
596 		}
597 	}
598 
599 //PRIVATE: Open the file with the correct name
open(bool bWchar)600 	bool CLog::open(bool bWchar)
601 	{
602 		if (!canWeTryToOpen())
603 		{
604 			incrementOpenFailed();
605 			return false;
606 		}
607 #ifdef WIN32
608 		WaitForSingleObject(LogMutex, INFINITE);
609 #else
610 		m_mutex.Lock();
611 #endif
612 
613 		if (m_f)	//Should not happend
614 		{
615 			close();
616 			throw CMWEXCEPTION(EIDMW_ERR_UNKNOWN);
617 		}
618 #ifndef WIN32
619 		m_flock = (struct flock *) malloc(sizeof(struct flock));
620 		m_flock->l_type = F_WRLCK;	/* F_RDLCK, F_WRLCK, F_UNLCK    */
621 		m_flock->l_whence = SEEK_SET;	/* SEEK_SET, SEEK_CUR, SEEK_END */
622 		m_flock->l_start = 0;	/* Offset from l_whence         */
623 		m_flock->l_len = 0;	/* length, 0 = to EOF           */
624 		m_flock->l_pid = getpid();	/* our PID                      */
625 #endif
626 
627 		int err = 0;
628 
629 		std::wstring filename;
630 
631 		int iLoop = 0;
632 
633 		do		//If the file is locked by another process, we wait
634 		{
635 			getFilename(filename);	//We get the file name in the loop because other process may rename the files
636 
637 #ifdef WIN32
638 			if (bWchar)
639 				err = _wfopen_s(&m_f, filename.c_str(),
640 						L"a, ccs=UTF-8");
641 			else
642 				err = fopen_s(&m_f,
643 					      utilStringNarrow(filename).
644 					      c_str(), "a");
645 #else
646 			(void)bWchar;
647 			m_f = fopen(utilStringNarrow(filename).c_str(),
648 				    "a, ccs=UTF-8");
649 			if (m_f == NULL)
650 				err = errno;
651 #endif
652 
653 			if (err != 0 && err != EACCES)
654 				m_f = NULL;
655 
656 			if (err == EACCES)
657 				CThread::SleepMillisecs(20);
658 
659 			iLoop++;
660 
661 		} while (err == EACCES && iLoop < 100);
662 
663 #ifndef WIN32
664 		// on Linux/Mac we set an advisory lock, i.e. it prevents
665 		// other processes from using the file only if they are collaborative
666 		// and check for the lock, otherwise they can do whatever they like ..
667 		if (m_f != NULL)
668 		{
669 			if (fcntl(fileno(m_f), F_SETLK, m_flock) == -1)	/* set the lock */
670 			{
671 				fclose(m_f);
672 				m_f = NULL;
673 			}
674 		}
675 #endif
676 
677 		if (!m_f)
678 		{
679 #ifdef WIN32
680 			ReleaseMutex(LogMutex);
681 #else
682 			m_mutex.Unlock();
683 #endif
684 			incrementOpenFailed();
685 			return false;
686 		}
687 
688 		resetOpenFailed();
689 		return true;
690 	}
691 
692 //PRIVATE: Close the file
close()693 	inline void CLog::close()
694 	{
695 		if (!m_f)
696 			throw CMWEXCEPTION(EIDMW_ERR_UNKNOWN);
697 
698 #ifndef WIN32
699 		m_flock->l_type = F_UNLCK;	/* tell it to unlock the region */
700 
701 		if (fcntl(fileno(m_f), F_SETLK, m_flock) == -1)	/* set the region to unlocked */
702 			throw CMWEXCEPTION(EIDMW_ERR_UNKNOWN);
703 
704 		free(m_flock);
705 #endif
706 
707 		fclose(m_f);
708 
709 		m_f = NULL;
710 
711 #ifdef WIN32
712 		ReleaseMutex(LogMutex);
713 #else
714 		m_mutex.Unlock();
715 #endif
716 	}
717 
718 //PRIVATE: Convert the enum into message
getLevel(tLOG_Level level)719 	const wchar_t *CLog::getLevel(tLOG_Level level)
720 	{
721 		switch (level)
722 		{
723 			case LOG_LEVEL_CRITICAL:
724 				return L"CRITICAL";
725 			case LOG_LEVEL_ERROR:
726 				return L"ERROR";
727 			case LOG_LEVEL_WARNING:
728 				return L"WARNING";
729 			case LOG_LEVEL_INFO:
730 				return L"INFO";
731 			case LOG_LEVEL_DEBUG:
732 				return L"DEBUG";
733 			default:
734 				return getLevel(LOG_LEVEL_DEFAULT);
735 		}
736 
737 	}
738 
739 //PRIVATE: Get local time in format YYYY-MM-DD hh:mm:ss by default
getLocalTimeW(std::wstring & timestamp,const wchar_t * format)740 	void CLog::getLocalTimeW(std::wstring & timestamp,
741 				 const wchar_t * format)
742 	{
743 		time_t rawtime;
744 		struct tm timeinfo;
745 		wchar_t buffer[20];
746 
747 		time(&rawtime);
748 #ifdef WIN32
749 		localtime_s(&timeinfo, &rawtime);
750 #else
751 		timeinfo = *(localtime(&rawtime));
752 #endif
753 
754 		wcsftime(buffer, 20, format, &timeinfo);
755 
756 		timestamp.assign(buffer);
757 	}
758 
getLocalTimeA(std::string & timestamp,const char * format)759 	void CLog::getLocalTimeA(std::string & timestamp, const char *format)
760 	{
761 		time_t rawtime;
762 		struct tm timeinfo;
763 		char buffer[20];
764 
765 		time(&rawtime);
766 #ifdef WIN32
767 		localtime_s(&timeinfo, &rawtime);
768 #else
769 		timeinfo = *(localtime(&rawtime));
770 #endif
771 
772 		strftime(buffer, 20, format, &timeinfo);
773 
774 		timestamp.assign(buffer);
775 	}
776 
777 //Write to log from a variable number of parameter
write(tLOG_Level level,const wchar_t * format,...)778 	void CLog::write(tLOG_Level level, const wchar_t * format, ...)
779 	{
780 		if (writeLineHeaderW(level))
781 		{
782 			va_list args;
783 
784 			va_start(args, format);
785 			writeLineMessageW(format, args);
786 			va_end(args);
787 		}
788 	}
789 
write(tLOG_Level level,const char * format,...)790 	void CLog::write(tLOG_Level level, const char *format, ...)
791 	{
792 		if (writeLineHeaderA(level))
793 		{
794 			va_list args;
795 
796 			va_start(args, format);
797 			writeLineMessageA(format, args);
798 			va_end(args);
799 		}
800 	}
801 
802 //Write to log from a va_list
write(tLOG_Level level,const wchar_t * format,va_list args)803 	void CLog::write(tLOG_Level level, const wchar_t * format,
804 			 va_list args)
805 	{
806 		if (writeLineHeaderW(level))
807 			writeLineMessageW(format, args);
808 	}
809 
write(tLOG_Level level,const char * format,va_list args)810 	void CLog::write(tLOG_Level level, const char *format, va_list args)
811 	{
812 		if (writeLineHeaderA(level))
813 			writeLineMessageA(format, args);
814 	}
815 
816 //Write to log from a variable number of parameter with filename and line number
write(tLOG_Level level,const int line,const wchar_t * file,const wchar_t * format,...)817 	void CLog::write(tLOG_Level level, const int line,
818 			 const wchar_t * file, const wchar_t * format, ...)
819 	{
820 		if (writeLineHeaderW(level, line, file))
821 		{
822 			va_list args;
823 
824 			va_start(args, format);
825 			writeLineMessageW(format, args);
826 			va_end(args);
827 		}
828 	}
829 
write(tLOG_Level level,const int line,const char * file,const char * format,...)830 	void CLog::write(tLOG_Level level, const int line, const char *file,
831 			 const char *format, ...)
832 	{
833 		if (writeLineHeaderA(level, line, file))
834 		{
835 			va_list args;
836 
837 			va_start(args, format);
838 			writeLineMessageA(format, args);
839 			va_end(args);
840 		}
841 	}
842 
843 //Write to log from a va_list with filename and line number
write(tLOG_Level level,const int line,const wchar_t * file,const wchar_t * format,va_list args)844 	void CLog::write(tLOG_Level level, const int line,
845 			 const wchar_t * file, const wchar_t * format,
846 			 va_list args)
847 	{
848 		if (writeLineHeaderW(level, line, file))
849 			writeLineMessageW(format, args);
850 	}
851 
write(tLOG_Level level,const int line,const char * file,const char * format,va_list args)852 	void CLog::write(tLOG_Level level, const int line, const char *file,
853 			 const char *format, va_list args)
854 	{
855 		if (writeLineHeaderA(level, line, file))
856 			writeLineMessageA(format, args);
857 	}
858 
859 
860 
861 //ATTENTION : Design for use with macro
862 //            Must be follow by writeLineMessage to close the file
863 //Write to log the first part of the line
writeLineHeaderW(tLOG_Level level,const int line,const wchar_t * file)864 	bool CLog::writeLineHeaderW(tLOG_Level level, const int line,
865 				    const wchar_t * file)
866 	{
867 
868 		if (level > m_maxlevel)
869 			return false;
870 
871 		long lPreviousOpenFailed = getOpenFailed();
872 
873 		if (!open(true))
874 			return false;
875 
876 		std::wstring timestamp;
877 		getLocalTimeW(timestamp);
878 
879 		if (lPreviousOpenFailed > 0)
880 		{
881 			if (isFileMixingGroups())
882 			{
883 				fwprintf_s(m_f,
884 					   L"%ls - %d - %ls: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
885 					   timestamp.c_str(),
886 					   CThread::getCurrentPid(),
887 					   m_group.c_str(),
888 					   lPreviousOpenFailed);
889 			} else
890 			{
891 				fwprintf_s(m_f,
892 					   L"%ls - %d: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
893 					   timestamp.c_str(),
894 					   CThread::getCurrentPid(),
895 					   lPreviousOpenFailed);
896 			}
897 		}
898 
899 		if (isFileMixingGroups())
900 		{
901 			if (line > 0 && wcslen(file) > 0)
902 				fwprintf_s(m_f,
903 					   L"%ls - %d|%ld - %ls - %ls -'%ls'-line=%d: ",
904 					   timestamp.c_str(),
905 					   CThread::getCurrentPid(),
906 					   CThread::getCurrentThreadId(),
907 					   m_group.c_str(), getLevel(level),
908 					   file, line);
909 			else
910 				fwprintf_s(m_f,
911 					   L"%ls - %d|%ld - %ls - %ls: ",
912 					   timestamp.c_str(),
913 					   CThread::getCurrentPid(),
914 					   CThread::getCurrentThreadId(),
915 					   m_group.c_str(), getLevel(level));
916 		} else
917 		{
918 			if (line > 0 && wcslen(file) > 0)
919 				fwprintf_s(m_f,
920 					   L"%ls - %d|%ld - %ls -'%ls'-line=%d: ",
921 					   timestamp.c_str(),
922 					   CThread::getCurrentPid(),
923 					   CThread::getCurrentThreadId(),
924 					   getLevel(level), file, line);
925 			else
926 				fwprintf_s(m_f, L"%ls - %d|%ld - %ls: ",
927 					   timestamp.c_str(),
928 					   CThread::getCurrentPid(),
929 					   CThread::getCurrentThreadId(),
930 					   getLevel(level));
931 		}
932 
933 		return true;
934 
935 	}
936 
writeLineHeaderA(tLOG_Level level_in,const int line,const char * file)937 	bool CLog::writeLineHeaderA(tLOG_Level level_in, const int line,
938 				    const char *file)
939 	{
940 
941 		if (level_in > m_maxlevel)
942 			return false;
943 
944 		long lPreviousOpenFailed = getOpenFailed();
945 
946 		if (!open(false))
947 			return false;
948 
949 		std::string timestamp;
950 		getLocalTimeA(timestamp);
951 
952 		if (lPreviousOpenFailed > 0)
953 		{
954 			if (isFileMixingGroups())
955 			{
956 				fprintf_s(m_f,
957 					  "%s - %ld - %ls: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
958 					  timestamp.c_str(),
959 					  CThread::getCurrentPid(),
960 					  m_group.c_str(),
961 					  lPreviousOpenFailed);
962 			} else
963 			{
964 				fprintf_s(m_f,
965 					  "%s - %ld: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
966 					  timestamp.c_str(),
967 					  CThread::getCurrentPid(),
968 					  lPreviousOpenFailed);
969 			}
970 		}
971 
972 		std::string level = utilStringNarrow(getLevel(level_in));
973 
974 		if (isFileMixingGroups())
975 		{
976 			std::string group = utilStringNarrow(m_group);
977 
978 			if (line > 0 && strlen(file) > 0)
979 				fprintf_s(m_f,
980 					  "%s - %ld|%ld - %s - %s -'%s'-line=%d: ",
981 					  timestamp.c_str(),
982 					  CThread::getCurrentPid(),
983 					  CThread::getCurrentThreadId(),
984 					  group.c_str(), level.c_str(), file,
985 					  line);
986 			else
987 				fprintf_s(m_f, "%s - %ld|%ld - %s - %s: ",
988 					  timestamp.c_str(),
989 					  CThread::getCurrentPid(),
990 					  CThread::getCurrentThreadId(),
991 					  group.c_str(), level.c_str());
992 		} else
993 		{
994 			if (line > 0 && strlen(file) > 0)
995 				fprintf_s(m_f,
996 					  "%s - %ld|%ld - %s -'%s'-line=%d: ",
997 					  timestamp.c_str(),
998 					  CThread::getCurrentPid(),
999 					  CThread::getCurrentThreadId(),
1000 					  level.c_str(), file, line);
1001 			else
1002 				fprintf_s(m_f, "%s - %ld|%ld - %s: ",
1003 					  timestamp.c_str(),
1004 					  CThread::getCurrentPid(),
1005 					  CThread::getCurrentThreadId(),
1006 					  level.c_str());
1007 		}
1008 
1009 		return true;
1010 
1011 	}
1012 //ATTENTION : Design for use with macro
1013 //            Must be preceded by writeLineHeader to close the file
1014 //Write to log the second part of the line
writeLineMessageW(const wchar_t * format,...)1015 	bool CLog::writeLineMessageW(const wchar_t * format, ...)
1016 	{
1017 		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
1018 			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);
1019 
1020 		va_list args;
1021 
1022 		va_start(args, format);
1023 		writeLineMessageW(format, args);
1024 		va_end(args);
1025 
1026 		return true;
1027 	}
1028 
writeLineMessageA(const char * format,...)1029 	bool CLog::writeLineMessageA(const char *format, ...)
1030 	{
1031 		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
1032 			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);
1033 
1034 		va_list args;
1035 
1036 		va_start(args, format);
1037 		writeLineMessageA(format, args);
1038 		va_end(args);
1039 
1040 		return true;
1041 	}
1042 
1043 //PRIVATE
1044 //Write to log the second part of the line
1045 
writeLineMessageW(const wchar_t * format,va_list argList)1046 	void CLog::writeLineMessageW(const wchar_t * format, va_list argList)
1047 	{
1048 
1049 		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
1050 			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);
1051 
1052 		vfwprintf_s(m_f, format, argList);
1053 		fwprintf_s(m_f, L"%c", '\n');
1054 		close();
1055 	}
1056 
writeLineMessageA(const char * format,va_list argList)1057 	void CLog::writeLineMessageA(const char *format, va_list argList)
1058 	{
1059 
1060 		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
1061 			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);
1062 
1063 		vfprintf_s(m_f, format, argList);
1064 		fprintf_s(m_f, "%c", '\n');
1065 		close();
1066 	}
1067 
1068 //Write Critical level to log
writeCritical(const wchar_t * format,...)1069 	void CLog::writeCritical(const wchar_t * format, ...)
1070 	{
1071 		if (writeLineHeaderW(LOG_LEVEL_CRITICAL))
1072 		{
1073 			va_list args;
1074 
1075 			va_start(args, format);
1076 			writeLineMessageW(format, args);
1077 			va_end(args);
1078 		}
1079 	}
1080 
writeCritical(const char * format,...)1081 	void CLog::writeCritical(const char *format, ...)
1082 	{
1083 		if (writeLineHeaderA(LOG_LEVEL_CRITICAL))
1084 		{
1085 			va_list args;
1086 
1087 			va_start(args, format);
1088 			writeLineMessageA(format, args);
1089 			va_end(args);
1090 		}
1091 	}
1092 
1093 //Write Critical level to log with filename and line number
writeCritical(const int line,const wchar_t * file,const wchar_t * format,...)1094 	void CLog::writeCritical(const int line, const wchar_t * file,
1095 				 const wchar_t * format, ...)
1096 	{
1097 		if (writeLineHeaderW(LOG_LEVEL_CRITICAL, line, file))
1098 		{
1099 			va_list args;
1100 
1101 			va_start(args, format);
1102 			writeLineMessageW(format, args);
1103 			va_end(args);
1104 		}
1105 	}
1106 
writeCritical(const int line,const char * file,const char * format,...)1107 	void CLog::writeCritical(const int line, const char *file,
1108 				 const char *format, ...)
1109 	{
1110 		if (writeLineHeaderA(LOG_LEVEL_CRITICAL, line, file))
1111 		{
1112 			va_list args;
1113 
1114 			va_start(args, format);
1115 			writeLineMessageA(format, args);
1116 			va_end(args);
1117 		}
1118 	}
1119 
1120 //Write Error level to log
writeError(const wchar_t * format,...)1121 	void CLog::writeError(const wchar_t * format, ...)
1122 	{
1123 		if (writeLineHeaderW(LOG_LEVEL_ERROR))
1124 		{
1125 			va_list args;
1126 
1127 			va_start(args, format);
1128 			writeLineMessageW(format, args);
1129 			va_end(args);
1130 		}
1131 
1132 	}
1133 
writeError(const char * format,...)1134 	void CLog::writeError(const char *format, ...)
1135 	{
1136 		if (writeLineHeaderA(LOG_LEVEL_ERROR))
1137 		{
1138 			va_list args;
1139 
1140 			va_start(args, format);
1141 			writeLineMessageA(format, args);
1142 			va_end(args);
1143 		}
1144 
1145 	}
1146 //Write Error level to log with filename and line number
writeError(const int line,const wchar_t * file,const wchar_t * format,...)1147 	void CLog::writeError(const int line, const wchar_t * file,
1148 			      const wchar_t * format, ...)
1149 	{
1150 		if (writeLineHeaderW(LOG_LEVEL_ERROR, line, file))
1151 		{
1152 			va_list args;
1153 
1154 			va_start(args, format);
1155 			writeLineMessageW(format, args);
1156 			va_end(args);
1157 		}
1158 	}
1159 
writeError(const int line,const char * file,const char * format,...)1160 	void CLog::writeError(const int line, const char *file,
1161 			      const char *format, ...)
1162 	{
1163 		if (writeLineHeaderA(LOG_LEVEL_ERROR, line, file))
1164 		{
1165 			va_list args;
1166 
1167 			va_start(args, format);
1168 			writeLineMessageA(format, args);
1169 			va_end(args);
1170 		}
1171 	}
1172 
1173 //Write Warning level to log
writeWarning(const wchar_t * format,...)1174 	void CLog::writeWarning(const wchar_t * format, ...)
1175 	{
1176 		if (writeLineHeaderW(LOG_LEVEL_WARNING))
1177 		{
1178 			va_list args;
1179 
1180 			va_start(args, format);
1181 			writeLineMessageW(format, args);
1182 			va_end(args);
1183 		}
1184 	}
1185 
writeWarning(const char * format,...)1186 	void CLog::writeWarning(const char *format, ...)
1187 	{
1188 		if (writeLineHeaderA(LOG_LEVEL_WARNING))
1189 		{
1190 			va_list args;
1191 
1192 			va_start(args, format);
1193 			writeLineMessageA(format, args);
1194 			va_end(args);
1195 		}
1196 	}
1197 
1198 //Write Warning level to log with filename and line number
writeWarning(const int line,const wchar_t * file,const wchar_t * format,...)1199 	void CLog::writeWarning(const int line, const wchar_t * file,
1200 				const wchar_t * format, ...)
1201 	{
1202 		if (writeLineHeaderW(LOG_LEVEL_WARNING, line, file))
1203 		{
1204 			va_list args;
1205 
1206 			va_start(args, format);
1207 			writeLineMessageW(format, args);
1208 			va_end(args);
1209 		}
1210 	}
1211 
writeWarning(const int line,const char * file,const char * format,...)1212 	void CLog::writeWarning(const int line, const char *file,
1213 				const char *format, ...)
1214 	{
1215 		if (writeLineHeaderA(LOG_LEVEL_WARNING, line, file))
1216 		{
1217 			va_list args;
1218 
1219 			va_start(args, format);
1220 			writeLineMessageA(format, args);
1221 			va_end(args);
1222 		}
1223 	}
1224 
1225 //Write Info level to log
writeInfo(const wchar_t * format,...)1226 	void CLog::writeInfo(const wchar_t * format, ...)
1227 	{
1228 		if (writeLineHeaderW(LOG_LEVEL_INFO))
1229 		{
1230 			va_list args;
1231 
1232 			va_start(args, format);
1233 			writeLineMessageW(format, args);
1234 			va_end(args);
1235 		}
1236 	}
1237 
writeInfo(const char * format,...)1238 	void CLog::writeInfo(const char *format, ...)
1239 	{
1240 		if (writeLineHeaderA(LOG_LEVEL_INFO))
1241 		{
1242 			va_list args;
1243 
1244 			va_start(args, format);
1245 			writeLineMessageA(format, args);
1246 			va_end(args);
1247 		}
1248 	}
1249 
1250 //Write Info level to log with filename and line number
writeInfo(const int line,const wchar_t * file,const wchar_t * format,...)1251 	void CLog::writeInfo(const int line, const wchar_t * file,
1252 			     const wchar_t * format, ...)
1253 	{
1254 		if (writeLineHeaderW(LOG_LEVEL_INFO, line, file))
1255 		{
1256 			va_list args;
1257 
1258 			va_start(args, format);
1259 			writeLineMessageW(format, args);
1260 			va_end(args);
1261 		}
1262 	}
1263 
writeInfo(const int line,const char * file,const char * format,...)1264 	void CLog::writeInfo(const int line, const char *file,
1265 			     const char *format, ...)
1266 	{
1267 		if (writeLineHeaderA(LOG_LEVEL_INFO, line, file))
1268 		{
1269 			va_list args;
1270 
1271 			va_start(args, format);
1272 			writeLineMessageA(format, args);
1273 			va_end(args);
1274 		}
1275 	}
1276 
1277 //Write Debug level to log
writeDebug(const wchar_t * format,...)1278 	void CLog::writeDebug(const wchar_t * format, ...)
1279 	{
1280 //    Beeps(1, 1000);
1281 		if (writeLineHeaderW(LOG_LEVEL_DEBUG))
1282 		{
1283 			va_list args;
1284 
1285 			va_start(args, format);
1286 			writeLineMessageW(format, args);
1287 			va_end(args);
1288 		}
1289 	}
1290 
writeDebug(const char * format,...)1291 	void CLog::writeDebug(const char *format, ...)
1292 	{
1293 //    Beeps(1, 1000);
1294 		if (writeLineHeaderA(LOG_LEVEL_DEBUG))
1295 		{
1296 			va_list args;
1297 
1298 			va_start(args, format);
1299 			writeLineMessageA(format, args);
1300 			va_end(args);
1301 		}
1302 	}
1303 
1304 //Write Debug level to log with filename and line number
writeDebug(const int line,const wchar_t * file,const wchar_t * format,...)1305 	void CLog::writeDebug(const int line, const wchar_t * file,
1306 			      const wchar_t * format, ...)
1307 	{
1308 
1309 		if (writeLineHeaderW(LOG_LEVEL_DEBUG, line, file))
1310 		{
1311 			va_list args;
1312 
1313 			va_start(args, format);
1314 			writeLineMessageW(format, args);
1315 			va_end(args);
1316 		}
1317 	}
1318 
writeDebug(const int line,const char * file,const char * format,...)1319 	void CLog::writeDebug(const int line, const char *file,
1320 			      const char *format, ...)
1321 	{
1322 
1323 		if (writeLineHeaderA(LOG_LEVEL_DEBUG, line, file))
1324 		{
1325 			va_list args;
1326 
1327 			va_start(args, format);
1328 			writeLineMessageA(format, args);
1329 			va_end(args);
1330 		}
1331 	}
1332 
isFileMixingGroups()1333 	bool CLog::isFileMixingGroups()
1334 	{
1335 		return (!m_groupinnewfile || m_group.size() == 0);
1336 	}
1337 
getOpenFailed()1338 	long CLog::getOpenFailed()
1339 	{
1340 		if (isFileMixingGroups())
1341 			return m_sopenfailed;
1342 		else
1343 			return m_openfailed;
1344 	}
1345 
canWeTryToOpen()1346 	bool CLog::canWeTryToOpen()
1347 	{
1348 		//To avoid delay if open failed 5 times consecutively,
1349 		//we retry only once every 100 times
1350 		if (isFileMixingGroups())
1351 			return (m_sopenfailed <= 5
1352 				|| (m_sopenfailed % 100) == 0);
1353 		else
1354 			return (m_openfailed <= 5
1355 				|| (m_openfailed % 100) == 0);
1356 	}
1357 
incrementOpenFailed()1358 	void CLog::incrementOpenFailed()
1359 	{
1360 		if (isFileMixingGroups())
1361 			m_sopenfailed++;
1362 		else
1363 			m_openfailed++;
1364 	}
1365 
resetOpenFailed()1366 	void CLog::resetOpenFailed()
1367 	{
1368 		if (isFileMixingGroups())
1369 			m_sopenfailed = 0;
1370 		else
1371 			m_openfailed = 0;
1372 	}
1373 
1374 }
1375