00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "stdmisc.h"
00025
00026
00027 #ifdef NL_OS_WINDOWS
00028 # include <io.h>
00029 # include <fcntl.h>
00030 # include <sys/types.h>
00031 # include <sys/stat.h>
00032 #else
00033 # include <cerrno>
00034 #endif // NL_OS_WINDOWS
00035
00036 #include <cstdio>
00037 #include <cstdlib>
00038 #include <ctime>
00039
00040 #include <iostream>
00041 #include <fstream>
00042
00043 #include <iomanip>
00044
00045 #include "nel/misc/path.h"
00046 #include "nel/misc/mutex.h"
00047 #include "nel/misc/report.h"
00048
00049 #include "nel/misc/debug.h"
00050
00051
00052 #ifdef NL_OS_WINDOWS
00053
00054
00055 # define _WIN32_WINDOWS 0x0410
00056 # define WINVER 0x0400
00057 # define NOMINMAX
00058 # include <windows.h>
00059 #else
00060 # define IsDebuggerPresent() false
00061 #endif
00062
00063 #include "nel/misc/displayer.h"
00064
00065 using namespace std;
00066
00067 namespace NLMISC
00068 {
00069
00070 static const char *LogTypeToString[][8] = {
00071 { "", "ERR", "WRN", "INF", "DBG", "STT", "AST", "UKN" },
00072 { "", "Error", "Warning", "Information", "Debug", "Statistic", "Assert", "Unknown" },
00073 { "", "A fatal error occurs. The program must quit", "", "", "", "", "A failed assertion occurs", "" },
00074 };
00075
00076 const char *IDisplayer::logTypeToString (CLog::TLogType logType, bool longFormat)
00077 {
00078 if (logType < CLog::LOG_NO || logType > CLog::LOG_UNKNOWN)
00079 return "<NotDefined>";
00080
00081 return LogTypeToString[longFormat?1:0][logType];
00082 }
00083
00084 const char *IDisplayer::dateToHumanString ()
00085 {
00086 time_t date;
00087 time (&date);
00088 return dateToHumanString (date);
00089 }
00090
00091 const char *IDisplayer::dateToHumanString (time_t date)
00092 {
00093 static char cstime[25];
00094 struct tm *tms = localtime(&date);
00095 if (tms)
00096 strftime (cstime, 25, "%Y/%m/%d %H:%M:%S", tms);
00097 else
00098 sprintf(cstime, "bad date %d", (uint32)date);
00099 return cstime;
00100 }
00101
00102 const char *IDisplayer::dateToComputerString (time_t date)
00103 {
00104 static char cstime[25];
00105 smprintf (cstime, 25, "%ld", &date);
00106 return cstime;
00107 }
00108
00109 const char *IDisplayer::HeaderString ()
00110 {
00111 static char header[1024];
00112 smprintf(header, 1024, "\nLog Starting [%s]\n", dateToHumanString());
00113 return header;
00114 }
00115
00116
00117 IDisplayer::IDisplayer(const char *displayerName)
00118 {
00119 _Mutex = new CMutex (string(displayerName)+"DISP");
00120 DisplayerName = displayerName;
00121 }
00122
00123 IDisplayer::~IDisplayer()
00124 {
00125 delete _Mutex;
00126 }
00127
00128
00129
00130
00131 void IDisplayer::display ( const CLog::TDisplayInfo& args, const char *message )
00132 {
00133 _Mutex->enter();
00134 try
00135 {
00136 doDisplay( args, message );
00137 }
00138 catch (Exception &)
00139 {
00140
00141 }
00142 _Mutex->leave();
00143 }
00144
00145
00146
00147 void CStdDisplayer::doDisplay ( const CLog::TDisplayInfo& args, const char *message )
00148 {
00149 bool needSpace = false;
00150
00151 string str;
00152
00153 if (args.LogType != CLog::LOG_NO)
00154 {
00155
00156 str += logTypeToString(args.LogType);
00157 needSpace = true;
00158 }
00159
00160
00161 if ( args.ThreadId != 0 )
00162 {
00163
00164 if (needSpace) { str += " "; needSpace = false; }
00165 #ifdef NL_OS_WINDOWS
00166 str += NLMISC::toString("%5x", args.ThreadId);
00167 #else
00168 str += NLMISC::toString("%08x", args.ThreadId);
00169 #endif
00170 needSpace = true;
00171 }
00172
00173 if (args.FileName != NULL)
00174 {
00175
00176 if (needSpace) { str += " "; needSpace = false; }
00177
00178 str += CFile::getFilename(args.FileName);
00179 needSpace = true;
00180 }
00181
00182 if (args.Line != -1)
00183 {
00184
00185 if (needSpace) { str += " "; needSpace = false; }
00186
00187 str += NLMISC::toString(args.Line);
00188 needSpace = true;
00189 }
00190
00191 if (args.FuncName != NULL)
00192 {
00193
00194 if (needSpace) { str += " "; needSpace = false; }
00195
00196 str += args.FuncName;
00197 needSpace = true;
00198 }
00199
00200 if (!args.ProcessName.empty())
00201 {
00202
00203 if (needSpace) { str += " "; needSpace = false; }
00204
00205 str += args.ProcessName;
00206 needSpace = true;
00207 }
00208
00209
00210 if (needSpace) { str += " : "; needSpace = false; }
00211
00212
00213 str += message;
00214
00215
00216
00217 static bool consoleMode = true;
00218
00219 #if defined(NL_OS_WINDOWS)
00220 static bool consoleModeTest = false;
00221 if (!consoleModeTest)
00222 {
00223 HANDLE handle = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
00224 consoleMode = handle != INVALID_HANDLE_VALUE;
00225 if (consoleMode)
00226 CloseHandle (handle);
00227 consoleModeTest = true;
00228 }
00229 #endif // NL_OS_WINDOWS
00230
00231
00232 if (consoleMode)
00233 {
00234
00235 if (!str.empty())
00236 printf ("%s", str.c_str());
00237
00238 if (!args.CallstackAndLog.empty())
00239 printf (args.CallstackAndLog.c_str());
00240
00241 fflush(stdout);
00242 }
00243
00244 #ifdef NL_OS_WINDOWS
00245
00246 if (IsDebuggerPresent ())
00247 {
00248
00249 string str2;
00250 needSpace = false;
00251
00252 if (args.FileName != NULL) str2 += args.FileName;
00253
00254 if (args.Line != -1)
00255 {
00256 str2 += "(" + NLMISC::toString(args.Line) + ")";
00257 needSpace = true;
00258 }
00259
00260 if (needSpace) { str2 += " : "; needSpace = false; }
00261
00262 if (args.FuncName != NULL) str2 += string(args.FuncName) + " ";
00263
00264 if (args.LogType != CLog::LOG_NO)
00265 {
00266 str2 += logTypeToString(args.LogType);
00267 needSpace = true;
00268 }
00269
00270
00271 if ( args.ThreadId != 0 )
00272 {
00273 str2 += NLMISC::toString("%5x: ", args.ThreadId);
00274 }
00275
00276 str2 += message;
00277
00278 const sint maxOutString = 2*1024;
00279
00280 if(str2.size() < maxOutString)
00281 {
00283
00284
00285
00286 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8(str2).c_str());
00287 }
00288 else
00289 {
00290
00291
00292
00293
00294
00295
00296 sint count = 0;
00297 uint n = strlen(message);
00298 std::string s(&str2.c_str()[0], (str2.size() - n));
00299 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8(s).c_str());
00300
00301 for(;;)
00302 {
00303
00304 if((n - count) < maxOutString )
00305 {
00306 s = std::string(&message[count], (n - count));
00307 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8(s).c_str());
00308 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8("\n").c_str());
00309 break;
00310 }
00311 else
00312 {
00313 s = std::string(&message[count] , count + maxOutString);
00314 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8(s).c_str());
00315 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8("\n\t\t\t").c_str());
00316 count += maxOutString;
00317 }
00318 }
00319 }
00320
00321
00322 uint32 pos = 0;
00323 string splited;
00324 for(;;)
00325 {
00326 if (pos+1000 < args.CallstackAndLog.size ())
00327 {
00328 splited = args.CallstackAndLog.substr (pos, 1000);
00329 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8(splited).c_str());
00330 pos += 1000;
00331 }
00332 else
00333 {
00334 splited = args.CallstackAndLog.substr (pos);
00335 OutputDebugStringW((LPCWSTR)ucstring::makeFromUtf8(splited).c_str());
00336 break;
00337 }
00338 }
00339 }
00340 #endif
00341 }
00342
00343 CFileDisplayer::CFileDisplayer (const std::string &filename, bool eraseLastLog, const char *displayerName, bool raw) :
00344 IDisplayer (displayerName), _NeedHeader(true), _LastLogSizeChecked(0), _Raw(raw)
00345 {
00346 _FilePointer = (FILE*)1;
00347 setParam (filename, eraseLastLog);
00348 }
00349
00350 CFileDisplayer::CFileDisplayer () :
00351 IDisplayer (""), _NeedHeader(true), _LastLogSizeChecked(0), _Raw(false)
00352 {
00353 _FilePointer = (FILE*)1;
00354 }
00355
00356 CFileDisplayer::~CFileDisplayer ()
00357 {
00358 if (_FilePointer > (FILE*)1)
00359 {
00360 fclose(_FilePointer);
00361 _FilePointer = NULL;
00362 }
00363 }
00364
00365 void CFileDisplayer::setParam (const std::string &filename, bool eraseLastLog)
00366 {
00367 _FileName = filename;
00368
00369 if (filename.empty())
00370 {
00371
00372 printf ("CFileDisplayer::setParam(): Can't create file with empty filename\n");
00373 return;
00374 }
00375
00376 if (eraseLastLog)
00377 {
00378
00379
00380
00381
00382
00383
00384
00385
00386 int i = 0;
00387 bool fileExist = true;
00388 while (fileExist)
00389 {
00390 string fileToDelete = CFile::getPath (filename) + CFile::getFilenameWithoutExtension (filename) +
00391 toString ("%03d.", i) + CFile::getExtension (filename);
00392 fileExist = CFile::isExists (fileToDelete);
00393 if (fileExist)
00394 CFile::deleteFile (fileToDelete);
00395 i++;
00396 }
00397 }
00398
00399 if (_FilePointer > (FILE*)1)
00400 {
00401 fclose (_FilePointer);
00402 _FilePointer = (FILE*)1;
00403 }
00404 }
00405
00406
00407 void CFileDisplayer::doDisplay ( const CLog::TDisplayInfo& args, const char *message )
00408 {
00409 bool needSpace = false;
00410
00411 string str;
00412
00413
00414 if (_FileName.empty()) return;
00415
00416 if (args.Date != 0 && !_Raw)
00417 {
00418 str += dateToHumanString(args.Date);
00419 needSpace = true;
00420 }
00421
00422 if (args.LogType != CLog::LOG_NO && !_Raw)
00423 {
00424 if (needSpace) { str += " "; needSpace = false; }
00425 str += logTypeToString(args.LogType);
00426 needSpace = true;
00427 }
00428
00429
00430 if ( args.ThreadId != 0 && !_Raw)
00431 {
00432 if (needSpace) { str += " "; needSpace = false; }
00433 #ifdef NL_OS_WINDOWS
00434 str += NLMISC::toString("%4x", args.ThreadId);
00435 #else
00436 str += NLMISC::toString("%4u", args.ThreadId);
00437 #endif
00438 needSpace = true;
00439 }
00440
00441 if (!args.ProcessName.empty() && !_Raw)
00442 {
00443 if (needSpace) { str += " "; needSpace = false; }
00444 str += args.ProcessName;
00445 needSpace = true;
00446 }
00447
00448 if (args.FileName != NULL && !_Raw)
00449 {
00450 if (needSpace) { str += " "; needSpace = false; }
00451 str += CFile::getFilename(args.FileName);
00452 needSpace = true;
00453 }
00454
00455 if (args.Line != -1 && !_Raw)
00456 {
00457 if (needSpace) { str += " "; needSpace = false; }
00458 str += NLMISC::toString(args.Line);
00459 needSpace = true;
00460 }
00461
00462 if (args.FuncName != NULL && !_Raw)
00463 {
00464 if (needSpace) { str += " "; needSpace = false; }
00465 str += args.FuncName;
00466 needSpace = true;
00467 }
00468
00469 if (needSpace) { str += " : "; needSpace = false; }
00470
00471 str += message;
00472
00473 if (_FilePointer > (FILE*)1)
00474 {
00475
00476 if (_LastLogSizeChecked++ > 20)
00477 {
00478 int res = ftell (_FilePointer);
00479 if (res > 5*1024*1024)
00480 {
00481 fclose (_FilePointer);
00482 rename (_FileName.c_str(), CFile::findNewFile (_FileName).c_str());
00483 _FilePointer = (FILE*) 1;
00484 _LastLogSizeChecked = 0;
00485 }
00486 }
00487 }
00488
00489 if (_FilePointer == (FILE*)1)
00490 {
00491 _FilePointer = fopen (_FileName.c_str(), "at");
00492 if (_FilePointer == NULL)
00493 printf ("Can't open log file '%s': %s\n", _FileName.c_str(), strerror (errno));
00494 }
00495
00496 if (_FilePointer != 0)
00497 {
00498 if (_NeedHeader)
00499 {
00500 const char *hs = HeaderString();
00501 fwrite (hs, strlen (hs), 1, _FilePointer);
00502 _NeedHeader = false;
00503 }
00504
00505 if(!str.empty())
00506 fwrite (str.c_str(), str.size(), 1, _FilePointer);
00507
00508 if(!args.CallstackAndLog.empty())
00509 fwrite (args.CallstackAndLog.c_str(), args.CallstackAndLog.size (), 1, _FilePointer);
00510
00511 fflush (_FilePointer);
00512 }
00513 }
00514
00515
00516
00517
00518 void CMsgBoxDisplayer::doDisplay ( const CLog::TDisplayInfo& args, const char *message)
00519 {
00520 #ifdef NL_OS_WINDOWS
00521
00522 bool needSpace = false;
00523
00524 string str;
00525
00526
00527
00528 if (args.Date != 0)
00529 {
00530 str += dateToHumanString(args.Date);
00531 needSpace = true;
00532 }
00533
00534 if (args.LogType != CLog::LOG_NO)
00535 {
00536
00537 if (needSpace) { str += " "; needSpace = false; }
00538 str += logTypeToString(args.LogType);
00539 needSpace = true;
00540 }
00541
00542 if (!args.ProcessName.empty())
00543 {
00544
00545 if (needSpace) { str += " "; needSpace = false; }
00546 str += args.ProcessName;
00547 needSpace = true;
00548 }
00549
00550 if (args.FileName != NULL)
00551 {
00552
00553 if (needSpace) { str += " "; needSpace = false; }
00554 str += CFile::getFilename(args.FileName);
00555 needSpace = true;
00556 }
00557
00558 if (args.Line != -1)
00559 {
00560
00561 if (needSpace) { str += " "; needSpace = false; }
00562 str += NLMISC::toString(args.Line);
00563 needSpace = true;
00564 }
00565
00566 if (args.FuncName != NULL)
00567 {
00568
00569 if (needSpace) { str += " "; needSpace = false; }
00570 str += args.FuncName;
00571 needSpace = true;
00572 }
00573
00574 if (needSpace) { str += ": "; needSpace = false; }
00575
00576 str += message;
00577
00578 if (OpenClipboard (NULL))
00579 {
00580 HGLOBAL mem = GlobalAlloc (GHND|GMEM_DDESHARE, str.size()+1);
00581 if (mem)
00582 {
00583 char *pmem = (char *)GlobalLock (mem);
00584 strcpy (pmem, str.c_str());
00585 GlobalUnlock (mem);
00586 EmptyClipboard ();
00587 SetClipboardData (CF_TEXT, mem);
00588 }
00589 CloseClipboard ();
00590 }
00591
00592
00593 needSpace = false;
00594
00595 string str2;
00596
00597 #ifdef NL_DEBUG
00598 if (!args.ProcessName.empty())
00599 {
00600 if (needSpace) { str2 += " "; needSpace = false; }
00601 str2 += args.ProcessName;
00602 needSpace = true;
00603 }
00604
00605 if (args.FileName != NULL)
00606 {
00607 if (needSpace) { str2 += " "; needSpace = false; }
00608 str2 += CFile::getFilename(args.FileName);
00609 needSpace = true;
00610 }
00611
00612 if (args.Line != -1)
00613 {
00614 if (needSpace) { str2 += " "; needSpace = false; }
00615 str2 += NLMISC::toString(args.Line);
00616 needSpace = true;
00617 }
00618
00619 if (args.FuncName != NULL)
00620 {
00621 if (needSpace) { str2 += " "; needSpace = false; }
00622 str2 += args.FuncName;
00623 needSpace = true;
00624 }
00625
00626 if (needSpace) { str2 += ": "; needSpace = false; }
00627
00628 #endif // NL_DEBUG
00629
00630 str2 += message;
00631 str2 += "\n\n(this message was copied in the clipboard)";
00632
00633
00634
00635
00636
00637
00638
00639 {
00640
00641
00642
00643 string body;
00644
00645 body += toString(LogTypeToString[2][args.LogType]) + "\n";
00646 body += "ProcName: " + args.ProcessName + "\n";
00647 body += "Date: " + string(dateToHumanString(args.Date)) + "\n";
00648 if(args.FileName == NULL)
00649 body += "File: <Unknown>\n";
00650 else
00651 body += "File: " + string(args.FileName) + "\n";
00652 body += "Line: " + toString(args.Line) + "\n";
00653 if (args.FuncName == NULL)
00654 body += "FuncName: <Unknown>\n";
00655 else
00656 body += "FuncName: " + string(args.FuncName) + "\n";
00657 body += "Reason: " + toString(message);
00658
00659 body += args.CallstackAndLog;
00660
00661 string subject;
00662
00663
00664 string procname;
00665 sint pos = args.ProcessName.find ("/");
00666 if (pos == string::npos)
00667 {
00668 procname = args.ProcessName;
00669 }
00670 else
00671 {
00672 string::size_type pos2 = args.ProcessName.find ("-", pos+1);
00673 if (pos2 == string::npos)
00674 {
00675 procname = args.ProcessName.substr (pos+1);
00676 }
00677 else
00678 {
00679 procname = args.ProcessName.substr (pos+1, pos2-pos-1);
00680 }
00681 }
00682
00683 subject += procname + " NeL " + toString(LogTypeToString[0][args.LogType]) + " " + (args.FileName?string(args.FileName):"") + " " + toString(args.Line) + " " + (args.FuncName?string(args.FuncName):"");
00684
00685
00686 if (getenv ("NEL_IGNORE_ASSERT") == NULL)
00687 {
00688
00689
00690 if (ReportDebug == report (args.ProcessName + " NeL " + toString(logTypeToString(args.LogType, true)), "", subject, body, true, 2, true, 1, !isCrashAlreadyReported(), IgnoreNextTime, NL_CRASH_DUMP_FILE))
00691 {
00692 INelContext::getInstance().setDebugNeedAssert(true);
00693 }
00694
00695
00696 setCrashAlreadyReported(true);
00697 }
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719 }
00720
00721 #endif
00722 }
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790 }