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

Last change on this file since 4693 was 4601, checked in by tbretz, 20 years ago
*** empty log message ***
File size: 18.3 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-2004
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 "MLogPlugin.h"
110#include "MParContainer.h"
111#include "MArgs.h"
112
113ClassImp(MLog);
114
115using namespace std;
116
117// root 3.02:
118// check for TObjectWarning, TObject::Info, gErrorIgnoreLevel
119
120const char MLog::kESC = '\033'; // (char)27
121const char *const MLog::kEsc = "\033[";
122const char *const MLog::kReset = "\033[0m";
123const char *const MLog::kRed = "\033[31m";
124const char *const MLog::kGreen = "\033[32m";
125#ifdef HAVE_DARKBACKGROUND
126const char *const MLog::kYellow = "\033[33m\033[1m";
127#else
128const char *const MLog::kYellow = "\033[33m";
129#endif
130const char *const MLog::kBlue = "\033[34m";
131const char *const MLog::kUnderline = "\033[4m";;
132const char *const MLog::kBlink = "\033[5m";;
133const char *const MLog::kBright = "\033[1m";;
134const char *const MLog::kDark = "\033[2m";;
135
136//
137// This is the definition of the global log facility
138//
139MLog gLog;
140
141// --------------------------------------------------------------------------
142//
143// this strange usage of an unbufferd buffer is a workaround
144// to make it work on Alpha and Linux!
145//
146void MLog::Init()
147{
148
149 //
150 // Creat drawing semaphore
151 //
152#ifdef _REENTRANT
153 fMuxGui = new TMutex;
154 fMuxStream = new TMutex;
155#endif
156
157 fPlugins = new TList;
158 gROOT->GetListOfCleanups()->Add(fPlugins);
159 fPlugins->SetBit(kMustCleanup);
160
161 setp(&fBuffer, &fBuffer+1);
162 *this << '\0';
163}
164
165// --------------------------------------------------------------------------
166//
167// default constructor which initializes the streamer and sets the device
168// which is used for the output (i)
169//
170MLog::MLog(int i) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(i), fIsNull(kFALSE), fOut(NULL), fOutAllocated(kFALSE), fGui(NULL), fNumLines(0)
171{
172 Init();
173}
174
175// --------------------------------------------------------------------------
176//
177// default constructor which initializes the streamer and sets the given
178// ofstream as the default output device
179//
180MLog::MLog(ofstream &out) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eFile), fIsNull(kFALSE), fOut(&out), fOutAllocated(kFALSE), fGui(NULL), fNumLines(0)
181{
182 Init();
183}
184
185// --------------------------------------------------------------------------
186//
187// default constructor which initializes the streamer and sets the given
188// TGTextView as the default output device
189//
190MLog::MLog(TGTextView &out) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eGui), fOut(NULL), fOutAllocated(kFALSE), fGui(&out), fNumLines(0)
191{
192 Init();
193}
194
195// --------------------------------------------------------------------------
196//
197// default constructor which initializes the streamer and opens a file with
198// the given name. Dependend on the flag the file is set as output device
199// or not.
200//
201MLog::MLog(const char *fname, int flag) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eFile), fIsNull(kFALSE), fGui(NULL), fNumLines(0)
202{
203 Init();
204
205 AllocateFile(fname);
206 CheckFlag(eFile, flag);
207}
208
209// --------------------------------------------------------------------------
210//
211// Destructor, destroying the gui mutex.
212//
213MLog::~MLog()
214{
215 DeallocateFile();
216
217 delete fPlugins;
218#ifdef _REENTRANT
219 delete fMuxStream;
220 delete fMuxGui;
221#endif
222}
223
224// --------------------------------------------------------------------------
225//
226// copyt constructor
227//
228/*
229MLog::MLog(MLog const& log)
230{
231// fOutputLevel = log.fOutputLevel;
232// fDebugLevel = log.fDebugLevel;
233// fDevice = log.fDevice;
234}
235*/
236
237void MLog::Underline()
238{
239 SetBit(kIsUnderlined);
240
241 fPlugins->ForEach(MLogPlugin, Underline)();
242
243 if (TestBit(eNoColors))
244 return;
245
246 if (fDevice&eStdout)
247 cout << kUnderline;
248
249 if (fDevice&eStderr)
250 cerr << kUnderline;
251}
252
253void MLog::Output(ostream &out, int len)
254{
255 if (!TestBit(eNoColors))
256 switch (fOutputLevel)
257 {
258 // do not output reset. Otherwise we reset underline in 0-mode
259 // case 1: out << MLog::kReset; break; // all
260 case 0: break; // all = background color
261 case 1: out << MLog::kRed; break; // err
262 case 2: out << MLog::kYellow; break; // warn
263 case 3: out << MLog::kGreen; break; // inf
264 default: out << MLog::kBlue; break; // all others (dbg)
265 }
266
267 if (len>0)
268 {
269 // Check for EOL
270 const Int_t endline = fBase[len-1]=='\n' ? 1 : 0;
271 // output text to screen (without trailing '\n')
272 out << TString(fBase, len-endline);
273 // reset colors if working with colors
274 if (!TestBit(eNoColors))
275 out << kReset;
276 // output EOL of check found EOL
277 if (endline)
278 {
279 out << '\n';
280 // Check whether text was underlined
281 if (TestBit(kIsUnderlined) && TestBit(eNoColors))
282 {
283 out << setw(len-1) << setfill('-') << "" << "\n";
284 ResetBit(kIsUnderlined);
285 }
286 }
287 }
288 out.flush();
289}
290
291void MLog::AddGuiLine(const TString &line)
292{
293 // add a new TString* to the array of gui lines
294 TString **newstr = new TString*[fNumLines+1];
295 memcpy(newstr, fGuiLines, fNumLines*sizeof(TString*));
296 if (fNumLines>0)
297 delete fGuiLines;
298 fGuiLines = newstr;
299
300 // add Gui line as last line of array
301 fGuiLines[fNumLines++] = new TString(line);
302}
303
304// --------------------------------------------------------------------------
305//
306// This is the function which writes the stream physically to a device.
307// If you want to add a new device this must be done here.
308//
309void MLog::WriteBuffer()
310{
311 //
312 // restart writing to the buffer at its first char
313 //
314 const int len = fPPtr - fBase;
315
316 fPPtr = fBase;
317
318 if (fIsNull)
319 return;
320
321 if (fDevice&eStdout)
322 Output(cout, len);
323
324 if (fDevice&eStderr)
325 Output(cerr, len);
326
327 if (fDevice&eFile && fOut)
328 fOut->write(fBase, len);
329
330 fPlugins->ForEach(MLogPlugin, SetColor)(fOutputLevel);
331 fPlugins->ForEach(MLogPlugin, WriteBuffer)(fBase, len);
332
333 if (fDevice&eGui && fGui)
334 {
335 // check whether the current text was flushed or endl'ed
336 const Int_t endline = fBase[len-1]=='\n' ? 1 : 0;
337
338 // for the gui remove trailing characters ('\n' or '\0')
339 fBase[len-endline]='\0';
340
341 // add new text to line storage
342 fGuiLine += fBase;
343
344 if (endline)
345 {
346 AddGuiLine(fGuiLine);
347 fGuiLine = "";
348
349 // Check whether text should be underlined
350 if (endline && TestBit(kIsUnderlined))
351 {
352 AddGuiLine("");
353 fGuiLines[fNumLines-1]->Append('-', fGuiLines[fNumLines-2]->Length());
354 ResetBit(kIsUnderlined);
355 }
356 }
357 }
358}
359
360void MLog::UpdateGui()
361{
362 if (fNumLines==0)
363 return;
364
365 // lock mutex
366 if (!LockUpdate("UpdateGui"))
367 {
368 Warning("UpdateGui", "Execution skipped");
369 return;
370 }
371
372 TGText &txt=*fGui->GetText();
373
374 // copy lines to TGListBox
375 for (int i=0; i<fNumLines; i++)
376 {
377 // Replace all tabs by 7 white spaces
378 fGuiLines[i]->ReplaceAll("\t", " ");
379 txt.InsText(TGLongPosition(0, txt.RowCount()), *fGuiLines[i]);
380 delete fGuiLines[i];
381 }
382 delete fGuiLines;
383
384 fNumLines=0;
385
386 // cut text box top 1000 lines
387 // while (txt.RowCount()>1000)
388 // txt.DelLine(1);
389
390 // show last entry
391 fGui->Layout();
392 fGui->SetVsbPosition(txt.RowCount()-1);
393
394 // tell a main loop, that list box contents have changed
395 fGui->SetBit(kHasChanged);
396
397 // release mutex
398 UnLockUpdate("UpdateGui");
399}
400
401bool MLog::LockUpdate(const char *msg)
402{
403#ifdef _REENTRANT
404 if (fMuxGui->Lock()==13)
405 {
406 Info("LockUpdate", "%s - mutex is already locked by this thread\n", msg);
407 return false;
408 }
409 return true;
410#endif
411}
412
413bool MLog::UnLockUpdate(const char *msg)
414{
415#ifdef _REENTRANT
416 if (fMuxGui->UnLock()==13)
417 {
418 Info("UnLockUpdate", "%s - tried to unlock mutex locked by other thread\n", msg);
419 return false;
420 }
421 return true;
422#endif
423}
424
425bool MLog::Lock(const char *msg)
426{
427#ifdef _REENTRANT
428 if (fMuxStream->Lock()==13)
429 {
430 Error("Lock", "%s - mutex is already locked by this thread\n", msg);
431 return false;
432 }
433// while (fMuxStream->Lock()==13)
434// usleep(1);
435// {
436// Error("Lock", "%s - mutex is already locked by this thread\n", msg);
437// return false;
438// }
439 return true;
440#endif
441}
442
443bool MLog::UnLock(const char *msg)
444{
445#ifdef _REENTRANT
446 if (fMuxStream->UnLock()==13)
447 {
448 Error("UnLock", "%s - tried to unlock mutex locked by other thread\n", msg);
449 return false;
450 }
451 return true;
452#endif
453}
454
455// --------------------------------------------------------------------------
456//
457// This is called to flush the buffer of the streaming devices
458//
459int MLog::sync()
460{
461 if (!LockUpdate("sync"))
462 usleep(1);
463 WriteBuffer();
464 UnLockUpdate("sync");
465
466 if (fDevice&eStdout)
467 {
468 if (!TestBit(eNoColors))
469 cout << kReset;
470 cout.flush();
471 }
472
473 if (fDevice&eStderr)
474 cerr.flush();
475
476 if (fDevice&eFile && fOut)
477 fOut->flush();
478
479 return 0;
480}
481
482// --------------------------------------------------------------------------
483//
484// This function comes from streambuf and should
485// output the buffer to the device (flush, endl)
486// or handle a buffer overflow (too many chars)
487// If a real overflow happens i contains the next
488// chars which doesn't fit into the buffer anymore.
489// If the buffer is not really filled i is EOF(-1).
490//
491int MLog::overflow(int i) // i=EOF means not a real overflow
492{
493 //
494 // no output if
495 //
496 if (fOutputLevel <= fDebugLevel)
497 {
498 if (!LockUpdate("overflow"))
499 usleep(1);
500
501 *fPPtr++ = (char)i;
502
503 if (fPPtr == fEPtr)
504 WriteBuffer();
505
506 UnLockUpdate("overflow");
507 }
508
509 return 0;
510}
511
512// --------------------------------------------------------------------------
513//
514// Setup MLog and global debug output from command line arguments.
515//
516// gLog << " -v# Verbosity level # [default=2]" << endl;
517// gLog << " -a, --no-colors Do not use Ansii color codes" << endl;
518// gLog << " --debug[=n] Enable root debugging (Default: gDebug=1)" << endl;
519//
520void MLog::Setup(MArgs &arg)
521{
522 if (arg.HasOnlyAndRemove("--no-colors") || arg.HasOnlyAndRemove("-a"))
523 SetNoColors();
524
525 SetDebugLevel(arg.HasOption("-v") ? arg.GetIntAndRemove("-v") : 2);
526
527 gDebug = arg.HasOption("--debug=") ? arg.GetIntAndRemove("--debug=") : 0;
528 if (gDebug==0 && arg.HasOnlyAndRemove("--debug"))
529 gDebug=1;
530}
531
532// --------------------------------------------------------------------------
533//
534// Read the setup from a TEnv:
535// MLog.VerbosityLevel: 0, 1, 2, 3, 4
536// MLog.DebugLevel: 0, 1, 2, 3, 4
537// MLog.NoColors
538//
539// Depending on your setup it might be correct to use something like:
540// Job1.MLog.VerbosityLevel: 1
541// Job1.DebugLevel: 2
542// Job1.MLog.NoColors
543//
544void MLog::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
545{
546 MParContainer mlog("MLog");
547
548 if (mlog.IsEnvDefined(env, prefix+"MLog", "VerbosityLevel", print))
549 SetDebugLevel(mlog.GetEnvValue(env, prefix+"MLog", "VerbosityLevel", 2));
550 else
551 if (mlog.IsEnvDefined(env, "MLog", "VerbosityLevel", print))
552 SetDebugLevel(mlog.GetEnvValue(env, "MLog", "VerbosityLevel", 2));
553
554 if (mlog.IsEnvDefined(env, prefix+"MLog", "DebugLevel", print))
555 gDebug = mlog.GetEnvValue(env, prefix+"MLog", "DebugLevel", 0);
556 else
557 if (mlog.IsEnvDefined(env, "MLog", "DebugLevel", print))
558 gDebug = mlog.GetEnvValue(env, "MLog", "DebugLevel", 0);
559
560 if (mlog.IsEnvDefined(env, prefix+"MLog", "NoColors", print))
561 SetNoColors(mlog.GetEnvValue(env, prefix+"MLog", "NoColors", kFALSE));
562 else
563 if (mlog.IsEnvDefined(env, "MLog", "NoColors", print))
564 SetNoColors(mlog.GetEnvValue(env, "MLog", "NoColors", kFALSE));
565}
566
567// --------------------------------------------------------------------------
568//
569// Read the setup from a TEnv:
570// MLog.VerbosityLevel: 0, 1, 2, 3, 4
571// MLog.DebugLevel: 0, 1, 2, 3, 4
572// MLog.NoColors
573//
574// Depending on your setup it might be correct to use something like:
575// Job1.MLog.VerbosityLevel: 1
576// Job1.DebugLevel: 2
577// Job1.MLog.NoColors
578//
579void MLog::WriteEnv(TEnv &env, TString prefix, Bool_t print) const
580{
581 if (!prefix.IsNull())
582 prefix += ".";
583 prefix += "MLog";
584
585 cout << "MLog::WriteEnv: not yet implemented!" << endl;
586}
587
588// --------------------------------------------------------------------------
589//
590// Create a new instance of an file output stream
591// an set the corresponding flag
592//
593void MLog::AllocateFile(const char *fname)
594{
595 // gcc 3.2:
596 char *txt = (char*)"logXXXXXX";
597 fOut = fname ? new ofstream(fname) : new ofstream(/*mkstemp(*/txt/*)*/);
598 fOutAllocated = kTRUE;
599}
600
601// --------------------------------------------------------------------------
602//
603// if fout was allocated by this instance of MLooging
604// delete it.
605//
606void MLog::DeallocateFile()
607{
608 if (fOutAllocated)
609 delete fOut;
610}
611
612// --------------------------------------------------------------------------
613//
614// if necessary delete the old in stance of the file
615// output stream and create a new one
616//
617void MLog::ReallocateFile(const char *fname)
618{
619 DeallocateFile();
620 AllocateFile(fname);
621}
622
623// --------------------------------------------------------------------------
624//
625// This function checks if a device should get enabled or disabled.
626//
627void MLog::CheckFlag(Flags_t chk, int flag)
628{
629 if (flag==-1)
630 return;
631
632 flag ? EnableOutputDevice(chk) : DisableOutputDevice(chk);
633}
634
635// --------------------------------------------------------------------------
636//
637// Add a plugin to which the output should be redirected, eg. MLogHtml
638// The user has to take care of its deletion. If the plugin is deleted
639// (and the kMustCleanup bit was not reset accidentaly) the plugin
640// is automatically removed from the list of active plugins.
641//
642void MLog::AddPlugin(MLogPlugin *plug)
643{
644 fPlugins->Add(plug);
645 plug->SetBit(kMustCleanup);
646}
Note: See TracBrowser for help on using the repository browser.