Files
wxWidgets/src/common/log.cpp
Vadim Zeitlin 5d256988be Add wxMSWFormatMessage() and use it from other places
Don't duplicate calls to ::FormatMessage(), which is difficult to use
correctly, in wxCrashReport and wxWebRequestWinHTTP, but just reuse the
same code that was already present in wxSysErrorMsgStr() after
refactoring it into a reusable function allowing to specify the module
name to use for the error code lookup (before falling back to
interpreting it as system error code).

This fixes not trimming the trailing "\r\n" from the string in the other
places (wxWinHTTPErrorToString() had code to do it, but it was wrong,
while wxCrashContext::GetExceptionString() didn't do it at all) and
avoids duplication.
2021-01-09 17:41:49 +01:00

1151 lines
31 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/log.cpp
// Purpose: Assorted wxLogXXX functions, and wxLog (sink for logs)
// Author: Vadim Zeitlin
// Modified by:
// Created: 29/01/98
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_LOG
// wxWidgets
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/app.h"
#include "wx/arrstr.h"
#include "wx/intl.h"
#include "wx/string.h"
#include "wx/utils.h"
#endif //WX_PRECOMP
#include "wx/apptrait.h"
#include "wx/datetime.h"
#include "wx/file.h"
#include "wx/msgout.h"
#include "wx/textfile.h"
#include "wx/thread.h"
#include "wx/private/threadinfo.h"
#include "wx/crt.h"
#include "wx/vector.h"
// other standard headers
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#if defined(__WINDOWS__)
#include "wx/msw/private.h" // includes windows.h
#endif
#undef wxLOG_COMPONENT
const char *wxLOG_COMPONENT = "";
// this macro allows to define an object which will be initialized before any
// other function in this file is called: this is necessary to allow log
// functions to be used during static initialization (this is not advisable
// anyhow but we should at least try to not crash) and to also ensure that they
// are initialized by the time static initialization is done, i.e. before any
// threads are created hopefully
//
// the net effect of all this is that you can use Get##name() function to
// access the object without worrying about it being not initialized
//
// see also WX_DEFINE_GLOBAL_CONV2() in src/common/strconv.cpp
#define WX_DEFINE_GLOBAL_VAR(type, name) \
inline type& Get##name() \
{ \
static type s_##name; \
return s_##name; \
} \
\
type *gs_##name##Ptr = &Get##name()
#if wxUSE_THREADS
namespace
{
// contains messages logged by the other threads and waiting to be shown until
// Flush() is called in the main one
typedef wxVector<wxLogRecord> wxLogRecords;
wxLogRecords gs_bufferedLogRecords;
#define WX_DEFINE_LOG_CS(name) WX_DEFINE_GLOBAL_VAR(wxCriticalSection, name##CS)
// this critical section is used for buffering the messages from threads other
// than main, i.e. it protects all accesses to gs_bufferedLogRecords above
WX_DEFINE_LOG_CS(BackgroundLog);
// this one is used for protecting TraceMasks() from concurrent access
WX_DEFINE_LOG_CS(TraceMask);
// and this one is used for GetComponentLevels()
WX_DEFINE_LOG_CS(Levels);
} // anonymous namespace
#endif // wxUSE_THREADS
// ----------------------------------------------------------------------------
// non member functions
// ----------------------------------------------------------------------------
// define this to enable wrapping of log messages
//#define LOG_PRETTY_WRAP
#ifdef LOG_PRETTY_WRAP
static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
#endif
// ----------------------------------------------------------------------------
// module globals
// ----------------------------------------------------------------------------
namespace
{
// this struct is used to store information about the previous log message used
// by OnLog() to (optionally) avoid logging multiple copies of the same message
struct PreviousLogInfo
{
PreviousLogInfo()
{
numRepeated = 0;
}
// previous message itself
wxString msg;
// its level
wxLogLevel level;
// other information about it
wxLogRecordInfo info;
// the number of times it was already repeated
unsigned numRepeated;
};
PreviousLogInfo gs_prevLog;
// map containing all components for which log level was explicitly set
//
// NB: all accesses to it must be protected by GetLevelsCS() critical section
WX_DEFINE_GLOBAL_VAR(wxStringToNumHashMap, ComponentLevels);
// ----------------------------------------------------------------------------
// wxLogOutputBest: wxLog wrapper around wxMessageOutputBest
// ----------------------------------------------------------------------------
class wxLogOutputBest : public wxLog
{
public:
wxLogOutputBest() { }
protected:
virtual void DoLogText(const wxString& msg) wxOVERRIDE
{
wxMessageOutputBest().Output(msg);
}
private:
wxDECLARE_NO_COPY_CLASS(wxLogOutputBest);
};
} // anonymous namespace
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// helper global functions
// ----------------------------------------------------------------------------
void wxSafeShowMessage(const wxString& title, const wxString& text)
{
#ifdef __WINDOWS__
::MessageBox(NULL, text.t_str(), title.t_str(), MB_OK | MB_ICONSTOP);
#else
wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str());
fflush(stderr);
#endif
}
// ----------------------------------------------------------------------------
// wxLogFormatter class implementation
// ----------------------------------------------------------------------------
wxString
wxLogFormatter::Format(wxLogLevel level,
const wxString& msg,
const wxLogRecordInfo& info) const
{
wxString prefix;
// don't time stamp debug messages under MSW as debug viewers usually
// already have an option to do it
#ifdef __WINDOWS__
if ( level != wxLOG_Debug && level != wxLOG_Trace )
#endif // __WINDOWS__
prefix = FormatTime(info.timestamp);
switch ( level )
{
case wxLOG_Error:
prefix += _("Error: ");
break;
case wxLOG_Warning:
prefix += _("Warning: ");
break;
// don't prepend "debug/trace" prefix under MSW as it goes to the debug
// window anyhow and so can't be confused with something else
#ifndef __WINDOWS__
case wxLOG_Debug:
// this prefix (as well as the one below) is intentionally not
// translated as nobody translates debug messages anyhow
prefix += "Debug: ";
break;
case wxLOG_Trace:
prefix += "Trace: ";
break;
#endif // !__WINDOWS__
}
return prefix + msg;
}
wxString
wxLogFormatter::FormatTime(time_t t) const
{
wxString str;
wxLog::TimeStamp(&str, t);
return str;
}
// ----------------------------------------------------------------------------
// wxLog class implementation
// ----------------------------------------------------------------------------
unsigned wxLog::LogLastRepeatIfNeeded()
{
const unsigned count = gs_prevLog.numRepeated;
if ( gs_prevLog.numRepeated )
{
wxString msg;
#if wxUSE_INTL
if ( gs_prevLog.numRepeated == 1 )
{
// We use a separate message for this case as "repeated 1 time"
// looks somewhat strange.
msg = _("The previous message repeated once.");
}
else
{
// Notice that we still use wxPLURAL() to ensure that multiple
// numbers of times are correctly formatted, even though we never
// actually use the singular string.
msg.Printf(wxPLURAL("The previous message repeated %u time.",
"The previous message repeated %u times.",
gs_prevLog.numRepeated),
gs_prevLog.numRepeated);
}
#else
msg.Printf(wxS("The previous message was repeated %u time(s)."),
gs_prevLog.numRepeated);
#endif
gs_prevLog.numRepeated = 0;
gs_prevLog.msg.clear();
DoLogRecord(gs_prevLog.level, msg, gs_prevLog.info);
}
return count;
}
wxLog::~wxLog()
{
// Flush() must be called before destroying the object as otherwise some
// messages could be lost
if ( gs_prevLog.numRepeated )
{
wxMessageOutputDebug().Printf
(
#if wxUSE_INTL
wxPLURAL
(
"Last repeated message (\"%s\", %u time) wasn't output",
"Last repeated message (\"%s\", %u times) wasn't output",
gs_prevLog.numRepeated
),
#else
wxS("Last repeated message (\"%s\", %u time(s)) wasn't output"),
#endif
gs_prevLog.msg,
gs_prevLog.numRepeated
);
}
delete m_formatter;
}
// ----------------------------------------------------------------------------
// wxLog logging functions
// ----------------------------------------------------------------------------
/* static */
void
wxLog::OnLog(wxLogLevel level, const wxString& msg, time_t t)
{
wxLogRecordInfo info;
info.timestamp = t;
#if wxUSE_THREADS
info.threadId = wxThread::GetCurrentId();
#endif // wxUSE_THREADS
OnLog(level, msg, info);
}
/* static */
void
wxLog::OnLog(wxLogLevel level,
const wxString& msg,
const wxLogRecordInfo& info)
{
// fatal errors can't be suppressed nor handled by the custom log target
// and always terminate the program
if ( level == wxLOG_FatalError )
{
wxSafeShowMessage(wxS("Fatal Error"), msg);
wxAbort();
}
wxLog *logger;
#if wxUSE_THREADS
if ( !wxThread::IsMain() )
{
logger = wxThreadInfo.logger;
if ( !logger )
{
if ( ms_pLogger )
{
// buffer the messages until they can be shown from the main
// thread
wxCriticalSectionLocker lock(GetBackgroundLogCS());
gs_bufferedLogRecords.push_back(wxLogRecord(level, msg, info));
// ensure that our Flush() will be called soon
wxWakeUpIdle();
}
//else: we don't have any logger at all, there is no need to log
// anything
return;
}
//else: we have a thread-specific logger, we can send messages to it
// directly
}
else
#endif // wxUSE_THREADS
{
logger = GetMainThreadActiveTarget();
if ( !logger )
return;
}
logger->CallDoLogNow(level, msg, info);
}
void
wxLog::CallDoLogNow(wxLogLevel level,
const wxString& msg,
const wxLogRecordInfo& info)
{
if ( GetRepetitionCounting() )
{
if ( msg == gs_prevLog.msg )
{
gs_prevLog.numRepeated++;
// nothing else to do, in particular, don't log the
// repeated message
return;
}
LogLastRepeatIfNeeded();
// reset repetition counter for a new message
gs_prevLog.msg = msg;
gs_prevLog.level = level;
gs_prevLog.info = info;
}
// handle extra data which may be passed to us by wxLogXXX()
wxString prefix, suffix;
wxUIntPtr num = 0;
if ( info.GetNumValue(wxLOG_KEY_SYS_ERROR_CODE, &num) )
{
const long err = static_cast<long>(num);
suffix.Printf(_(" (error %ld: %s)"), err, wxSysErrorMsgStr(err));
}
#if wxUSE_LOG_TRACE
wxString str;
if ( level == wxLOG_Trace && info.GetStrValue(wxLOG_KEY_TRACE_MASK, &str) )
{
prefix = "(" + str + ") ";
}
#endif // wxUSE_LOG_TRACE
DoLogRecord(level, prefix + msg + suffix, info);
}
void wxLog::DoLogRecord(wxLogLevel level,
const wxString& msg,
const wxLogRecordInfo& info)
{
#if WXWIN_COMPATIBILITY_2_8
// call the old DoLog() to ensure that existing custom log classes still
// work
//
// as the user code could have defined it as either taking "const char *"
// (in ANSI build) or "const wxChar *" (in ANSI/Unicode), we have no choice
// but to call both of them
DoLog(level, (const char*)msg.mb_str(), info.timestamp);
DoLog(level, (const wchar_t*)msg.wc_str(), info.timestamp);
#else // !WXWIN_COMPATIBILITY_2_8
wxUnusedVar(info);
#endif // WXWIN_COMPATIBILITY_2_8/!WXWIN_COMPATIBILITY_2_8
// Use wxLogFormatter to format the message
DoLogTextAtLevel(level, m_formatter->Format (level, msg, info));
}
void wxLog::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
{
// we know about debug messages (because using wxMessageOutputDebug is the
// right thing to do in 99% of all cases and also for compatibility) but
// anything else needs to be handled in the derived class
if ( level == wxLOG_Debug || level == wxLOG_Trace )
{
wxMessageOutputDebug().Output(msg + wxS('\n'));
}
else
{
DoLogText(msg);
}
}
void wxLog::DoLogText(const wxString& WXUNUSED(msg))
{
// in 2.8-compatible build the derived class might override DoLog() or
// DoLogString() instead so we can't have this assert there
#if !WXWIN_COMPATIBILITY_2_8
wxFAIL_MSG( "must be overridden if it is called" );
#endif // WXWIN_COMPATIBILITY_2_8
}
#if WXWIN_COMPATIBILITY_2_8
void wxLog::DoLog(wxLogLevel WXUNUSED(level), const char *szString, time_t t)
{
DoLogString(szString, t);
}
void wxLog::DoLog(wxLogLevel WXUNUSED(level), const wchar_t *wzString, time_t t)
{
DoLogString(wzString, t);
}
#endif // WXWIN_COMPATIBILITY_2_8
// ----------------------------------------------------------------------------
// wxLog active target management
// ----------------------------------------------------------------------------
wxLog *wxLog::GetActiveTarget()
{
#if wxUSE_THREADS
if ( !wxThread::IsMain() )
{
// check if we have a thread-specific log target
wxLog * const logger = wxThreadInfo.logger;
// the code below should be only executed for the main thread as
// CreateLogTarget() is not meant for auto-creating log targets for
// worker threads so skip it in any case
return logger ? logger : ms_pLogger;
}
#endif // wxUSE_THREADS
return GetMainThreadActiveTarget();
}
/* static */
wxLog *wxLog::GetMainThreadActiveTarget()
{
if ( ms_bAutoCreate && ms_pLogger == NULL ) {
// prevent infinite recursion if someone calls wxLogXXX() from
// wxApp::CreateLogTarget()
static bool s_bInGetActiveTarget = false;
if ( !s_bInGetActiveTarget ) {
s_bInGetActiveTarget = true;
// ask the application to create a log target for us
if ( wxTheApp != NULL )
ms_pLogger = wxTheApp->GetTraits()->CreateLogTarget();
else
ms_pLogger = new wxLogOutputBest;
s_bInGetActiveTarget = false;
// do nothing if it fails - what can we do?
}
}
return ms_pLogger;
}
wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
{
if ( ms_pLogger != NULL ) {
// flush the old messages before changing because otherwise they might
// get lost later if this target is not restored
ms_pLogger->Flush();
}
wxLog *pOldLogger = ms_pLogger;
ms_pLogger = pLogger;
return pOldLogger;
}
#if wxUSE_THREADS
/* static */
wxLog *wxLog::SetThreadActiveTarget(wxLog *logger)
{
wxASSERT_MSG( !wxThread::IsMain(), "use SetActiveTarget() for main thread" );
wxLog * const oldLogger = wxThreadInfo.logger;
if ( oldLogger )
oldLogger->Flush();
wxThreadInfo.logger = logger;
return oldLogger;
}
#endif // wxUSE_THREADS
void wxLog::DontCreateOnDemand()
{
ms_bAutoCreate = false;
// this is usually called at the end of the program and we assume that it
// is *always* called at the end - so we free memory here to avoid false
// memory leak reports from wxWin memory tracking code
ClearTraceMasks();
}
void wxLog::DoCreateOnDemand()
{
ms_bAutoCreate = true;
}
// ----------------------------------------------------------------------------
// wxLog components levels
// ----------------------------------------------------------------------------
/* static */
void wxLog::SetComponentLevel(const wxString& component, wxLogLevel level)
{
if ( component.empty() )
{
SetLogLevel(level);
}
else
{
wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
GetComponentLevels()[component] = level;
}
}
/* static */
wxLogLevel wxLog::GetComponentLevel(const wxString& componentOrig)
{
wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
// Make a copy before modifying it in the loop.
wxString component = componentOrig;
const wxStringToNumHashMap& componentLevels = GetComponentLevels();
while ( !component.empty() )
{
wxStringToNumHashMap::const_iterator
it = componentLevels.find(component);
if ( it != componentLevels.end() )
return static_cast<wxLogLevel>(it->second);
component = component.BeforeLast('/');
}
return GetLogLevel();
}
// ----------------------------------------------------------------------------
// wxLog trace masks
// ----------------------------------------------------------------------------
namespace
{
// because IsAllowedTraceMask() may be called during static initialization
// (this is not recommended but it may still happen, see #11592) we can't use a
// simple static variable which might be not initialized itself just yet to
// store the trace masks, but need this accessor function which will ensure
// that the variable is always correctly initialized before being accessed
//
// notice that this doesn't make accessing it MT-safe, of course, you need to
// serialize accesses to it using GetTraceMaskCS() for this
wxArrayString& TraceMasks()
{
static wxArrayString s_traceMasks;
return s_traceMasks;
}
} // anonymous namespace
/* static */ const wxArrayString& wxLog::GetTraceMasks()
{
// because of this function signature (it returns a reference, not the
// object), it is inherently MT-unsafe so there is no need to acquire the
// lock here anyhow
return TraceMasks();
}
void wxLog::AddTraceMask(const wxString& str)
{
wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
TraceMasks().push_back(str);
}
void wxLog::RemoveTraceMask(const wxString& str)
{
wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
int index = TraceMasks().Index(str);
if ( index != wxNOT_FOUND )
TraceMasks().RemoveAt((size_t)index);
}
void wxLog::ClearTraceMasks()
{
wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
TraceMasks().Clear();
}
/*static*/ bool wxLog::IsAllowedTraceMask(const wxString& mask)
{
wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
const wxArrayString& masks = GetTraceMasks();
for ( wxArrayString::const_iterator it = masks.begin(),
en = masks.end();
it != en;
++it )
{
if ( *it == mask)
return true;
}
return false;
}
// ----------------------------------------------------------------------------
// wxLog miscellaneous other methods
// ----------------------------------------------------------------------------
#if wxUSE_DATETIME
void wxLog::TimeStamp(wxString *str)
{
if ( !ms_timestamp.empty() )
{
*str = wxDateTime::UNow().Format(ms_timestamp);
*str += wxS(": ");
}
}
void wxLog::TimeStamp(wxString *str, time_t t)
{
if ( !ms_timestamp.empty() )
{
*str = wxDateTime(t).Format(ms_timestamp);
*str += wxS(": ");
}
}
#else // !wxUSE_DATETIME
void wxLog::TimeStamp(wxString*)
{
}
void wxLog::TimeStamp(wxString*, time_t)
{
}
#endif // wxUSE_DATETIME/!wxUSE_DATETIME
#if wxUSE_THREADS
void wxLog::FlushThreadMessages()
{
// check if we have queued messages from other threads
wxLogRecords bufferedLogRecords;
{
wxCriticalSectionLocker lock(GetBackgroundLogCS());
bufferedLogRecords.swap(gs_bufferedLogRecords);
// release the lock now to not keep it while we are logging the
// messages below, allowing background threads to run
}
if ( !bufferedLogRecords.empty() )
{
for ( wxLogRecords::const_iterator it = bufferedLogRecords.begin();
it != bufferedLogRecords.end();
++it )
{
CallDoLogNow(it->level, it->msg, it->info);
}
}
}
/* static */
bool wxLog::IsThreadLoggingEnabled()
{
return !wxThreadInfo.loggingDisabled;
}
/* static */
bool wxLog::EnableThreadLogging(bool enable)
{
const bool wasEnabled = !wxThreadInfo.loggingDisabled;
wxThreadInfo.loggingDisabled = !enable;
return wasEnabled;
}
#endif // wxUSE_THREADS
wxLogFormatter *wxLog::SetFormatter(wxLogFormatter* formatter)
{
wxLogFormatter* formatterOld = m_formatter;
m_formatter = formatter ? formatter : new wxLogFormatter;
return formatterOld;
}
void wxLog::Flush()
{
LogLastRepeatIfNeeded();
}
/* static */
void wxLog::FlushActive()
{
if ( ms_suspendCount )
return;
wxLog * const log = GetActiveTarget();
if ( log )
{
#if wxUSE_THREADS
if ( wxThread::IsMain() )
log->FlushThreadMessages();
#endif // wxUSE_THREADS
log->Flush();
}
}
// ----------------------------------------------------------------------------
// wxLogBuffer implementation
// ----------------------------------------------------------------------------
void wxLogBuffer::Flush()
{
wxLog::Flush();
if ( !m_str.empty() )
{
wxMessageOutputBest out;
out.Printf(wxS("%s"), m_str.c_str());
m_str.clear();
}
}
void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
{
// don't put debug messages in the buffer, we don't want to show
// them to the user in a msg box, log them immediately
switch ( level )
{
case wxLOG_Debug:
case wxLOG_Trace:
wxLog::DoLogTextAtLevel(level, msg);
break;
default:
m_str << msg << wxS("\n");
}
}
// ----------------------------------------------------------------------------
// wxLogStderr class implementation
// ----------------------------------------------------------------------------
wxLogStderr::wxLogStderr(FILE *fp, const wxMBConv& conv)
: wxMessageOutputStderr(fp ? fp : stderr, conv)
{
}
void wxLogStderr::DoLogText(const wxString& msg)
{
// First send it to stderr, even if we don't have it (e.g. in a Windows GUI
// application under) it's not a problem to try to use it and it's easier
// than determining whether we do have it or not.
wxMessageOutputStderr::Output(msg);
// under GUI systems such as Windows or Mac, programs usually don't have
// stderr at all, so show the messages also somewhere else, typically in
// the debugger window so that they go at least somewhere instead of being
// simply lost
if ( m_fp == stderr )
{
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( traits && !traits->HasStderr() )
{
wxMessageOutputDebug().Output(msg + wxS('\n'));
}
}
}
// ----------------------------------------------------------------------------
// wxLogStream implementation
// ----------------------------------------------------------------------------
#if wxUSE_STD_IOSTREAM
#include "wx/ioswrap.h"
wxLogStream::wxLogStream(wxSTD ostream *ostr, const wxMBConv& conv)
: wxMessageOutputWithConv(conv)
{
if ( ostr == NULL )
m_ostr = &wxSTD cerr;
else
m_ostr = ostr;
}
void wxLogStream::DoLogText(const wxString& msg)
{
const wxCharBuffer& buf = PrepareForOutput(msg);
m_ostr->write(buf, buf.length());
}
#endif // wxUSE_STD_IOSTREAM
// ----------------------------------------------------------------------------
// wxLogChain
// ----------------------------------------------------------------------------
wxLogChain::wxLogChain(wxLog *logger)
{
m_bPassMessages = true;
m_logNew = logger;
// Notice that we use GetActiveTarget() here instead of directly calling
// SetActiveTarget() to trigger wxLog auto-creation: if we're created as
// the first logger, we should still chain with the standard, implicit and
// possibly still not created standard logger instead of disabling normal
// logging entirely.
m_logOld = wxLog::GetActiveTarget();
wxLog::SetActiveTarget(this);
}
wxLogChain::~wxLogChain()
{
wxLog::SetActiveTarget(m_logOld);
if ( m_logNew != this )
delete m_logNew;
}
void wxLogChain::SetLog(wxLog *logger)
{
if ( m_logNew != this )
delete m_logNew;
m_logNew = logger;
}
void wxLogChain::Flush()
{
if ( m_logOld )
m_logOld->Flush();
// be careful to avoid infinite recursion
if ( m_logNew && m_logNew != this )
m_logNew->Flush();
}
void wxLogChain::DoLogRecord(wxLogLevel level,
const wxString& msg,
const wxLogRecordInfo& info)
{
// let the previous logger show it
if ( m_logOld && IsPassingMessages() )
m_logOld->LogRecord(level, msg, info);
// and also send it to the new one
if ( m_logNew )
{
// don't call m_logNew->LogRecord() to avoid infinite recursion when
// m_logNew is this object itself
if ( m_logNew != this )
m_logNew->LogRecord(level, msg, info);
else
wxLog::DoLogRecord(level, msg, info);
}
}
#ifdef __VISUALC__
// "'this' : used in base member initializer list" - so what?
#pragma warning(disable:4355)
#endif // VC++
// ----------------------------------------------------------------------------
// wxLogInterposer
// ----------------------------------------------------------------------------
wxLogInterposer::wxLogInterposer()
: wxLogChain(this)
{
}
// ----------------------------------------------------------------------------
// wxLogInterposerTemp
// ----------------------------------------------------------------------------
wxLogInterposerTemp::wxLogInterposerTemp()
: wxLogChain(this)
{
DetachOldLog();
}
#ifdef __VISUALC__
#pragma warning(default:4355)
#endif // VC++
// ============================================================================
// Global functions/variables
// ============================================================================
// ----------------------------------------------------------------------------
// static variables
// ----------------------------------------------------------------------------
bool wxLog::ms_bRepetCounting = false;
wxLog *wxLog::ms_pLogger = NULL;
bool wxLog::ms_doLog = true;
bool wxLog::ms_bAutoCreate = true;
bool wxLog::ms_bVerbose = false;
wxLogLevel wxLog::ms_logLevel = wxLOG_Max; // log everything by default
size_t wxLog::ms_suspendCount = 0;
wxString wxLog::ms_timestamp(wxS("%X")); // time only, no date
#if WXWIN_COMPATIBILITY_2_8
wxTraceMask wxLog::ms_ulTraceMask = (wxTraceMask)0;
#endif // wxDEBUG_LEVEL
// ----------------------------------------------------------------------------
// stdout error logging helper
// ----------------------------------------------------------------------------
// helper function: wraps the message and justifies it under given position
// (looks more pretty on the terminal). Also adds newline at the end.
//
// TODO this is now disabled until I find a portable way of determining the
// terminal window size (ok, I found it but does anybody really cares?)
#ifdef LOG_PRETTY_WRAP
static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
{
size_t nMax = 80; // FIXME
size_t nStart = strlen(pszPrefix);
fputs(pszPrefix, f);
size_t n;
while ( *psz != '\0' ) {
for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
putc(*psz++, f);
// wrapped?
if ( *psz != '\0' ) {
/*putc('\n', f);*/
for ( n = 0; n < nStart; n++ )
putc(' ', f);
// as we wrapped, squeeze all white space
while ( isspace(*psz) )
psz++;
}
}
putc('\n', f);
}
#endif //LOG_PRETTY_WRAP
// ----------------------------------------------------------------------------
// error code/error message retrieval functions
// ----------------------------------------------------------------------------
// get error code from syste
unsigned long wxSysErrorCode()
{
#if defined(__WINDOWS__)
return ::GetLastError();
#else //Unix
return errno;
#endif //Win/Unix
}
#if defined(__WINDOWS__)
wxString wxMSWFormatMessage(DWORD nErrCode, HMODULE hModule)
{
DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
if ( hModule )
flags |= FORMAT_MESSAGE_FROM_HMODULE;
// get error message from system
LPVOID lpMsgBuf;
if ( ::FormatMessage
(
flags,
hModule,
nErrCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
) == 0 )
{
wxLogDebug(wxS("FormatMessage failed with error 0x%lx"), GetLastError());
// if this happens, something is seriously wrong, so don't use _() here
// for safety
return wxString::Format(wxS("unknown error 0x%lx"), nErrCode);
}
wxString str;
// copy it to our buffer and free memory
// Crashes on SmartPhone (FIXME)
if( lpMsgBuf != 0 )
{
str = static_cast<const wxChar *>(lpMsgBuf);
LocalFree(lpMsgBuf);
// returned string is ended with '\r\n' - bad
size_t len = str.length();
if ( len >= 2 ) {
// truncate string
if ( str[len - 2] == wxS('\r') )
str.Truncate(len - 2);
}
}
return str;
}
#endif // __WINDOWS__
wxString wxSysErrorMsgStr(unsigned long nErrCode)
{
if ( nErrCode == 0 )
nErrCode = wxSysErrorCode();
#if defined(__WINDOWS__)
return wxMSWFormatMessage(nErrCode);
#else // !__WINDOWS__
char buffer[1024];
char *errorMsg = buffer;
#if defined(__GLIBC__) && defined(_GNU_SOURCE) // GNU-specific strerror_r
// GNU's strerror_r has a weird interface -- it doesn't
// necessarily copy anything to the buffer given; use return
// value instead.
errorMsg = strerror_r((int)nErrCode, buffer, sizeof(buffer));
#elif defined( __VMS )
errorMsg = strerror((int)nErrCode);
#else // XSI-compliant strerror_r
strerror_r((int)nErrCode, buffer, sizeof(buffer));
#endif
// at this point errorMsg might not point to buffer anymore
return errorMsg;
#endif // __WINDOWS__/!__WINDOWS__
}
// get error message from system as a char pointer: this function has to use a
// static buffer of fixed size, so should be avoided in favour of the function
// returning wxString
const wxChar *wxSysErrorMsg(unsigned long nErrCode)
{
static wxChar s_szBuf[1024];
wxStrlcpy(s_szBuf, wxSysErrorMsgStr(nErrCode), WXSIZEOF(s_szBuf));
return s_szBuf;
}
#endif // wxUSE_LOG