xmscore  1.0
XmLog.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
7 //------------------------------------------------------------------------------
8 
9 #pragma warning(push)
10 #pragma warning(disable : 4100) // unreferenced formal parameter
11 #pragma warning(disable : 4103)
12 #pragma warning(disable : 4512) // assignment operator could not be generated
13 #pragma warning(disable : 4701) // potentially uninitialized local variable
14 #pragma warning(disable : 4244)
15 
16 //----- Included files ---------------------------------------------------------
17 
18 // 1. Precompiled header
19 
20 // 2. My own header
21 #include <xmscore/misc/XmLog.h>
22 
23 // 3. Standard library headers
24 #include <fstream>
25 #include <sstream>
26 
27 // 4. External library headers
28 #include <boost/filesystem.hpp>
29 #include <boost/log/attributes.hpp>
30 #include <boost/log/core/core.hpp>
31 #include <boost/log/expressions.hpp>
32 #include <boost/log/sources/global_logger_storage.hpp>
33 #include <boost/log/sinks/sync_frontend.hpp>
34 #include <boost/log/sinks/text_ostream_backend.hpp>
35 #include <boost/log/sources/record_ostream.hpp>
36 #include <boost/log/sources/severity_logger.hpp>
37 #include <boost/log/support/date_time.hpp>
38 #include <boost/log/utility/setup/common_attributes.hpp>
39 #include <boost/log/utility/manipulators/add_value.hpp>
40 
41 // 5. Shared code headers
42 #ifdef CXX_TEST
44 #endif
45 
46 // 6. Non-shared code headers
47 
48 //----- Namespace declaration --------------------------------------------------
49 namespace bfs = boost::filesystem;
50 namespace expr = boost::log::expressions;
51 namespace sinks = boost::log::sinks;
52 
53 //----- Constants / Enumerations -----------------------------------------------
54 
55 //----- Forward declarations ---------------------------------------------------
56 
57 //----- Global variables -------------------------------------------------------
58 #if _WIN32 || _WIN64 // WIN
59 namespace xms
60 {
61 std::string g_xmUtil;
62 }
63 #endif
64 
65 //----- Structs / Classes ------------------------------------------------------
66 
67 namespace xmlog
68 {
69 //------------------------------------------------------------------------------
74 //------------------------------------------------------------------------------
75 template <typename CharT, typename TraitsT>
76 inline std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& strm,
78 {
79  static const char* const str[] = {" info", "warning", " error", " debug"};
80  if (static_cast<std::size_t>(lvl) < (sizeof(str) / sizeof(*str)))
81  strm << str[lvl];
82  else
83  strm << static_cast<int>(lvl);
84  return strm;
85 }
86 
87 } // namespace xmlog
88 
89 namespace xms
90 {
93  boost::log::sources::severity_logger_mt<xmlog::MessageTypeEnum>)
94 {
95  boost::log::sources::severity_logger_mt<xmlog::MessageTypeEnum> lg;
96  // Configure the logger here
97  lg.add_attribute("TimeStamp", boost::log::attributes::local_clock());
98  lg.add_attribute("Process", boost::log::attributes::current_process_name());
99  lg.add_attribute("Scope", boost::log::attributes::named_scope());
100  return lg;
101 }
102 BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", xmlog::MessageTypeEnum)
103 // BOOST_LOG_ATTRIBUTE_KEYWORD(file_name, "FileName", const char* const)
104 // BOOST_LOG_ATTRIBUTE_KEYWORD(line_num, "LineNumber", int)
105 
108 
114 {
115  Impl()
116  : m_firstRun(true)
117  {
118  }
119 
124 
125  void StackedErrToStream(std::ostream& a_os);
126 };
127 
128 //----- Internal function prototypes -------------------------------------------
129 
130 //----- Function definitions ---------------------------------------------------
131 
136 //------------------------------------------------------------------------------
138 //------------------------------------------------------------------------------
140 : Singleton()
141 , m(new Impl())
142 {
143  // Setup the formatter for file sinks
144  boost::log::formatter file_fmt = expr::stream << "[" << severity << "]"
145  << "["
146  << expr::format_date_time<boost::posix_time::ptime>(
147  "TimeStamp", "%Y-%m-%d %H:%M:%S")
148  << "]"
149  << "[" << expr::attr<std::string>("Process") << ":"
150  << expr::attr<std::string>("FileName") << ":"
151  << expr::attr<int>("LineNumber") << "]"
152  << ": " << expr::smessage;
153 
154  // Initialize sinks
155  // Create a backend and attach a couple of streams to it
156  boost::shared_ptr<sinks::text_ostream_backend> backend =
157  boost::make_shared<sinks::text_ostream_backend>();
158  // backend->add_stream(boost::shared_ptr< std::ostream >(&std::clog, boost::empty_deleter()));
159  backend->add_stream(boost::shared_ptr<std::ostream>(new std::ofstream(XmLog::LogFilename())));
160 
161  // Enable auto-flushing after each log record written
162  backend->auto_flush(true);
163 
164  // Wrap it into the frontend and register in the core.
165  // The backend requires synchronization in the frontend.
166  typedef sinks::synchronous_sink<sinks::text_ostream_backend> sink_t;
167  boost::shared_ptr<sink_t> sink(new sink_t(backend));
168  sink->set_formatter(file_fmt);
169  boost::log::core::get()->add_sink(sink);
170 
171  // Add attributes
172  boost::log::add_common_attributes();
173 
174  // Add Log to BugTracker
175  std::string tmp = XmLog::LogFilename();
176 } // XmLog::XmLog
177 //------------------------------------------------------------------------------
179 //------------------------------------------------------------------------------
181 {
182 }
183 //------------------------------------------------------------------------------
189 //------------------------------------------------------------------------------
190 void XmLog::Log(const char* const a_file,
191  int a_line,
192  xmlog::MessageTypeEnum a_level,
193  std::string a_message)
194 {
195  if (m->m_firstRun)
196  {
197  m->m_firstRun = false;
198  // Make sure the log file is created
199  XM_LOG(xmlog::debug, "Start Log");
200  }
201 
202  if (a_level == xmlog::debug)
203  {
204  BOOST_LOG_SEV(xms_global_log::get(), a_level)
205  << boost::log::add_value("FileName", a_file) << boost::log::add_value("LineNumber", a_line)
206  << a_message;
207  }
208  else
209  {
210  m->m_stackedMessages.push_back(std::make_pair(a_level, a_message));
211  BOOST_LOG_SEV(xms_global_log::get(), a_level)
212  << boost::log::add_value("FileName", a_file) << boost::log::add_value("LineNumber", a_line)
213  << a_message;
214  }
215 } // XmLog::Log
216 //------------------------------------------------------------------------------
219 //------------------------------------------------------------------------------
221 {
222  return (int)m->m_stackedMessages.size();
223 } // XmLog::ErrCount
224 //------------------------------------------------------------------------------
227 //------------------------------------------------------------------------------
229 {
230  std::stringstream ss;
231  m->StackedErrToStream(ss);
232  return ss.str();
233 } // XmLog::GetAndClearStackStr
234 //------------------------------------------------------------------------------
237 //------------------------------------------------------------------------------
239 {
240  MessageStack messages = m->m_stackedMessages;
241  m->m_stackedMessages.clear();
242  return messages;
243 } // XmLog::GetAndClearStack
244 //------------------------------------------------------------------------------
250 //------------------------------------------------------------------------------
252 {
253  return fg_logFilenameCallback;
254 } // XmLog::LogFilenameCallback
255 //------------------------------------------------------------------------------
261 //------------------------------------------------------------------------------
262 std::string XmLog::LogFilename()
263 {
264  if (!LogFilenameCallback().empty())
265  {
266  return LogFilenameCallback()(); // Call the callback function
267  }
268  else
269  {
270  static std::string fg_logPath;
271  if (fg_logPath.empty())
272  {
273  bfs::path p = bfs::temp_directory_path() / bfs::unique_path();
274  fg_logPath = p.string() + "debug.log";
275  }
276  return fg_logPath;
277  }
278 } // XmLog::LogFilename
279 //------------------------------------------------------------------------------
282 //------------------------------------------------------------------------------
283 void XmLog::Impl::StackedErrToStream(std::ostream& a_os)
284 {
285  if (m_stackedMessages.size() > 0)
286  {
287  for (auto it = m_stackedMessages.begin(); it != m_stackedMessages.end(); it++)
288  {
289  a_os << "---" << it->second << "\n"
290  << "\n";
291  }
292  m_stackedMessages.clear();
293  }
294 } // XmLog::StackedErrToStream
295 
296 } // namespace xms
297 
298 #ifdef CXX_TEST
299 // TESTS
302 
303 #include <xmscore/misc/XmLog.t.h>
304 
305 namespace xms
306 { // unnamed namespace
307 
308 //------------------------------------------------------------------------------
310 //------------------------------------------------------------------------------
312 {
313  XM_LOG(xmlog::debug, "Debug Log Test");
314 
315  std::ifstream t(XmLog::LogFilename());
316  std::string str;
317 
318  t.seekg(0, std::ios::end);
319  str.reserve((int)t.tellg());
320  t.seekg(0, std::ios::beg);
321 
322  str.assign((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
323  TS_ASSERT(str.find(" debug]") != std::string::npos);
324  TS_ASSERT(str.find("Start Log") != std::string::npos);
325  TS_ASSERT(str.find("Debug Log Test") != std::string::npos);
326 }
327 //------------------------------------------------------------------------------
329 //------------------------------------------------------------------------------
331 {
332  XM_LOG(xmlog::info, "Stackable Log Test");
333 
334  std::ifstream t(XmLog::LogFilename());
335  std::string str;
336 
337  t.seekg(0, std::ios::end);
338  str.reserve((int)t.tellg());
339  t.seekg(0, std::ios::beg);
340 
341  str.assign((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
342 
343  TS_ASSERT(str.find(" debug]") != std::string::npos);
344  TS_ASSERT(str.find("Start Log") != std::string::npos);
345  TS_ASSERT(str.find("Stackable Log Test") != std::string::npos);
346 }
347 //------------------------------------------------------------------------------
349 //------------------------------------------------------------------------------
351 {
352  XM_LOG(xmlog::error, "Test Log");
353 }
354 
355 } // xms namespace
356 
361 //------------------------------------------------------------------------------
363 //------------------------------------------------------------------------------
365 {
366  // Uncomment these to test but then comment them out again because we don't
367  // want them to run every time
368 
369  // xms::iTest_XM_LOG_debug();
370  // xms::iTest_XM_LOG_stackable();
371  // xms::iTest_XM_LOG_gui();
372 
373  // BOOST_LOG_FUNCTION();
374  // std::string s = BOOST_CURRENT_FUNCTION;
375  // std::string s2 = s;
376  // TS_ASSERT_EQUALS(s2, s);
377 }
378 #endif
379 
380 #pragma warning(pop)
#define XM_LOG(A, B)
Log message which can have a log type (Debug, Stackable, Gui).
Definition: XmLog.h:56
std::vector< std::pair< xmlog::MessageTypeEnum, std::string > > MessageStack
Container type used to store log messages.
Definition: XmLog.h:104
~XmLog()
destructor
Definition: XmLog.cpp:180
Details of internal operation.
Definition: XmLog.h:82
int ErrCount()
Returns the current count of the error stack.
Definition: XmLog.cpp:220
MessageStack GetAndClearStack()
Returns a copy of the error stack before clearing it.
Definition: XmLog.cpp:238
void iTest_XM_LOG_gui()
Run GUI log test.
Definition: XmLog.cpp:350
MessageTypeEnum
Log level for XM_LOG.
Definition: XmLog.h:78
void testAll()
Test XM_LOG.
Definition: XmLog.cpp:364
static XmLogFilenameCallback & LogFilenameCallback()
Set the callback that returns the log filename.
Definition: XmLog.cpp:251
std::string GetAndClearStackStr()
Clears the error stack and returns its contents as a string.
Definition: XmLog.cpp:228
Base class for classes that follow the singleton pattern.
Definition: Singleton.h:26
void iTest_XM_LOG_stackable()
Run stackable log test.
Definition: XmLog.cpp:330
boost::scoped_ptr< Impl > m
Implementation pointer.
Definition: XmLog.h:132
void Log(const char *const a_file, int a_line, xmlog::MessageTypeEnum a_level, std::string a_message)
Logs.
Definition: XmLog.cpp:190
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(xms_global_log, boost::log::sources::severity_logger_mt< xmlog::MessageTypeEnum >)
Used for convenience to declare a global log object.
Definition: XmLog.cpp:92
MessageStack m_stackedMessages
Stack of messages that can be shown at a later time.
Definition: XmLog.cpp:121
void StackedErrToStream(std::ostream &a_os)
Sends error stack to passed stream. Formats for dialog box.
Definition: XmLog.cpp:283
Definition: XmLog.cpp:67
XmLog()
Constructor hidden and not implemented.
Definition: XmLog.cpp:139
boost::function< std::string()> XmLogFilenameCallback
Callback to return the name of the log file.
Definition: XmLog.h:101
implementation of XmLog
Definition: XmLog.cpp:113
static std::string LogFilename()
Return the name of the log file.
Definition: XmLog.cpp:262
Informational message for the user.
Definition: XmLog.h:79
Routines for creating and writing to logs and stacking errors.
void iTest_XM_LOG_debug()
Run debug log test.
Definition: XmLog.cpp:311
static XmLogFilenameCallback fg_logFilenameCallback
Callback to return the name of the log file.
Definition: XmLog.cpp:107
Critical error message for the user.
Definition: XmLog.h:81
bool m_firstRun
Used to setup log file first time something is logged.
Definition: XmLog.cpp:123