journaller.cpp
Go to the documentation of this file.
1 
2 // Copyright (c) 2003-2021 Xsens Technologies B.V. or subsidiaries worldwide.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions, and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions, and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // 3. Neither the names of the copyright holders nor the names of their contributors
16 // may be used to endorse or promote products derived from this software without
17 // specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
26 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.THE LAWS OF THE NETHERLANDS
28 // SHALL BE EXCLUSIVELY APPLICABLE AND ANY DISPUTES SHALL BE FINALLY SETTLED UNDER THE RULES
29 // OF ARBITRATION OF THE INTERNATIONAL CHAMBER OF COMMERCE IN THE HAGUE BY ONE OR MORE
30 // ARBITRATORS APPOINTED IN ACCORDANCE WITH SAID RULES.
31 //
32 
33 
34 // Copyright (c) 2003-2021 Xsens Technologies B.V. or subsidiaries worldwide.
35 // All rights reserved.
36 //
37 // Redistribution and use in source and binary forms, with or without modification,
38 // are permitted provided that the following conditions are met:
39 //
40 // 1. Redistributions of source code must retain the above copyright notice,
41 // this list of conditions, and the following disclaimer.
42 //
43 // 2. Redistributions in binary form must reproduce the above copyright notice,
44 // this list of conditions, and the following disclaimer in the documentation
45 // and/or other materials provided with the distribution.
46 //
47 // 3. Neither the names of the copyright holders nor the names of their contributors
48 // may be used to endorse or promote products derived from this software without
49 // specific prior written permission.
50 //
51 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
52 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
54 // THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 // SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
56 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
58 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.THE LAWS OF THE NETHERLANDS
60 // SHALL BE EXCLUSIVELY APPLICABLE AND ANY DISPUTES SHALL BE FINALLY SETTLED UNDER THE RULES
61 // OF ARBITRATION OF THE INTERNATIONAL CHAMBER OF COMMERCE IN THE HAGUE BY ONE OR MORE
62 // ARBITRATORS APPOINTED IN ACCORDANCE WITH SAID RULES.
63 //
64 
65 #include "journalstackwalker.h"
66 #include "journaller.h"
67 #include "journalthreader.h"
68 #include "journalfile.h"
69 
70 #include <map>
71 #include <fstream>
72 #include <xstypes/xstime.h>
73 #include <xstypes/pstdint.h> // for PRINTF_INT64_MODIFIER
74 #include <xstypes/xsthread.h> // for xsGetCurrentThreadId
75 #include <xstypes/xstimestamp.h>
76 #include <xstypes/xstimeinfo.h>
77 #include <memory>
78 
79 #ifndef XSENS_NO_AUTOLIB
80  #pragma comment(lib, "psapi.lib")
81 #endif
82 
85 
86 inline int threadId()
87 {
88 #if JOURNALLER_WITH_THREAD_SUPPORT && !defined(__APPLE__)
89  return (int) xsGetCurrentThreadId();
90 #else
91  return 0;
92 #endif
93 }
94 
105 #if __GNUC__ >= 6
106  // Ignore warning: nonnull argument ‘this’ compared to NULL [-Wnonnull-compare]
107  // This happens for the JL macro's which here have a this argument, which is always nonnull
108  // In the macros we compare this to NULL, which is now a warning in GCC6
109  // On other locations, we do not use this for the macro, hence the warning does not show there
110  #pragma GCC diagnostic push
111  #pragma GCC diagnostic ignored "-Wnonnull-compare"
112 #endif
113 
121 Journaller::Journaller(const XsString& pathfile, bool purge, JournalLogLevel initialLogLevel)
122  : m_file(nullptr)
123  , m_level(initialLogLevel)
124  , m_debugLevel(initialLogLevel)
125  , m_flushLevel(JLL_Alert)
126  , m_threader(new JournalThreader)
127  , m_useDateTime(false)
128 {
129  init(pathfile, purge);
130 }
131 
134 {
135 }
136 
138 void Journaller::init(XsString const& pathfile, bool purge)
139 {
140 #ifdef ANDROID
141  setTag(tagFromFilename(pathfile.toStdString()));
142 #endif
143  m_file.reset(new JournalFile(pathfile, purge));
144 }
145 
151 std::string Journaller::tagFromFilename(const std::string& fn)
152 {
153  const size_t b = fn.find_last_of("/") + 1;
154  const size_t l = fn.find_first_of(".");
155  return fn.substr(b, l - b);
156 }
157 
163 {
164  m_useDateTime = yes;
165 }
166 
173 {
174  m_additionalLogger = additionallogger;
175 }
176 
178 static const char* gLogLevelString[] =
179 {
180  "[TRACE] ",
181  "[DEBUG] ",
182  "[ALERT] ",
183  "[ERROR] ",
184  "[FATAL] ",
185  "[WRITE] "
186 };
187 
190 void Journaller::writeFileHeader(const std::string& appName)
191 {
192  m_appName = appName;
193  JLWRITE(this, "Journaller logging to " << m_file->filename() << (appName.empty() ? XsString() : XsString(" for ") + appName) << " on " << XsTimeStamp::now().toString());
194 }
195 
201 void Journaller::log(JournalLogLevel level, const std::string& msg)
202 {
203 #ifdef ANDROID
204  __android_log_write(level, tag().c_str(), msg.c_str());
205 #else
206  if (level < m_level && level < m_debugLevel)
207  return;
208 
209  m_threader->setLineLevel(threadId(), level);
210  writeTime();
211 #if JOURNALLER_WITH_THREAD_SUPPORT
212  writeThread();
213 #endif
214  writeTag();
215  writeLevel(level);
216  writeMessage(msg);
217  writeMessage(std::string("\n"));
218  if (level >= m_flushLevel)
219  flush();
220 #endif
221 }
222 
225 {
226  XsTimeStamp ts = XsTimeStamp::now();
227  if (!m_useDateTime)
228  {
229  // when using timestamp format we use UTC time!
230  char timebuf[32];
231  sprintf(timebuf, "%10" PRINTF_INT64_MODIFIER "d.%03d ", ts.secondTime(), (int) ts.milliSecondPart());
232  writeMessage(timebuf);
233  }
234  else
235  {
236  // when using date time format we use LOCAL time!
237  writeMessage(ts.utcToLocalTime().toString().toStdString());
238  }
239 }
240 
243 {
244  char buf[32];
245 #ifdef __GNUC__
246  sprintf(buf, "<%08X> ", (unsigned int) threadId());
247 #else
248  sprintf(buf, "<%04X> ", (unsigned int) threadId());
249 #endif
250  writeMessage(buf);
251 }
252 
255 {
256  std::string tmp = tag();
257  if (!tmp.empty())
258  writeMessage(tmp);
259 }
260 
266 {
267  writeMessage(std::string(gLogLevelString[level]));
268 }
269 
276 void Journaller::writeMessage(const std::string& msg)
277 {
278  if (msg.empty())
279  {
280  flushLine();
281  return;
282  }
283 
284  m_threader->line(threadId()).append(msg);
285  char eol = *msg.rbegin();
286  if (eol == '\n' || eol == '\r')
287  flushLine();
288 }
289 
293 {
294  int thread = threadId();
295  std::string& line = m_threader->line(thread);
296  JournalLogLevel lineLevel = m_threader->lineLevel(thread);
297  if (!line.empty())
298  {
299  if (lineLevel >= m_level)
300  m_threader->writeLine(thread, m_file.get());
301  if (lineLevel > m_debugLevel)
302  m_threader->writeLine(thread, nullptr);
303  line.clear();
304  }
305 }
306 
309 {
310  if (m_file)
311  {
312  flushLine();
313  m_file->flush();
314  }
315 }
316 
318 void Journaller::setFlushLevel(JournalLogLevel level, bool writeLogLine)
319 {
320  m_flushLevel = level;
321  if (writeLogLine)
322  JLGENERIC(this, JLL_Write, "Flush level switched to " << gLogLevelString[m_flushLevel]);
323 }
324 
328 {
329  if (level < m_level)
330  return;
331 
332  JLGENERIC(this, level, "************ Dump Begin ************");
333  JournalStackWalker sw(this);
334  sw.ShowCallstack();
335  JLGENERIC(this, level, "************* Dump End *************");
336 }
337 
343 void Journaller::setLogLevel(JournalLogLevel level, bool writeLogLine)
344 {
345  m_level = level;
346  if (writeLogLine)
347  JLGENERIC(this, JLL_Write, "Log level set to " << gLogLevelString[level]);
348 }
349 
355 void Journaller::setDebugLevel(JournalLogLevel level, bool writeLogLine)
356 {
357  m_debugLevel = level;
358  if (writeLogLine)
359  JLGENERIC(this, JLL_Write, "Debugger output log level set to " << gLogLevelString[level]);
360 }
361 
364 {
365  return m_file->filename();
366 }
367 
370 void Journaller::setTag(const std::string& tag)
371 {
372  m_tag = tag;
373 }
374 
382 std::string Journaller::tag() const
383 {
384  return m_tag;
385 }
386 
387 template <>
388 std::ostream& operator << (std::ostream& os, JlHexLogger<char> const& hex)
389 {
390  return os << JlHexLogger<int>((int) hex.m_value);
391 }
392 
398 void Journaller::moveLogFile(const XsString& pathfile, bool purge, bool eraseOld)
399 {
400  if (m_file && m_file->filename().replacedAll("\\", "/") == pathfile.replacedAll("\\", "/"))
401  return;
402 
403  JournalFile* newFile = new JournalFile(pathfile, purge);
404 
405  if (!newFile->xsFile().isOpen())
406  {
407  JLERROR(this, "Could not switch to new log location " << pathfile);
408  delete newFile;
409  return;
410  }
411 
412  XsString oldFileName;
413  std::unique_ptr<uint8_t[]> buffer;
414  XsFilePos sz = 0;
415  if (m_file && m_file->xsFile().isOpen())
416  {
417  oldFileName = m_file->filename();
418  JLWRITE(this, "Switching to file " << pathfile);
419 
420  XsFile& file = m_file->xsFile();
421  sz = file.tell();
422  if (sz)
423  {
424  file.seek(0);
425  buffer.reset(new uint8_t[(int)sz]);
426  sz = file.read(buffer.get(), 1, sz);
427  }
428  }
429 
430  m_file.reset(newFile);
431  if (buffer && sz)
432  m_file->xsFile().write(buffer.get(), 1, sz);
433  else
435 
436  if (!oldFileName.empty())
437  {
438  if (eraseOld)
439  _unlink(oldFileName.c_str());
440  JLDEBUG(this, "Switched from " << oldFileName << " to " << pathfile);
441  }
442  else
443  JLDEBUG(this, "Switched to " << pathfile);
444 }
445 
451 void Journaller::moveLogs(Journaller* target, bool eraseOld)
452 {
453  assert(target);
454  if (target->filename() == filename())
455  return;
456 
457  auto newFile = target->m_file;
458  XsString oldFileName;
459  std::unique_ptr<uint8_t[]> buffer;
460  XsFilePos sz = 0;
461  if (m_file && m_file->xsFile().isOpen())
462  {
463  oldFileName = m_file->filename();
464  JLDEBUG(target, "************ Moving logs from " << oldFileName << " to " << target->filename());
465 
466  XsFile& file = m_file->xsFile();
467  sz = file.tell();
468  if (sz)
469  {
470  file.seek(0);
471  buffer.reset(new uint8_t[(int)sz]);
472  sz = file.read(buffer.get(), 1, sz);
473  }
474  }
475 
476  if (buffer && sz && newFile)
477  newFile->xsFile().write(buffer.get(), 1, sz);
478  m_file = newFile;
479 
480  if (!oldFileName.empty())
481  {
482  if (eraseOld)
483  _unlink(oldFileName.c_str());
484  JLDEBUG(target, "************ Moved logs from " << oldFileName);
485  }
486 }
487 
492 {
493 #if JOURNALLER_WITH_THREAD_SUPPORT
494  flush();
495  if (m_threader)
496  m_threader->cleanup(threadId());
497 #endif
498 }
499 
506 {
507  if (gj && *gj)
508  {
509  delete *gj;
510  *gj = nullptr;
511  }
512 }
513 
514 #if __GNUC__ >= 6
515  #pragma GCC diagnostic pop
516 #endif
XsString
struct XsString XsString
Definition: xsstring.h:87
JLL_Write
@ JLL_Write
only log 'write' messages
Definition: journalloglevel.h:95
Journaller::writeFileHeader
void writeFileHeader(const std::string &appName)
Write a header for the log file including some meta-data about the journaller.
Definition: journaller.cpp:190
Journaller::setAdditionalLogger
static void setAdditionalLogger(AbstractAdditionalLogger *additionalLogger)
Sets the additional logger.
Definition: journaller.cpp:172
msg
msg
Journaller::m_debugLevel
JournalLogLevel m_debugLevel
Definition: journaller.h:163
Journaller::m_tag
std::string m_tag
Definition: journaller.h:160
StackWalker::ShowCallstack
BOOL ShowCallstack(HANDLE hThread=GetCurrentThread(), const CONTEXT *context=NULL, PReadProcessMemoryRoutine readMemoryFunction=NULL, LPVOID pUserData=NULL)
Definition: stackwalker.cpp:981
Journaller::cleanupThread
void cleanupThread()
Removes stored information on the current thread after flushing all data.
Definition: journaller.cpp:491
Journaller::moveLogFile
void moveLogFile(const XsString &pathfile, bool purge=true, bool eraseOld=true)
Move the log file to the supplied path, keeping current contents intact.
Definition: journaller.cpp:398
Journaller::writeTime
void writeTime()
Write the current time to the file.
Definition: journaller.cpp:224
JournalLogLevel
JournalLogLevel
Definition: journalloglevel.h:88
Journaller::moveLogs
void moveLogs(Journaller *target, bool eraseOld=true)
Copy the contents of the current log file to target.
Definition: journaller.cpp:451
JlHexLogger::m_value
T m_value
A hex value.
Definition: journaller.h:312
Journaller::setUseDateTime
void setUseDateTime(bool yes)
When setting the Date Time to yes, the timestamps are translated from unix timestamp into a redable d...
Definition: journaller.cpp:162
AbstractAdditionalLogger
Definition: abstractadditionallogger.h:74
Journaller::~Journaller
~Journaller()
Destructor, detaches from the logfile and closes it if this was the last reference.
Definition: journaller.cpp:133
xstimestamp.h
xsthread.h
journalstackwalker.h
Journaller::writeMessage
void writeMessage(const std::string &msg)
Write msg to the file without decoration or added newline.
Definition: journaller.cpp:276
JLL_Alert
@ JLL_Alert
only log fatal, error and alert messages
Definition: journalloglevel.h:92
journaller.h
JournalFile
A class containing a journal file and some meta-data.
Definition: journalfile.h:73
threadId
int threadId()
Definition: journaller.cpp:86
Journaller
A journalling class for debugging applications.
Definition: journaller.h:79
gLogLevelString
static const char * gLogLevelString[]
A list of strings representing the different log levels, use JournalLogLevel to index.
Definition: journaller.cpp:178
Journaller::m_appName
std::string m_appName
Definition: journaller.h:161
Journaller::m_flushLevel
JournalLogLevel m_flushLevel
Definition: journaller.h:164
Journaller::m_file
std::shared_ptr< JournalFile > m_file
Definition: journaller.h:159
JLDEBUG
#define JLDEBUG(...)
Definition: journaller.h:239
JLGENERIC
#define JLGENERIC(journal, level, msg)
Definition: journaller.h:196
JlHexLogger
A support class of journaller that is used for the logging of hex values.
Definition: journaller.h:309
Journaller::m_threader
std::shared_ptr< JournalThreader > m_threader
Definition: journaller.h:165
Journaller::m_additionalLogger
static AbstractAdditionalLogger * m_additionalLogger
The optional additional logger.
Definition: journaller.h:169
journalthreader.h
Journaller::flushLine
void flushLine()
Flush any data currently queued for logging to the file buffer.
Definition: journaller.cpp:292
journalfile.h
jlTerminate
void jlTerminate(Journaller **gj)
Cleans up any remaining stuff.
Definition: journaller.cpp:505
xstimeinfo.h
Journaller::m_useDateTime
bool m_useDateTime
Definition: journaller.h:167
pstdint.h
JLWRITE
#define JLWRITE(journal, msg)
Definition: journaller.h:274
Journaller::writeThread
void writeThread()
Write the current time to the file.
Definition: journaller.cpp:242
XsFile
Encapsulates a file, providing a platform independent interface.
Definition: xsfile.h:131
Journaller::m_level
JournalLogLevel m_level
Definition: journaller.h:162
JLERROR
#define JLERROR(journal, msg)
Definition: journaller.h:258
Journaller::setTag
void setTag(const std::string &tag)
Set a tag to be added before the log level tag in each log file.
Definition: journaller.cpp:370
Journaller::Journaller
Journaller(const XsString &pathfile, bool purge=true, JournalLogLevel initialLogLevel=JLL_Alert)
Constructor.
Definition: journaller.cpp:121
Journaller::setDebugLevel
void setDebugLevel(JournalLogLevel level, bool writeLogLine=true)
Set the log level for logging to debug output.
Definition: journaller.cpp:355
xstime.h
Journaller::log
void log(JournalLogLevel level, const std::string &msg)
Write a log message to the file if level is at least equal to the current log level.
Definition: journaller.cpp:201
Journaller::tagFromFilename
static std::string tagFromFilename(const std::string &fn)
Definition: journaller.cpp:151
Journaller::writeCallstack
void writeCallstack(JournalLogLevel level)
Write the current callstack to the file if level is at least equal to the current log level.
Definition: journaller.cpp:327
Journaller::setLogLevel
void setLogLevel(JournalLogLevel level, bool writeLogLine=true)
Set the log level for logging to file.
Definition: journaller.cpp:343
Journaller::setFlushLevel
void setFlushLevel(JournalLogLevel level, bool writeLogLine=true)
Set level threshold for automatically flushing lines to disk.
Definition: journaller.cpp:318
Journaller::writeTag
void writeTag()
Write the tag to the file.
Definition: journaller.cpp:254
operator<<
std::ostream & operator<<(std::ostream &os, JlHexLogger< char > const &hex)
Definition: journaller.cpp:388
Journaller::flush
void flush()
Flush any data to disk.
Definition: journaller.cpp:308
Journaller::writeLevel
void writeLevel(JournalLogLevel level)
Write the supplied log level to the file.
Definition: journaller.cpp:265
JournalFile::xsFile
XsFile & xsFile()
Return the internal XsFile object, only to be used directly when absolutely necessary.
Definition: journalfile.h:88
xsGetCurrentThreadId
#define xsGetCurrentThreadId()
Definition: xsthread.h:191
XsFilePos
int64_t XsFilePos
The type that is used for positioning inside a file.
Definition: xsfilepos.h:102
XsString
A 0-terminated managed string of characters.
Journaller::init
void init(XsString const &pathfile, bool purge)
Initialize the Journaller by (re-)creating the internal journal file.
Definition: journaller.cpp:138
Journaller::filename
const XsString filename() const
Returns the filename of the used journal file.
Definition: journaller.cpp:363
JournalThreader
Manages threaded writes for the Journaller objects.
Definition: journalthreader.h:79
Journaller::tag
std::string tag() const
Definition: journaller.cpp:382
XsTimeStamp
This class contains method to set, retrieve and compare timestamps.
Definition: xstimestamp.h:115
JournalStackWalker
A journaller class that is used for walking the stack.
Definition: journalstackwalker.h:78


xsens_mti_driver
Author(s):
autogenerated on Sun Sep 3 2023 02:43:20