source: trunk/Mars/mbase/MLog.cc@ 20096

Last change on this file since 20096 was 20096, checked in by tbretz, 3 months ago
root 6.24 requires some more includes, clasng 10 is a bit more picky about overload warinings and casting, gErrorMutex i deprecated -> so we can only lock our error handler again ourselves... well... mess ahead... thank you.
File size: 26.8 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 12/2000 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2006
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MLog
28//
29// This is what we call the logging-system.
30//
31// It is derived from the C++ streaming classes and can handle our
32// logging. The log output can be redirected to stdout, stderr, any other
33// stream or a root window.
34//
35// There is a global log-instance which you can use like cout, id is gLog.
36// A log-instance of your choice (gLog by default) is destributed to all
37// Task which are used in an eventloop, so that you can redirect the output
38// of one eventloop to where you want..
39//
40// The MLog stream has the advantage, that it can be used like the common
41// C++ streams (for example cout). It can redirect the stream to different
42// outputs (console, file, GUI) if necessary at the same time.
43//
44// It supports different debug levels. The debug level of the current
45// stream contents is set by SetDebugLevel, the output level of the
46// current stream can be set by SetOutputLevel.
47//
48// The header file MLogManip.h contains so called manipulators (like flush
49// or setw from iomanip.h) which can manipulate these levels from within
50// stream, for example:
51// gLog << debug(3) << "Hallo World " << endl;
52// sets the debug level of the following stream to 3
53//
54// edev(), ddev() can be used to enable/disable an output device from
55// within the stream. The enumerations are defined in MLog::_flags
56//
57// Commonly used abbreviations are also defined:
58// dbginf Prints source file name and line number. Used for output
59// which people may like to look up in the code
60// all Is streamed to the output in any case. Used for outputs
61// which are requested by the user (eg TObject::Print)
62// err Should be used for fatal errors which stops the current
63// processing, eg:
64// gLog << err << "ERROR: TObject::Copy - Stopped" << endl;
65// warn Warning means an error occured, but it is not clear whether
66// this results further procesing or not.
67// inf Informs the user about what's going on. Mostly usefull for
68// debugging, but in general not necessary at all.
69// dbg Use this for your private purpose to mark something as debug
70// output. This is _not_ ment to be persistent!
71//
72// If your console is capable of ANSI colors the stream is displayed
73// in several colors:
74// all: default
75// err: red
76// warn: yellow/brown
77// inf: green
78// dbg: blue (and all other levels)
79//
80// If you have a dark background on your console you might want to set
81// an environment variable, eg:
82// export MARSDEFINES=-DHAVE_DARKBACKGROUND
83// and recompile MLog.
84//
85// If your console can display it also 'underline' can be used. This
86// underlines a text till the next 'endl', eg:
87// gLog << underline << "This is important!" << endl;
88//
89// To switch off ANSI support call: SetNoColors()
90//
91// gLog is a global stream defined like cout or cerr
92//
93//////////////////////////////////////////////////////////////////////////////
94#include "MLog.h"
95
96#include <stdlib.h> // mkstemp
97
98#include <fstream>
99#include <iomanip>
100
101#if ROOT_VERSION_CODE >= ROOT_VERSION(6,24,00)
102#include <mutex>
103#endif
104
105
106#include <TROOT.h> // gROOT->GetListOfCleanups()
107#include <TSystem.h>
108
109#ifdef _REENTRANT
110#include <TMutex.h>
111#endif
112#include <TGTextView.h>
113
114#include <TEnv.h> // gEnv (ErrorHandler)
115#include <TError.h> // TError (SetErrorHandler)
116
117#include "MArgs.h"
118#include "MTime.h"
119#include "MString.h"
120#include "MParContainer.h"
121
122#include "MLogHtml.h"
123#include "MLogManip.h" // inf,warn,err (MLog::ErrorHandler)
124
125ClassImp(MLog);
126
127using namespace std;
128
129#undef DEBUG
130//#define DEBUG
131
132
133// root 3.02:
134// check for TObjectWarning, TObject::Info, gErrorIgnoreLevel
135
136const char MLog::kESC = '\033'; // (char)27
137const char *const MLog::kEsc = "\033[";
138const char *const MLog::kReset = "\033[0m";
139const char *const MLog::kRed = "\033[31m";
140const char *const MLog::kGreen = "\033[32m";
141#ifdef HAVE_DARKBACKGROUND
142const char *const MLog::kYellow = "\033[33m\033[1m";
143#else
144const char *const MLog::kYellow = "\033[33m";
145#endif
146const char *const MLog::kBlue = "\033[34m";
147const char *const MLog::kUnderline = "\033[4m";
148const char *const MLog::kBlink = "\033[5m";
149const char *const MLog::kBright = "\033[1m";
150const char *const MLog::kDark = "\033[2m";
151
152//
153// This is the definition of the global log facility
154//
155MLog gLog;
156
157/// Serializes error output, destructed by the gROOT destructor via ReleaseDefaultErrorHandler()
158 static std::mutex *GetErrorMutex() {
159 static std::mutex *m = new std::mutex();
160 return m;
161 }
162
163// --------------------------------------------------------------------------
164//
165// this strange usage of an unbufferd buffer is a workaround
166// to make it work on Alpha and Linux!
167//
168void MLog::Init()
169{
170 //
171 // Creat drawing semaphore
172 //
173#ifdef _REENTRANT
174 fMuxGui = new TMutex;
175 fMuxStream = new TMutex;
176#endif
177
178 fPlugins = new TList;
179 gROOT->GetListOfCleanups()->Add(fPlugins);
180 fPlugins->SetBit(kMustCleanup);
181
182 setp(&fBuffer, &fBuffer+1);
183 *this << '\0';
184}
185
186// --------------------------------------------------------------------------
187//
188// default constructor which initializes the streamer and sets the device
189// which is used for the output (i)
190//
191MLog::MLog(int i) : ostream(this), fPPtr(fBase), fEPtr(fBase+fgBufferSize), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(i), fIsNull(kFALSE), fOut(NULL), fOutAllocated(kFALSE), fGui(NULL), fNumLines(0)
192{
193 Init();
194}
195
196// --------------------------------------------------------------------------
197//
198// default constructor which initializes the streamer and sets the given
199// ofstream as the default output device
200//
201MLog::MLog(ofstream &sout) : ostream(this), fPPtr(fBase), fEPtr(fBase+fgBufferSize), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eFile), fIsNull(kFALSE), fOut(&sout), fOutAllocated(kFALSE), fGui(NULL), fNumLines(0)
202{
203 Init();
204}
205
206// --------------------------------------------------------------------------
207//
208// default constructor which initializes the streamer and sets the given
209// TGTextView as the default output device
210//
211MLog::MLog(TGTextView &sout) : ostream(this), fPPtr(fBase), fEPtr(fBase+fgBufferSize), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eGui), fOut(NULL), fOutAllocated(kFALSE), fGui(&sout), fNumLines(0)
212{
213 Init();
214}
215
216// --------------------------------------------------------------------------
217//
218// default constructor which initializes the streamer and opens a file with
219// the given name. Dependend on the flag the file is set as output device
220// or not.
221//
222MLog::MLog(const char *fname, int flag) : ostream(this), fPPtr(fBase), fEPtr(fBase+fgBufferSize), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eFile), fIsNull(kFALSE), fGui(NULL), fNumLines(0)
223{
224 Init();
225
226 AllocateFile(fname);
227 CheckFlag(eFile, flag);
228}
229
230// --------------------------------------------------------------------------
231//
232// Destructor, destroying the gui mutex.
233//
234MLog::~MLog()
235{
236 DeallocateFile();
237
238#ifdef DEBUG
239 TIter Next(fPlugins);
240 TObject *o=0;
241 while ((o=Next()))
242 {
243 cout << "Delete: " << o->GetName() << std::flush;
244 cout << " [" << o->ClassName() << "]" << endl;
245 delete o;
246 }
247
248 cout << "Delete: fPlugins " << fPlugins << "..." << std::flush;
249#endif
250
251 delete fPlugins;
252#ifdef DEBUG
253 cout << "done." << endl;
254#endif
255
256#ifdef _REENTRANT
257 delete fMuxStream;
258 delete fMuxGui;
259#endif
260}
261
262// --------------------------------------------------------------------------
263//
264// copyt constructor
265//
266/*
267MLog::MLog(MLog const& log)
268{
269// fOutputLevel = log.fOutputLevel;
270// fDebugLevel = log.fDebugLevel;
271// fDevice = log.fDevice;
272}
273*/
274
275void MLog::Underline()
276{
277 if (fIsNull)
278 return;
279
280 SetBit(kIsUnderlined);
281
282 fPlugins->R__FOR_EACH(MLogPlugin, Underline)();
283
284 if (TestBit(eNoColors))
285 return;
286
287 if (fDevice&eStdout)
288 cout << kUnderline;
289
290 if (fDevice&eStderr)
291 cerr << kUnderline;
292}
293
294void MLog::Output(ostream &sout, int len)
295{
296 if (!TestBit(eNoColors))
297 switch (fOutputLevel)
298 {
299 // do not output reset. Otherwise we reset underline in 0-mode
300 // case 1: out << MLog::kReset; break; // all
301 case 0: break; // all = background color
302 case 1: sout << MLog::kRed; break; // err
303 case 2: sout << MLog::kYellow; break; // warn
304 case 3: // inf
305 case 4: // inf2
306 case 5: sout << MLog::kGreen; break; // inf3
307 default: sout << MLog::kBlue; break; // all others (dbg)
308 }
309
310 if (len>0)
311 {
312 // Check for EOL
313 const Int_t endline = fBase[len-1]=='\n' ? 1 : 0;
314 // output text to screen (without trailing '\n')
315 sout << TString(fBase, len-endline);
316 // reset colors if working with colors
317 if (!TestBit(eNoColors))
318 sout << kReset;
319 // output EOL of check found EOL
320 if (endline)
321 {
322 sout << '\n';
323 // Check whether text was underlined
324 if (TestBit(kIsUnderlined) && TestBit(eNoColors))
325 {
326 sout << setw(len-1) << setfill('-') << "" << "\n";
327 ResetBit(kIsUnderlined);
328 }
329 }
330 }
331 sout.flush();
332}
333
334void MLog::AddGuiLine(const TString &line)
335{
336 // add a new TString* to the array of gui lines
337 TString **newstr = new TString*[fNumLines+1];
338 memcpy(newstr, fGuiLines, fNumLines*sizeof(TString*));
339 if (fNumLines>0)
340 delete [] fGuiLines;
341 fGuiLines = newstr;
342
343 // add Gui line as last line of array
344 fGuiLines[fNumLines++] = new TString(line);
345}
346
347// --------------------------------------------------------------------------
348//
349// This is the function which writes the stream physically to a device.
350// If you want to add a new device this must be done here.
351//
352void MLog::WriteBuffer()
353{
354 //
355 // restart writing to the buffer at its first char
356 //
357 const int len = fPPtr - fBase;
358
359 fPPtr = fBase;
360
361 if (fIsNull)
362 return;
363
364 if (fDevice&eStdout)
365 Output(cout, len);
366
367 if (fDevice&eStderr)
368 Output(cerr, len);
369
370 if (fDevice&eFile && fOut)
371 fOut->write(fBase, len);
372
373 fPlugins->R__FOR_EACH(MLogPlugin, SetColor)(fOutputLevel);
374 fPlugins->R__FOR_EACH(MLogPlugin, WriteBuffer)(fBase, len);
375
376 if (fDevice&eGui && fGui)
377 {
378 // check whether the current text was flushed or endl'ed
379 const Int_t endline = fBase[len-1]=='\n' ? 1 : 0;
380
381 // for the gui remove trailing characters ('\n' or '\0')
382 fBase[len-endline]='\0';
383
384 // add new text to line storage
385 fGuiLine += fBase;
386
387 if (endline)
388 {
389 AddGuiLine(fGuiLine);
390 fGuiLine = "";
391
392 // Check whether text should be underlined
393 if (endline && TestBit(kIsUnderlined))
394 {
395 AddGuiLine("");
396 fGuiLines[fNumLines-1]->Append('-', fGuiLines[fNumLines-2]->Length());
397 ResetBit(kIsUnderlined);
398 }
399 }
400 }
401}
402
403void MLog::UpdateGui()
404{
405 if (fNumLines==0)
406 return;
407
408 // lock mutex
409 if (!LockUpdate("UpdateGui"))
410 {
411 Warning("UpdateGui", "Execution skipped");
412 return;
413 }
414
415 TGText &txt=*fGui->GetText();
416
417 // copy lines to TGListBox
418 for (int i=0; i<fNumLines; i++)
419 {
420 // Replace all tabs by 7 white spaces
421 fGuiLines[i]->ReplaceAll("\t", " ");
422 txt.InsText(TGLongPosition(0, txt.RowCount()), *fGuiLines[i]);
423 delete fGuiLines[i];
424 }
425 delete [] fGuiLines;
426
427 fNumLines=0;
428
429 // cut text box top 1000 lines
430 // while (txt.RowCount()>1000)
431 // txt.DelLine(1);
432
433 // show last entry
434 fGui->Layout();
435 fGui->SetVsbPosition(txt.RowCount()-1);
436
437 // tell a main loop, that list box contents have changed
438 fGui->SetBit(kHasChanged);
439
440 // release mutex
441 UnLockUpdate("UpdateGui");
442}
443
444bool MLog::LockUpdate(const char *msg)
445{
446#ifdef _REENTRANT
447 if (fMuxGui->Lock()==13)
448 {
449 Info("LockUpdate", "%s - mutex is already locked by this thread\n", msg);
450 return false;
451 }
452#endif
453 return true;
454}
455
456bool MLog::UnLockUpdate(const char *msg)
457{
458#ifdef _REENTRANT
459 if (fMuxGui->UnLock()==13)
460 {
461 Info("UnLockUpdate", "%s - tried to unlock mutex locked by other thread\n", msg);
462 return false;
463 }
464#endif
465 return true;
466}
467
468bool MLog::Lock(const char *msg)
469{
470#ifdef _REENTRANT
471 if (fMuxStream->Lock()==13)
472 {
473 Error("Lock", "%s - mutex is already locked by this thread\n", msg);
474 return false;
475 }
476// while (fMuxStream->Lock()==13)
477// usleep(1);
478// {
479// Error("Lock", "%s - mutex is already locked by this thread\n", msg);
480// return false;
481// }
482#endif
483 return true;
484}
485
486bool MLog::UnLock(const char *msg)
487{
488#ifdef _REENTRANT
489 if (fMuxStream->UnLock()==13)
490 {
491 Error("UnLock", "%s - tried to unlock mutex locked by other thread\n", msg);
492 return false;
493 }
494#endif
495 return true;
496}
497
498// --------------------------------------------------------------------------
499//
500// This is called to flush the buffer of the streaming devices
501//
502int MLog::sync()
503{
504 if (!LockUpdate("sync"))
505 usleep(1);
506 WriteBuffer();
507 UnLockUpdate("sync");
508
509 if (fDevice&eStdout)
510 {
511 if (!fIsNull && !TestBit(eNoColors))
512 cout << kReset;
513 cout.flush();
514 }
515
516 if (fDevice&eStderr)
517 cerr.flush();
518
519 if (fDevice&eFile && fOut)
520 fOut->flush();
521
522 return 0;
523}
524
525// --------------------------------------------------------------------------
526//
527// This function comes from streambuf and should
528// output the buffer to the device (flush, endl)
529// or handle a buffer overflow (too many chars)
530// If a real overflow happens i contains the next
531// chars which doesn't fit into the buffer anymore.
532// If the buffer is not really filled i is EOF(-1).
533//
534int MLog::overflow(int i) // i=EOF means not a real overflow
535{
536 //
537 // no output if
538 //
539 if (fOutputLevel <= fDebugLevel)
540 {
541 if (!LockUpdate("overflow"))
542 usleep(1);
543
544 *fPPtr++ = (char)i;
545
546 if (fPPtr == fEPtr)
547 WriteBuffer();
548
549 UnLockUpdate("overflow");
550 }
551
552 return 0;
553}
554
555// --------------------------------------------------------------------------
556//
557// Print usage information setup in Setup()
558//
559void MLog::Usage()
560{
561 // 1 2 3 4 5 6 7 8
562 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
563 *this << " -v# Verbosity level # [default=2]" << endl;
564 *this << " -a, --no-colors Do not use Ansii color codes" << endl;
565 *this << " --log[=file] Write log-out to ascii-file [default: prgname.log]" << endl;
566 *this << " --html[=file] Write log-out to html-file [default: prgname.html]" << endl;
567 *this << " --debug[=n] Enable root debugging [default: gDebug=1]" << endl;
568 *this << " --null Null output (supresses all output)" << endl;
569}
570
571// --------------------------------------------------------------------------
572//
573// Setup MLog and global debug output from command line arguments.
574//
575void MLog::Setup(MArgs &arg)
576{
577 // FXIME: This is not really at a place where it belongs to!
578 gDebug = arg.HasOption("--debug=") ? arg.GetIntAndRemove("--debug=") : 0;
579 if (gDebug==0 && arg.HasOnlyAndRemove("--debug"))
580 gDebug=1;
581
582 TString f1 = arg.GetStringAndRemove("--log=", "");
583 if (f1.IsNull() && arg.HasOnlyAndRemove("--log"))
584 f1 = MString::Format("%s.log", arg.GetName());
585 if (!f1.IsNull())
586 {
587 SetOutputFile(f1);
588 EnableOutputDevice(eFile);
589 }
590
591 TString f2 = arg.GetStringAndRemove("--html=", "");
592 if (f2.IsNull() && arg.HasOnlyAndRemove("--html"))
593 f2 = MString::Format("%s.html", arg.GetName());
594 if (!f2.IsNull())
595 {
596 MLogHtml *html = new MLogHtml(f2);
597 html->SetBit(kCanDelete);
598 AddPlugin(html);
599 }
600
601 const Bool_t null = arg.HasOnlyAndRemove("--null");
602 if (null)
603 SetNullOutput();
604
605 if (arg.HasOnlyAndRemove("--no-colors") || arg.HasOnlyAndRemove("-a"))
606 SetNoColors();
607
608 SetDebugLevel(arg.GetIntAndRemove("-v", 2));
609}
610
611// --------------------------------------------------------------------------
612//
613// Read the setup from a TEnv:
614// MLog.VerbosityLevel: 0, 1, 2, 3, 4
615// MLog.DebugLevel: 0, 1, 2, 3, 4
616// MLog.NoColors
617//
618// Depending on your setup it might be correct to use something like:
619// Job1.MLog.VerbosityLevel: 1
620// Job1.DebugLevel: 2
621// Job1.MLog.NoColors
622//
623void MLog::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
624{
625 MParContainer mlog("MLog");
626
627 if (mlog.IsEnvDefined(env, prefix+"MLog", "VerbosityLevel", print))
628 SetDebugLevel(mlog.GetEnvValue(env, prefix+"MLog", "VerbosityLevel", 2));
629 else
630 if (mlog.IsEnvDefined(env, "MLog", "VerbosityLevel", print))
631 SetDebugLevel(mlog.GetEnvValue(env, "MLog", "VerbosityLevel", 2));
632
633 if (mlog.IsEnvDefined(env, prefix+"MLog", "DebugLevel", print))
634 gDebug = mlog.GetEnvValue(env, prefix+"MLog", "DebugLevel", 0);
635 else
636 if (mlog.IsEnvDefined(env, "MLog", "DebugLevel", print))
637 gDebug = mlog.GetEnvValue(env, "MLog", "DebugLevel", 0);
638
639 if (mlog.IsEnvDefined(env, prefix+"MLog", "NoColors", print))
640 SetNoColors(mlog.GetEnvValue(env, prefix+"MLog", "NoColors", kFALSE));
641 else
642 if (mlog.IsEnvDefined(env, "MLog", "NoColors", print))
643 SetNoColors(mlog.GetEnvValue(env, "MLog", "NoColors", kFALSE));
644}
645
646// --------------------------------------------------------------------------
647//
648// Read the setup from a TEnv:
649// MLog.VerbosityLevel: 0, 1, 2, 3, 4
650// MLog.DebugLevel: 0, 1, 2, 3, 4
651// MLog.NoColors
652//
653// Depending on your setup it might be correct to use something like:
654// Job1.MLog.VerbosityLevel: 1
655// Job1.DebugLevel: 2
656// Job1.MLog.NoColors
657//
658void MLog::WriteEnv(TEnv &, TString prefix, Bool_t) const
659{
660 if (!prefix.IsNull())
661 prefix += ".";
662 prefix += "MLog";
663
664 cout << "MLog::WriteEnv: not yet implemented!" << endl;
665}
666
667// --------------------------------------------------------------------------
668//
669// Create a new instance of an file output stream
670// an set the corresponding flag
671//
672void MLog::AllocateFile(const char *fname)
673{
674 // gcc 3.2:
675 const char *txt = "logXXXXXX";
676
677 TString n(fname ? fname : txt);
678 gSystem->ExpandPathName(n);
679
680 fOut = new ofstream(n.Data());
681
682 // switch off buffering
683 fOut->rdbuf()->pubsetbuf(0,0);
684
685 fOutAllocated = kTRUE;
686}
687
688// --------------------------------------------------------------------------
689//
690// if fout was allocated by this instance of MLooging
691// delete it.
692//
693void MLog::DeallocateFile()
694{
695 if (fOutAllocated)
696 delete fOut;
697}
698
699// --------------------------------------------------------------------------
700//
701// if necessary delete the old in stance of the file
702// output stream and create a new one
703//
704void MLog::ReallocateFile(const char *fname)
705{
706 DeallocateFile();
707 AllocateFile(fname);
708}
709
710// --------------------------------------------------------------------------
711//
712// This function checks if a device should get enabled or disabled.
713//
714void MLog::CheckFlag(Flags_t chk, int flag)
715{
716 if (flag==-1)
717 return;
718
719 flag ? EnableOutputDevice(chk) : DisableOutputDevice(chk);
720}
721
722// --------------------------------------------------------------------------
723//
724// Add a plugin to which the output should be redirected, eg. MLogHtml
725// The user has to take care of its deletion. If the plugin is deleted
726// (and the kMustCleanup bit was not reset accidentaly) the plugin
727// is automatically removed from the list of active plugins.
728//
729// If MLog should take the ownership call plug->SetBit(kCanDelete);
730//
731void MLog::AddPlugin(MLogPlugin *plug)
732{
733 fPlugins->Add(plug);
734
735 // Make sure that it is recursively deleted from all objects in ListOfCleanups
736 plug->SetBit(kMustCleanup);
737}
738
739// --------------------------------------------------------------------------
740//
741// Returns "yyyy-mm-dd user@host gROOT->GetName()[pid]"
742//
743TString MLog::Intro()
744{
745 UserGroup_t *user = gSystem->GetUserInfo();
746
747 TString rc;
748 rc += MTime(-1).GetSqlDateTime();
749 rc += " ";
750 rc += user->fUser;
751 rc += "@";
752 rc += gSystem->HostName();
753 rc += " ";
754 rc += gROOT->GetName();
755 rc += "[";
756 rc += gSystem->GetPid();
757 rc += "] ";
758
759 delete user;
760
761 return rc;
762}
763
764// --------------------------------------------------------------------------
765//
766// Check whether errors at this level should be ignored.
767//
768bool MLog::ErrorHandlerIgnore(Int_t level)
769{
770 // The default error handler function. It prints the message on stderr and
771 // if abort is set it aborts the application.
772 if (gErrorIgnoreLevel == kUnset) {
773#if ROOT_VERSION_CODE < ROOT_VERSION(6,24,00)
774 R__LOCKGUARD2(gErrorMutex);
775#else
776 std::lock_guard<std::mutex> guard(*GetErrorMutex());
777#endif
778
779 gErrorIgnoreLevel = 0;
780 if (gEnv) {
781 TString lvl = gEnv->GetValue("Root.ErrorIgnoreLevel", "Info");
782 if (!lvl.CompareTo("Info",TString::kIgnoreCase))
783 gErrorIgnoreLevel = kInfo;
784 else if (!lvl.CompareTo("Warning",TString::kIgnoreCase))
785 gErrorIgnoreLevel = kWarning;
786 else if (!lvl.CompareTo("Error",TString::kIgnoreCase))
787 gErrorIgnoreLevel = kError;
788 else if (!lvl.CompareTo("Break",TString::kIgnoreCase))
789 gErrorIgnoreLevel = kBreak;
790 else if (!lvl.CompareTo("SysError",TString::kIgnoreCase))
791 gErrorIgnoreLevel = kSysError;
792 else if (!lvl.CompareTo("Fatal",TString::kIgnoreCase))
793 gErrorIgnoreLevel = kFatal;
794 }
795 }
796
797 return level < gErrorIgnoreLevel;
798}
799
800// --------------------------------------------------------------------------
801//
802// Output the root error message to the log-stream.
803//
804void MLog::ErrorHandlerPrint(Int_t level, const char *location, const char *msg)
805{
806#if ROOT_VERSION_CODE < ROOT_VERSION(6,24,00)
807 R__LOCKGUARD2(gErrorMutex);
808#else
809 std::lock_guard<std::mutex> guard(*GetErrorMutex());
810#endif
811
812 if (level >= kError)
813 gLog << "ROOT:Error";
814 else
815 if (level >= kSysError)
816 gLog << "SysError";
817 else
818 if (level >= kBreak)
819 gLog << "\n *** Break ***";
820 else
821 if (level >= kFatal)
822 gLog << "Fatal";
823 else
824 if (level >= kWarning)
825 gLog << "ROOT:Warning";
826 else
827 if (level >= kInfo)
828 gLog << "ROOT:Info";
829
830 if (level >= kBreak && level < kSysError)
831 gLog << ": " << msg << std::endl;
832 else
833 if (location==0 || location[0]==0)
834 gLog << ": " << msg << std::endl;
835 else
836 gLog << " in <" << location << ">: " << msg << std::endl;
837}
838
839// --------------------------------------------------------------------------
840//
841// A new error handler using gLog instead of stderr as output.
842// It is mainly a copy of root's DefaultErrorHandler
843// (see TError.h and TError.cxx)
844//
845void MLog::ErrorHandlerCol(Int_t level, Bool_t abort, const char *location, const char *msg)
846{
847 if (ErrorHandlerIgnore(level))
848 return;
849
850 // This is a really stupid hack/workaround to suppress these
851 // annoying errors in case of a log-scale set too early
852 if (level==kError && !strcmp(location, "THistPainter::PaintInit"))
853 level=kInfo+2;
854
855 // This is a really stupid hack/workaround to suppress these
856 // annoying errors in case of 0 number of points
857 if (level==kError && !strcmp(location, "TGraphPainter::PaintGraph"))
858 level=kInfo+2;
859
860 // This is a really stupid hack/workaround to suppress these
861 // annoying errors in case of 0 number of points
862 if (level==kWarning && !strcmp(location, "Fit"))
863 level=kInfo+2;
864
865 gLog << std::flush;
866
867 const Int_t store = gLog.GetOutputLevel();
868
869 if (level >= kInfo)
870 gLog << inf;
871 if (level==kInfo+1)
872 gLog << inf2;
873 if (level==kInfo+2)
874 gLog << inf3;
875 if (level >= kWarning)
876 gLog << warn;
877 if (level >= kError)
878 gLog << err;
879
880 ErrorHandlerPrint(level, location, msg);
881
882 gLog << std::flush;
883
884 gLog.SetOutputLevel(store);
885 if (!abort)
886 return;
887
888 gLog << err << "aborting" << std::endl;
889 if (gSystem) {
890 gSystem->StackTrace();
891 gLog.SetOutputLevel(store);
892 gSystem->Abort();
893 }
894 else
895 {
896 gLog.SetOutputLevel(store);
897 ::abort();
898 }
899}
900
901// --------------------------------------------------------------------------
902//
903// A new error handler using gLog instead of stderr as output.
904// It is mainly a copy of root's DefaultErrorHandler
905// (see TError.h and TError.cxx)
906//
907void MLog::ErrorHandlerAll(Int_t level, Bool_t abort, const char *location, const char *msg)
908{
909 if (ErrorHandlerIgnore(level))
910 return;
911
912 gLog << std::flush << all;
913
914 ErrorHandlerPrint(level, location, msg);
915
916 gLog << std::flush;
917 if (!abort)
918 return;
919
920 gLog << err << "aborting" << std::endl;
921 if (gSystem) {
922 gSystem->StackTrace();
923 gSystem->Abort();
924 } else
925 ::abort();
926}
927
928// --------------------------------------------------------------------------
929//
930// Redirect the root ErrorHandler (see TError.h) output to gLog.
931//
932// The diffrent types are:
933// kColor: Use gLog colors
934// kBlackWhite: Use all-qualifier (as in gLog << all << endl;)
935// kDefault: Set back to root's default error handler
936// (redirect output to stderr)
937//
938void MLog::RedirectErrorHandler(ELogType typ)
939{
940 switch (typ)
941 {
942 case kColor:
943 SetErrorHandler(MLog::ErrorHandlerCol);
944 break;
945 case kBlackWhite:
946 SetErrorHandler(MLog::ErrorHandlerAll);
947 break;
948 case kDefault:
949 SetErrorHandler(DefaultErrorHandler);
950 }
951}
Note: See TracBrowser for help on using the repository browser.