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