# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

displayer.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2000 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
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 // these defines is for IsDebuggerPresent(). it'll not compile on windows 95
00054 // just comment this and the IsDebuggerPresent to compile on windows 95
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  * Display the string where it does.
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                 // silence
00140         }
00141         _Mutex->leave();
00142 }
00143 
00144 
00145 // Log format : "<LogType> <ThreadNo> <FileName> <Line> <ProcessName> : <Msg>"
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         // Write thread identifier
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         // we don't use cout because sometimes, it crashs because cout isn't already init, printf doesn t crash.
00192         printf ("%s", s.c_str()); 
00193         printf (args.CallstackAndLog.c_str());
00194 
00195         fflush(stdout);
00196 
00197 #ifdef NL_OS_WINDOWS
00198         // display the string in the debugger is the application is started with the debugger
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                 // Write thread identifier
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                         // WARNING: READ THIS !!!!!!!!!!!!!!!! ///////////////////////////
00234                         // If at the release time, it freezes here, it's a microsoft bug:
00235                         // http://support.microsoft.com/support/kb/articles/q173/2/60.asp
00236                         OutputDebugString(ss2.str().c_str());
00237                 }
00238                 else
00239                 {
00240                         /*OutputDebugString(ss2.str().c_str());
00241                         OutputDebugString("\n\t\t\t");
00242                         OutputDebugString("message end: ");
00243                         OutputDebugString(&message[strlen(message) - 1024]);
00244                         OutputDebugString("\n");*/
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                 // OutputDebugString is a big shit, we can't display big string in one time, we need to split
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 // Log format: "2000/01/15 12:05:30 <ProcessName> <LogType> <ThreadId> <Filename> <Line> : <Msg>"
00333 void CFileDisplayer::doDisplay ( const TDisplayInfo& args, const char *message )
00334 {
00335         bool needSpace = false;
00336         stringstream ss;
00337 
00338         // if the filename is not set, don't log
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         // Write thread identifier
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                 // if the file is too big (>5mb), rename it and create another one
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 // Log format in clipboard: "2000/01/15 12:05:30 <LogType> <ProcessName> <FileName> <Line>: <Msg>"
00422 // Log format on the screen: in debug   "<ProcessName> <FileName> <Line>: <Msg>"
00423 //                           in release "<Msg>"
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         // create the string for the clipboard
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         // create the string on the screen
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 /*      if (IsDebuggerPresent ())
00518         {
00519                 // Must break in assert call
00520                 DebugNeedAssert = true;
00521         }
00522         else
00523 */      {
00524 
00525                 // Display the report
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                 // procname is host/service_name-sid we only want the service_name to avoid redondant mail
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                 // Check the envvar NEL_IGNORE_ASSERT
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 /*              // Check the envvar NEL_IGNORE_ASSERT
00575                 if (getenv ("NEL_IGNORE_ASSERT") == NULL)
00576                 {
00577                         // Ask the user to continue, debug or ignore
00578                         int result = MessageBox (NULL, ss2.str().c_str (), logTypeToString(args.LogType, true), MB_ABORTRETRYIGNORE | MB_ICONSTOP);
00579                         if (result == IDABORT)
00580                         {
00581                                 // Exit the program now
00582                                 exit (EXIT_FAILURE);
00583                         }
00584                         else if (result == IDRETRY)
00585                         {
00586                                 // Give the debugger a try
00587                                 DebugNeedAssert = true;
00588                         }
00589                         else if (result == IDIGNORE)
00590                         {
00591                                 // Continue, do nothing
00592                         }
00593                 }
00594 */      }
00595 
00596 #endif
00597 }
00598 
00599 
00600 
00601 /***************************************************************/
00602 /******************* THE FOLLOWING CODE IS COMMENTED OUT *******/
00603 /***************************************************************
00604 void CStdDisplayer::display (const std::string& str)
00605 {
00606 //      printf("%s", str.c_str ());
00607         cout << str;
00608 
00609 #ifdef NL_OS_WINDOWS
00610         // display the string in the debugger is the application is started with the debugger
00611         if (IsDebuggerPresent ())
00612                 OutputDebugString(str.c_str ());
00613 #endif
00614 }
00615 
00616 
00617 void CFileDisplayer::display (const std::string& str)
00618 {
00619         if (_FileName.size () == 0) return;
00620 
00621         ofstream ofs (_FileName.c_str (), ios::out | ios::app);
00622         if (ofs.is_open ())
00623         {
00624                 ofs << str;
00625                 ofs.close();
00626         }
00627 
00628 
00629 //      FILE *fp = fopen (_FileName.c_str (), "a");
00630 //      if (fp == NULL) return;
00631 
00632 //      fprintf (fp, "%s", str.c_str ());
00633         
00634 //      fclose (fp);
00635 }
00636 
00637 
00638 
00639 void CMsgBoxDisplayer::display (const std::string& str)
00640 {
00641 #ifdef NL_OS_WINDOWS
00642 
00643         if (OpenClipboard (NULL))
00644         {
00645                 HGLOBAL mem = GlobalAlloc (GHND|GMEM_DDESHARE, str.size()+1);
00646                 if (mem)
00647                 {
00648                         char *pmem = (char *)GlobalLock (mem);
00649                         strcpy (pmem, str.c_str());
00650                         GlobalUnlock (mem);
00651                         EmptyClipboard ();
00652                         SetClipboardData (CF_TEXT, mem);
00653                 }
00654                 CloseClipboard ();
00655         }
00656         
00657         string strf = str;
00658         strf += "\n\n(this message was copied in the clipboard)";
00659         MessageBox (NULL, strf.c_str (), "", MB_OK | MB_ICONEXCLAMATION);
00660 #endif
00661 }
00662 **************************************************************************/
00663 
00664 
00665 } // NLMISC