Initial revision
[wxWidgets.git] / src / common / log.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: log.cpp
3 // Purpose: Assorted wxLogXXX functions, and wxLog (sink for logs)
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 29/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 #ifdef __GNUG__
20 #pragma implementation "log.h"
21 #endif
22
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
25
26 #ifdef __BORLANDC__
27 #pragma hdrstop
28 #endif
29
30 // wxWindows
31 #ifndef WX_PRECOMP
32 #include <wx/string.h>
33 #include <wx/app.h>
34 #include <wx/generic/msgdlgg.h>
35 #endif
36
37 #include <wx/intl.h>
38 #include <wx/log.h>
39
40 // other standard headers
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <time.h>
44
45 // C++ headers
46 #include <iostream.h>
47
48 // _WINDOWS_ is defined when windows.h is included,
49 // __WINDOWS__ is defined for MS Windows compilation
50 #if defined(__WINDOWS__) && !defined(_WINDOWS_)
51 #include <windows.h>
52 #endif //windows.h
53
54 // ----------------------------------------------------------------------------
55 // non member functions
56 // ----------------------------------------------------------------------------
57
58 // define this to enable wrapping of log messages
59 //#define LOG_PRETTY_WRAP
60
61 #ifdef LOG_PRETTY_WRAP
62 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
63 #endif
64
65 // ============================================================================
66 // implementation
67 // ============================================================================
68
69 // ----------------------------------------------------------------------------
70 // implementation of Log functions
71 //
72 // NB: unfortunately we need all these distinct functions, we can't make them
73 // macros and not all compilers inline vararg functions.
74 // ----------------------------------------------------------------------------
75
76 // log functions can't allocate memory (LogError("out of memory...") should
77 // work!), so we use a static buffer for all log messages
78 #define LOG_BUFFER_SIZE (4096)
79
80 // static buffer for error messages (@@@ MT-unsafe)
81 static char s_szBuf[LOG_BUFFER_SIZE];
82
83 // generic log function
84 void wxLogGeneric(wxLog::Level level, wxTString strFormat, ...)
85 {
86 if ( wxLog::GetActiveTarget() != NULL ) {
87 va_list argptr;
88 va_start(argptr, strFormat);
89 vsprintf(s_szBuf, strFormat, argptr);
90 va_end(argptr);
91
92 wxLog::OnLog(level, s_szBuf);
93 }
94 }
95
96 #define IMPLEMENT_LOG_FUNCTION(level) \
97 void wxLog##level(wxTString strFormat, ...) \
98 { \
99 if ( wxLog::GetActiveTarget() != NULL ) { \
100 va_list argptr; \
101 va_start(argptr, strFormat); \
102 vsprintf(s_szBuf, strFormat, argptr); \
103 va_end(argptr); \
104 \
105 wxLog::OnLog(wxLog::level, s_szBuf); \
106 } \
107 }
108
109 IMPLEMENT_LOG_FUNCTION(FatalError)
110 IMPLEMENT_LOG_FUNCTION(Error)
111 IMPLEMENT_LOG_FUNCTION(Warning)
112 IMPLEMENT_LOG_FUNCTION(Message)
113 IMPLEMENT_LOG_FUNCTION(Info)
114 IMPLEMENT_LOG_FUNCTION(Status)
115
116 // debug functions don't use wxTString
117 #undef IMPLEMENT_LOG_FUNCTION
118 #define IMPLEMENT_LOG_FUNCTION(level) \
119 void wxLog##level(const char *szFormat, ...) \
120 { \
121 if ( wxLog::GetActiveTarget() != NULL ) { \
122 va_list argptr; \
123 va_start(argptr, szFormat); \
124 vsprintf(s_szBuf, szFormat, argptr); \
125 va_end(argptr); \
126 \
127 wxLog::OnLog(wxLog::level, s_szBuf); \
128 } \
129 }
130
131 IMPLEMENT_LOG_FUNCTION(Debug)
132 IMPLEMENT_LOG_FUNCTION(Trace)
133
134 void wxLogVerbose(wxTString strFormat, ...)
135 {
136 if ( wxLog::GetVerbose() && wxLog::GetActiveTarget() != NULL ) {
137 va_list argptr;
138 va_start(argptr, strFormat);
139 vsprintf(s_szBuf, strFormat, argptr);
140 va_end(argptr);
141
142 wxLog::OnLog(wxLog::Info, s_szBuf);
143 }
144 }
145
146 void wxLogSysError(wxTString str, ...)
147 {
148 if ( wxLog::GetActiveTarget() != NULL ) {
149 va_list argptr;
150 va_start(argptr, str);
151 vsprintf(s_szBuf, str, argptr);
152 va_end(argptr);
153
154 char szErrMsg[LOG_BUFFER_SIZE / 2];
155 sprintf(szErrMsg, _(" (error %ld: %s)"), wxSysErrorCode(), wxSysErrorMsg());
156 strncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - strlen(s_szBuf));
157
158 wxLog::OnLog(wxLog::Error, s_szBuf);
159 }
160 }
161
162 void WXDLLEXPORT wxLogSysError(long lErrCode, wxTString strFormat, ...)
163 {
164 if ( wxLog::GetActiveTarget() != NULL ) {
165 va_list argptr;
166 va_start(argptr, strFormat);
167 vsprintf(s_szBuf, strFormat, argptr);
168 va_end(argptr);
169
170 char szErrMsg[LOG_BUFFER_SIZE / 2];
171 sprintf(szErrMsg, _(" (error %ld: %s)"), lErrCode, wxSysErrorMsg(lErrCode));
172 strncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - strlen(s_szBuf));
173
174 wxLog::OnLog(wxLog::Error, s_szBuf);
175 }
176 }
177
178 // ----------------------------------------------------------------------------
179 // wxLog class implementation
180 // ----------------------------------------------------------------------------
181
182 wxLog::wxLog()
183 {
184 m_bHasMessages = FALSE;
185 }
186
187 wxLog *wxLog::GetActiveTarget()
188 {
189 if ( !ms_bInitialized ) {
190 // prevent infinite recursion if someone calls wxLogXXX() from wxApp
191 ms_bInitialized = TRUE;
192
193 // ask the application to create a log target for us if it exists
194 if ( wxTheApp != NULL )
195 ms_pLogger = wxTheApp->CreateLogTarget();
196 else
197 ms_pLogger = new wxLogStderr;
198
199 // do nothing if it fails - what can we do?
200 }
201
202 return ms_pLogger;
203 }
204
205 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
206 {
207 // flush the old messages before changing
208 if ( ms_pLogger != NULL )
209 ms_pLogger->Flush();
210
211 ms_bInitialized = TRUE;
212
213 wxLog *pOldLogger = ms_pLogger;
214 ms_pLogger = pLogger;
215 return pOldLogger;
216 }
217
218 void wxLog::DoLog(Level level, const char *szString)
219 {
220 char szBuf[128];
221 time_t timeNow;
222 struct tm *ptmNow;
223
224 time(&timeNow);
225 ptmNow = localtime(&timeNow);
226
227 strftime(szBuf, WXSIZEOF(szBuf), ms_szTimeFormat, ptmNow);
228 wxString str = szBuf;
229
230 switch ( level ) {
231 case FatalError:
232 DoLogString(str << _("Fatal error: ") << szString);
233 DoLogString(_("Program aborted."));
234 Flush();
235 abort();
236 break;
237
238 case Error:
239 DoLogString(str << _("Error: ") << szString);
240 break;
241
242 case Warning:
243 DoLogString(str << _("Warning: ") << szString);
244 break;
245
246 case Info:
247 if ( GetVerbose() )
248 case Message:
249 DoLogString(str + szString);
250 // fall through
251
252 case Status:
253 // nothing to do
254 break;
255
256 case Trace:
257 case Debug:
258 #ifdef __DEBUG__
259 #ifdef __WIN32__
260 // in addition to normal logging, also send the string to debugger
261 // (don't prepend "Debug" here: it will go to debug window anyhow)
262 ::OutputDebugString(str + szString + "\n\r");
263 #endif //Win32
264 DoLogString(str << (level == Trace ? _("Trace") : _("Debug"))
265 << ": " << szString);
266 #endif
267
268 break;
269
270 default:
271 wxFAIL_MSG("unknown log level in wxLog::DoLog");
272 }
273 }
274
275 void wxLog::DoLogString(const char *WXUNUSED(szString))
276 {
277 wxFAIL_MSG("DoLogString must be overrided if it's called.");
278 }
279
280 void wxLog::Flush()
281 {
282 // do nothing
283 }
284
285 // ----------------------------------------------------------------------------
286 // wxLogStderr class implementation
287 // ----------------------------------------------------------------------------
288
289 wxLogStderr::wxLogStderr(FILE *fp)
290 {
291 if ( fp == NULL )
292 m_fp = stderr;
293 else
294 m_fp = fp;
295 }
296
297 void wxLogStderr::DoLogString(const char *szString)
298 {
299 fputs(szString, m_fp);
300 fputc('\n', m_fp);
301 fflush(m_fp);
302 }
303
304 // ----------------------------------------------------------------------------
305 // wxLogStream implementation
306 // ----------------------------------------------------------------------------
307
308 wxLogStream::wxLogStream(ostream *ostr)
309 {
310 if ( ostr == NULL )
311 m_ostr = &cerr;
312 else
313 m_ostr = ostr;
314 }
315
316 void wxLogStream::DoLogString(const char *szString)
317 {
318 (*m_ostr) << szString << endl << flush;
319 }
320
321 // ----------------------------------------------------------------------------
322 // wxLogTextCtrl implementation
323 // ----------------------------------------------------------------------------
324
325 /*
326 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
327 : wxLogStream(new ostream(pTextCtrl))
328 {
329 }
330
331 wxLogTextCtrl::~wxLogTextCtrl()
332 {
333 delete m_ostr;
334 }
335 */
336
337 // ----------------------------------------------------------------------------
338 // wxLogGui implementation
339 // ----------------------------------------------------------------------------
340
341 #ifndef WX_TEST_MINIMAL
342
343 wxLogGui::wxLogGui()
344 {
345 m_bErrors = FALSE;
346 }
347
348 void wxLogGui::Flush()
349 {
350 if ( !m_bHasMessages )
351 return;
352
353 // @@@ ugly...
354
355 // concatenate all strings (but not too many to not overfill the msg box)
356 wxString str;
357 uint nLines = 0,
358 nMsgCount = m_aMessages.Count();
359
360 // start from the most recent message
361 for ( uint n = nMsgCount; n > 0; n-- ) {
362 // for Windows strings longer than this value are wrapped (NT 4.0)
363 const uint nMsgLineWidth = 156;
364
365 nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
366
367 if ( nLines > 25 ) // don't put too many lines in message box
368 break;
369
370 str << m_aMessages[n - 1] << "\n";
371 }
372
373 if ( m_bErrors ) {
374 wxMessageBox(str, _("Error"), wxOK | wxICON_EXCLAMATION);
375 }
376 else {
377 wxMessageBox(str, _("Information"), wxOK | wxICON_INFORMATION);
378 }
379
380 // no undisplayed messages whatsoever
381 m_bHasMessages =
382 m_bErrors = FALSE;
383 m_aMessages.Empty();
384 }
385
386 // the default behaviour is to discard all informational messages if there
387 // are any errors/warnings.
388 void wxLogGui::DoLog(Level level, const char *szString)
389 {
390 switch ( level ) {
391 case Info:
392 if ( GetVerbose() )
393 case Message:
394 if ( !m_bErrors ) {
395 m_aMessages.Add(szString);
396 m_bHasMessages = TRUE;
397 }
398 break;
399
400 case Status:
401 {
402 // find the top window and set it's status text if it has any
403 wxWindow *pWin = wxTheApp->GetTopWindow();
404 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
405 wxFrame *pFrame = (wxFrame *)pWin;
406 pFrame->SetStatusText(szString);
407 }
408 }
409 break;
410
411 case Trace:
412 case Debug:
413 #ifdef __DEBUG__
414 #ifdef __WIN32__
415 OutputDebugString(szString);
416 OutputDebugString("\n\r");
417 #else //!WIN32
418 // send them to stderr
419 printf(stderr, level == Trace ? "Trace: %s\n"
420 : "Debug: %s\n", szString);
421 fflush(stderr);
422 #endif // WIN32
423 #endif
424 break;
425
426 case FatalError:
427 // show this one immediately
428 wxMessageBox(szString, "Fatal error", wxICON_HAND);
429 break;
430
431 case Error:
432 case Warning:
433 // discard earlier informational messages if this is the 1st error
434 if ( !m_bErrors ) {
435 m_aMessages.Empty();
436 m_bHasMessages = TRUE;
437 m_bErrors = TRUE;
438 }
439
440 m_aMessages.Add(szString);
441 break;
442
443 default:
444 wxFAIL_MSG("unknown log level in wxLogGui::DoLog");
445 }
446 }
447
448 #endif //WX_TEST_MINIMAL
449
450 // ============================================================================
451 // Global functions/variables
452 // ============================================================================
453
454 // ----------------------------------------------------------------------------
455 // static variables
456 // ----------------------------------------------------------------------------
457 wxLog *wxLog::ms_pLogger = NULL;
458 bool wxLog::ms_bInitialized = FALSE;
459 bool wxLog::ms_bVerbose = FALSE;
460 const char *wxLog::ms_szTimeFormat = "[%d/%b/%y %H:%M:%S] ";
461
462 // ----------------------------------------------------------------------------
463 // stdout error logging helper
464 // ----------------------------------------------------------------------------
465
466 // helper function: wraps the message and justifies it under given position
467 // (looks more pretty on the terminal). Also adds newline at the end.
468 //
469 // @@ this is now disabled until I find a portable way of determining the
470 // terminal window size
471
472 #ifdef LOG_PRETTY_WRAP
473 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
474 {
475 size_t nMax = 80; // @@@@
476 size_t nStart = strlen(pszPrefix);
477 fputs(pszPrefix, f);
478
479 size_t n;
480 while ( *psz != '\0' ) {
481 for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
482 putc(*psz++, f);
483
484 // wrapped?
485 if ( *psz != '\0' ) {
486 /*putc('\n', f);*/
487 for ( n = 0; n < nStart; n++ )
488 putc(' ', f);
489
490 // as we wrapped, squeeze all white space
491 while ( isspace(*psz) )
492 psz++;
493 }
494 }
495
496 putc('\n', f);
497 }
498 #endif //LOG_PRETTY_WRAP
499
500 // ----------------------------------------------------------------------------
501 // error code/error message retrieval functions
502 // ----------------------------------------------------------------------------
503
504 // get error code from syste
505 unsigned long wxSysErrorCode()
506 {
507 #ifdef __WINDOWS__
508 #ifdef __WIN32__
509 return ::GetLastError();
510 #else //WIN16
511 // @@@@ what to do on Windows 3.1?
512 return 0;
513 #endif //WIN16/32
514 #else //Unix
515 return errno;
516 #endif //Win/Unix
517 }
518
519 // get error message from system
520 const char *wxSysErrorMsg(unsigned long nErrCode)
521 {
522 if ( nErrCode == 0 )
523 nErrCode = wxSysErrorCode();
524
525 #ifdef __WINDOWS__
526 #ifdef __WIN32__
527 static char s_szBuf[LOG_BUFFER_SIZE / 2];
528
529 // get error message from system
530 LPVOID lpMsgBuf;
531 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
532 NULL, nErrCode,
533 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
534 (LPTSTR)&lpMsgBuf,
535 0, NULL);
536
537 // copy it to our buffer and free memory
538 strncpy(s_szBuf, (const char *)lpMsgBuf, SIZEOF(s_szBuf) - 1);
539 s_szBuf[SIZEOF(s_szBuf) - 1] = '\0';
540 LocalFree(lpMsgBuf);
541
542 // returned string is capitalized and ended with '\r\n' - bad
543 s_szBuf[0] = (char)tolower(s_szBuf[0]);
544 size_t len = strlen(s_szBuf);
545 if ( len > 0 ) {
546 // truncate string
547 if ( s_szBuf[len - 2] == '\r' )
548 s_szBuf[len - 2] = '\0';
549 }
550
551 return s_szBuf;
552 #else
553 // TODO: Windows 3.1
554 return NULL;
555 #endif
556 #else
557 return strerror(nErrCode);
558 #endif
559 }
560
561 // ----------------------------------------------------------------------------
562 // debug helper
563 // ----------------------------------------------------------------------------
564
565 #ifdef __DEBUG__
566
567 // this function is called when an assert fails
568 void wxOnAssert(const char *szFile, int nLine, const char *szMsg)
569 {
570 // this variable can be set to true to suppress "assert failure" messages
571 static s_bNoAsserts = FALSE;
572
573 char szBuf[LOG_BUFFER_SIZE];
574 sprintf(szBuf, _("Assert failed in file %s at line %d"), szFile, nLine);
575 if ( szMsg != NULL ) {
576 strcat(szBuf, ": ");
577 strcat(szBuf, szMsg);
578 }
579 else {
580 strcat(szBuf, ".");
581 }
582
583 // send it to the normal log destination
584 wxLogDebug(szBuf);
585
586 #ifdef __WINDOWS__
587 if ( !s_bNoAsserts ) {
588 strcat(szBuf, _("\nDo you want to stop the program?"
589 "\nYou can also choose [Cancel] to suppress "
590 "further warnings."));
591
592 switch ( ::MessageBox(NULL, szBuf, _("Debug"),
593 MB_YESNOCANCEL | MB_ICONINFORMATION) ) {
594 case IDYES:
595 DebugBreak();
596 break;
597
598 case IDCANCEL:
599 s_bNoAsserts = TRUE;
600 break;
601 }
602 }
603 #else
604 // @@@@ don't know how to start the debugger under generic Unix
605 s_bNoAsserts = TRUE; // suppress 'unused var' warning
606 abort();
607 #endif
608 }
609
610 #endif //DEBUG
611