Main Page · Modules · All Classes · Class Hierarchy
MCLog.cpp
1 /*
2  * This file is part of the AiBO+ project
3  *
4  * Copyright (C) 2005-2016 Csaba Kertész (csaba.kertesz@gmail.com)
5  *
6  * AiBO+ is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * AiBO+ is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #include "MCLog.hpp"
23 
24 #include "MCThreadLocalData.hpp"
25 
26 #ifndef __AIBO_BUILD__
27 #include <qdebug.h>
28 #include <qregexp.h>
29 #endif
30 
31 #include <boost/algorithm/string/find.hpp>
32 #include <boost/algorithm/string/join.hpp>
33 #include <boost/algorithm/string/predicate.hpp>
34 #include <boost/algorithm/string/split.hpp>
35 
36 #include <boost/scoped_ptr.hpp>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 
41 namespace
42 {
43 // Global message handler
44 static boost::scoped_ptr<MCLogMessageHandler> GlobalMessageHandler;
45 // Global debug status
46 static bool GlobalDebug = false;
47 // Enabled debug mode
48 MCThreadLocalData<bool> Debug(true);
49 // Debug filter
50 MCThreadLocalData<MC::StringList> DebugFilter(true);
51 // Debug color filter
52 MCThreadLocalData<MC::StringList> DebugColorFilters(true);
53 // Custom log message handler
54 MCThreadLocalData<MCLogMessageHandler> MessageHandler(true);
55 
56 void CheckStaticDebugVariables()
57 {
58  if (unlikely(!Debug.get()))
59  {
60  Debug.reset(new bool);
61  *Debug = false;
62  DebugFilter.reset(new MC::StringList);
63  DebugColorFilters.reset(new MC::StringList);
64  }
65 }
66 }
67 
68 bool MCLog::GetDebugStatus(bool global)
69 {
70  if (global)
71  {
72  return GlobalDebug;
73  }
74  CheckStaticDebugVariables();
75  return *Debug;
76 }
77 
78 
79 void MCLog::SetDebugStatus(bool new_status, bool global)
80 {
81  if (global)
82  {
83  GlobalDebug = new_status;
84  } else {
85  CheckStaticDebugVariables();
86  *Debug = new_status;
87  }
88 }
89 
90 
91 std::string MCLog::GetDebugFilter()
92 {
93  CheckStaticDebugVariables();
94  return boost::algorithm::join(*DebugFilter, ",");
95 }
96 
97 
98 void MCLog::SetDebugFilter(const std::string& new_filter)
99 {
100  CheckStaticDebugVariables();
101  boost::algorithm::split(*DebugFilter, new_filter, boost::is_any_of(","));
102 }
103 
104 
106 {
107  CheckStaticDebugVariables();
108  return boost::algorithm::join(*DebugColorFilters, ",");
109 }
110 
111 
112 void MCLog::SetDebugColorFilters(const std::string& new_color_filter)
113 {
114  CheckStaticDebugVariables();
115  boost::algorithm::split(*DebugColorFilters, new_color_filter, boost::is_any_of(","));
116 }
117 
118 
119 void MCLog::SetCustomHandler(MCLogMessageHandler* handler, bool global)
120 {
121  if (global)
122  GlobalMessageHandler.reset(handler);
123  else
124  MessageHandler.reset(handler);
125 }
126 
127 
128 bool MCLog::IsAnyFilterMatched(const MC::StringList& filters, const std::string& function_name)
129 {
130  if (filters.empty() || function_name.empty())
131  return false;
132 
133  std::string FunctionName = function_name;
134  MC::StringList FunctionNameParts;
135 
136  MC_TRY_BEGIN
137  // Cut down the non-interesting stuff after the function name
138  iter_split(FunctionNameParts, FunctionName, boost::algorithm::first_finder("("));
139  // Cut down the non-interesting stuff before the function name
140  iter_split(FunctionNameParts, FunctionNameParts[0], boost::algorithm::first_finder(" "));
141  // Remove the namespace
142  iter_split(FunctionNameParts, FunctionNameParts[FunctionNameParts.size()-1], boost::algorithm::first_finder("::"));
143  int CutNumber = (int)FunctionNameParts.size()-2;
144 
145  for (int i = 0; i < CutNumber; ++i)
146  {
147  FunctionNameParts.erase(FunctionNameParts.begin());
148  }
149  FunctionName = boost::algorithm::join(FunctionNameParts, "::");
150  // Return true in the case of exact match
151  if (std::find(filters.begin(), filters.end(), FunctionName) != filters.end())
152  return true;
153 
154  // Search for the class name if exists
155  iter_split(FunctionNameParts, FunctionName, boost::algorithm::first_finder("::"));
156  if (FunctionNameParts.size() > 1)
157  return std::find(filters.begin(), filters.end(), FunctionNameParts[0]) != filters.end();
158  MC_CATCH_BEGIN
159  printf("boost::algorithm::split in MCLog::IsAnyFilterMatched FAILED\n");
160  exit(1);
161  MC_CATCH_END
162  return false;
163 }
164 
165 
166 void MCLog::LogMessage(const char* function, const char* format, ...)
167 {
168  if (!function || !format)
169  return;
170 
171  // We must avoid to create static members in tls here for performance reasons.
172  // Since the default value of Debug is false, we don't need to output anything if the tls is empty.
173  if (!GlobalDebug &&
174  (!Debug.get() || !*Debug || (DebugFilter.get() && !DebugFilter->empty() &&
175  !IsAnyFilterMatched(*DebugFilter, function))))
176  {
177  return;
178  }
179 
180  va_list ap;
181  va_start(ap, format);
182 
183  if (GlobalMessageHandler.get())
184  {
185  GlobalMessageHandler->Message(MCLog::Normal, function, format, ap);
186  va_end(ap);
187  return;
188  } else
189  if (MessageHandler.get())
190  {
191  MessageHandler->Message(MCLog::Normal, function, format, ap);
192  va_end(ap);
193  return;
194  }
195  std::string TempStr(function);
196 #if defined(__ANDROID__)
197  char OtherTempStr[512];
198 
199  vsnprintf(OtherTempStr, sizeof(OtherTempStr), format, ap);
200  qCritical("%s", OtherTempStr);
201  va_end(ap);
202  return;
203 #endif
204 
205  if (TempStr.find('(') != std::string::npos)
206  {
207  MC::StringList FunctionNameSplitParts;
208 
209  MC_TRY_BEGIN
210  boost::algorithm::split(FunctionNameSplitParts, TempStr, boost::is_any_of("("));
211  MC_CATCH_BEGIN
212  printf("boost::algorithm::split FAILED\n");
213  exit(1);
214  MC_CATCH_END
215  TempStr = FunctionNameSplitParts[0];
216  }
217  const char* color_str = MC_ANSI_BLACK;
218 
219  if (!DebugColorFilters->empty() && IsAnyFilterMatched(*DebugColorFilters, function))
220  color_str = MC_ANSI_GREEN;
221 
222  fprintf(stdout, "%s%45s%s - ", color_str, TempStr.c_str(), MC_ANSI_COLOR_END);
223  vfprintf(stdout, format, ap);
224  fprintf(stdout, "\n");
225  va_end(ap);
226 }
227 
228 
229 void MCLog::ErrorMessage(const char* function, const char* format, ...)
230 {
231  if (!function || !format)
232  return;
233 
234  va_list ap;
235 
236  va_start(ap, format);
237  if (MessageHandler.get())
238  {
239  MessageHandler->Message(MCLog::Error, function, format, ap);
240  va_end(ap);
241 #ifdef __AIBO_BUILD__
242  ::abort();
243 #else
244  exit(1);
245 #endif
246  }
247  std::string TempStr(function);
248 #if defined(__ANDROID__)
249  char OtherTempStr[512];
250 
251  // cppcheck-suppress va_list_usedBeforeStarted
252  vsnprintf(OtherTempStr, sizeof(OtherTempStr), format, ap);
253  qCritical("%s", OtherTempStr);
254  // cppcheck-suppress va_list_usedBeforeStarted
255  va_end(ap);
256  exit(1);
257 #endif
258  if (TempStr.find('(') != std::string::npos)
259  {
260  MC::StringList FunctionNameSplitParts;
261 
262  MC_TRY_BEGIN
263  boost::algorithm::split(FunctionNameSplitParts, TempStr, boost::is_any_of("("));
264  MC_CATCH_BEGIN
265  printf("boost::algorithm::split FAILED\n");
266  exit(1);
267  MC_CATCH_END
268  TempStr = FunctionNameSplitParts[0];
269  }
270 
271  fprintf(stdout, "%s%45s%s - ", MC_ANSI_RED, TempStr.c_str(), MC_ANSI_COLOR_END);
272  vfprintf(stdout, format, ap);
273  fprintf(stdout, "\n");
274  va_end(ap);
275  exit(1);
276 }
277 
278 
279 void MCLog::WarningMessage(const char* function, const char* format, ...)
280 {
281  if (!function || !format)
282  return;
283 
284  va_list ap;
285  va_start(ap, format);
286 
287  if (MessageHandler.get())
288  {
289  MessageHandler->Message(MCLog::Warning, function, format, ap);
290  va_end(ap);
291  return;
292  }
293  std::string TempStr(function);
294 #if defined(__ANDROID__)
295  char OtherTempStr[512];
296 
297  vsnprintf(OtherTempStr, sizeof(OtherTempStr), format, ap);
298  qCritical("%s", OtherTempStr);
299  va_end(ap);
300  return;
301 #endif
302  if (TempStr.find('(') != std::string::npos)
303  {
304  MC::StringList FunctionNameSplitParts;
305 
306  MC_TRY_BEGIN
307  boost::algorithm::split(FunctionNameSplitParts, TempStr, boost::is_any_of("("));
308  MC_CATCH_BEGIN
309  printf("boost::algorithm::split FAILED\n");
310  exit(1);
311  MC_CATCH_END
312  TempStr = FunctionNameSplitParts[0];
313  }
314  fprintf(stdout, "%s%45s%s - ", MC_ANSI_PURPLE, TempStr.c_str(), MC_ANSI_COLOR_END);
315  vfprintf(stdout, format, ap);
316  fprintf(stdout, "\n");
317  va_end(ap);
318 }
319 
320 
321 void MCLog::FormattedMessage(MCLog::ColorType color, bool bold, const char* format, ...)
322 {
323  if (!format)
324  return;
325 
326  va_list ap;
327  va_start(ap, format);
328 #if defined(__ANDROID__)
329  char OtherTempStr[4096];
330 
331  vsnprintf(OtherTempStr, sizeof(OtherTempStr), format, ap);
332  qCritical("%s", OtherTempStr);
333  va_end(ap);
334  return;
335 #endif
336 #ifdef WIN32
337  vfprintf(stdout, format, ap);
338  va_end(ap);
339  return;
340 #endif
341  // Workaround to avoid the new-line code among the ANSI codes
342  std::string TempStr(format);
343  bool NewLine = false;
344 
345  if (TempStr.c_str()[TempStr.size()-1] == '\n')
346  {
347  NewLine = true;
348  TempStr.erase(TempStr.size()-1, 1);
349  }
350 
351  if (color != NoColor)
352  {
353  char OtherTempStr[256];
354 
355  if (bold)
356  {
357  snprintf(OtherTempStr, sizeof(OtherTempStr), "\033[1;%dm%s\033[0;39m", (int)color, TempStr.c_str());
358  } else {
359  snprintf(OtherTempStr, sizeof(OtherTempStr), "\033[;%dm%s\033[0;39m", (int)color, TempStr.c_str());
360  }
361  TempStr = OtherTempStr;
362  }
363  vfprintf(stdout, TempStr.c_str(), ap);
364 
365  if (NewLine)
366  {
367  fprintf(stdout, "\n");
368  }
369  va_end(ap);
370 }
371 
372 
373 #ifndef __AIBO_BUILD__
374 QString MCLog::ConvertFromAnsiToQtMarkup(const QString& orig_str)
375 {
376  if (!orig_str.contains('\033')) // -V536 (PVS Studio suppression)
377  return orig_str;
378 
379  QString NewStr(orig_str);
380 
381  for (int i = MinColor; i <= MaxColor; ++i)
382  {
383  QString NormalCodeStr = QString("\033[;%1m").arg(QString::number(i));
384  QString BoldCodeStr = QString("\033[1;%1m").arg(QString::number(i));
385 
386  if (i == Black)
387  {
388  NewStr.replace(NormalCodeStr, "<span style=\"color:black\">");
389  NewStr.replace(BoldCodeStr, "<span style=\"color:black;font-weight:bold\">");
390  }
391  if (i == Red)
392  {
393  NewStr.replace(NormalCodeStr, "<span style=\"color:#ff363c\">");
394  NewStr.replace(BoldCodeStr, "<span style=\"color:#ff363c;font-weight:bold\">");
395  }
396  if (i == Green)
397  {
398  NewStr.replace(NormalCodeStr, "<span style=\"color:#389e0b\">");
399  NewStr.replace(BoldCodeStr, "<span style=\"color:#389e0b;font-weight:bold\">");
400  }
401  if (i == Yellow)
402  {
403  NewStr.replace(NormalCodeStr, "<span style=\"color:yellow\">");
404  NewStr.replace(BoldCodeStr, "<span style=\"color:yellow;font-weight:bold\">");
405  }
406  if (i == Blue)
407  {
408  NewStr.replace(NormalCodeStr, "<span style=\"color:blue\">");
409  NewStr.replace(BoldCodeStr, "<span style=\"color:blue;font-weight:bold\">");
410  }
411  if (i == Purple)
412  {
413  NewStr.replace(NormalCodeStr, "<span style=\"color:purple\">");
414  NewStr.replace(BoldCodeStr, "<span style=\"color:purple;font-weight:bold\">");
415  }
416  if (i == Cyan)
417  {
418  NewStr.replace(NormalCodeStr, "<span style=\"color:cyan\">");
419  NewStr.replace(BoldCodeStr, "<span style=\"color:cyan;font-weight:bold\">");
420  }
421  if (i == White)
422  {
423  NewStr.replace(NormalCodeStr, "<span style=\"color:white\">");
424  NewStr.replace(BoldCodeStr, "<span style=\"color:white;font-weight:bold\">");
425  }
426  }
427  NewStr.replace("\033[0;39m", "</span>");
428  return NewStr;
429 }
430 
431 
432 QString MCLog::RemoveMarkup(const QString& orig_str)
433 {
434  QString NewStr(orig_str);
435 
436  // Note: Kinda hack, not really good
437  NewStr.remove("<b>");
438  NewStr.remove("</b>");
439  NewStr.remove("</span>");
440  NewStr.remove(QRegExp("<*>", Qt::CaseInsensitive, QRegExp::Wildcard));
441  return NewStr;
442 }
443 #endif
444 
445 
446 void MCLogEcho::Message(MCLog::MessageType message_type, const char* function, const char* format, va_list arg_list)
447 {
448  MC_UNUSED(function);
449  int Position = 0;
450  const int BufferSize = 1024;
451  char Buffer[BufferSize];
452 
453  Position += vsnprintf(&Buffer[Position], BufferSize-Position, format, arg_list);
454  snprintf(&Buffer[Position], BufferSize-Position, "\n");
455  printf("%s", Buffer);
456  if (message_type == MCLog::Error)
457  {
458 #ifdef __AIBO_BUILD__
459  ::abort();
460 #else
461  exit(1);
462 #endif
463  }
464 }
465 
466 
467 #ifndef __AIBO_BUILD__
468 void MCQtLogEcho::Message(MCLog::MessageType message_type, const char* function, const char* format, va_list arg_list)
469 {
470  MC_UNUSED(function);
471  const int BufferSize = 1024;
472  char Buffer[BufferSize];
473 
474  vsnprintf(&Buffer[0], BufferSize, format, arg_list);
475  if (message_type == MCLog::Normal)
476  {
477  qDebug() << Buffer;
478  } else
479  if (message_type == MCLog::Warning)
480  {
481  qWarning() << Buffer;
482  } else
483  if (message_type == MCLog::Error)
484  {
485  exit(1);
486  }
487 }
488 #endif
virtual void Message(MCLog::MessageType message_type, const char *function, const char *format, va_list arg_list) override
Print a log message.
Definition: MCLog.cpp:468
static void ErrorMessage(const char *function, const char *format,...) CLANG_ANALYZER_NORETURN
Print error message with red color.
Definition: MCLog.cpp:229
Log message handler.
Definition: MCLog.hpp:238
ColorType
Definition: MCLog.hpp:61
static QString RemoveMarkup(const QString &orig_str)
Remove the markup.
Definition: MCLog.cpp:432
static QString ConvertFromAnsiToQtMarkup(const QString &orig_str)
Convert the ANSI codes to Qt markup format.
Definition: MCLog.cpp:374
static void SetCustomHandler(MCLogMessageHandler *handler, bool global=false)
Set custom message handler.
Definition: MCLog.cpp:119
static void SetDebugStatus(bool new_status, bool global=false)
Set debug status.
Definition: MCLog.cpp:79
static std::string GetDebugColorFilters()
Get debug color filter.
Definition: MCLog.cpp:105
#define MC_UNUSED(a)
Helper macro to avoid compiler warning about unused function parameters.
Definition: MCDefs.hpp:601
static void FormattedMessage(MCLog::ColorType color, bool bold, const char *format,...)
Print normal message.
Definition: MCLog.cpp:321
static std::string GetDebugFilter()
Get debug filter.
Definition: MCLog.cpp:91
static void LogMessage(const char *function, const char *format,...)
Print log message.
Definition: MCLog.cpp:166
MessageType
Definition: MCLog.hpp:77
static bool GetDebugStatus(bool global=false)
Get debug status.
Definition: MCLog.cpp:68
static void WarningMessage(const char *function, const char *format,...)
Print warning message with yellow color.
Definition: MCLog.cpp:279
A wrapper class to cover boost::thread_specific_ptr/folly::ThreadLocal API on certain targets...
virtual void Message(MCLog::MessageType message_type, const char *function, const char *format, va_list arg_list) override
Print a log message.
Definition: MCLog.cpp:446
static void SetDebugFilter(const std::string &new_filter)
Set debug filter.
Definition: MCLog.cpp:98
static bool IsAnyFilterMatched(const MC::StringList &filters, const std::string &function_name)
Check if any filter matches with the function name.
Definition: MCLog.cpp:128
static void SetDebugColorFilters(const std::string &new_color_filters)
Set debug color filters.
Definition: MCLog.cpp:112