Files
wxWidgets/src/common/log.cpp
Lauri Nurmi 3926538fea Don't de-capitalize system error messages on MSW
The returned string being capitalized is not 'bad' as the ancient comment
from the 90s suggested.

At least on Windows 7+, system error messages are full sentences beginning
with a capital letter, ending in a full stop; there is no point in
lowercasing the first letter.
2016-11-21 18:07:55 +02:00

1122 lines
30 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"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#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 <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, wxSysErrorMsg(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(wxString component)
{
wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
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)
{
if ( fp == NULL )
m_fp = stderr;
else
m_fp = fp;
}
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(m_fp).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)
{
if ( ostr == NULL )
m_ostr = &wxSTD cerr;
else
m_ostr = ostr;
}
void wxLogStream::DoLogText(const wxString& msg)
{
(*m_ostr) << msg << wxSTD endl;
}
#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
}
// get error message from system
const wxChar *wxSysErrorMsg(unsigned long nErrCode)
{
if ( nErrCode == 0 )
nErrCode = wxSysErrorCode();
#if defined(__WINDOWS__)
static wxChar s_szBuf[1024];
// get error message from system
LPVOID lpMsgBuf;
if ( ::FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
nErrCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
) == 0 )
{
// if this happens, something is seriously wrong, so don't use _() here
// for safety
wxSprintf(s_szBuf, wxS("unknown error %lx"), nErrCode);
return s_szBuf;
}
// copy it to our buffer and free memory
// Crashes on SmartPhone (FIXME)
if( lpMsgBuf != 0 )
{
wxStrlcpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf));
LocalFree(lpMsgBuf);
// returned string is ended with '\r\n' - bad
size_t len = wxStrlen(s_szBuf);
if ( len >= 2 ) {
// truncate string
if ( s_szBuf[len - 2] == wxS('\r') )
s_szBuf[len - 2] = wxS('\0');
}
}
else
{
s_szBuf[0] = wxS('\0');
}
return s_szBuf;
#else // !__WINDOWS__
#if wxUSE_UNICODE
static wchar_t s_wzBuf[1024];
wxConvCurrent->MB2WC(s_wzBuf, strerror((int)nErrCode),
WXSIZEOF(s_wzBuf) - 1);
return s_wzBuf;
#else
return strerror((int)nErrCode);
#endif
#endif // __WINDOWS__/!__WINDOWS__
}
#endif // wxUSE_LOG