1 // distribution boxbackup-0.11_trunk_2979 (svn version: 2979)
2 // Box Backup, http://www.boxbackup.org/
3 //
4 // Copyright (c) 2003-2010, Ben Summers and contributors.
5 // All rights reserved.
6 //
7 // Note that this project uses mixed licensing. Any file with this license
8 // attached, or where the code LICENSE-DUAL appears on the first line, falls
9 // under this license. See the file COPYING.txt for more information.
10 //
11 // This file is dual licensed. You may use and distribute it providing that you
12 // comply EITHER with the terms of the BSD license, OR the GPL license. It is
13 // not necessary to comply with both licenses, only one.
14 //
15 // The BSD license option follows:
16 //
17 // Redistribution and use in source and binary forms, with or without
18 // modification, are permitted provided that the following conditions are met:
19 //
20 // 1. Redistributions of source code must retain the above copyright
21 // notice, this list of conditions and the following disclaimer.
22 //
23 // 2. Redistributions in binary form must reproduce the above copyright
24 // notice, this list of conditions and the following disclaimer in the
25 // documentation and/or other materials provided with the distribution.
26 //
27 // 3. Neither the name of the Box Backup nor the names of its contributors may
28 // be used to endorse or promote products derived from this software without
29 // specific prior written permission.
30 //
31 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
35 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
40 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 //
42 // [http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29]
43 //
44 // The GPL license option follows:
45 //
46 // This program is free software; you can redistribute it and/or
47 // modify it under the terms of the GNU General Public License
48 // as published by the Free Software Foundation; either version 2
49 // of the License, or (at your option) any later version.
50 //
51 // This program is distributed in the hope that it will be useful,
52 // but WITHOUT ANY WARRANTY; without even the implied warranty of
53 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 // GNU General Public License for more details.
55 //
56 // You should have received a copy of the GNU General Public License
57 // along with this program; if not, write to the Free Software
58 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
59 //
60 // [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
61 // --------------------------------------------------------------------------
62 //
63 // File
64 // Name: Logging.cpp
65 // Purpose: Generic logging core routines implementation
66 // Created: 2006/12/16
67 //
68 // --------------------------------------------------------------------------
69
70 #include "Box.h"
71
72 #include <errno.h>
73 #include <time.h>
74 #include <string.h> // for stderror
75
76 // c.f. http://bugs.debian.org/512510
77 #include <cstdio>
78
79 #ifdef HAVE_SYSLOG_H
80 #include <syslog.h>
81 #endif
82 #ifdef HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
85 #ifdef WIN32
86 #include <process.h>
87 #endif
88
89 #include <cstring>
90 #include <iomanip>
91
92 #include "BoxTime.h"
93 #include "Logging.h"
94
95 bool Logging::sLogToSyslog = false;
96 bool Logging::sLogToConsole = false;
97 bool Logging::sContextSet = false;
98
99 bool HideExceptionMessageGuard::sHiddenState = false;
100
101 std::vector<Logger*> Logging::sLoggers;
102 std::string Logging::sContext;
103 Console* Logging::spConsole = NULL;
104 Syslog* Logging::spSyslog = NULL;
105 Log::Level Logging::sGlobalLevel = Log::EVERYTHING;
106 Logging Logging::sGlobalLogging; //automatic initialisation
107 std::string Logging::sProgramName;
108
Logging()109 Logging::Logging()
110 {
111 ASSERT(!spConsole);
112 ASSERT(!spSyslog);
113 spConsole = new Console();
114 spSyslog = new Syslog();
115 sLogToConsole = true;
116 sLogToSyslog = true;
117 }
118
~Logging()119 Logging::~Logging()
120 {
121 sLogToConsole = false;
122 sLogToSyslog = false;
123 delete spConsole;
124 delete spSyslog;
125 spConsole = NULL;
126 spSyslog = NULL;
127 }
128
ToSyslog(bool enabled)129 void Logging::ToSyslog(bool enabled)
130 {
131 if (!sLogToSyslog && enabled)
132 {
133 Add(spSyslog);
134 }
135
136 if (sLogToSyslog && !enabled)
137 {
138 Remove(spSyslog);
139 }
140
141 sLogToSyslog = enabled;
142 }
143
ToConsole(bool enabled)144 void Logging::ToConsole(bool enabled)
145 {
146 if (!sLogToConsole && enabled)
147 {
148 Add(spConsole);
149 }
150
151 if (sLogToConsole && !enabled)
152 {
153 Remove(spConsole);
154 }
155
156 sLogToConsole = enabled;
157 }
158
FilterConsole(Log::Level level)159 void Logging::FilterConsole(Log::Level level)
160 {
161 spConsole->Filter(level);
162 }
163
FilterSyslog(Log::Level level)164 void Logging::FilterSyslog(Log::Level level)
165 {
166 spSyslog->Filter(level);
167 }
168
Add(Logger * pNewLogger)169 void Logging::Add(Logger* pNewLogger)
170 {
171 for (std::vector<Logger*>::iterator i = sLoggers.begin();
172 i != sLoggers.end(); i++)
173 {
174 if (*i == pNewLogger)
175 {
176 return;
177 }
178 }
179
180 sLoggers.insert(sLoggers.begin(), pNewLogger);
181 }
182
Remove(Logger * pOldLogger)183 void Logging::Remove(Logger* pOldLogger)
184 {
185 for (std::vector<Logger*>::iterator i = sLoggers.begin();
186 i != sLoggers.end(); i++)
187 {
188 if (*i == pOldLogger)
189 {
190 sLoggers.erase(i);
191 return;
192 }
193 }
194 }
195
Log(Log::Level level,const std::string & rFile,int line,const std::string & rMessage)196 void Logging::Log(Log::Level level, const std::string& rFile,
197 int line, const std::string& rMessage)
198 {
199 if (level > sGlobalLevel)
200 {
201 return;
202 }
203
204 std::string newMessage;
205
206 if (sContextSet)
207 {
208 newMessage += "[" + sContext + "] ";
209 }
210
211 newMessage += rMessage;
212
213 for (std::vector<Logger*>::iterator i = sLoggers.begin();
214 i != sLoggers.end(); i++)
215 {
216 bool result = (*i)->Log(level, rFile, line, newMessage);
217 if (!result)
218 {
219 return;
220 }
221 }
222 }
223
LogToSyslog(Log::Level level,const std::string & rFile,int line,const std::string & rMessage)224 void Logging::LogToSyslog(Log::Level level, const std::string& rFile,
225 int line, const std::string& rMessage)
226 {
227 if (!sLogToSyslog)
228 {
229 return;
230 }
231
232 if (level > sGlobalLevel)
233 {
234 return;
235 }
236
237 std::string newMessage;
238
239 if (sContextSet)
240 {
241 newMessage += "[" + sContext + "] ";
242 }
243
244 newMessage += rMessage;
245
246 spSyslog->Log(level, rFile, line, newMessage);
247 }
248
SetContext(std::string context)249 void Logging::SetContext(std::string context)
250 {
251 sContext = context;
252 sContextSet = true;
253 }
254
GetNamedLevel(const std::string & rName)255 Log::Level Logging::GetNamedLevel(const std::string& rName)
256 {
257 if (rName == "nothing") { return Log::NOTHING; }
258 else if (rName == "fatal") { return Log::FATAL; }
259 else if (rName == "error") { return Log::ERROR; }
260 else if (rName == "warning") { return Log::WARNING; }
261 else if (rName == "notice") { return Log::NOTICE; }
262 else if (rName == "info") { return Log::INFO; }
263 else if (rName == "trace") { return Log::TRACE; }
264 else if (rName == "everything") { return Log::EVERYTHING; }
265 else
266 {
267 BOX_ERROR("Unknown verbosity level: " << rName);
268 return Log::INVALID;
269 }
270 }
271
ClearContext()272 void Logging::ClearContext()
273 {
274 sContextSet = false;
275 }
276
SetProgramName(const std::string & rProgramName)277 void Logging::SetProgramName(const std::string& rProgramName)
278 {
279 sProgramName = rProgramName;
280
281 for (std::vector<Logger*>::iterator i = sLoggers.begin();
282 i != sLoggers.end(); i++)
283 {
284 (*i)->SetProgramName(rProgramName);
285 }
286 }
287
SetFacility(int facility)288 void Logging::SetFacility(int facility)
289 {
290 spSyslog->SetFacility(facility);
291 }
292
Logger()293 Logger::Logger()
294 : mCurrentLevel(Log::EVERYTHING)
295 {
296 Logging::Add(this);
297 }
298
Logger(Log::Level Level)299 Logger::Logger(Log::Level Level)
300 : mCurrentLevel(Level)
301 {
302 Logging::Add(this);
303 }
304
~Logger()305 Logger::~Logger()
306 {
307 Logging::Remove(this);
308 }
309
310 bool Console::sShowTime = false;
311 bool Console::sShowTimeMicros = false;
312 bool Console::sShowTag = false;
313 bool Console::sShowPID = false;
314 std::string Console::sTag;
315
SetProgramName(const std::string & rProgramName)316 void Console::SetProgramName(const std::string& rProgramName)
317 {
318 sTag = rProgramName;
319 }
320
SetShowTag(bool enabled)321 void Console::SetShowTag(bool enabled)
322 {
323 sShowTag = enabled;
324 }
325
SetShowTime(bool enabled)326 void Console::SetShowTime(bool enabled)
327 {
328 sShowTime = enabled;
329 }
330
SetShowTimeMicros(bool enabled)331 void Console::SetShowTimeMicros(bool enabled)
332 {
333 sShowTimeMicros = enabled;
334 }
335
SetShowPID(bool enabled)336 void Console::SetShowPID(bool enabled)
337 {
338 sShowPID = enabled;
339 }
340
Log(Log::Level level,const std::string & rFile,int line,std::string & rMessage)341 bool Console::Log(Log::Level level, const std::string& rFile,
342 int line, std::string& rMessage)
343 {
344 if (level > GetLevel())
345 {
346 return true;
347 }
348
349 FILE* target = stdout;
350
351 if (level <= Log::WARNING)
352 {
353 target = stderr;
354 }
355
356 std::ostringstream buf;
357
358 if (sShowTime)
359 {
360 buf << FormatTime(GetCurrentBoxTime(), false, sShowTimeMicros);
361 buf << " ";
362 }
363
364 if (sShowTag)
365 {
366 if (sShowPID)
367 {
368 buf << "[" << sTag << " " << getpid() << "] ";
369 }
370 else
371 {
372 buf << "[" << sTag << "] ";
373 }
374 }
375 else if (sShowPID)
376 {
377 buf << "[" << getpid() << "] ";
378 }
379
380 if (level <= Log::FATAL)
381 {
382 buf << "FATAL: ";
383 }
384 else if (level <= Log::ERROR)
385 {
386 buf << "ERROR: ";
387 }
388 else if (level <= Log::WARNING)
389 {
390 buf << "WARNING: ";
391 }
392 else if (level <= Log::NOTICE)
393 {
394 buf << "NOTICE: ";
395 }
396 else if (level <= Log::INFO)
397 {
398 buf << "INFO: ";
399 }
400 else if (level <= Log::TRACE)
401 {
402 buf << "TRACE: ";
403 }
404
405 buf << rMessage;
406
407 #ifdef WIN32
408 std::string output = buf.str();
409 ConvertUtf8ToConsole(output.c_str(), output);
410 fprintf(target, "%s\n", output.c_str());
411 #else
412 fprintf(target, "%s\n", buf.str().c_str());
413 #endif
414
415 return true;
416 }
417
Log(Log::Level level,const std::string & rFile,int line,std::string & rMessage)418 bool Syslog::Log(Log::Level level, const std::string& rFile,
419 int line, std::string& rMessage)
420 {
421 if (level > GetLevel())
422 {
423 return true;
424 }
425
426 int syslogLevel = LOG_ERR;
427
428 switch(level)
429 {
430 case Log::NOTHING: /* fall through */
431 case Log::INVALID: /* fall through */
432 case Log::FATAL: syslogLevel = LOG_CRIT; break;
433 case Log::ERROR: syslogLevel = LOG_ERR; break;
434 case Log::WARNING: syslogLevel = LOG_WARNING; break;
435 case Log::NOTICE: syslogLevel = LOG_NOTICE; break;
436 case Log::INFO: syslogLevel = LOG_INFO; break;
437 case Log::TRACE: /* fall through */
438 case Log::EVERYTHING: syslogLevel = LOG_DEBUG; break;
439 }
440
441 std::string msg;
442
443 if (level <= Log::FATAL)
444 {
445 msg = "FATAL: ";
446 }
447 else if (level <= Log::ERROR)
448 {
449 msg = "ERROR: ";
450 }
451 else if (level <= Log::WARNING)
452 {
453 msg = "WARNING: ";
454 }
455 else if (level <= Log::NOTICE)
456 {
457 msg = "NOTICE: ";
458 }
459
460 msg += rMessage;
461
462 syslog(syslogLevel, "%s", msg.c_str());
463
464 return true;
465 }
466
Syslog()467 Syslog::Syslog() : mFacility(LOG_LOCAL6)
468 {
469 ::openlog("Box Backup", LOG_PID, mFacility);
470 }
471
~Syslog()472 Syslog::~Syslog()
473 {
474 ::closelog();
475 }
476
SetProgramName(const std::string & rProgramName)477 void Syslog::SetProgramName(const std::string& rProgramName)
478 {
479 mName = rProgramName;
480 ::closelog();
481 ::openlog(mName.c_str(), LOG_PID, mFacility);
482 }
483
SetFacility(int facility)484 void Syslog::SetFacility(int facility)
485 {
486 mFacility = facility;
487 ::closelog();
488 ::openlog(mName.c_str(), LOG_PID, mFacility);
489 }
490
GetNamedFacility(const std::string & rFacility)491 int Syslog::GetNamedFacility(const std::string& rFacility)
492 {
493 #define CASE_RETURN(x) if (rFacility == #x) { return LOG_ ## x; }
494 CASE_RETURN(LOCAL0)
495 CASE_RETURN(LOCAL1)
496 CASE_RETURN(LOCAL2)
497 CASE_RETURN(LOCAL3)
498 CASE_RETURN(LOCAL4)
499 CASE_RETURN(LOCAL5)
500 CASE_RETURN(LOCAL6)
501 CASE_RETURN(DAEMON)
502 #undef CASE_RETURN
503
504 BOX_ERROR("Unknown log facility '" << rFacility << "', "
505 "using default LOCAL6");
506 return LOG_LOCAL6;
507 }
508
Log(Log::Level Level,const std::string & rFile,int line,std::string & rMessage)509 bool FileLogger::Log(Log::Level Level, const std::string& rFile,
510 int line, std::string& rMessage)
511 {
512 if (mLogFile.StreamClosed())
513 {
514 /* skip this logger to allow logging failure to open
515 the log file, without causing an infinite loop */
516 return true;
517 }
518
519 if (Level > GetLevel())
520 {
521 return true;
522 }
523
524 /* avoid infinite loop if this throws an exception */
525 Logging::Remove(this);
526
527 std::ostringstream buf;
528 buf << FormatTime(GetCurrentBoxTime(), true, false);
529 buf << " ";
530
531 if (Level <= Log::FATAL)
532 {
533 buf << "[FATAL] ";
534 }
535 else if (Level <= Log::ERROR)
536 {
537 buf << "[ERROR] ";
538 }
539 else if (Level <= Log::WARNING)
540 {
541 buf << "[WARNING] ";
542 }
543 else if (Level <= Log::NOTICE)
544 {
545 buf << "[NOTICE] ";
546 }
547 else if (Level <= Log::INFO)
548 {
549 buf << "[INFO] ";
550 }
551 else if (Level <= Log::TRACE)
552 {
553 buf << "[TRACE] ";
554 }
555
556 buf << rMessage << "\n";
557 std::string output = buf.str();
558
559 #ifdef WIN32
560 ConvertUtf8ToConsole(output.c_str(), output);
561 #endif
562
563 mLogFile.Write(output.c_str(), output.length());
564
565 Logging::Add(this);
566 return true;
567 }
568
PrintEscapedBinaryData(const std::string & rInput)569 std::string PrintEscapedBinaryData(const std::string& rInput)
570 {
571 std::ostringstream output;
572
573 for (size_t i = 0; i < rInput.length(); i++)
574 {
575 if (isprint(rInput[i]))
576 {
577 output << rInput[i];
578 }
579 else
580 {
581 output << "\\x" << std::hex << std::setw(2) <<
582 std::setfill('0') << (int) rInput[i] <<
583 std::dec;
584 }
585 }
586
587 return output.str();
588 }
589