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

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