source: box/trunk/lib/common/Logging.cpp @ 3101

Revision 3101, 9.8 KB checked in by chris, 4 weeks ago (diff)

Allow hiding specific exceptions to keep test output cleaner.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    Logging.cpp
5//              Purpose: Generic logging core routines implementation
6//              Created: 2006/12/16
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <errno.h>
13#include <time.h>
14#include <string.h> // for stderror
15
16// c.f. http://bugs.debian.org/512510
17#include <cstdio>
18
19#ifdef HAVE_SYSLOG_H
20        #include <syslog.h>
21#endif
22#ifdef HAVE_UNISTD_H
23        #include <unistd.h>
24#endif
25#ifdef WIN32
26        #include <process.h>
27#endif
28
29#include <cstring>
30#include <iomanip>
31
32#include "BoxTime.h"
33#include "Logging.h"
34
35bool Logging::sLogToSyslog  = false;
36bool Logging::sLogToConsole = false;
37bool Logging::sContextSet   = false;
38
39bool HideExceptionMessageGuard::sHiddenState = false;
40
41std::vector<Logger*> Logging::sLoggers;
42std::string Logging::sContext;
43Console*    Logging::spConsole = NULL;
44Syslog*     Logging::spSyslog  = NULL;
45Log::Level  Logging::sGlobalLevel = Log::EVERYTHING;
46Logging     Logging::sGlobalLogging; //automatic initialisation
47std::string Logging::sProgramName;
48
49HideSpecificExceptionGuard::SuppressedExceptions_t
50        HideSpecificExceptionGuard::sSuppressedExceptions;
51
52int Logging::Guard::sGuardCount = 0;
53Log::Level Logging::Guard::sOriginalLevel = Log::INVALID;
54
55Logging::Logging()
56{
57        ASSERT(!spConsole);
58        ASSERT(!spSyslog);
59        spConsole = new Console();
60        spSyslog  = new Syslog();
61        sLogToConsole = true;
62        sLogToSyslog  = true;
63}
64
65Logging::~Logging()
66{
67        sLogToConsole = false;
68        sLogToSyslog  = false;
69        delete spConsole;
70        delete spSyslog;
71        spConsole = NULL;
72        spSyslog  = NULL;
73}
74
75void Logging::ToSyslog(bool enabled)
76{
77        if (!sLogToSyslog && enabled)
78        {
79                Add(spSyslog);
80        }
81       
82        if (sLogToSyslog && !enabled)
83        {
84                Remove(spSyslog);
85        }
86       
87        sLogToSyslog = enabled;
88}
89
90void Logging::ToConsole(bool enabled)
91{
92        if (!sLogToConsole && enabled)
93        {
94                Add(spConsole);
95        }
96       
97        if (sLogToConsole && !enabled)
98        {
99                Remove(spConsole);
100        }
101       
102        sLogToConsole = enabled;
103}
104
105void Logging::FilterConsole(Log::Level level)
106{
107        spConsole->Filter(level);
108}
109
110void Logging::FilterSyslog(Log::Level level)
111{
112        spSyslog->Filter(level);
113}
114
115void Logging::Add(Logger* pNewLogger)
116{
117        for (std::vector<Logger*>::iterator i = sLoggers.begin();
118                i != sLoggers.end(); i++)
119        {
120                if (*i == pNewLogger)
121                {
122                        return;
123                }
124        }
125       
126        sLoggers.insert(sLoggers.begin(), pNewLogger);
127}
128
129void Logging::Remove(Logger* pOldLogger)
130{
131        for (std::vector<Logger*>::iterator i = sLoggers.begin();
132                i != sLoggers.end(); i++)
133        {
134                if (*i == pOldLogger)
135                {
136                        sLoggers.erase(i);
137                        return;
138                }
139        }
140}
141
142void Logging::Log(Log::Level level, const std::string& rFile, 
143        int line, const std::string& rMessage)
144{
145        if (level > sGlobalLevel)
146        {
147                return;
148        }
149
150        std::string newMessage;
151       
152        if (sContextSet)
153        {
154                newMessage += "[" + sContext + "] ";
155        }
156       
157        newMessage += rMessage;
158       
159        for (std::vector<Logger*>::iterator i = sLoggers.begin();
160                i != sLoggers.end(); i++)
161        {
162                bool result = (*i)->Log(level, rFile, line, newMessage);
163                if (!result)
164                {
165                        return;
166                }
167        }
168}
169
170void Logging::LogToSyslog(Log::Level level, const std::string& rFile, 
171        int line, const std::string& rMessage)
172{
173        if (!sLogToSyslog)
174        {
175                return;
176        }
177
178        if (level > sGlobalLevel)
179        {
180                return;
181        }
182
183        std::string newMessage;
184       
185        if (sContextSet)
186        {
187                newMessage += "[" + sContext + "] ";
188        }
189       
190        newMessage += rMessage;
191
192        spSyslog->Log(level, rFile, line, newMessage);
193}
194
195void Logging::SetContext(std::string context)
196{
197        sContext = context;
198        sContextSet = true;
199}
200
201Log::Level Logging::GetNamedLevel(const std::string& rName)
202{
203        if      (rName == "nothing") { return Log::NOTHING; }
204        else if (rName == "fatal")   { return Log::FATAL; }
205        else if (rName == "error")   { return Log::ERROR; }
206        else if (rName == "warning") { return Log::WARNING; }
207        else if (rName == "notice")  { return Log::NOTICE; }
208        else if (rName == "info")    { return Log::INFO; }
209        else if (rName == "trace")   { return Log::TRACE; }
210        else if (rName == "everything") { return Log::EVERYTHING; }
211        else
212        {
213                BOX_ERROR("Unknown verbosity level: " << rName);
214                return Log::INVALID;
215        }
216}
217
218void Logging::ClearContext()
219{
220        sContextSet = false;
221}
222
223void Logging::SetProgramName(const std::string& rProgramName)
224{
225        sProgramName = rProgramName;
226
227        for (std::vector<Logger*>::iterator i = sLoggers.begin();
228                i != sLoggers.end(); i++)
229        {
230                (*i)->SetProgramName(rProgramName);
231        }
232}
233
234void Logging::SetFacility(int facility)
235{
236        spSyslog->SetFacility(facility);
237}
238
239Logger::Logger() 
240: mCurrentLevel(Log::EVERYTHING) 
241{
242        Logging::Add(this);
243}
244
245Logger::Logger(Log::Level Level) 
246: mCurrentLevel(Level) 
247{
248        Logging::Add(this);
249}
250
251Logger::~Logger() 
252{
253        Logging::Remove(this);
254}
255
256bool Console::sShowTime = false;
257bool Console::sShowTimeMicros = false;
258bool Console::sShowTag = false;
259bool Console::sShowPID = false;
260std::string Console::sTag;
261
262void Console::SetProgramName(const std::string& rProgramName)
263{
264        sTag = rProgramName;
265}
266
267void Console::SetShowTag(bool enabled)
268{
269        sShowTag = enabled;
270}
271
272void Console::SetShowTime(bool enabled)
273{
274        sShowTime = enabled;
275}
276
277void Console::SetShowTimeMicros(bool enabled)
278{
279        sShowTimeMicros = enabled;
280}
281
282void Console::SetShowPID(bool enabled)
283{
284        sShowPID = enabled;
285}
286
287bool Console::Log(Log::Level level, const std::string& rFile, 
288        int line, std::string& rMessage)
289{
290        if (level > GetLevel())
291        {
292                return true;
293        }
294       
295        FILE* target = stdout;
296       
297        if (level <= Log::WARNING)
298        {
299                target = stderr;
300        }
301
302        std::ostringstream buf;
303
304        if (sShowTime)
305        {
306                buf << FormatTime(GetCurrentBoxTime(), false, sShowTimeMicros);
307                buf << " ";
308        }
309
310        if (sShowTag)
311        {
312                if (sShowPID)
313                {
314                        buf << "[" << sTag << " " << getpid() << "] ";
315                }
316                else
317                {
318                        buf << "[" << sTag << "] ";
319                }
320        }
321        else if (sShowPID)
322        {
323                buf << "[" << getpid() << "] ";
324        }
325
326        if (level <= Log::FATAL)
327        {
328                buf << "FATAL:   ";
329        }
330        else if (level <= Log::ERROR)
331        {
332                buf << "ERROR:   ";
333        }
334        else if (level <= Log::WARNING)
335        {
336                buf << "WARNING: ";
337        }
338        else if (level <= Log::NOTICE)
339        {
340                buf << "NOTICE:  ";
341        }
342        else if (level <= Log::INFO)
343        {
344                buf << "INFO:    ";
345        }
346        else if (level <= Log::TRACE)
347        {
348                buf << "TRACE:   ";
349        }
350
351        buf << rMessage;
352
353        #ifdef WIN32
354                std::string output = buf.str();
355                if(ConvertUtf8ToConsole(output.c_str(), output) == false)
356                {
357                        fprintf(target, "%s (and failed to convert to console encoding)\n",
358                                output.c_str());
359                }
360                else
361                {
362                        fprintf(target, "%s\n", output.c_str());
363                }
364        #else
365                fprintf(target, "%s\n", buf.str().c_str());
366        #endif
367       
368        return true;
369}
370
371bool Syslog::Log(Log::Level level, const std::string& rFile, 
372        int line, std::string& rMessage)
373{
374        if (level > GetLevel())
375        {
376                return true;
377        }
378       
379        int syslogLevel = LOG_ERR;
380       
381        switch(level)
382        {
383                case Log::NOTHING:    /* fall through */
384                case Log::INVALID:    /* fall through */
385                case Log::FATAL:      syslogLevel = LOG_CRIT;    break;
386                case Log::ERROR:      syslogLevel = LOG_ERR;     break;
387                case Log::WARNING:    syslogLevel = LOG_WARNING; break;
388                case Log::NOTICE:     syslogLevel = LOG_NOTICE;  break;
389                case Log::INFO:       syslogLevel = LOG_INFO;    break;
390                case Log::TRACE:      /* fall through */
391                case Log::EVERYTHING: syslogLevel = LOG_DEBUG;   break;
392        }
393
394        std::string msg;
395
396        if (level <= Log::FATAL)
397        {
398                msg = "FATAL: ";
399        }
400        else if (level <= Log::ERROR)
401        {
402                msg = "ERROR: ";
403        }
404        else if (level <= Log::WARNING)
405        {
406                msg = "WARNING: ";
407        }
408        else if (level <= Log::NOTICE)
409        {
410                msg = "NOTICE: ";
411        }
412
413        msg += rMessage;
414
415        syslog(syslogLevel, "%s", msg.c_str());
416       
417        return true;
418}
419
420Syslog::Syslog() : mFacility(LOG_LOCAL6)
421{
422        ::openlog("Box Backup", LOG_PID, mFacility);
423}
424
425Syslog::~Syslog()
426{
427        ::closelog();
428}
429
430void Syslog::SetProgramName(const std::string& rProgramName)
431{
432        mName = rProgramName;
433        ::closelog();
434        ::openlog(mName.c_str(), LOG_PID, mFacility);
435}
436
437void Syslog::SetFacility(int facility)
438{
439        mFacility = facility;
440        ::closelog();
441        ::openlog(mName.c_str(), LOG_PID, mFacility);
442}
443
444int Syslog::GetNamedFacility(const std::string& rFacility)
445{
446        #define CASE_RETURN(x) if (rFacility == #x) { return LOG_ ## x; }
447        CASE_RETURN(LOCAL0)
448        CASE_RETURN(LOCAL1)
449        CASE_RETURN(LOCAL2)
450        CASE_RETURN(LOCAL3)
451        CASE_RETURN(LOCAL4)
452        CASE_RETURN(LOCAL5)
453        CASE_RETURN(LOCAL6)
454        CASE_RETURN(DAEMON)
455        #undef CASE_RETURN
456
457        BOX_ERROR("Unknown log facility '" << rFacility << "', "
458                "using default LOCAL6");
459        return LOG_LOCAL6;
460}
461
462bool FileLogger::Log(Log::Level Level, const std::string& rFile, 
463        int line, std::string& rMessage)
464{
465        if (mLogFile.StreamClosed())
466        {
467                /* skip this logger to allow logging failure to open
468                the log file, without causing an infinite loop */
469                return true;
470        }
471
472        if (Level > GetLevel())
473        {
474                return true;
475        }
476       
477        /* avoid infinite loop if this throws an exception */
478        Log::Level oldLevel = GetLevel();
479        Filter(Log::NOTHING);
480
481        std::ostringstream buf;
482        buf << FormatTime(GetCurrentBoxTime(), true, false);
483        buf << " ";
484
485        if (Level <= Log::FATAL)
486        {
487                buf << "[FATAL]   ";
488        }
489        else if (Level <= Log::ERROR)
490        {
491                buf << "[ERROR]   ";
492        }
493        else if (Level <= Log::WARNING)
494        {
495                buf << "[WARNING] ";
496        }
497        else if (Level <= Log::NOTICE)
498        {
499                buf << "[NOTICE]  ";
500        }
501        else if (Level <= Log::INFO)
502        {
503                buf << "[INFO]    ";
504        }
505        else if (Level <= Log::TRACE)
506        {
507                buf << "[TRACE]   ";
508        }
509
510        buf << rMessage << "\n";
511        std::string output = buf.str();
512
513        #ifdef WIN32
514                ConvertUtf8ToConsole(output.c_str(), output);
515        #endif
516
517        mLogFile.Write(output.c_str(), output.length());
518
519        // no infinite loop, reset to saved logging level
520        Filter(oldLevel);
521        return true;
522}
523
524std::string PrintEscapedBinaryData(const std::string& rInput)
525{
526        std::ostringstream output;
527
528        for (size_t i = 0; i < rInput.length(); i++)
529        {
530                if (isprint(rInput[i]))
531                {
532                        output << rInput[i];
533                }
534                else
535                {
536                        output << "\\x" << std::hex << std::setw(2) <<
537                                std::setfill('0') << (int) rInput[i] <<
538                                std::dec;
539                }
540        }
541
542        return output.str();
543}
544
545bool HideSpecificExceptionGuard::IsHidden(int type, int subtype)
546{
547        for (SuppressedExceptions_t::iterator
548                i  = sSuppressedExceptions.begin();
549                i != sSuppressedExceptions.end(); i++)
550        {
551                if(i->first == type && i->second == subtype)
552                {
553                        return true;
554                }
555        }
556        return false;
557}
558
Note: See TracBrowser for help on using the repository browser.