Medical Imaging Interaction Toolkit  2018.4.99-389bf124
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 (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "mbilogTextBackendBase.h"
14 #include "mbilogLoggingTypes.h"
15 #include <ctime>
16 #include <iomanip>
17 #include <iostream>
18 #include <vector>
19 
20 #ifdef _WIN32
21 #define USE_WIN32COLOREDCONSOLE
22 #include <windows.h>
23 #include "mbilogTextDictionary.h"
24 #endif
25 
26 static bool g_init = false;
27 
29 {
30 }
31 
32 #ifdef USE_WIN32COLOREDCONSOLE
33 
34 static HANDLE g_hConsole;
35 
36 class AutoCategorize
37 {
38 protected:
39  std::vector<std::string> path;
40 
41  std::string current, category;
42 
43  int pos;
44 
45  void flush()
46  {
47  if (current.size() > 0)
48  {
49  if (current.compare("..") == 0)
50  {
51  if (path.size() > 0)
52  path.pop_back();
53  }
54  else
55  {
56  path.push_back(current);
57  }
58  current = "";
59  }
60  }
61 
62  std::string simplify(std::string x)
63  {
64  bool redo;
65 
66  std::string lft(""), rgt("");
67 
68  do
69  {
70  redo = false;
71 
72  for (int r = 0; r < sizeof(mbilog::replace) / sizeof(char *); r += 2)
73  {
74  int s = static_cast<int>(strlen(mbilog::replace[r]));
75  int xs = static_cast<int>(x.size());
76 
77  if (xs == s)
78  {
79  if (mbilog::replace[r + 1][0] || !lft.empty() || !rgt.empty())
80  if (x.compare(mbilog::replace[r]) == 0)
81  x = mbilog::replace[r + 1];
82  }
83  else if (xs > s)
84  {
85  if (strncmp(mbilog::replace[r], &x.c_str()[xs - s], s) == 0)
86  {
87  std::string rp = mbilog::replace[r + 1];
88  if (!rp.empty())
89  rp[0] = toupper(rp[0]);
90  x = x.substr(0, xs - s);
91  rgt = rp + rgt;
92  redo = true;
93  }
94  else if (strncmp(mbilog::replace[r], x.c_str(), s) == 0)
95  {
96  std::string rp = mbilog::replace[r + 1];
97  if (!rp.empty())
98  rp[0] = toupper(rp[0]);
99  x = x.substr(s, xs - s);
100  lft = lft + rp;
101  redo = true;
102  }
103  }
104  }
105  } while (redo);
106 
107  x[0] = toupper(x[0]);
108 
109  x = lft + x + rgt;
110 
111  x[0] = tolower(x[0]);
112 
113  return x;
114  }
115 
116  std::string concat(std::string a, std::string b, bool opt)
117  {
118  int as = static_cast<int>(a.size());
119  int bs = static_cast<int>(b.size());
120  if (opt && as <= bs)
121  {
122  if (as == bs && a.compare(b) == 0)
123  return a;
124 
125  if (strncmp(a.c_str(), b.c_str(), as) == 0)
126  {
127  b = b.substr(as, bs - as);
128  b[0] = tolower(b[0]);
129  }
130  }
131 
132  return a + "." + b;
133  }
134 
135  bool search2p2(char *a, char *b, bool optimize = true)
136  {
137  int size = static_cast<int>(path.size()) - 3;
138  for (int r = 0; r < size; r++)
139  if (path[r].compare(a) == 0 && path[r + 1].compare(b) == 0)
140  {
141  pos = r + 2;
142  category = concat(simplify(path[pos]), simplify(path[path.size() - 1]), optimize);
143  return true;
144  }
145  return false;
146  }
147 
148  bool search2p1(char *a, char *b)
149  {
150  int size = static_cast<int>(path.size()) - 2;
151  for (int r = 0; r < size; r++)
152  if (path[r].compare(a) == 0 && path[r + 1].compare(b) == 0)
153  {
154  pos = r + 2;
155  category = simplify(path[path.size() - 1]);
156  return true;
157  }
158  return false;
159  }
160 
161  bool search1p2(char *a, bool optimize = true)
162  {
163  int size = static_cast<int>(path.size()) - 2;
164  for (int r = 0; r < size; r++)
165  if (path[r].compare(a) == 0)
166  {
167  pos = r + 1;
168  category = concat(simplify(path[pos]), simplify(path[path.size() - 1]), optimize);
169  return true;
170  }
171  return false;
172  }
173 
174 public:
175  AutoCategorize(const mbilog::LogMessage &l)
176  {
177  int size = static_cast<int>(strlen(l.filePath));
178 
179  current = "";
180 
181  for (int r = 0; r < size; r++)
182  {
183  char c = l.filePath[r];
184  if (c == '\\' || c == '/')
185  flush();
186  else
187  current += tolower(c);
188  }
189 
190  flush();
191  }
192 
193  std::string GetPrefix()
194  {
195  category = "";
196  if (search2p2("mbi-sb", "core", false))
197  return "sb.";
198  if (search2p1("mbi-sb", "q4mitk"))
199  return "sb.ui.";
200  if (search2p2("mbi", "applications"))
201  return "sb.app.";
202  if (search2p2("mbi-sb", "q4applications"))
203  return "sb.app.";
204  if (search2p2("mbi-sb", "utilities"))
205  return "sb.util.";
206  if (search2p2("mbi-sb", "bundles"))
207  return "sb.bun.";
208  if (search2p2("mbi-sb", "bundlesqt"))
209  return "sb.bun.";
210  if (search2p2("mbi", "modules"))
211  return "sb.mod.";
212 
213  if (search2p2("mbi-qm", "core", false))
214  return "qm.";
215  if (search2p2("mbi-qm", "utilities"))
216  return "qm.util.";
217 
218  if (search2p2("modules", "mitkext", false))
219  return "ext.";
220  if (search2p1("modules", "qmitkext"))
221  return "ext.ui.";
222  if (search2p2("modules", "bundles"))
223  return "ext.bun.";
224 
225  if (search2p2("blueberry", "bundles"))
226  return "blueberry.";
227 
228  if (search2p2("core", "code", false))
229  return "core.";
230  if (search2p1("coreui", "qmitk"))
231  return "core.ui.";
232  if (search2p2("coreui", "bundles"))
233  return "core.bun.";
234 
235  // following must come last:
236  if (search1p2("modules"))
237  return "core.mod.";
238  if (search1p2("utilities"))
239  return "core.util.";
240  if (search1p2("applications"))
241  return "core.app.";
242 
243  return "";
244  }
245 
246  std::string GetCategory() { return category; }
247 };
248 
249 #endif
250 
251 void mbilog::TextBackendBase::FormatSmart(std::ostream &out, const LogMessage &l, int /*threadID*/)
252 {
253  char c_open = '[';
254  char c_close = ']';
255 
256  switch (l.level)
257  {
258  case mbilog::Info:
259  break;
260 
261  case mbilog::Warn:
262  c_open = '!';
263  c_close = '!';
264  break;
265 
266  case mbilog::Error:
267  c_open = '#';
268  c_close = '#';
269  break;
270 
271  case mbilog::Fatal:
272  c_open = '*';
273  c_close = '*';
274  break;
275 
276  case mbilog::Debug:
277  c_open = '{';
278  c_close = '}';
279  break;
280  }
281 
282  out << c_open;
283 
284  if (!g_init)
285  {
286  g_init = true;
287  AppendTimeStamp(out);
288  out << std::endl;
289  }
290 
291  std::locale C("C");
292  std::locale originalLocale = out.getloc();
293  out.imbue(C);
294 
295  out << std::fixed << std::setprecision(3) << ((double)std::clock()) / CLOCKS_PER_SEC;
296 
297  out.imbue(originalLocale);
298 
299  out << c_close << " ";
300 
301  if (!l.category.empty())
302  {
303  out << "[" << l.category << "] ";
304  }
305 
306  switch (l.level)
307  {
308  case mbilog::Info:
309  break;
310 
311  case mbilog::Warn:
312  out << "WARNING: ";
313  break;
314 
315  case mbilog::Error:
316  out << "ERROR: ";
317  break;
318 
319  case mbilog::Fatal:
320  out << "FATAL: ";
321  break;
322 
323  case mbilog::Debug:
324  out << "DEBUG: ";
325  break;
326  }
327 
328  out << l.message << std::endl;
329 }
330 
331 void mbilog::TextBackendBase::FormatFull(std::ostream &out, const LogMessage &l, int threadID)
332 {
333  switch (l.level)
334  {
335  case mbilog::Info:
336  out << "INFO";
337  break;
338 
339  case mbilog::Warn:
340  out << "WARN";
341  break;
342 
343  case mbilog::Error:
344  out << "ERROR";
345  break;
346 
347  case mbilog::Fatal:
348  out << "FATAL";
349  break;
350 
351  case mbilog::Debug:
352  out << "DEBUG";
353  break;
354  }
355 
356  out << "|";
357 
358  AppendTimeStamp(out);
359 
360  out << "|";
361 
362  out << "|" << std::string(l.filePath) << "(" << l.lineNumber << ")";
363 
364  out << "|" << std::string(l.functionName);
365 
366  // if(threadID)
367  {
368  out << "|" << std::hex << threadID;
369  }
370 
371  // if(NA_STRING != l.moduleName)
372  {
373  out << "|" << std::string(l.moduleName);
374  }
375 
376  // if(!l.category.empty())
377  {
378  out << "|" << l.category;
379  }
380 
381  out << l.message << std::endl;
382 }
383 
385 {
386 #ifdef USE_WIN32COLOREDCONSOLE
387  FormatSmartWindows(l, threadID);
388 #else
389  FormatSmart(std::cout, l, threadID);
390 #endif
391 }
392 
394 {
395  FormatFull(std::cout, l, threadID);
396 }
397 
399 {
400  time_t rawtime = time(nullptr);
401  std::string timestring(ctime(&rawtime));
402  timestring.replace(timestring.length() - 1,
403  1,
404  " "); // replace \n by " " (separates date/time from following output of relative time since start)
405 
406  std::locale C("C");
407  std::locale originalLocale = out.getloc();
408  out.imbue(C);
409 
410  out << timestring;
411 
412  out.imbue(originalLocale);
413 }
414 
415 #ifdef USE_WIN32COLOREDCONSOLE
416 
417 // Get the horizontal and vertical screen sizes in pixel
418 void GetDesktopResolution(int& horizontal, int& vertical)
419 {
420  RECT desktop;
421  // Get a handle to the desktop window
422  const HWND hDesktop = GetDesktopWindow();
423  // Get the size of screen to the variable desktop
424  GetWindowRect(hDesktop, &desktop);
425  // The top left corner will have coordinates (0,0)
426  // and the bottom right corner will have coordinates
427  // (horizontal, vertical)
428  horizontal = desktop.right;
429  vertical = desktop.bottom;
430 }
431 
432 BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
433  {
434  int *Count = (int*)dwData;
435  (*Count)++;
436  return TRUE;
437  }
438 
439 int GetMonitorCount()
440  {
441  int Count = 0;
442  if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&Count))
443  return Count;
444  return -1;//signals an error
445 }
446 
448 {
449  int colorNormal = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
450 
451  int lastColor = colorNormal;
452 #define ChangeColor(_col) \
453  { \
454  int col = (_col); \
455  if (lastColor != (col)) \
456  { \
457  SetConsoleTextAttribute(g_hConsole, (col)); \
458  lastColor = (col); \
459  } \
460  }
461 
462  int colorTime = FOREGROUND_GREEN;
463  int colorText = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
464  int colorCat = FOREGROUND_BLUE | FOREGROUND_RED;
465  bool showColon = true;
466  bool forceCat = false;
467 
468  if (!g_init)
469  {
470  g_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
471  g_init = true;
472 
473  std::string title = "mbilog";
474 
475  SetConsoleTitle(title.c_str());
476 
477  /* Title rendering
478  ChangeColor( FOREGROUND_GREEN|FOREGROUND_BLUE|BACKGROUND_BLUE );
479  std::cout << " <<< " << std::flush;
480  ChangeColor( FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY|BACKGROUND_BLUE );
481  std::cout << title << std::flush;
482  ChangeColor( FOREGROUND_GREEN|FOREGROUND_BLUE|BACKGROUND_BLUE );
483  std::cout << " >>> " << std::flush;
484  ChangeColor( colorNormal );
485  std::cout << std::endl;
486  */
487 
488  // Give out start time
489  ChangeColor(colorTime);
490  AppendTimeStamp(std::cout);
491  std::cout << std::endl;
492  }
493 
494  switch (l.level)
495  {
496  case mbilog::Info:
497  break;
498 
499  case mbilog::Warn:
500  colorTime = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
501  // colorText = FOREGROUND_RED|FOREGROUND_GREEN;
502  colorCat = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
503  showColon = false;
504  forceCat = true;
505  break;
506 
507  case mbilog::Error:
508  colorTime = FOREGROUND_RED | FOREGROUND_INTENSITY;
509  // colorText = FOREGROUND_RED;
510  colorCat = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
511  showColon = false;
512  forceCat = true;
513  break;
514 
515  case mbilog::Fatal:
516  colorTime = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
517  // colorText = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY;
518  colorCat = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
519  showColon = false;
520  forceCat = true;
521  break;
522 
523  case mbilog::Debug:
524  colorTime = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
525  // colorText |= FOREGROUND_INTENSITY;
526  showColon = false;
527  break;
528  }
529 
530  ChangeColor(colorTime);
531 
532  std::locale C("C");
533  std::locale originalLocale = std::cout.getloc();
534  std::cout.imbue(C);
535 
536  std::cout << std::fixed << std::setprecision(2) << ((double)std::clock()) / CLOCKS_PER_SEC << " ";
537 
538  std::cout.imbue(originalLocale);
539 
540  // category
541  {
542  AutoCategorize ac(l);
543  std::string pre = ac.GetPrefix();
544  std::string cat = ac.GetCategory();
545 
546  cat = pre + cat;
547 
548  if (cat.empty())
549  cat = l.category;
550 
551  if (!cat.empty())
552  {
553  ChangeColor(colorCat);
554  // static std::string lastCat;
555  // if(forceCat||lastCat.compare(cat))
556  {
557  std::cout << cat << std::flush;
558  // lastCat = cat;
559  }
560  // else
561  // std::cout << "..." << std::flush;
562 
563  if (showColon)
564  {
565  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
566  std::cout << ": " << std::flush;
567  }
568  else
569  std::cout << " ";
570  }
571  }
572 
573  switch (l.level)
574  {
575  case mbilog::Info:
576  break;
577 
578  case mbilog::Warn:
579  ChangeColor(colorTime);
580  std::cout << "WARNING" << std::flush;
581  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
582  std::cout << ": " << std::flush;
583  break;
584 
585  case mbilog::Error:
586  ChangeColor(colorTime);
587  std::cout << "ERROR" << std::flush;
588  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
589  std::cout << ": " << std::flush;
590  break;
591 
592  case mbilog::Fatal:
593  ChangeColor(colorTime);
594  std::cout << "FATAL" << std::flush;
595  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
596  std::cout << ": " << std::flush;
597  break;
598 
599  case mbilog::Debug:
600  ChangeColor(colorTime);
601  std::cout << "DBG" << std::flush;
602  ChangeColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
603  std::cout << ": " << std::flush;
604  break;
605  }
606 
607  ChangeColor(colorText);
608  std::cout << l.message << std::endl;
609 
610  ChangeColor(colorNormal);
611 
612  int monitorCount = GetMonitorCount();
613  if (monitorCount > 1) {
614  HWND consoleWindow = GetConsoleWindow();
615  int horizontal = 0, vertical = 0;
616  const int verticalSizeOfConsoleWindow = 300;
617  GetDesktopResolution(horizontal, vertical);
618  SetWindowPos(consoleWindow, 0, horizontal, vertical/2 - verticalSizeOfConsoleWindow, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
619  }
620 
621 }
622 
623 #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.