00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "stdmisc.h"
00027
00028
00029 #ifdef NL_OS_WINDOWS
00030 # include <io.h>
00031 # include <fcntl.h>
00032 # include <sys/types.h>
00033 # include <sys/stat.h>
00034 #endif // NL_OS_WINDOWS
00035
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <time.h>
00039
00040 #include <iostream>
00041 #include <fstream>
00042 #include <sstream>
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 # include <windows.h>
00058 #else
00059 # define IsDebuggerPresent() false
00060 #endif
00061
00062 #include "nel/misc/displayer.h"
00063
00064 using namespace std;
00065
00066 namespace NLMISC
00067 {
00068
00069 static char *LogTypeToString[][8] = {
00070 { "", "ERR", "WRN", "INF", "DBG", "STT", "AST", "UKN" },
00071 { "", "Error", "Warning", "Information", "Debug", "Statistic", "Assert", "Unknown" },
00072 { "", "A fatal error occurs. The program must quit", "", "", "", "", "A failed assertion occurs", "" },
00073 };
00074
00075 const char *IDisplayer::logTypeToString (CLog::TLogType logType, bool longFormat)
00076 {
00077 if (logType < CLog::LOG_NO || logType > CLog::LOG_UNKNOWN)
00078 return "<NotDefined>";
00079
00080 return LogTypeToString[longFormat?1:0][logType];
00081 }
00082
00083 const char *IDisplayer::dateToHumanString ()
00084 {
00085 time_t date;
00086 time (&date);
00087 return dateToHumanString (date);
00088 }
00089
00090 const char *IDisplayer::dateToHumanString (time_t date)
00091 {
00092 static char cstime[25];
00093 struct tm *tms = localtime(&date);
00094 if (tms)
00095 strftime (cstime, 25, "%Y/%m/%d %H:%M:%S", tms);
00096 else
00097 sprintf(cstime, "bad date %d", (uint32)date);
00098 return cstime;
00099 }
00100
00101 const char *IDisplayer::dateToComputerString (time_t date)
00102 {
00103 static char cstime[25];
00104 smprintf (cstime, 25, "%ld", &date);
00105 return cstime;
00106 }
00107
00108 const char *IDisplayer::HeaderString ()
00109 {
00110 static char header[1024];
00111 smprintf(header, 1024, "\nLog Starting [%s]\n", dateToHumanString());
00112 return header;
00113 }
00114
00115
00116 IDisplayer::IDisplayer(const char *displayerName)
00117 {
00118 _Mutex = new CMutex (string(displayerName)+"DISP");
00119 DisplayerName = displayerName;
00120 }
00121
00122 IDisplayer::~IDisplayer()
00123 {
00124 delete _Mutex;
00125 }
00126
00127
00128
00129
00130 void IDisplayer::display ( const TDisplayInfo& args, const char *message )
00131 {
00132 _Mutex->enter();
00133 try
00134 {
00135 doDisplay( args, message );
00136 }
00137 catch (Exception &)
00138 {
00139
00140 }
00141 _Mutex->leave();
00142 }
00143
00144
00145
00146 void CStdDisplayer::doDisplay ( const TDisplayInfo& args, const char *message )
00147 {
00148 bool needSpace = false;
00149 stringstream ss;
00150
00151 if (args.LogType != CLog::LOG_NO)
00152 {
00153 ss << logTypeToString(args.LogType);
00154 needSpace = true;
00155 }
00156
00157
00158 if ( args.ThreadId != 0 )
00159 {
00160 ss << setw(5) << args.ThreadId;
00161 needSpace = true;
00162 }
00163
00164 if (args.Filename != NULL)
00165 {
00166 if (needSpace) { ss << " "; needSpace = false; }
00167 ss << CFile::getFilename(args.Filename);
00168 needSpace = true;
00169 }
00170
00171 if (args.Line != -1)
00172 {
00173 if (needSpace) { ss << " "; needSpace = false; }
00174 ss << args.Line;
00175 needSpace = true;
00176 }
00177
00178 if (!args.ProcessName.empty())
00179 {
00180 if (needSpace) { ss << " "; needSpace = false; }
00181 ss << args.ProcessName;
00182 needSpace = true;
00183 }
00184
00185 if (needSpace) { ss << " : "; needSpace = false; }
00186
00187 ss << message;
00188
00189 string s = ss.str();
00190
00191
00192 printf ("%s", s.c_str());
00193 printf (args.CallstackAndLog.c_str());
00194
00195 fflush(stdout);
00196
00197 #ifdef NL_OS_WINDOWS
00198
00199 if (IsDebuggerPresent ())
00200 {
00201 stringstream ss2;
00202 needSpace = false;
00203
00204 if (args.Filename != NULL) ss2 << args.Filename;
00205
00206 if (args.Line != -1)
00207 {
00208 ss2 << '(' << args.Line << ')';
00209 needSpace = true;
00210 }
00211
00212 if (needSpace) { ss2 << " : "; needSpace = false; }
00213
00214 if (args.LogType != CLog::LOG_NO)
00215 {
00216 ss2 << logTypeToString(args.LogType);
00217 needSpace = true;
00218 }
00219
00220
00221 if ( args.ThreadId != 0 )
00222 {
00223 ss2 << setw(5) << args.ThreadId << ": ";
00224 }
00225
00226 ss2 << message;
00227
00228 const sint maxOutString = 2*1024;
00229
00230 if(ss2.str().size() < maxOutString)
00231 {
00233
00234
00235
00236 OutputDebugString(ss2.str().c_str());
00237 }
00238 else
00239 {
00240
00241
00242
00243
00244
00245
00246 sint count = 0;
00247 uint n = strlen(message);
00248 std::string s(&ss2.str().c_str()[0], (ss2.str().size() - n));
00249 OutputDebugString(s.c_str());
00250
00251 while(true)
00252 {
00253
00254 if((n - count) < maxOutString )
00255 {
00256 s = std::string(&message[count], (n - count));
00257 OutputDebugString(s.c_str());
00258 OutputDebugString("\n");
00259 break;
00260 }
00261 else
00262 {
00263 s = std::string(&message[count] , count + maxOutString);
00264 OutputDebugString(s.c_str());
00265 OutputDebugString("\n\t\t\t");
00266 count += maxOutString;
00267 }
00268 }
00269 }
00270
00271
00272 uint32 pos = 0;
00273 string splited;
00274 while(true)
00275 {
00276 if (pos+1000 < args.CallstackAndLog.size ())
00277 {
00278 splited = args.CallstackAndLog.substr (pos, 1000);
00279 OutputDebugString(splited.c_str());
00280 pos += 1000;
00281 }
00282 else
00283 {
00284 splited = args.CallstackAndLog.substr (pos);
00285 OutputDebugString(splited.c_str());
00286 break;
00287 }
00288 }
00289 }
00290 #endif
00291 }
00292
00293 CFileDisplayer::CFileDisplayer (const std::string &filename, bool eraseLastLog, const char *displayerName) : IDisplayer (displayerName), _NeedHeader(true)
00294 {
00295 setParam (filename, eraseLastLog);
00296 _FilePointer = (FILE*)1;
00297 }
00298
00299 CFileDisplayer::CFileDisplayer () : IDisplayer (""), _NeedHeader(true)
00300 {
00301 _FilePointer = (FILE*)1;
00302 }
00303
00304 void CFileDisplayer::setParam (const std::string &filename, bool eraseLastLog)
00305 {
00306 _FileName = filename;
00307
00308 if (filename.empty())
00309 {
00310 nlwarning ("CFileDisplayer::setParam(): Can't create file with empty filename, don't log");
00311 return;
00312 }
00313
00314 if (eraseLastLog)
00315 {
00316 ofstream ofs (filename.c_str(), ios::out | ios::trunc);
00317 if (!ofs.is_open())
00318 {
00319 nlwarning ("CFileDisplayer::setParam(): Can't open and clear the log file '%s', don't log", filename.c_str());
00320 }
00321 }
00322 }
00323
00324
00325 uint32 toto (FILE *fp)
00326 {
00327 if (fp == NULL) return 0;
00328 fseek (fp, 0, SEEK_END);
00329 return ftell (fp);
00330 }
00331
00332
00333 void CFileDisplayer::doDisplay ( const TDisplayInfo& args, const char *message )
00334 {
00335 bool needSpace = false;
00336 stringstream ss;
00337
00338
00339 if (_FileName.empty()) return;
00340
00341 if (args.Date != 0)
00342 {
00343 ss << dateToHumanString(args.Date);
00344 needSpace = true;
00345 }
00346
00347 if (args.LogType != CLog::LOG_NO)
00348 {
00349 if (needSpace) { ss << " "; needSpace = false; }
00350 ss << logTypeToString(args.LogType);
00351 needSpace = true;
00352 }
00353
00354
00355 if ( args.ThreadId != 0 )
00356 {
00357 if (needSpace) { ss << " "; needSpace = false; }
00358 ss << args.ThreadId;
00359 needSpace = true;
00360 }
00361
00362 if (!args.ProcessName.empty())
00363 {
00364 if (needSpace) { ss << " "; needSpace = false; }
00365 ss << args.ProcessName;
00366 needSpace = true;
00367 }
00368
00369 if (args.Filename != NULL)
00370 {
00371 if (needSpace) { ss << " "; needSpace = false; }
00372 ss << CFile::getFilename(args.Filename);
00373 needSpace = true;
00374 }
00375
00376 if (args.Line != -1)
00377 {
00378 if (needSpace) { ss << " "; needSpace = false; }
00379 ss << args.Line;
00380 needSpace = true;
00381 }
00382
00383 if (needSpace) { ss << " : "; needSpace = false; }
00384
00385 ss << message;
00386
00387 if (_FilePointer > (FILE*)1)
00388 {
00389
00390 if (ftell (_FilePointer) > 5*1024*1024)
00391 {
00392 fclose (_FilePointer);
00393 rename (_FileName.c_str(), CFile::findNewFile (_FileName).c_str());
00394 fclose (_FilePointer);
00395 _FilePointer = (FILE*) 1;
00396 }
00397 }
00398
00399 if (_FilePointer == (FILE*)1)
00400 {
00401 _FilePointer = fopen (_FileName.c_str(), "at");
00402 if (_FilePointer == NULL)
00403 perror ("Can't open log file");
00404 }
00405
00406 if (_FilePointer != 0)
00407 {
00408 if (_NeedHeader)
00409 {
00410 const char *hs = HeaderString();
00411 fwrite (hs, strlen (hs), 1, _FilePointer);
00412 _NeedHeader = false;
00413 }
00414
00415 fwrite (ss.str().c_str(), ss.str().size (), 1, _FilePointer);
00416 fwrite (args.CallstackAndLog.c_str(), args.CallstackAndLog.size (), 1, _FilePointer);
00417 fflush (_FilePointer);
00418 }
00419 }
00420
00421
00422
00423
00424 void CMsgBoxDisplayer::doDisplay ( const TDisplayInfo& args, const char *message)
00425 {
00426 #ifdef NL_OS_WINDOWS
00427
00428 bool needSpace = false;
00429 stringstream ss;
00430
00431
00432
00433 if (args.Date != 0)
00434 {
00435 ss << dateToHumanString(args.Date);
00436 needSpace = true;
00437 }
00438
00439 if (args.LogType != CLog::LOG_NO)
00440 {
00441 if (needSpace) { ss << " "; needSpace = false; }
00442 ss << logTypeToString(args.LogType);
00443 needSpace = true;
00444 }
00445
00446 if (!args.ProcessName.empty())
00447 {
00448 if (needSpace) { ss << " "; needSpace = false; }
00449 ss << args.ProcessName;
00450 needSpace = true;
00451 }
00452
00453 if (args.Filename != NULL)
00454 {
00455 if (needSpace) { ss << " "; needSpace = false; }
00456 ss << CFile::getFilename(args.Filename);
00457 needSpace = true;
00458 }
00459
00460 if (args.Line != -1)
00461 {
00462 if (needSpace) { ss << " "; needSpace = false; }
00463 ss << args.Line;
00464 needSpace = true;
00465 }
00466
00467 if (needSpace) { ss << ": "; needSpace = false; }
00468
00469 ss << message;
00470
00471 if (OpenClipboard (NULL))
00472 {
00473 HGLOBAL mem = GlobalAlloc (GHND|GMEM_DDESHARE, ss.str().size()+1);
00474 if (mem)
00475 {
00476 char *pmem = (char *)GlobalLock (mem);
00477 strcpy (pmem, ss.str().c_str());
00478 GlobalUnlock (mem);
00479 EmptyClipboard ();
00480 SetClipboardData (CF_TEXT, mem);
00481 }
00482 CloseClipboard ();
00483 }
00484
00485
00486 needSpace = false;
00487 stringstream ss2;
00488
00489 #ifdef NL_DEBUG
00490 if (!args.ProcessName.empty())
00491 {
00492 if (needSpace) { ss2 << " "; needSpace = false; }
00493 ss2 << args.ProcessName;
00494 needSpace = true;
00495 }
00496
00497 if (args.Filename != NULL)
00498 {
00499 if (needSpace) { ss2 << " "; needSpace = false; }
00500 ss2 << CFile::getFilename(args.Filename);
00501 needSpace = true;
00502 }
00503
00504 if (args.Line != -1)
00505 {
00506 if (needSpace) { ss2 << " "; needSpace = false; }
00507 ss2 << args.Line;
00508 needSpace = true;
00509 }
00510
00511 if (needSpace) { ss2 << ": "; needSpace = false; }
00512 #endif // NL_DEBUG
00513
00514 ss2 << message;
00515 ss2 << endl << endl << "(this message was copied in the clipboard)";
00516
00517
00518
00519
00520
00521
00522
00523 {
00524
00525
00526
00527 string body;
00528
00529 body += toString(LogTypeToString[2][args.LogType]) + "\n";
00530 body += "ProcName: " + args.ProcessName + "\n";
00531 body += "Date: " + string(dateToHumanString(args.Date)) + "\n";
00532 if(args.Filename == NULL)
00533 body += "File: <Unknown>\n";
00534 else
00535 body += "File: " + string(args.Filename) + "\n";
00536 body += "Line: " + toString(args.Line) + "\n";
00537 body += "Reason: " + toString(message);
00538
00539 body += args.CallstackAndLog;
00540
00541 string subject;
00542
00543
00544 string procname;
00545 sint pos = args.ProcessName.find ("/");
00546 if (pos == string::npos)
00547 {
00548 procname = args.ProcessName;
00549 }
00550 else
00551 {
00552 sint pos2 = args.ProcessName.find ("-", pos+1);
00553 if (pos2 == string::npos)
00554 {
00555 procname = args.ProcessName.substr (pos+1);
00556 }
00557 else
00558 {
00559 procname = args.ProcessName.substr (pos+1, pos2-pos-1);
00560 }
00561 }
00562
00563 subject += procname + " NeL " + toString(LogTypeToString[0][args.LogType]) + " " + string(args.Filename) + " " + toString(args.Line);
00564
00565
00566 if (getenv ("NEL_IGNORE_ASSERT") == NULL)
00567 {
00568 if (ReportDebug == report (args.ProcessName + " NeL " + toString(logTypeToString(args.LogType, true)), "", subject, body, true, 2, true, 1, true, IgnoreNextTime))
00569 {
00570 DebugNeedAssert = true;
00571 }
00572 }
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594 }
00595
00596 #endif
00597 }
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665 }