Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mbilogTextBackendBase.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include "mbilogTextBackendBase.h"
18 #include "mbilogLoggingTypes.h"
19 #include <ctime>
20 #include <iomanip>
21 #include <iostream>
22 #include <vector>
23 
24 #ifdef _WIN32
25 #define USE_WIN32COLOREDCONSOLE
26 #include <windows.h>
27 #include "mbilogTextDictionary.h"
28 #endif
29 
30 static bool g_init = false;
31 
33 {
34 }
35 
36 #ifdef USE_WIN32COLOREDCONSOLE
37 
38 static HANDLE g_hConsole;
39 
40 class AutoCategorize
41 {
42 protected:
43  std::vector<std::string> path;
44 
45  std::string current, category;
46 
47  int pos;
48 
49  void flush()
50  {
51  if (current.size() > 0)
52  {
53  if (current.compare("..") == 0)
54  {
55  if (path.size() > 0)
56  path.pop_back();
57  }
58  else
59  {
60  path.push_back(current);
61  }
62  current = "";
63  }
64  }
65 
66  std::string simplify(std::string x)
67  {
68  bool redo;
69 
70  std::string lft(""), rgt("");
71 
72  do
73  {
74  redo = false;
75 
76  for (int r = 0; r < sizeof(mbilog::replace) / sizeof(char *); r += 2)
77  {
78  int s = static_cast<int>(strlen(mbilog::replace[r]));
79  int xs = static_cast<int>(x.size());
80 
81  if (xs == s)
82  {
83  if (mbilog::replace[r + 1][0] || !lft.empty() || !rgt.empty())
84  if (x.compare(mbilog::replace[r]) == 0)
85  x = mbilog::replace[r + 1];
86  }
87  else if (xs > s)
88  {
89  if (strncmp(mbilog::replace[r], &x.c_str()[xs - s], s) == 0)
90  {
91  std::string rp = mbilog::replace[r + 1];
92  if (!rp.empty())
93  rp[0] = toupper(rp[0]);
94  x = x.substr(0, xs - s);
95  rgt = rp + rgt;
96  redo = true;
97  }
98  else if (strncmp(mbilog::replace[r], x.c_str(), s) == 0)
99  {
100  std::string rp = mbilog::replace[r + 1];
101  if (!rp.empty())
102  rp[0] = toupper(rp[0]);
103  x = x.substr(s, xs - s);
104  lft = lft + rp;
105  redo = true;
106  }
107  }
108  }
109  } while (redo);
110 
111  x[0] = toupper(x[0]);
112 
113  x = lft + x + rgt;
114 
115  x[0] = tolower(x[0]);
116 
117  return x;
118  }
119 
120  std::string concat(std::string a, std::string b, bool opt)
121  {
122  int as = static_cast<int>(a.size());
123  int bs = static_cast<int>(b.size());
124  if (opt && as <= bs)
125  {
126  if (as == bs && a.compare(b) == 0)
127  return a;
128 
129  if (strncmp(a.c_str(), b.c_str(), as) == 0)
130  {
131  b = b.substr(as, bs - as);
132  b[0] = tolower(b[0]);
133  }
134  }
135 
136  return a + "." + b;
137  }
138 
139  bool search2p2(char *a, char *b, bool optimize = true)
140  {
141  int size = static_cast<int>(path.size()) - 3;
142  for (int r = 0; r < size; r++)
143  if (path[r].compare(a) == 0 && path[r + 1].compare(b) == 0)
144  {
145  pos = r + 2;
146  category = concat(simplify(path[pos]), simplify(path[path.size() - 1]), optimize);
147  return true;
148  }
149  return false;
150  }
151 
152  bool search2p1(char *a, char *b)
153  {
154  int size = static_cast<int>(path.size()) - 2;
155  for (int r = 0; r < size; r++)
156  if (path[r].compare(a) == 0 && path[r + 1].compare(b) == 0)
157  {
158  pos = r + 2;
159  category = simplify(path[path.size() - 1]);
160  return true;
161  }
162  return false;
163  }
164 
165  bool search1p2(char *a, bool optimize = true)
166  {
167  int size = static_cast<int>(path.size()) - 2;
168  for (int r = 0; r < size; r++)
169  if (path[r].compare(a) == 0)
170  {
171  pos = r + 1;
172  category = concat(simplify(path[pos]), simplify(path[path.size() - 1]), optimize);
173  return true;
174  }
175  return false;
176  }
177 
178 public:
179  AutoCategorize(const mbilog::LogMessage &l)
180  {
181  int size = static_cast<int>(strlen(l.filePath));
182 
183  current = "";
184 
185  for (int r = 0; r < size; r++)
186  {
187  char c = l.filePath[r];
188  if (c == '\\' || c == '/')
189  flush();
190  else
191  current += tolower(c);
192  }
193 
194  flush();
195  }
196 
197  std::string GetPrefix()
198  {
199  category = "";
200  if (search2p2("mbi-sb", "core", false))
201  return "sb.";
202  if (search2p1("mbi-sb", "q4mitk"))
203  return "sb.ui.";
204  if (search2p2("mbi", "applications"))
205  return "sb.app.";
206  if (search2p2("mbi-sb", "q4applications"))
207  return "sb.app.";
208  if (search2p2("mbi-sb", "utilities"))
209  return "sb.util.";
210  if (search2p2("mbi-sb", "bundles"))
211  return "sb.bun.";
212  if (search2p2("mbi-sb", "bundlesqt"))
213  return "sb.bun.";
214  if (search2p2("mbi", "modules"))
215  return "sb.mod.";
216 
217  if (search2p2("mbi-qm", "core", false))
218  return "qm.";
219  if (search2p2("mbi-qm", "utilities"))
220  return "qm.util.";
221 
222  if (search2p2("modules", "mitkext", false))
223  return "ext.";
224  if (search2p1("modules", "qmitkext"))
225  return "ext.ui.";
226  if (search2p2("modules", "bundles"))
227  return "ext.bun.";
228 
229  if (search2p2("blueberry", "bundles"))
230  return "blueberry.";
231 
232  if (search2p2("core", "code", false))
233  return "core.";
234  if (search2p1("coreui", "qmitk"))
235  return "core.ui.";
236  if (search2p2("coreui", "bundles"))
237  return "core.bun.";
238 
239  // following must come last:
240  if (search1p2("modules"))
241  return "core.mod.";
242  if (search1p2("utilities"))
243  return "core.util.";
244  if (search1p2("applications"))
245  return "core.app.";
246 
247  return "";
248  }
249 
250  std::string GetCategory() { return category; }
251 };
252 
253 #endif
254 
255 void mbilog::TextBackendBase::FormatSmart(std::ostream &out, const LogMessage &l, int /*threadID*/)
256 {
257  char c_open = '[';
258  char c_close = ']';
259 
260  switch (l.level)
261  {
262  case mbilog::Info:
263  break;
264 
265  case mbilog::Warn:
266  c_open = '!';
267  c_close = '!';
268  break;
269 
270  case mbilog::Error:
271  c_open = '#';
272  c_close = '#';
273  break;
274 
275  case mbilog::Fatal:
276  c_open = '*';
277  c_close = '*';
278  break;
279 
280  case mbilog::Debug:
281  c_open = '{';
282  c_close = '}';
283  break;
284  }
285 
286  out << c_open;
287 
288  if (!g_init)
289  {
290  g_init = true;
291  AppendTimeStamp(out);
292  out << std::endl;
293  }
294 
295  std::locale C("C");
296  std::locale originalLocale = out.getloc();
297  out.imbue(C);
298 
299  out << std::fixed << std::setprecision(3) << ((double)std::clock()) / CLOCKS_PER_SEC;
300 
301  out.imbue(originalLocale);
302 
303  out << c_close << " ";
304 
305  if (!l.category.empty())
306  {
307  out << "[" << l.category << "] ";
308  }
309 
310  switch (l.level)
311  {
312  case mbilog::Info:
313  break;
314 
315  case mbilog::Warn:
316  out << "WARNING: ";
317  break;
318 
319  case mbilog::Error:
320  out << "ERROR: ";
321  break;
322 
323  case mbilog::Fatal:
324  out << "FATAL: ";
325  break;
326 
327  case mbilog::Debug:
328  out << "DEBUG: ";
329  break;
330  }
331 
332  out << l.message << std::endl;
333 }
334 
335 void mbilog::TextBackendBase::FormatFull(std::ostream &out, const LogMessage &l, int threadID)
336 {
337  switch (l.level)
338  {
339  case mbilog::Info:
340  out << "INFO";
341  break;
342 
343  case mbilog::Warn:
344  out << "WARN";
345  break;
346 
347  case mbilog::Error:
348  out << "ERROR";
349  break;
350 
351  case mbilog::Fatal:
352  out << "FATAL";
353  break;
354 
355  case mbilog::Debug:
356  out << "DEBUG";
357  break;
358  }
359 
360  out << "|";
361 
362  AppendTimeStamp(out);
363 
364  out << "|";
365 
366  out << "|" << std::string(l.filePath) << "(" << l.lineNumber << ")";
367 
368  out << "|" << std::string(l.functionName);
369 
370  // if(threadID)
371  {
372  out << "|" << std::hex << threadID;
373  }
374 
375  // if(NA_STRING != l.moduleName)
376  {
377  out << "|" << std::string(l.moduleName);
378  }
379 
380  // if(!l.category.empty())
381  {
382  out << "|" << l.category;
383  }
384 
385  out << l.message << std::endl;
386 }
387 
389 {
390 #ifdef USE_WIN32COLOREDCONSOLE
391  FormatSmartWindows(l, threadID);
392 #else
393  FormatSmart(std::cout, l, threadID);
394 #endif
395 }
396 
398 {
399  FormatFull(std::cout, l, threadID);
400 }
401 
403 {
404  time_t rawtime = time(nullptr);
405  std::string timestring(ctime(&rawtime));
406  timestring.replace(timestring.length() - 1,
407  1,
408  " "); // replace \n by " " (separates date/time from following output of relative time since start)
409 
410  std::locale C("C");
411  std::locale originalLocale = out.getloc();
412  out.imbue(C);
413 
414  out << timestring;
415 
416  out.imbue(originalLocale);
417 }
418 
419 #ifdef USE_WIN32COLOREDCONSOLE
420 
422 {
423  int colorNormal = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
424 
425  int lastColor = colorNormal;
426 #define ChangeColor(_col) \
427  { \
428  int col = (_col); \
429  if (lastColor != (col)) \
430  { \
431  SetConsoleTextAttribute(g_hConsole, (col)); \
432  lastColor = (col); \
433  } \
434  }
435 
436  int colorTime = FOREGROUND_GREEN;
437  int colorText = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
438  int colorCat = FOREGROUND_BLUE | FOREGROUND_RED;
439  bool showColon = true;
440  bool forceCat = false;
441 
442  if (!g_init)
443  {
444  g_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
445  g_init = true;
446 
447  std::string title = "mbilog";
448 
449  SetConsoleTitle(title.c_str());
450 
451  /* Title rendering
452  ChangeColor( FOREGROUND_GREEN|FOREGROUND_BLUE|BACKGROUND_BLUE );
453  std::cout << " <<< " << std::flush;
454  ChangeColor( FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY|BACKGROUND_BLUE );
455  std::cout << title << std::flush;
456  ChangeColor( FOREGROUND_GREEN|FOREGROUND_BLUE|BACKGROUND_BLUE );
457  std::cout << " >>> " << std::flush;
458  ChangeColor( colorNormal );
459  std::cout << std::endl;
460  */
461 
462  // Give out start time
463  ChangeColor(colorTime);
464  AppendTimeStamp(std::cout);
465  std::cout << std::endl;
466  }
467 
468  switch (l.level)
469  {
470  case mbilog::Info:
471  break;
472 
473  case mbilog::Warn:
474  colorTime = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
475  // colorText = FOREGROUND_RED|FOREGROUND_GREEN;
476  colorCat = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
477  showColon = false;
478  forceCat = true;
479  break;
480 
481  case mbilog::Error:
482  colorTime = FOREGROUND_RED | FOREGROUND_INTENSITY;
483  // colorText = FOREGROUND_RED;
484  colorCat = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
485  showColon = false;
486  forceCat = true;
487  break;
488 
489  case mbilog::Fatal:
490  colorTime = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
491  // colorText = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY;
492  colorCat = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
493  showColon = false;
494  forceCat = true;
495  break;
496 
497  case mbilog::Debug:
498  colorTime = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
499  // colorText |= FOREGROUND_INTENSITY;
500  showColon = false;
501  break;
502  }
503 
504  ChangeColor(colorTime);
505 
506  std::locale C("C");
507  std::locale originalLocale = std::cout.getloc();
508  std::cout.imbue(C);
509 
510  std::cout << std::fixed << std::setprecision(2) << ((double)std::clock()) / CLOCKS_PER_SEC << " ";
511 
512  std::cout.imbue(originalLocale);
513 
514  // category
515  {
516  AutoCategorize ac(l);
517  std::string pre = ac.GetPrefix();
518  std::string cat = ac.GetCategory();
519 
520  cat = pre + cat;
521 
522  if (cat.empty())
523  cat = l.category;
524 
525  if (!cat.empty())
526  {
527  ChangeColor(colorCat);
528  // static std::string lastCat;
529  // if(forceCat||lastCat.compare(cat))
530  {
531  std::cout << cat << std::flush;
532  // lastCat = cat;
533  }
534  // else
535  // std::cout << "..." << std::flush;
536 
537  if (showColon)
538  {
539  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
540  std::cout << ": " << std::flush;
541  }
542  else
543  std::cout << " ";
544  }
545  }
546 
547  switch (l.level)
548  {
549  case mbilog::Info:
550  break;
551 
552  case mbilog::Warn:
553  ChangeColor(colorTime);
554  std::cout << "WARNING" << std::flush;
555  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
556  std::cout << ": " << std::flush;
557  break;
558 
559  case mbilog::Error:
560  ChangeColor(colorTime);
561  std::cout << "ERROR" << std::flush;
562  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
563  std::cout << ": " << std::flush;
564  break;
565 
566  case mbilog::Fatal:
567  ChangeColor(colorTime);
568  std::cout << "FATAL" << std::flush;
569  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
570  std::cout << ": " << std::flush;
571  break;
572 
573  case mbilog::Debug:
574  ChangeColor(colorTime);
575  std::cout << "DBG" << std::flush;
576  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
577  std::cout << ": " << std::flush;
578  break;
579  }
580 
581  ChangeColor(colorText);
582  std::cout << l.message << std::endl;
583 
584  ChangeColor(colorNormal);
585 }
586 
587 #endif
const char * moduleName
Name of the module where the logging message was emitted which is generated by the macros in file mbi...
const char * filePath
File name of the source file where the logging message was emitted which is generated by the macros i...
std::string message
The actual logging message.
static bool g_init
An object of this class represents a single logging message (logging event) of the mbi logging mechan...
const int lineNumber
Line of the source source file where the logging message was emitted which is generated by the macros...
void FormatSmartWindows(const mbilog::LogMessage &l, int)
Special variant of method FormatSmart which uses colored messages (only for windows).
const char * functionName
Name of the method where the logging message was emitted which is generated by the macros in file mbi...
bool compare(std::pair< double, int > i, std::pair< double, int > j)
std::string category
Category of the logging event, which was defined by the user.
const int level
Logging level which is defined in the enum mbilogLoggingTypes.h TODO: convert to enum.
static const char * replace[]
This is a dictionary to replace long names of classes, modules, etc. to shorter versions in the conso...
void AppendTimeStamp(std::ostream &out)
Writes the system time to the given stream.
void FormatFull(const LogMessage &l, int threadID=0)
Method formats the given LogMessage in the full/long format and writes it to std::cout.
void FormatSmart(const LogMessage &l, int threadID=0)
Method formats the given LogMessage in the smart/short format and writes it to std::cout.