Core Library 1.7.0.0 Library containing core utilities and tools for threading, networking, logging, INI and CSV file management etc.

DebugLog.h
Go to the documentation of this file.
1 // This file is part of CoreLibrary containing useful reusable utility
2 // classes.
3 //
4 // Copyright (C) 2014 to present, Duncan Crutchley
5 // Contact <dac1976github@outlook.com>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Lesser General Public License as published
9 // by the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License and GNU Lesser General Public License
16 // for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // and GNU Lesser General Public License along with this program. If
20 // not, see <http://www.gnu.org/licenses/>.
21
27 #ifndef DEBUGLOG
28 #define DEBUGLOG
29
30 #include "CoreLibraryDllGlobal.h"
31 #include "Platform/PlatformDefines.h"
32
33 #include <ctime>
34 #include <chrono>
35 #include <string>
36 #include <fstream>
37 #include <sstream>
38 #include <set>
39 #include <unordered_map>
40 #include <algorithm>
41 #ifdef USE_EXPLICIT_MOVE_
42 #include <utility>
43 #endif
44 #include <memory>
45 #include <functional>
46 #include <type_traits>
47 #include <stdexcept>
48 #define BOOST_NO_CXX11_SCOPED_ENUMS
49 #include <boost/filesystem.hpp>
50 #include <boost/throw_exception.hpp>
51 #include "Threads/MessageQueueThread.h"
52
54 namespace core_lib
55 {
57 namespace log
58 {
59
61 enum class eLogMessageLevel
62 {
64 not_defined = 0,
66 debug,
68 info,
70 warning,
72 error,
74 fatal
75 };
76
77 } // namespace log
78 } // namespace core_lib
79
81 namespace std
82 {
83
85 template <> struct hash<core_lib::log::eLogMessageLevel>
86 {
88 using argument_t = core_lib::log::eLogMessageLevel;
90 using result_t = std::size_t;
92 using enumType_t = TYPENAME_DECL_ std::underlying_type<argument_t>::type;
93
99 result_t operator()(const argument_t& a) const
100 {
101 auto a2 = static_cast<enumType_t>(a);
102 std::hash<enumType_t> h;
103 return h(a2);
104 }
105 };
106
107 } // namespace std
108
110 namespace core_lib
111 {
113 namespace log
114 {
115
128 struct CORE_LIBRARY_DLL_SHARED_API DefaultLogFormat
129 {
141 void operator()(std::ostream& os, std::time_t timeStamp, const std::string& message,
142 const std::string& logMsgLevel, const std::string& file,
143 const std::string& function, int lineNo, const std::thread::id& threadID) const;
144 };
145
147 enum eDefLogSize : size_t
148 {
149 BYTES_IN_MEBIBYTE = 1024 * 1024
150 };
151
152 namespace dl_private
153 {
154
156 enum eMsgId : int
157 {
158 MESSAGE_ID = 1
159 };
160
168 class CORE_LIBRARY_DLL_SHARED_API LogQueueMessage
169 {
170 public:
172 LogQueueMessage() = default;
186 LogQueueMessage(const std::string& message, time_t timeStamp, const std::string& file,
187 const std::string& function, int lineNo, const std::thread::id& threadID,
188 eLogMessageLevel errorLevel);
190 LogQueueMessage(const LogQueueMessage&) = default;
192 ~LogQueueMessage() = default;
194 LogQueueMessage& operator=(const LogQueueMessage&) = default;
195 #ifdef USE_EXPLICIT_MOVE_
196
197 LogQueueMessage(LogQueueMessage&& msg);
199 LogQueueMessage& operator=(LogQueueMessage&& msg);
200 #else
201
202 LogQueueMessage(LogQueueMessage&&) = default;
204 LogQueueMessage& operator=(LogQueueMessage&&) = default;
205 #endif
206
210 const std::string& Message() const;
215 time_t TimeStamp() const;
220 const std::string& File() const;
225 const std::string& Function() const;
230 int LineNo() const;
235 const std::thread::id& ThreadID() const;
240 eLogMessageLevel ErrorLevel() const;
241
242 private:
244 std::string m_message{};
246 time_t m_timeStamp{0};
248 std::string m_file{};
250 std::string m_function{};
252 int m_lineNo{0};
254 std::thread::id m_threadID{};
256 eLogMessageLevel m_errorLevel{eLogMessageLevel::not_defined};
257 };
258
259 } // namespace dl_private
260
276 template <class Formatter> class DebugLog final
277 {
278 public:
289 #ifdef USE_DEFAULT_CONSTRUCTOR_
290 DebugLog()
291 : m_unknownLogMsgLevel("?")
292 {
293 InitialiseLogMessageLevelLookupMap();
294 }
295 #else
296 DebugLog() = default;
297 #endif
298
308 DebugLog(const std::string& softwareVersion, const std::string& logFolderPath,
309 const std::string& logName, long maxLogSize = 5 * BYTES_IN_MEBIBYTE)
310 :
311 #ifdef USE_DEFAULT_CONSTRUCTOR_
312 m_unknownLogMsgLevel("?")
313 ,
314 #endif
315 m_maxLogSize(maxLogSize)
316 , m_softwareVersion(softwareVersion)
317 , m_logFilePath(logFolderPath)
318 , m_oldLogFilePath(logFolderPath)
319 {
320 m_logFilePath.append(logName);
321 m_logFilePath.append(".txt");
322
323 m_oldLogFilePath.append(logName);
324 m_oldLogFilePath.append("_old.txt");
325
326 #ifdef USE_DEFAULT_CONSTRUCTOR_
327 InitialiseLogMessageLevelLookupMap();
328 #endif
329 RegisterLogQueueMessageId();
330 OpenOfStream(m_logFilePath, eFileOpenOptions::append_file);
331 }
333 DebugLog(const DebugLog&) = delete;
335 DebugLog& operator=(const DebugLog&) = delete;
337 DebugLog(DebugLog&&) = delete;
339 DebugLog& operator=(DebugLog&&) = delete;
341 ~DebugLog()
342 {
343 // Manually reset so we process all remaining
344 // messages before closing file.
345 m_logMsgQueueThread.reset();
346 CloseOfStream();
347 }
363 void Instantiate(const std::string& softwareVersion, const std::string& logFolderPath,
364 const std::string& logName, long maxLogSize = 5 * BYTES_IN_MEBIBYTE)
365 {
366 m_maxLogSize = maxLogSize;
367
368 if ((m_softwareVersion != "") || (m_logFilePath != "") || (m_oldLogFilePath != ""))
369 {
370 BOOST_THROW_EXCEPTION(std::runtime_error("DebugLog already instantiated"));
371 }
372
373 m_softwareVersion = softwareVersion;
374 m_logFilePath = logFolderPath;
375 m_oldLogFilePath = logFolderPath;
376
377 m_logFilePath.append(logName);
378 m_logFilePath.append(".txt");
379
380 m_oldLogFilePath.append(logName);
381 m_oldLogFilePath.append("_old.txt");
382
383 RegisterLogQueueMessageId();
384 OpenOfStream(m_logFilePath, eFileOpenOptions::append_file);
385 }
398 void AddLogMsgLevelFilter(eLogMessageLevel logMessageLevel)
399 {
400 std::lock_guard<std::mutex> lock{m_mutex};
401
402 if (!IsLogMsgLevelFilterSetNoMutex(logMessageLevel))
403 {
404 m_logMsgFilterSet.insert(logMessageLevel);
405 }
406 }
418 void RemoveLogMsgLevelFilter(eLogMessageLevel logMessageLevel)
419 {
420 std::lock_guard<std::mutex> lock{m_mutex};
421
422 if (IsLogMsgLevelFilterSetNoMutex(logMessageLevel))
423 {
424 m_logMsgFilterSet.insert(logMessageLevel);
425 }
426 }
433 void ClearLogMsgLevelFilters()
434 {
435 std::lock_guard<std::mutex> lock{m_mutex};
436 m_logMsgFilterSet.clear();
437 }
445 void AddLogMessage(const std::string& message)
446 {
447 using std::chrono::system_clock;
448 time_t messageTime = system_clock::to_time_t(system_clock::now());
449 std::thread::id noThread;
450 m_logMsgQueueThread->Push(dl_private::LogQueueMessage(
451 message, messageTime, "", "", -1, noThread, eLogMessageLevel::not_defined));
452 }
453
468 void AddLogMessage(const std::string& message, const std::string& file,
469 const std::string& function, int lineNo, eLogMessageLevel logMsgLevel)
470 {
471 if (!IsLogMsgLevelFilterSet(logMsgLevel))
472 {
473 using std::chrono::system_clock;
474 time_t messageTime = system_clock::to_time_t(system_clock::now());
475 m_logMsgQueueThread->Push(dl_private::LogQueueMessage(message,
476 messageTime,
477 file,
478 function,
479 lineNo,
480 std::this_thread::get_id(),
481 logMsgLevel));
482 }
483 }
484
485 private:
486 #ifdef USE_DEFAULT_CONSTRUCTOR_
487
488 void InitialiseLogMessageLevelLookupMap()
489 {
490 m_logMsgLevelLookup.emplace(eLogMessageLevel::not_defined, "");
491 m_logMsgLevelLookup.emplace(eLogMessageLevel::debug, "Debug");
492 m_logMsgLevelLookup.emplace(eLogMessageLevel::info, "Info");
493 m_logMsgLevelLookup.emplace(eLogMessageLevel::warning, "Warning");
494 m_logMsgLevelLookup.emplace(eLogMessageLevel::error, "Error");
495 m_logMsgLevelLookup.emplace(eLogMessageLevel::fatal, "Fatal");
496 }
497 #endif
498
499 long MaxLogSize() const
500 {
501 return m_maxLogSize;
502 }
504 void RegisterLogQueueMessageId()
505 {
506 m_logMsgQueueThread->RegisterMessageHandler(
507 dl_private::MESSAGE_ID,
508 std::bind(&DebugLog<Formatter>::MessageHandler, this, std::placeholders::_1));
509 }
515 static int MessageDecoder(const dl_private::LogQueueMessage&)
516 {
517 return dl_private::MESSAGE_ID;
518 }
524 bool MessageHandler(dl_private::LogQueueMessage& message)
525 {
526 CheckLogFileSize(static_cast<long>(message.Message().size()));
527 WriteMessageToLog(std::forward<dl_private::LogQueueMessage>(message));
528 return true;
529 }
535 bool IsLogMsgLevelInLookup(eLogMessageLevel logMessageLevel) const
536 {
537 return m_logMsgLevelLookup.count(logMessageLevel) > 0;
538 }
544 const std::string& GetLogMsgLevelAsString(eLogMessageLevel logMessageLevel) const
545 {
546 return IsLogMsgLevelInLookup(logMessageLevel)
547 ? m_logMsgLevelLookup.find(logMessageLevel)->second
548 : m_unknownLogMsgLevel;
549 }
555 bool IsLogMsgLevelFilterSetNoMutex(eLogMessageLevel logMessageLevel) const
556 {
557 return (m_logMsgFilterSet.find(logMessageLevel) != m_logMsgFilterSet.end());
558 }
564 bool IsLogMsgLevelFilterSet(eLogMessageLevel logMessageLevel) const
565 {
566 std::lock_guard<std::mutex> lock{m_mutex};
567 return IsLogMsgLevelFilterSetNoMutex(logMessageLevel);
568 }
570 enum eFileOpenOptions
571 {
573 truncate_file,
575 append_file
576 };
582 void OpenOfStream(const std::string& filePath, eFileOpenOptions fileOptions)
583 {
584 if (m_ofStream.is_open())
585 {
586 return;
587 }
588
589 m_ofStream.open(filePath,
590 fileOptions == eFileOpenOptions::truncate_file ? std::ofstream::trunc
591 : std::ofstream::app);
592
593 using std::chrono::system_clock;
594 time_t messageTime = system_clock::to_time_t(system_clock::now());
595 std::thread::id noThread;
596 WriteMessageToLog(dl_private::LogQueueMessage(
597 "DEBUG LOG STARTED", messageTime, "", "", -1, noThread, eLogMessageLevel::not_defined));
598
599 if (m_softwareVersion != "")
600 {
601 std::string message("Software Version ");
602 message += m_softwareVersion;
603 WriteMessageToLog(dl_private::LogQueueMessage(
604 message, messageTime, "", "", -1, noThread, eLogMessageLevel::not_defined));
605 }
606 }
608 void CloseOfStream()
609 {
610 if (!m_ofStream.is_open())
611 {
612 return;
613 }
614
615 using std::chrono::system_clock;
616 time_t messageTime = system_clock::to_time_t(system_clock::now());
617 std::thread::id noThread;
618 WriteMessageToLog(dl_private::LogQueueMessage(
619 "DEBUG LOG STOPPED", messageTime, "", "", -1, noThread, eLogMessageLevel::not_defined));
620 m_ofStream.close();
621 }
626 void CheckLogFileSize(long requiredSpace)
627 {
628 if (!m_ofStream.is_open())
629 {
630 return;
631 }
632
633 auto pos = static_cast<long>(m_ofStream.tellp());
634
635 if ((MaxLogSize() - pos) < requiredSpace)
636 {
637 CloseOfStream();
638 boost::filesystem::copy_file(m_logFilePath,
639 m_oldLogFilePath,
640 boost::filesystem::copy_option::overwrite_if_exists);
641 OpenOfStream(m_logFilePath, eFileOpenOptions::truncate_file);
642 }
643 }
648 void WriteMessageToLog(dl_private::LogQueueMessage&& logMessage)
649 {
650 m_logFormatter(m_ofStream,
651 logMessage.TimeStamp(),
652 logMessage.Message(),
653 GetLogMsgLevelAsString(logMessage.ErrorLevel()),
654 logMessage.File(),
655 logMessage.Function(),
656 logMessage.LineNo(),
657 logMessage.ThreadID());
658 m_ofStream.flush();
659 }
660
661 private:
663 mutable std::mutex m_mutex;
664 #ifdef USE_DEFAULT_CONSTRUCTOR_
665
666 std::string m_unknownLogMsgLevel;
668 std::unordered_map<eLogMessageLevel, std::string> m_logMsgLevelLookup;
669 #else
670
671 std::string m_unknownLogMsgLevel{"?"};
673 std::unordered_map<eLogMessageLevel, std::string> m_logMsgLevelLookup{
674 {eLogMessageLevel::not_defined, ""},
675 {eLogMessageLevel::debug, "Debug"},
676 {eLogMessageLevel::info, "Info"},
677 {eLogMessageLevel::warning, "Warning"},
678 {eLogMessageLevel::error, "Error"},
679 {eLogMessageLevel::fatal, "Fatal"}};
680 #endif
681
682 std::set<eLogMessageLevel> m_logMsgFilterSet{};
684 Formatter m_logFormatter{};
686 long m_maxLogSize{5 * BYTES_IN_MEBIBYTE};
688 std::ofstream m_ofStream{};
690 std::string m_softwareVersion{};
692 std::string m_logFilePath{};
694 std::string m_oldLogFilePath{};
696 using log_msg_queue = threads::MessageQueueThread<int, dl_private::LogQueueMessage>;
698 std::unique_ptr<log_msg_queue> m_logMsgQueueThread{
699 new log_msg_queue(std::bind(&DebugLog<Formatter>::MessageDecoder, std::placeholders::_1),
700 threads::eOnDestroyOptions::processRemainingItems)};
701 };
702
703 } // namespace log
704 } // namespace core_lib
705
706 #endif // DEBUGLOG
core_lib::log::eLogMessageLevel::warning
Warning level defined for message.
core_lib::log::DebugLog::~DebugLog
~DebugLog()
Destructor.
Definition: DebugLog.h:341
core_lib::log::DebugLog::GetLogMsgLevelAsString
const std::string & GetLogMsgLevelAsString(eLogMessageLevel logMessageLevel) const
Get message level as a string.
Definition: DebugLog.h:544
core_lib::log::eLogMessageLevel::error
Error level defined for message.
core_lib::log::DebugLog::MessageHandler
bool MessageHandler(dl_private::LogQueueMessage &message)
Method to process message.
Definition: DebugLog.h:524
core_lib::log::DebugLog::IsLogMsgLevelFilterSetNoMutex
bool IsLogMsgLevelFilterSetNoMutex(eLogMessageLevel logMessageLevel) const
Is message level in filter set (no mutex).
Definition: DebugLog.h:555
core_lib::log::DebugLog::ClearLogMsgLevelFilters
void ClearLogMsgLevelFilters()
Clear all message levels from filter.
Definition: DebugLog.h:433
core_lib::log::DebugLog::MessageDecoder
static int MessageDecoder(const dl_private::LogQueueMessage &)
Method to decode message ID.
Definition: DebugLog.h:515
std
The std namespace.
Definition: DebugLog.h:81
core_lib::threads::eOnDestroyOptions::processRemainingItems
core_lib::log::DebugLog::AddLogMessage
void AddLogMessage(const std::string &message)
Add message to the log file.
Definition: DebugLog.h:445
core_lib::log::dl_private::eMsgId
eMsgId
Static constant defining message ID for log messages.
Definition: DebugLog.h:156
std::hash< core_lib::log::eLogMessageLevel >::result_t
std::size_t result_t
Typedef for result type.
Definition: DebugLog.h:90
core_lib::log::DebugLog::CheckLogFileSize
void CheckLogFileSize(long requiredSpace)
Check size of current log file.
Definition: DebugLog.h:626
MessageQueueThread.h
File containing declaration of MessageQueueThread class.
core_lib::log::dl_private::LogQueueMessage::Message
const std::string & Message() const
Get message string.
Definition: DebugLog.cpp:130
core_lib::log::DebugLog::m_mutex
std::mutex m_mutex
Mutex to lock access.
Definition: DebugLog.h:663
core_lib::log::DebugLog::RemoveLogMsgLevelFilter
void RemoveLogMsgLevelFilter(eLogMessageLevel logMessageLevel)
Remove level from filter.
Definition: DebugLog.h:418
core_lib::log::eDefLogSize
eDefLogSize
Static constant defining number of bytes in a mebibyte.
Definition: DebugLog.h:147
core_lib
The core_lib namespace.
Definition: AsioDefines.h:59
std::hash< core_lib::log::eLogMessageLevel >::enumType_t
TYPENAME_DECL_ std::underlying_type< argument_t >::type enumType_t
Typedef for underlying type.
Definition: DebugLog.h:92
core_lib::log::eLogMessageLevel::not_defined
No level defined for message.
core_lib::log::DebugLog::AddLogMsgLevelFilter
void AddLogMsgLevelFilter(eLogMessageLevel logMessageLevel)
Add level to filter.
Definition: DebugLog.h:398
core_lib::log::DebugLog
DebugLog class.
Definition: DebugLog.h:276
core_lib::log::DebugLog::IsLogMsgLevelInLookup
bool IsLogMsgLevelInLookup(eLogMessageLevel logMessageLevel) const
Is message level in map.
Definition: DebugLog.h:535
core_lib::log::DebugLog::AddLogMessage
void AddLogMessage(const std::string &message, const std::string &file, const std::string &function, int lineNo, eLogMessageLevel logMsgLevel)
Add message to the log file.
Definition: DebugLog.h:468
core_lib::log::DebugLog::OpenOfStream
void OpenOfStream(const std::string &filePath, eFileOpenOptions fileOptions)
Open file stream.
Definition: DebugLog.h:582
core_lib::log::eLogMessageLevel
eLogMessageLevel
Enumeration containing log message level options.
Definition: DebugLog.h:61
CoreLibraryDllGlobal.h
File containing declaration of DLL import/export control defines.
core_lib::log::DebugLog::MaxLogSize
long MaxLogSize() const
Get the max log size in bytes.
Definition: DebugLog.h:499
core_lib::log::eLogMessageLevel::debug
Debug level defined for message.
core_lib::log::DebugLog::WriteMessageToLog
void WriteMessageToLog(dl_private::LogQueueMessage &&logMessage)
Write log message to file stream.
Definition: DebugLog.h:648
core_lib::log::DebugLog::IsLogMsgLevelFilterSet
bool IsLogMsgLevelFilterSet(eLogMessageLevel logMessageLevel) const
Is message level in filter set (with mutex).
Definition: DebugLog.h:564
core_lib::log::DebugLog::DebugLog
DebugLog(const std::string &softwareVersion, const std::string &logFolderPath, const std::string &logName, long maxLogSize=5 *BYTES_IN_MEBIBYTE)
Initialisation constructor.
Definition: DebugLog.h:308
PlatformDefines.h
File containing platform specific definitions.
core_lib::log::eLogMessageLevel::fatal
Fatal level defined for message.
core_lib::log::DebugLog::Instantiate
void Instantiate(const std::string &softwareVersion, const std::string &logFolderPath, const std::string &logName, long maxLogSize=5 *BYTES_IN_MEBIBYTE)
Instantiate a previously default constructed DebugLog object.
Definition: DebugLog.h:363
std::hash< core_lib::log::eLogMessageLevel >::operator()
result_t operator()(const argument_t &a) const
Function operator to perform the hash.
Definition: DebugLog.h:99
TYPENAME_DECL_
#define TYPENAME_DECL_
TYPENAME_DECL_ definition mapping to typename.
Definition: PlatformDefines.h:63
core_lib::log::DefaultLogFormat
Default log line formater.
Definition: DebugLog.h:128
core_lib::log::DebugLog::truncate_file
Option to truncate file when opened.
Definition: DebugLog.h:573
core_lib::log::DebugLog::eFileOpenOptions
eFileOpenOptions
Enumeration containing file opening options.
Definition: DebugLog.h:570
core_lib::log::dl_private::LogQueueMessage
Log Queue Message class.
Definition: DebugLog.h:168
core_lib::log::DebugLog::RegisterLogQueueMessageId
void RegisterLogQueueMessageId()
Register the log queue message ID.
Definition: DebugLog.h:504
core_lib::threads::MessageQueueThread
Message Queue Thread.
Definition: MessageQueueThread.h:68
core_lib::log::eLogMessageLevel::info
Info level defined for message.
core_lib::log::DebugLog::CloseOfStream
void CloseOfStream()
Close file stream.
Definition: DebugLog.h:608