Really support milliseconds in wxLog
Although this was supposed to work, specifying "%l" in wxLog time stamp format actually didn't because wxLog timestamps were stored as seconds. Fix this by storing them as milliseconds and add a simple test (not executed by default) showing that "%l" works correctly now. Still keep the old wxLogRecordInfo::timestamp field for compatibility, as it was documented. See #13059.
This commit is contained in:
@@ -62,6 +62,7 @@ class WXDLLIMPEXP_FWD_BASE wxObject;
|
|||||||
#include "wx/dynarray.h"
|
#include "wx/dynarray.h"
|
||||||
#include "wx/hashmap.h"
|
#include "wx/hashmap.h"
|
||||||
#include "wx/msgout.h"
|
#include "wx/msgout.h"
|
||||||
|
#include "wx/time.h"
|
||||||
|
|
||||||
#if wxUSE_THREADS
|
#if wxUSE_THREADS
|
||||||
#include "wx/thread.h"
|
#include "wx/thread.h"
|
||||||
@@ -160,9 +161,11 @@ public:
|
|||||||
|
|
||||||
// don't initialize the timestamp yet, we might not need it at all if
|
// don't initialize the timestamp yet, we might not need it at all if
|
||||||
// the message doesn't end up being logged and otherwise we'll fill it
|
// the message doesn't end up being logged and otherwise we'll fill it
|
||||||
// just before logging it, which won't change it by much and definitely
|
// just before logging it, which won't change it by much
|
||||||
// less than a second resolution of the timestamp
|
timestampMS = 0;
|
||||||
|
#if WXWIN_COMPATIBILITY_3_0
|
||||||
timestamp = 0;
|
timestamp = 0;
|
||||||
|
#endif // WXWIN_COMPATIBILITY_3_0
|
||||||
|
|
||||||
#if wxUSE_THREADS
|
#if wxUSE_THREADS
|
||||||
threadId = wxThread::GetCurrentId();
|
threadId = wxThread::GetCurrentId();
|
||||||
@@ -208,8 +211,13 @@ public:
|
|||||||
// not set (i.e. wxLOG_COMPONENT not defined). It must be in ASCII.
|
// not set (i.e. wxLOG_COMPONENT not defined). It must be in ASCII.
|
||||||
const char *component;
|
const char *component;
|
||||||
|
|
||||||
// time of record generation
|
// time of record generation in milliseconds since Epoch
|
||||||
|
wxLongLong_t timestampMS;
|
||||||
|
|
||||||
|
#if WXWIN_COMPATIBILITY_3_0
|
||||||
|
// preserved for compatibility only, use timestampMS instead now
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
|
#endif // WXWIN_COMPATIBILITY_3_0
|
||||||
|
|
||||||
#if wxUSE_THREADS
|
#if wxUSE_THREADS
|
||||||
// id of the thread which logged this record
|
// id of the thread which logged this record
|
||||||
@@ -334,7 +342,12 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
// Override this method to change just the time stamp formatting. It is
|
// Override this method to change just the time stamp formatting. It is
|
||||||
// called by default Format() implementation.
|
// called by default Format() implementation.
|
||||||
|
virtual wxString FormatTimeMS(wxLongLong_t msec) const;
|
||||||
|
|
||||||
|
#if WXWIN_COMPATIBILITY_3_0
|
||||||
|
// Old function which only worked at second resolution.
|
||||||
virtual wxString FormatTime(time_t t) const;
|
virtual wxString FormatTime(time_t t) const;
|
||||||
|
#endif // WXWIN_COMPATIBILITY_3_0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1166,7 +1179,11 @@ private:
|
|||||||
// As explained in wxLogRecordInfo ctor, we don't initialize its
|
// As explained in wxLogRecordInfo ctor, we don't initialize its
|
||||||
// timestamp to avoid calling time() unnecessary, but now that we are
|
// timestamp to avoid calling time() unnecessary, but now that we are
|
||||||
// about to log the message, we do need to do it.
|
// about to log the message, we do need to do it.
|
||||||
m_info.timestamp = time(NULL);
|
m_info.timestampMS = wxGetUTCTimeMillis().GetValue();
|
||||||
|
|
||||||
|
#if WXWIN_COMPATIBILITY_3_0
|
||||||
|
m_info.timestamp = m_info.timestampMS / 1000;
|
||||||
|
#endif // WXWIN_COMPATIBILITY_3_0
|
||||||
|
|
||||||
wxLog::OnLog(level, wxString::FormatV(format, argptr), m_info);
|
wxLog::OnLog(level, wxString::FormatV(format, argptr), m_info);
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
const char *func;
|
const char *func;
|
||||||
|
|
||||||
/// Time when the log message was generated.
|
/**
|
||||||
|
Time in milliseconds since Epoch when the log message was generated.
|
||||||
|
|
||||||
|
@since 3.1.5
|
||||||
|
*/
|
||||||
|
wxLongLong_t timestampMS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Time when the log message was generated.
|
||||||
|
|
||||||
|
@deprecated Please use timestampMS instead.
|
||||||
|
*/
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,9 +159,30 @@ public:
|
|||||||
const wxLogRecordInfo& info) const;
|
const wxLogRecordInfo& info) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/**
|
||||||
|
This function formats the time stamp part of the log message including
|
||||||
|
milliseconds.
|
||||||
|
|
||||||
|
Override this function if you need to customize just the time stamp
|
||||||
|
formatting in the log messages.
|
||||||
|
|
||||||
|
@param msec
|
||||||
|
Time to format as the number of milliseconds since
|
||||||
|
1970-01-01T00:00:00.
|
||||||
|
|
||||||
|
@return
|
||||||
|
The formatted time string, may be empty.
|
||||||
|
|
||||||
|
@since 3.1.5
|
||||||
|
*/
|
||||||
|
virtual wxString FormatTimeMS(wxLongLong_t msec) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This function formats the time stamp part of the log message.
|
This function formats the time stamp part of the log message.
|
||||||
|
|
||||||
|
@deprecated This function only exists for compatibility, please
|
||||||
|
override FormatTimeMS() in the new code.
|
||||||
|
|
||||||
Override this function if you need to customize just the time stamp.
|
Override this function if you need to customize just the time stamp.
|
||||||
|
|
||||||
@param time
|
@param time
|
||||||
|
@@ -471,7 +471,7 @@ MyFrame::DoLogRecord(wxLogLevel level,
|
|||||||
DoLogLine
|
DoLogLine
|
||||||
(
|
(
|
||||||
m_txtctrl,
|
m_txtctrl,
|
||||||
wxDateTime(info.timestamp).FormatISOTime(),
|
wxDateTime(wxLongLong(info.timestampMS)).FormatISOTime(),
|
||||||
info.threadId == wxThread::GetMainId()
|
info.threadId == wxThread::GetMainId()
|
||||||
? wxString("main")
|
? wxString("main")
|
||||||
: wxString::Format("%lx", info.threadId),
|
: wxString::Format("%lx", info.threadId),
|
||||||
|
@@ -194,6 +194,21 @@ void wxSafeShowMessage(const wxString& title, const wxString& text)
|
|||||||
// wxLogFormatter class implementation
|
// wxLogFormatter class implementation
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if WXWIN_COMPATIBILITY_3_0
|
||||||
|
|
||||||
|
// Special string used to check if FormatTime() is overridden: hopefully
|
||||||
|
// different from anything that could be reasonably returned by the overridden
|
||||||
|
// version without being as long as a GUID.
|
||||||
|
static const char* DEFAULT_FORMAT_TIME = "??";
|
||||||
|
|
||||||
|
wxString
|
||||||
|
wxLogFormatter::FormatTime(time_t WXUNUSED(t)) const
|
||||||
|
{
|
||||||
|
return wxString::FromAscii(DEFAULT_FORMAT_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WXWIN_COMPATIBILITY_3_0
|
||||||
|
|
||||||
wxString
|
wxString
|
||||||
wxLogFormatter::Format(wxLogLevel level,
|
wxLogFormatter::Format(wxLogLevel level,
|
||||||
const wxString& msg,
|
const wxString& msg,
|
||||||
@@ -206,7 +221,15 @@ wxLogFormatter::Format(wxLogLevel level,
|
|||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
if ( level != wxLOG_Debug && level != wxLOG_Trace )
|
if ( level != wxLOG_Debug && level != wxLOG_Trace )
|
||||||
#endif // __WINDOWS__
|
#endif // __WINDOWS__
|
||||||
|
{
|
||||||
|
#if WXWIN_COMPATIBILITY_3_0
|
||||||
|
// Another backwards compatibility hack: check if FormatTime() was
|
||||||
|
// overridden in the user code.
|
||||||
prefix = FormatTime(info.timestamp);
|
prefix = FormatTime(info.timestamp);
|
||||||
|
if ( prefix == DEFAULT_FORMAT_TIME )
|
||||||
|
#endif // WXWIN_COMPATIBILITY_3_0
|
||||||
|
prefix = FormatTimeMS(info.timestampMS);
|
||||||
|
}
|
||||||
|
|
||||||
switch ( level )
|
switch ( level )
|
||||||
{
|
{
|
||||||
@@ -237,10 +260,14 @@ wxLogFormatter::Format(wxLogLevel level,
|
|||||||
}
|
}
|
||||||
|
|
||||||
wxString
|
wxString
|
||||||
wxLogFormatter::FormatTime(time_t t) const
|
wxLogFormatter::FormatTimeMS(wxLongLong_t msec) const
|
||||||
{
|
{
|
||||||
wxString str;
|
wxString str;
|
||||||
wxLog::TimeStamp(&str, t);
|
|
||||||
|
#if wxUSE_DATETIME
|
||||||
|
str = wxDateTime(wxLongLong(msec)).Format(wxLog::GetTimestamp());
|
||||||
|
str += wxS(": ");
|
||||||
|
#endif // wxUSE_DATETIME
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@@ -321,7 +348,7 @@ void
|
|||||||
wxLog::OnLog(wxLogLevel level, const wxString& msg, time_t t)
|
wxLog::OnLog(wxLogLevel level, const wxString& msg, time_t t)
|
||||||
{
|
{
|
||||||
wxLogRecordInfo info;
|
wxLogRecordInfo info;
|
||||||
info.timestamp = t;
|
info.timestampMS = 1000*t;
|
||||||
#if wxUSE_THREADS
|
#if wxUSE_THREADS
|
||||||
info.threadId = wxThread::GetCurrentId();
|
info.threadId = wxThread::GetCurrentId();
|
||||||
#endif // wxUSE_THREADS
|
#endif // wxUSE_THREADS
|
||||||
|
@@ -342,7 +342,7 @@ void wxLogGui::DoLogRecord(wxLogLevel level,
|
|||||||
{
|
{
|
||||||
m_aMessages.Add(msg);
|
m_aMessages.Add(msg);
|
||||||
m_aSeverity.Add(wxLOG_Message);
|
m_aSeverity.Add(wxLOG_Message);
|
||||||
m_aTimes.Add((long)info.timestamp);
|
m_aTimes.Add((long)(info.timestampMS / 1000));
|
||||||
m_bHasMessages = true;
|
m_bHasMessages = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -395,7 +395,7 @@ void wxLogGui::DoLogRecord(wxLogLevel level,
|
|||||||
|
|
||||||
m_aMessages.Add(msg);
|
m_aMessages.Add(msg);
|
||||||
m_aSeverity.Add((int)level);
|
m_aSeverity.Add((int)level);
|
||||||
m_aTimes.Add((long)info.timestamp);
|
m_aTimes.Add((long)(info.timestampMS / 1000));
|
||||||
m_bHasMessages = true;
|
m_bHasMessages = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@@ -454,4 +454,16 @@ void macroCompilabilityTest()
|
|||||||
wxLogDebug("hello debug %d", 42);
|
wxLogDebug("hello debug %d", 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This allows to check wxLogTrace() interactively by running this test with
|
||||||
|
// WXTRACE=logtest.
|
||||||
|
TEST_CASE("wxLog::Trace", "[log][.]")
|
||||||
|
{
|
||||||
|
// Running this test without setting WXTRACE is useless.
|
||||||
|
REQUIRE( wxGetEnv("WXTRACE", NULL) );
|
||||||
|
|
||||||
|
wxLogTrace("logtest", "Starting test");
|
||||||
|
wxMilliSleep(250);
|
||||||
|
wxLogTrace("logtest", "Ending test 1/4s later");
|
||||||
|
}
|
||||||
|
|
||||||
#endif // wxUSE_LOG
|
#endif // wxUSE_LOG
|
||||||
|
Reference in New Issue
Block a user