source: trunk/MagicSoft/Mars/mbase/MLog.cc@ 7825

Last change on this file since 7825 was 7808, checked in by tbretz, 18 years ago
*** empty log message ***
File size: 20.2 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-2005
21!
22!
23\* ======================================================================== */
24
25
26//////////////////////////////////////////////////////////////////////////////
27//
28// MLog
29//
30// This is what we call the logging-system.
31//
32// It is derived from the C++ streaming classes and can handle our
33// logging. The log output can be redirected to stdout, stderr, any other
34// stream or a root window.
35//
36// There is a global log-instance which you can use like cout, id is gLog.
37// A log-instance of your choice (gLog by default) is destributed to all
38// Task which are used in an eventloop, so that you can redirect the output
39// of one eventloop to where you want..
40//
41// The MLog stream has the advantage, that it can be used like the common
42// C++ streams (for example cout). It can redirect the stream to different
43// outputs (console, file, GUI) if necessary at the same time.
44//
45// It supports different debug levels. The debug level of the current
46// stream contents is set by SetDebugLevel, the output level of the
47// current stream can be set by SetOutputLevel.
48//
49// The header file MLogManip.h contains so called manipulators (like flush
50// or setw from iomanip.h) which can manipulate these levels from within
51// stream, for example:
52// gLog << debug(3) << "Hallo World " << endl;
53// sets the debug level of the following stream to 3
54//
55// edev(), ddev() can be used to enable/disable an output device from
56// within the stream. The enumerations are defined in MLog::_flags
57//
58// Commonly used abbreviations are also defined:
59// dbginf Prints source file name and line number. Used for output
60// which people may like to look up in the code
61// all Is streamed to the output in any case. Used for outputs
62// which are requested by the user (eg TObject::Print)
63// err Should be used for fatal errors which stops the current
64// processing, eg:
65// gLog << err << "ERROR: TObject::Copy - Stopped" << endl;
66// warn Warning means an error occured, but it is not clear whether
67// this results further procesing or not.
68// inf Informs the user about what's going on. Mostly usefull for
69// debugging, but in general not necessary at all.
70// dbg Use this for your private purpose to mark something as debug
71// output. This is _not_ ment to be persistent!
72//
73// If your console is capable of ANSI colors the stream is displayed
74// in several colors:
75// all: default
76// err: red
77// warn: yellow/brown
78// inf: green
79// dbg: blue (and all other levels)
80//
81// If you have a dark background on your console you might want to set
82// an environment variable, eg:
83// export MARSDEFINES=-DHAVE_DARKBACKGROUND
84// and recompile MLog.
85//
86// If your console can display it also 'underline' can be used. This
87// underlines a text till the next 'endl', eg:
88// gLog << underline << "This is important!" << endl;
89//
90// To switch off ANSI support call: SetNoColors()
91//
92// gLog is a global stream defined like cout or cerr
93//
94//////////////////////////////////////////////////////////////////////////////
95#include "MLog.h"
96
97#include <stdlib.h> // mkstemp
98
99#include <fstream>
100#include <iomanip>
101
102#include <TROOT.h> // gROOT->GetListOfCleanups()
103
104#ifdef _REENTRANT
105#include <TMutex.h>
106#endif
107#include <TGTextView.h>
108
109#include "MArgs.h"
110#include "MParContainer.h"
111
112#include "MLogHtml.h"
113
114ClassImp(MLog);
115
116using namespace std;
117
118#undef DEBUG
119//#define DEBUG
120
121
122// root 3.02:
123// check for TObjectWarning, TObject::Info, gErrorIgnoreLevel
124
125const char MLog::kESC = '\033'; // (char)27
126const char *const MLog::kEsc = "\033[";
127const char *const MLog::kReset = "\033[0m";
128const char *const MLog::kRed = "\033[31m";
129const char *const MLog::kGreen = "\033[32m";
130#ifdef HAVE_DARKBACKGROUND
131const char *const MLog::kYellow = "\033[33m\033[1m";
132#else
133const char *const MLog::kYellow = "\033[33m";
134#endif
135const char *const MLog::kBlue = "\033[34m";
136const char *const MLog::kUnderline = "\033[4m";
137const char *const MLog::kBlink = "\033[5m";
138const char *const MLog::kBright = "\033[1m";
139const char *const MLog::kDark = "\033[2m";
140
141//
142// This is the definition of the global log facility
143//
144MLog gLog;
145
146// --------------------------------------------------------------------------
147//
148// this strange usage of an unbufferd buffer is a workaround
149// to make it work on Alpha and Linux!
150//
151void MLog::Init()
152{
153
154 //
155 // Creat drawing semaphore
156 //
157#ifdef _REENTRANT
158 fMuxGui = new TMutex;
159 fMuxStream = new TMutex;
160#endif
161
162 fPlugins = new TList;
163 gROOT->GetListOfCleanups()->Add(fPlugins);
164 fPlugins->SetBit(kMustCleanup);
165
166 setp(&fBuffer, &fBuffer+1);
167 *this << '\0';
168}
169
170// --------------------------------------------------------------------------
171//
172// default constructor which initializes the streamer and sets the device
173// which is used for the output (i)
174//
175MLog::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)
176{
177 Init();
178}
179
180// --------------------------------------------------------------------------
181//
182// default constructor which initializes the streamer and sets the given
183// ofstream as the default output device
184//
185MLog::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)
186{
187 Init();
188}
189
190// --------------------------------------------------------------------------
191//
192// default constructor which initializes the streamer and sets the given
193// TGTextView as the default output device
194//
195MLog::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)
196{
197 Init();
198}
199
200// --------------------------------------------------------------------------
201//
202// default constructor which initializes the streamer and opens a file with
203// the given name. Dependend on the flag the file is set as output device
204// or not.
205//
206MLog::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)
207{
208 Init();
209
210 AllocateFile(fname);
211 CheckFlag(eFile, flag);
212}
213
214// --------------------------------------------------------------------------
215//
216// Destructor, destroying the gui mutex.
217//
218MLog::~MLog()
219{
220 DeallocateFile();
221
222#ifdef DEBUG
223 TIter Next(fPlugins);
224 TObject *o=0;
225 while ((o=Next()))
226 {
227 cout << "Delete: " << o->GetName() << std::flush;
228 cout << " [" << o->ClassName() << "]" << endl;
229 delete o;
230 }
231
232 cout << "Delete: fPlugins " << fPlugins << "..." << std::flush;
233#endif
234
235 delete fPlugins;
236#ifdef DEBUG
237 cout << "done." << endl;
238#endif
239
240#ifdef _REENTRANT
241 delete fMuxStream;
242 delete fMuxGui;
243#endif
244}
245
246// --------------------------------------------------------------------------
247//
248// copyt constructor
249//
250/*
251MLog::MLog(MLog const& log)
252{
253// fOutputLevel = log.fOutputLevel;
254// fDebugLevel = log.fDebugLevel;
255// fDevice = log.fDevice;
256}
257*/
258
259void MLog::Underline()
260{
261 if (fIsNull)
262 return;
263
264 SetBit(kIsUnderlined);
265
266 fPlugins->R__FOR_EACH(MLogPlugin, Underline)();
267
268 if (TestBit(eNoColors))
269 return;
270
271 if (fDevice&eStdout)
272 cout << kUnderline;
273
274 if (fDevice&eStderr)
275 cerr << kUnderline;
276}
277
278void MLog::Output(ostream &sout, int len)
279{
280 if (!TestBit(eNoColors))
281 switch (fOutputLevel)
282 {
283 // do not output reset. Otherwise we reset underline in 0-mode
284 // case 1: out << MLog::kReset; break; // all
285 case 0: break; // all = background color
286 case 1: sout << MLog::kRed; break; // err
287 case 2: sout << MLog::kYellow; break; // warn
288 case 3: sout << MLog::kGreen; break; // inf
289 default: sout << MLog::kBlue; break; // all others (dbg)
290 }
291
292 if (len>0)
293 {
294 // Check for EOL
295 const Int_t endline = fBase[len-1]=='\n' ? 1 : 0;
296 // output text to screen (without trailing '\n')
297 sout << TString(fBase, len-endline);
298 // reset colors if working with colors
299 if (!TestBit(eNoColors))
300 sout << kReset;
301 // output EOL of check found EOL
302 if (endline)
303 {
304 sout << '\n';
305 // Check whether text was underlined
306 if (TestBit(kIsUnderlined) && TestBit(eNoColors))
307 {
308 sout << setw(len-1) << setfill('-') << "" << "\n";
309 ResetBit(kIsUnderlined);
310 }
311 }
312 }
313 sout.flush();
314}
315
316void MLog::AddGuiLine(const TString &line)
317{
318 // add a new TString* to the array of gui lines
319 TString **newstr = new TString*[fNumLines+1];
320 memcpy(newstr, fGuiLines, fNumLines*sizeof(TString*));
321 if (fNumLines>0)
322 delete fGuiLines;
323 fGuiLines = newstr;
324
325 // add Gui line as last line of array
326 fGuiLines[fNumLines++] = new TString(line);
327}
328
329// --------------------------------------------------------------------------
330//
331// This is the function which writes the stream physically to a device.
332// If you want to add a new device this must be done here.
333//
334void MLog::WriteBuffer()
335{
336 //
337 // restart writing to the buffer at its first char
338 //
339 const int len = fPPtr - fBase;
340
341 fPPtr = fBase;
342
343 if (fIsNull)
344 return;
345
346 if (fDevice&eStdout)
347 Output(cout, len);
348
349 if (fDevice&eStderr)
350 Output(cerr, len);
351
352 if (fDevice&eFile && fOut)
353 fOut->write(fBase, len);
354
355 fPlugins->R__FOR_EACH(MLogPlugin, SetColor)(fOutputLevel);
356 fPlugins->R__FOR_EACH(MLogPlugin, WriteBuffer)(fBase, len);
357
358 if (fDevice&eGui && fGui)
359 {
360 // check whether the current text was flushed or endl'ed
361 const Int_t endline = fBase[len-1]=='\n' ? 1 : 0;
362
363 // for the gui remove trailing characters ('\n' or '\0')
364 fBase[len-endline]='\0';
365
366 // add new text to line storage
367 fGuiLine += fBase;
368
369 if (endline)
370 {
371 AddGuiLine(fGuiLine);
372 fGuiLine = "";
373
374 // Check whether text should be underlined
375 if (endline && TestBit(kIsUnderlined))
376 {
377 AddGuiLine("");
378 fGuiLines[fNumLines-1]->Append('-', fGuiLines[fNumLines-2]->Length());
379 ResetBit(kIsUnderlined);
380 }
381 }
382 }
383}
384
385void MLog::UpdateGui()
386{
387 if (fNumLines==0)
388 return;
389
390 // lock mutex
391 if (!LockUpdate("UpdateGui"))
392 {
393 Warning("UpdateGui", "Execution skipped");
394 return;
395 }
396
397 TGText &txt=*fGui->GetText();
398
399 // copy lines to TGListBox
400 for (int i=0; i<fNumLines; i++)
401 {
402 // Replace all tabs by 7 white spaces
403 fGuiLines[i]->ReplaceAll("\t", " ");
404 txt.InsText(TGLongPosition(0, txt.RowCount()), *fGuiLines[i]);
405 delete fGuiLines[i];
406 }
407 delete fGuiLines;
408
409 fNumLines=0;
410
411 // cut text box top 1000 lines
412 // while (txt.RowCount()>1000)
413 // txt.DelLine(1);
414
415 // show last entry
416 fGui->Layout();
417 fGui->SetVsbPosition(txt.RowCount()-1);
418
419 // tell a main loop, that list box contents have changed
420 fGui->SetBit(kHasChanged);
421
422 // release mutex
423 UnLockUpdate("UpdateGui");
424}
425
426bool MLog::LockUpdate(const char *msg)
427{
428#ifdef _REENTRANT
429 if (fMuxGui->Lock()==13)
430 {
431 Info("LockUpdate", "%s - mutex is already locked by this thread\n", msg);
432 return false;
433 }
434#endif
435 return true;
436}
437
438bool MLog::UnLockUpdate(const char *msg)
439{
440#ifdef _REENTRANT
441 if (fMuxGui->UnLock()==13)
442 {
443 Info("UnLockUpdate", "%s - tried to unlock mutex locked by other thread\n", msg);
444 return false;
445 }
446#endif
447 return true;
448}
449
450bool MLog::Lock(const char *msg)
451{
452#ifdef _REENTRANT
453 if (fMuxStream->Lock()==13)
454 {
455 Error("Lock", "%s - mutex is already locked by this thread\n", msg);
456 return false;
457 }
458// while (fMuxStream->Lock()==13)
459// usleep(1);
460// {
461// Error("Lock", "%s - mutex is already locked by this thread\n", msg);
462// return false;
463// }
464#endif
465 return true;
466}
467
468bool MLog::UnLock(const char *msg)
469{
470#ifdef _REENTRANT
471 if (fMuxStream->UnLock()==13)
472 {
473 Error("UnLock", "%s - tried to unlock mutex locked by other thread\n", msg);
474 return false;
475 }
476#endif
477 return true;
478}
479
480// --------------------------------------------------------------------------
481//
482// This is called to flush the buffer of the streaming devices
483//
484int MLog::sync()
485{
486 if (!LockUpdate("sync"))
487 usleep(1);
488 WriteBuffer();
489 UnLockUpdate("sync");
490
491 if (fDevice&eStdout)
492 {
493 if (!fIsNull && !TestBit(eNoColors))
494 cout << kReset;
495 cout.flush();
496 }
497
498 if (fDevice&eStderr)
499 cerr.flush();
500
501 if (fDevice&eFile && fOut)
502 fOut->flush();
503
504 return 0;
505}
506
507// --------------------------------------------------------------------------
508//
509// This function comes from streambuf and should
510// output the buffer to the device (flush, endl)
511// or handle a buffer overflow (too many chars)
512// If a real overflow happens i contains the next
513// chars which doesn't fit into the buffer anymore.
514// If the buffer is not really filled i is EOF(-1).
515//
516int MLog::overflow(int i) // i=EOF means not a real overflow
517{
518 //
519 // no output if
520 //
521 if (fOutputLevel <= fDebugLevel)
522 {
523 if (!LockUpdate("overflow"))
524 usleep(1);
525
526 *fPPtr++ = (char)i;
527
528 if (fPPtr == fEPtr)
529 WriteBuffer();
530
531 UnLockUpdate("overflow");
532 }
533
534 return 0;
535}
536
537// --------------------------------------------------------------------------
538//
539// Print usage information setup in Setup()
540//
541void MLog::Usage()
542{
543 // 1 2 3 4 5 6 7 8
544 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
545 *this << " -v# Verbosity level # [default=2]" << endl;
546 *this << " -a, --no-colors Do not use Ansii color codes" << endl;
547 *this << " --log[=file] Write log-out to ascii-file [default: prgname.log]" << endl;
548 *this << " --html[=file] Write log-out to html-file [default: prgname.html]" << endl;
549 *this << " --debug[=n] Enable root debugging [default: gDebug=1]" << endl;
550 *this << " --null Null output (supresses all output)" << endl;
551}
552
553// --------------------------------------------------------------------------
554//
555// Setup MLog and global debug output from command line arguments.
556//
557void MLog::Setup(MArgs &arg)
558{
559 // FXIME: This is not really at a place where it belongs to!
560 gDebug = arg.HasOption("--debug=") ? arg.GetIntAndRemove("--debug=") : 0;
561 if (gDebug==0 && arg.HasOnlyAndRemove("--debug"))
562 gDebug=1;
563
564 TString f1 = arg.GetStringAndRemove("--log=", "");
565 if (f1.IsNull() && arg.HasOnlyAndRemove("--log"))
566 f1 = Form("%s.log", arg.GetName());
567 if (!f1.IsNull())
568 {
569 SetOutputFile(f1);
570 EnableOutputDevice(eFile);
571 }
572
573 TString f2 = arg.GetStringAndRemove("--html=", "");
574 if (f2.IsNull() && arg.HasOnlyAndRemove("--html"))
575 f2 = Form("%s.html", arg.GetName());
576 if (!f2.IsNull())
577 {
578 MLogHtml *html = new MLogHtml(f2);
579 html->SetBit(kCanDelete);
580 AddPlugin(html);
581 }
582
583 const Bool_t null = arg.HasOnlyAndRemove("--null");
584 if (null)
585 SetNullOutput();
586
587 if (arg.HasOnlyAndRemove("--no-colors") || arg.HasOnlyAndRemove("-a"))
588 SetNoColors();
589
590 SetDebugLevel(arg.GetIntAndRemove("-v", 2));
591}
592
593// --------------------------------------------------------------------------
594//
595// Read the setup from a TEnv:
596// MLog.VerbosityLevel: 0, 1, 2, 3, 4
597// MLog.DebugLevel: 0, 1, 2, 3, 4
598// MLog.NoColors
599//
600// Depending on your setup it might be correct to use something like:
601// Job1.MLog.VerbosityLevel: 1
602// Job1.DebugLevel: 2
603// Job1.MLog.NoColors
604//
605void MLog::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
606{
607 MParContainer mlog("MLog");
608
609 if (mlog.IsEnvDefined(env, prefix+"MLog", "VerbosityLevel", print))
610 SetDebugLevel(mlog.GetEnvValue(env, prefix+"MLog", "VerbosityLevel", 2));
611 else
612 if (mlog.IsEnvDefined(env, "MLog", "VerbosityLevel", print))
613 SetDebugLevel(mlog.GetEnvValue(env, "MLog", "VerbosityLevel", 2));
614
615 if (mlog.IsEnvDefined(env, prefix+"MLog", "DebugLevel", print))
616 gDebug = mlog.GetEnvValue(env, prefix+"MLog", "DebugLevel", 0);
617 else
618 if (mlog.IsEnvDefined(env, "MLog", "DebugLevel", print))
619 gDebug = mlog.GetEnvValue(env, "MLog", "DebugLevel", 0);
620
621 if (mlog.IsEnvDefined(env, prefix+"MLog", "NoColors", print))
622 SetNoColors(mlog.GetEnvValue(env, prefix+"MLog", "NoColors", kFALSE));
623 else
624 if (mlog.IsEnvDefined(env, "MLog", "NoColors", print))
625 SetNoColors(mlog.GetEnvValue(env, "MLog", "NoColors", kFALSE));
626}
627
628// --------------------------------------------------------------------------
629//
630// Read the setup from a TEnv:
631// MLog.VerbosityLevel: 0, 1, 2, 3, 4
632// MLog.DebugLevel: 0, 1, 2, 3, 4
633// MLog.NoColors
634//
635// Depending on your setup it might be correct to use something like:
636// Job1.MLog.VerbosityLevel: 1
637// Job1.DebugLevel: 2
638// Job1.MLog.NoColors
639//
640void MLog::WriteEnv(TEnv &, TString prefix, Bool_t) const
641{
642 if (!prefix.IsNull())
643 prefix += ".";
644 prefix += "MLog";
645
646 cout << "MLog::WriteEnv: not yet implemented!" << endl;
647}
648
649// --------------------------------------------------------------------------
650//
651// Create a new instance of an file output stream
652// an set the corresponding flag
653//
654void MLog::AllocateFile(const char *fname)
655{
656 // gcc 3.2:
657 char *txt = (char*)"logXXXXXX";
658
659 TString n(fname ? fname : txt);
660 gSystem->ExpandPathName(n);
661 fOut = new ofstream(n.Data());
662 fOutAllocated = kTRUE;
663}
664
665// --------------------------------------------------------------------------
666//
667// if fout was allocated by this instance of MLooging
668// delete it.
669//
670void MLog::DeallocateFile()
671{
672 if (fOutAllocated)
673 delete fOut;
674}
675
676// --------------------------------------------------------------------------
677//
678// if necessary delete the old in stance of the file
679// output stream and create a new one
680//
681void MLog::ReallocateFile(const char *fname)
682{
683 DeallocateFile();
684 AllocateFile(fname);
685}
686
687// --------------------------------------------------------------------------
688//
689// This function checks if a device should get enabled or disabled.
690//
691void MLog::CheckFlag(Flags_t chk, int flag)
692{
693 if (flag==-1)
694 return;
695
696 flag ? EnableOutputDevice(chk) : DisableOutputDevice(chk);
697}
698
699// --------------------------------------------------------------------------
700//
701// Add a plugin to which the output should be redirected, eg. MLogHtml
702// The user has to take care of its deletion. If the plugin is deleted
703// (and the kMustCleanup bit was not reset accidentaly) the plugin
704// is automatically removed from the list of active plugins.
705//
706// If MLog should take the ownership call plug->SetBit(kCanDelete);
707//
708void MLog::AddPlugin(MLogPlugin *plug)
709{
710 fPlugins->Add(plug);
711
712 // Make sure that it is recursively deleted from all objects in ListOfCleanups
713 plug->SetBit(kMustCleanup);
714}
Note: See TracBrowser for help on using the repository browser.